From 7f11d815d906ffebf48c9e873279e0775ad98cf6 Mon Sep 17 00:00:00 2001 From: John Woodlock Date: Fri, 6 May 2022 10:03:27 +0100 Subject: [PATCH 01/11] FINERACT-1557 enable previous disabled refundByCash accounting tests --- .../service/LoanReadPlatformServiceImpl.java | 12 +++++------- .../integrationtests/ClientLoanIntegrationTest.java | 9 +++------ 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java index bf126f58d..c9504b4b2 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java @@ -2100,20 +2100,18 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { @Override public PaidInAdvanceData retrieveTotalPaidInAdvance(Long loanId) { - // TODO Auto-generated method stub try { - final String sql = " select (SUM(COALESCE(mr.principal_completed_derived, 0)) +" + final String sql = " select (SUM(COALESCE(mr.principal_completed_derived, 0))" + " + SUM(COALESCE(mr.interest_completed_derived, 0)) " + " + SUM(COALESCE(mr.fee_charges_completed_derived, 0)) " + " + SUM(COALESCE(mr.penalty_charges_completed_derived, 0))) as total_in_advance_derived " + " from m_loan ml INNER JOIN m_loan_repayment_schedule mr on mr.loan_id = ml.id " - + " where ml.id=? and mr.duedate >= ? group by ml.id having " + " (SUM(COALESCE(mr.principal_completed_derived, 0)) " - + " + SUM(COALESCE(mr.interest_completed_derived, 0)) " + " + SUM(COALESCE(mr.fee_charges_completed_derived, 0)) " + + " where ml.id=? and mr.duedate >= " + sqlGenerator.currentDate() + " group by ml.id having " + + " (SUM(COALESCE(mr.principal_completed_derived, 0)) " + " + SUM(COALESCE(mr.interest_completed_derived, 0)) " + + " + SUM(COALESCE(mr.fee_charges_completed_derived, 0)) " + "+ SUM(COALESCE(mr.penalty_charges_completed_derived, 0))) > 0"; - BigDecimal bigDecimal = this.jdbcTemplate.queryForObject(sql, BigDecimal.class, - new Object[] { loanId, sqlGenerator.currentDate() }); // NOSONAR + BigDecimal bigDecimal = this.jdbcTemplate.queryForObject(sql, BigDecimal.class, new Object[] { loanId }); // NOSONAR return new PaidInAdvanceData(bigDecimal); } catch (DataAccessException e) { - // TODO Auto-generated catch block return new PaidInAdvanceData(new BigDecimal(0)); } } diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanIntegrationTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanIntegrationTest.java index 965e84c9d..b5324799b 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanIntegrationTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanIntegrationTest.java @@ -4714,8 +4714,7 @@ public class ClientLoanIntegrationTest { /*** * Test case to verify default Style payment strategy */ - // @Test // TODO: @galovics failing since - // https://github.com/apache/fineract/commit/d5c611564f7b8c877136f3b3aca647bf7de15fe0 + @Test public void testLoanRefundByCashCashBasedAccounting() { this.journalEntryHelper = new JournalEntryHelper(this.requestSpec, this.responseSpec); @@ -4898,8 +4897,7 @@ public class ClientLoanIntegrationTest { /*** * Test case to verify Default style payment strategy */ - // @Test // TODO: @galovics failing since - // https://github.com/apache/fineract/commit/d5c611564f7b8c877136f3b3aca647bf7de15fe0 + @Test public void testLoanRefundByCashAccrualBasedAccounting() { this.journalEntryHelper = new JournalEntryHelper(this.requestSpec, this.responseSpec); @@ -5077,8 +5075,7 @@ public class ClientLoanIntegrationTest { } - // @Test // TODO: @galovics failing since - // https://github.com/apache/fineract/commit/d5c611564f7b8c877136f3b3aca647bf7de15fe0 + @Test public void testLoanRefundByTransferCashBasedAccounting() { this.journalEntryHelper = new JournalEntryHelper(this.requestSpec, this.responseSpec); this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec, this.responseSpec); From 6111e693292a66e754a266ee299c40453c89b7bc Mon Sep 17 00:00:00 2001 From: Aleksandar Vidakovic Date: Mon, 9 May 2022 17:36:14 +0200 Subject: [PATCH 02/11] FINERACT-1608: Add introduction to Asciidoc --- README.md | 2 +- fineract-client/build.gradle | 14 + fineract-doc/build.gradle | 6 +- .../documentation/asciidoc-cheatsheet.adoc | 398 ++++++++++++++++++ .../documentation/asciidoc-tutorial.adoc | 5 - .../en/chapters/documentation/asciidoc.adoc | 8 +- .../diagram-archimate-cheatsheet.adoc | 5 + .../documentation/diagram-c4-cheatsheet.adoc | 5 + .../diagram-plantuml-cheatsheet.adoc | 5 + .../diagram-vega-cheatsheet.adoc | 5 + .../en/chapters/documentation/diagram.adoc | 13 + .../docs/en/chapters/documentation/index.adoc | 6 +- .../en/chapters/documentation/layout.adoc | 25 +- .../documentation/plantuml-cheatsheet.adoc | 3 - .../documentation/plantuml-tutorial.adoc | 3 - .../en/chapters/documentation/plantuml.adoc | 9 - .../release/configuration-gradle.adoc | 2 +- .../src/docs/en/chapters/release/index.adoc | 2 +- .../en/chapters/release/process-step01.adoc | 6 +- .../en/chapters/release/process-step02.adoc | 6 +- .../en/chapters/release/process-step03.adoc | 10 +- .../en/chapters/release/process-step05.adoc | 4 +- .../en/chapters/release/process-step06.adoc | 4 +- .../en/chapters/release/process-step07.adoc | 2 +- .../en/chapters/release/process-step08.adoc | 4 +- .../en/chapters/release/process-step09.adoc | 2 +- .../en/chapters/release/process-step10.adoc | 6 +- .../en/chapters/release/process-step11.adoc | 6 +- .../en/chapters/release/process-step12.adoc | 4 +- .../en/chapters/release/process-step13.adoc | 4 +- .../en/chapters/release/process-step15.adoc | 8 +- .../src/docs/en/chapters/security/oauth.adoc | 2 +- .../docs/en/chapters/testing/cucumber.adoc | 1 + fineract-doc/src/docs/en/colophon.adoc | 1 + fineract-doc/src/docs/en/config.adoc | 4 +- fineract-doc/src/docs/en/index.adoc | 19 + fineract-provider/build.gradle | 4 +- .../swagger/fineract-input.yaml.template | 8 +- .../survey/api/SurveyApiResource.java | 3 +- .../accounts/api/AccountsApiResource.java | 2 +- .../api/SelfAuthenticationApiResource.java | 2 +- .../api/SelfUserDetailsApiResource.java | 2 +- .../ClasspathDuplicatesStepDefinitions.java | 6 + 43 files changed, 537 insertions(+), 99 deletions(-) delete mode 100644 fineract-doc/src/docs/en/chapters/documentation/asciidoc-tutorial.adoc create mode 100644 fineract-doc/src/docs/en/chapters/documentation/diagram-archimate-cheatsheet.adoc create mode 100644 fineract-doc/src/docs/en/chapters/documentation/diagram-c4-cheatsheet.adoc create mode 100644 fineract-doc/src/docs/en/chapters/documentation/diagram-plantuml-cheatsheet.adoc create mode 100644 fineract-doc/src/docs/en/chapters/documentation/diagram-vega-cheatsheet.adoc create mode 100644 fineract-doc/src/docs/en/chapters/documentation/diagram.adoc delete mode 100644 fineract-doc/src/docs/en/chapters/documentation/plantuml-cheatsheet.adoc delete mode 100644 fineract-doc/src/docs/en/chapters/documentation/plantuml-tutorial.adoc delete mode 100644 fineract-doc/src/docs/en/chapters/documentation/plantuml.adoc diff --git a/README.md b/README.md index ddfd35c2b..85738369f 100644 --- a/README.md +++ b/README.md @@ -338,7 +338,7 @@ complies with the [Apache Software Foundation third-party license policy](https: Apache Fineract Platform API ============ -The API for Fineract is documented in [apiLive.htm](fineract-provider/src/main/resources/static/api-docs/apiLive.htm), and the [apiLive.htm can be viewed on Fineract.dev](https://demo.fineract.dev/fineract-provider/legacy-docs/apiLive.htm "API Documentation"). If you have your own Fineract instance running, you can find this documentation under [/fineract-provider/api-docs/apiLive.htm](https://localhost:8443/fineract-provider/api-docs/apiLive.htm). +The API for Fineract is documented in [apiLive.htm](fineract-provider/src/main/resources/static/api-docs/apiLive.htm), and the [apiLive.htm can be viewed on Fineract.dev](https://fineract.apache.org/legacy-docs/apiLive.htm "API Documentation"). If you have your own Fineract instance running, you can find this documentation under [/fineract-provider/api-docs/apiLive.htm](https://localhost:8443/fineract-provider/api-docs/apiLive.htm). The Swagger documentation (work in progress; see [FINERACT-733](https://issues.apache.org/jira/browse/FINERACT-733)) can be accessed under [/fineract-provider/swagger-ui/index.html](https://localhost:8443/fineract-provider/swagger-ui/index.html) and [live Swagger UI here on Fineract.dev](https://demo.fineract.dev/fineract-provider/swagger-ui/index.html). diff --git a/fineract-client/build.gradle b/fineract-client/build.gradle index 717cb3652..2d9b508d9 100644 --- a/fineract-client/build.gradle +++ b/fineract-client/build.gradle @@ -93,6 +93,20 @@ task buildAsciidoc(type: org.openapitools.generator.gradle.plugin.tasks.Generate apiPackage = 'org.apache.fineract.client.services' invokerPackage = 'org.apache.fineract.client' modelPackage = 'org.apache.fineract.client.models' + configOptions = [ + appName: 'Apache Fineract REST API', + appDescription: '''Apache Fineract is a secure, multi-tenanted microfinance platform. + The goal of the Apache Fineract API is to empower developers to build apps on top of the Apache Fineract Platform. + The https://cui.fineract.dev[reference app] (username: mifos, password: password) works on the same demo tenant as the interactive links in this documentation. + Until we complete the new REST API documentation you still have the legacy documentation available https://fineract.apache.org/legacy-docs/apiLive.htm[here]. + Please check https://fineract.apache.org/docs/current[the Fineract documentation] for more information.''', + headerAttributes: 'false', + infoEmail: 'dev@fineract.apache.org', + infoUrl: 'https://fineract.apache.org', + licenseInfo: 'Apache 2.0', + licenseUrl: 'http://www.apache.org/licenses/LICENSE-2.0.html', + useMethodAndPath: 'true' + ] dependsOn(':fineract-provider:resolve') } diff --git a/fineract-doc/build.gradle b/fineract-doc/build.gradle index 8d5d6219a..f33e7a7a6 100644 --- a/fineract-doc/build.gradle +++ b/fineract-doc/build.gradle @@ -57,7 +57,7 @@ asciidoctor { } outputDir 'build/docs/html' - dependsOn ':fineract-client:buildAsciidoc' + dependsOn(':fineract-client:clean', ':fineract-client:buildAsciidoc') } // asciidoctorEpub { @@ -92,11 +92,11 @@ asciidoctorPdf { // } // } // fontsDir "$projectDir/src/resources/fonts" - dependsOn ':fineract-client:buildAsciidoc' + dependsOn(':fineract-client:clean', ':fineract-client:buildAsciidoc') } task doc(type: Zip) { - dependsOn asciidoctor, asciidoctorPdf + dependsOn(asciidoctor, asciidoctorPdf) into("fineract-${project.version}/html") { from("$buildDir/docs/html") { diff --git a/fineract-doc/src/docs/en/chapters/documentation/asciidoc-cheatsheet.adoc b/fineract-doc/src/docs/en/chapters/documentation/asciidoc-cheatsheet.adoc index 58c70e86e..bf68340e8 100644 --- a/fineract-doc/src/docs/en/chapters/documentation/asciidoc-cheatsheet.adoc +++ b/fineract-doc/src/docs/en/chapters/documentation/asciidoc-cheatsheet.adoc @@ -1,3 +1,401 @@ = Cheatsheet +You can find the definitive manual on AsciiDoc syntax at https://docs.asciidoctor.org/asciidoc/latest[AsciiDoc documentation]. To help people get started, however, here is a simpler cheat sheet. + +== AsciiDoc vs Asciidoctor (format vs tool) + +When we refer to _AsciiDoc_ then we mean the language or format that this documentation is written in. AsciiDoc is a markup language similar to Markdown (but more powerful and expressive) designed for technical documentation. You don't need necessarily any specialized editors or tools to write your documentation in AsciiDoc, a plain text editor will do, but there are plenty of choices that give you a better experience (in this documentation we describe the basic usage with AsciiDoc plugins for IntelliJ, Eclipse and VSCode). + +Asciidoctor on the other hand is the command line tool we use to transform documents written in AsciiDoc into HTML and PDF (Epub3 and Docbook are also available). There are three variants available: + +- Asciidoctor (written in Ruby) +- Asciidoctor.js (written in JavaScript, often used for browser previews) +- AsciidoctorJ (Java lib that integrates the Ruby implementation via JRuby, e. g. the Asciidoctor Gradle plugin is based on that) + +CAUTION: Sometimes you will still find documentation related to the original incarnation of AsciiDoc/tor (written in Python). The format evolved quite a bit since then and the tools try to maintain a certain degree of backward compatibility, but there is no guarantee. We prefer to use the latest language specs as documented https://docs.asciidoctor.org/asciidoc/latest[here]. + +== Basic AsciiDoc Syntax + +=== Bold + +Put asterisks around text to make it *bold*. + +NOTE: More info at https://docs.asciidoctor.org/asciidoc/latest/text/bold + + +=== Italics + +Use underlines on either side of a string to put text into _italics_. + +NOTE: More info at https://docs.asciidoctor.org/asciidoc/latest/text/italic + +=== Headings + +Equal signs (`=`) are used for heading levels. Each equal sign is a level. Each page can *only* have one top level (i.e., only one section with a single `=`). + +Levels should be appropriately nested. During the build, validation occurs to ensure that level 3s are preceded by level 2s, level 4s are preceded by level 3s, etc. Including out-of-sequence heading levels (such as a level 3 then a level 5) will not fail the build, but will produce an error. + +NOTE: More info at https://docs.asciidoctor.org/asciidoc/latest/sections/titles-and-levels/ + +== Code Examples + +Use backticks ``` for text that should be monospaced, such as code or a class name in the body of a paragraph. + +NOTE: More info at https://docs.asciidoctor.org/asciidoc/latest/text/monospace/ + +Longer code examples can be separated from text with `source` blocks. +These allow defining the syntax being used so the code is properly highlighted. + +.Example Source Block +[source,asciidoc] +---- +[source,xml] + +---- + +If your code block will include line breaks, put 4 hyphens (`----`) before and after the entire block. + +NOTE: More info at https://docs.asciidoctor.org/asciidoc/latest/verbatim/source-blocks/ + +=== Source Block Syntax Highlighting + +The HTML output uses Rouge to add syntax highlighting to code examples. This is done by adding the language of the code block after the `source`, as shown in the above example source block (`xml` in that case). + +Rouge has a long selection of lexers available. You can see the full list at https://github.com/rouge-ruby/rouge/wiki/List-of-supported-languages-and-lexers. Use one of the valid short names to get syntax highlighting for that language. + +Ideally, we will have an appropriate lexer to use for all source blocks, but that's not possible. +When in doubt, choose `text`, or leave it blank. + +=== Importing Code Snippets from Other Files + +The build system has the ability to "include" snippets located in other files -- even non-AsciiDoc files such as `*.java` source code files. + +We've configured a global attribute called `\{rootdir}` that you can use to reference these files consistently from Fineract's project root folder. + +Snippets are bounded by _tag_ comments placed at the start and end of the section you would like to import. Opening tags look like: `// tag::snippetName[]`. Closing tags follow the format: `// end::snippetName[]`. + +Snippets can be inserted into an `.adoc` file using an `include` directive, following the format: `include::\{rootdir}//[tag=snippetName]`. + +IMPORTANT: You could also use relative paths to reference include files, but it is preferred to always use the root folder as a starting point. Like this you can be sure that the preview in your editor of choice works. + +For example, if we wanted to highlight a specific section of the following Cucumber test definition (more on that in section Cucumber Testing) `ClasspathDuplicatesStepDefinitions.java` file located under `fineract-provider/src/test/java/org/apache/fineract/infrastructure/classpath/`. + +[source,asciidoc] +-- +[source,java,indent=0] +---- +\include::{rootdir}/fineract-provider/src/test/java/org/apache/fineract/infrastructure/classpath/ClasspathDuplicatesStepDefinitions.java[tag=then] +---- +-- + +For more information on the `include` directive, see the documentation at https://docs.asciidoctor.org/asciidoc/latest/directives/include. + +=== Block Titles + +Titles can be added to most blocks (images, source blocks, tables, etc.) by simply prefacing the title with a period (`.`). For example, to add a title to the source block example above: + +[source,asciidoc] +---- +.Example ID field +[source,xml] + +---- + +NOTE: More info at https://docs.asciidoctor.org/asciidoc/latest/blocks/add-title + +== Links + +=== Link to Sites on the Internet + +When converting content to HTML, Asciidoctor will automatically render many link types (such as `http:` and `mailto:`) without any additional syntax. However, you can add a name to a link by adding the URI followed by square brackets: + +[source,asciidoc] +---- +http://fineract.apache.org/[Fineract Website] +---- + +NOTE: More info at https://docs.asciidoctor.org/asciidoc/latest/macros/url-macro + +=== Link to Other Pages/Sections of the Guide + +A warning up front, linking to other pages can be a little painful. There are slightly different rules depending on the type of link you want to create, and where you are linking from. The build process includes a validation for _internal_ or _inter-page_ links, so if you can build the docs locally, you can use that to verify you constructed your link properly. With all the below examples, you can add text to display as the link title by putting the display text in brackets after the link, as in: + +[source,asciidoc] +---- +xref:indexing-guide:schema-api.adoc#modify-the-schema[Modify the Schema] +---- + +You can also use the title of the Page or Section you are linking to by using an empty display text. +This is useful in case the title of the page or section changes. In that case you won't need to change the display text for every link that refers to that page/section. + +See an example below: + +[source,asciidoc] +---- +xref:indexing-guide:schema-api.adoc#modify-the-schema[] +---- + +==== Link to a Section on the Same Page + +To link to an anchor (or section title) on the _same page_, you can simply use double angle brackets (`<< >>`) around the anchor/heading/section title you want to link to. Any section title (a heading that starts with equal signs) automatically becomes an anchor during conversion and is available for deep linking. + +Example:: +If I have a section on a page that looks like this (from `process.adoc`): ++ +[source,asciidoc] +---- +== Steps + +Common parameters for all steps are: +---- ++ +To link to this section from another part of the same `process.adoc` page, I simply need to put the section title in double angle brackets, as in: ++ +[source,asciidoc] +See also the <> section. ++ +The section title will be used as the display text; to customize that add a comma after the the section title, then the text you want used for display. + +NOTE: More info at https://docs.asciidoctor.org/asciidoc/latest/macros/xref/#internal-cross-references + +==== Link to a Section with an Anchor ID + +When linking to any section (on the same page or another one), you must also be aware of any pre-defined anchors that may be in use (these will be in double brackets, like `[[ ]]`). +When the page is converted, those will be the references your link needs to point to. + +Example:: +Take this example from `configsets-api.adoc`: ++ +[source,asciidoc] +---- +[[configsets-create]] +== Create a ConfigSet +---- ++ +To link to this section, there are two approaches depending on where you are linking from: + +* From the same page, simply use the anchor name: `\<>`. +* From another page, use the page name and the anchor name: `\xref:configuration-guide:configsets-api.adoc#configsets-create[]`. + +==== Link to Another Page + +To link to _another page_ or a section on another page, you must refer to the full filename and refer to the section you want to link to. + +When you want to refer the reader to another page without deep-linking to a section, Asciidoctor allows this by merely omitting the `#` and section id. + +Example:: +To construct a link to the `process.adoc` page, we need to refer to the file name (`process.adoc`), as well as the module that the file resides in (`release/`). ++ +It's preferred to also always use the page name to give the reader better context for where the link goes. +As in: ++ +[source,asciidoc] +---- +For more about upgrades, see xref:release:process.adoc[Fineract Release Process]. +---- + +==== Link to Another Page in the same folder + +If the page that contains the link and the page being linked to reside in the same module, there is no need to include the module name after `xref:` + +Example:: +To construct a link to the `process-step01.adoc` page from `process.adoc` page, we do not need to include the module name because they both reside in the `upgrade-notes` module. ++ +[source,asciidoc] +---- +For more information on the first step of the release process, see the section \xref:process-step01.adoc[]. +---- + +==== Link to a Section on Another Page + +Linking to a section is the same conceptually as linking to the top of a page, you just need to take a little extra care to format the anchor ID in your link reference properly. + +When you link to a section on another page, you must make a simple conversion of the title into the format of the section ID that will be created during the conversion. These are the rules that transform the sections: + +Example:: TBD + +NOTE: More info at https://docs.asciidoctor.org/asciidoc/latest/macros/inter-document-xref + +== Ordered and Unordered Lists + +AsciiDoc supports three types of lists: + +* Unordered lists +* Ordered lists +* Labeled lists + +Each type of list can be mixed with the other types. So, you could have an ordered list inside a labeled list if necessary. + +=== Unordered Lists + +Simple bulleted lists need each line to start with an asterisk (`*`). It should be the first character of the line, and be followed by a space. + +NOTE: More info at https://docs.asciidoctor.org/asciidoc/latest/lists/unordered + +=== Ordered Lists + +Numbered lists need each line to start with a period (`.`). It should be the first character of the line, and be followed by a space. This style is preferred over manually numbering your list. + +NOTE: More info at https://docs.asciidoctor.org/asciidoc/latest/lists/ordered + +=== Description Lists + +These are like question & answer lists or glossary definitions. +Each line should start with the list item followed by double colons (`::`), then a space or new line. Labeled lists can be nested by adding an additional colon (such as `:::`, etc.). If your content will span multiple paragraphs or include source blocks, etc., you will want to add a plus sign (`+`) to keep the sections together for your reader. + +TIP: We prefer this style of list for parameters because it allows more freedom in how you present the details for each parameter. For example, it supports ordered or unordered lists inside it automatically, and you can include multiple paragraphs and source blocks without trying to cram them into a smaller table cell. + +NOTE: More info at https://docs.asciidoctor.org/asciidoc/latest/lists/description + +== Images + +There are two ways to include an image: inline or as a block. Inline images are those where text will flow around the image. Block images are those that appear on their own line, set off from any other text on the page. Both approaches use the `image` tag before the image filename, but the number of colons after `image` define if it is inline or a block. Inline images use one colon (`image:`), while block images use two colons (`image::`). Block images automatically include a caption label and a number (such as `Figure 1`). If a block image includes a title, it will be included as the text of the caption. Optional attributes allow you to set the alt text, the size of the image, if it should be a link, float and alignment. We have defined a global attribute `\{imagesdir}` to standardize the location for all images (`fineract-doc/src/docs/en/images`). + +NOTE: More info at https://docs.asciidoctor.org/asciidoc/latest/macros/images + +== Tables + +Tables can be complex, but it is pretty easy to make a basic table that fits most needs. + +=== Basic Tables + +The basic structure of a table is similar to Markdown, with pipes (`|`) delimiting columns between rows: + +[source,asciidoc] +---- +|=== +| col 1 row 1 | col 2 row 1| +| col 1 row 2 | col 2 row 2| +|=== +---- + +Note the use of `|===` at the start and end. For basic tables that's not exactly required, but it does help to delimit the start and end of the table in case you accidentally introduce (or maybe prefer) spaces between the rows. + +=== Header Rows + +To add a header to a table, you need only set the `header` attribute at the start of the table: + +[source,asciidoc] +---- +[options="header"] +|=== +| header col 1 | header col 2| +| col 1 row 1 | col 2 row 1| +| col 1 row 2 | col 2 row 2| +|=== +---- + +=== Defining Column Styles + +If you need to define specific styles to all rows in a column, you can do so with the attributes. + +This example will center all content in all rows: + +[source,asciidoc] +---- +[cols="2*^" options="header"] +|=== +| header col 1 | header col 2| +| col 1 row 1 | col 2 row 1| +| col 1 row 2 | col 2 row 2| +|=== +---- + +Alignments or any other styles can be applied only to a specific column. For example, this would only center the last column of the table: + +[source,asciidoc] +---- +[cols="2*,^" options="header"] +|=== +| header col 1 | header col 2| +| col 1 row 1 | col 2 row 1| +| col 1 row 2 | col 2 row 2| +|=== +---- + +[NOTE] +.Many more examples of formatting: +==== +* Columns: https://docs.asciidoctor.org/asciidoc/latest/tables/add-columns/ +* Cells and rows: https://docs.asciidoctor.org/asciidoc/latest/tables/add-cells-and-rows/ +==== + +=== More Options + +Tables can also be given footer rows, borders, and captions. You can determine the width of columns, or the width of the table as a whole. + +CSV or DSV can also be used instead of formatting the data in pipes. + +NOTE: More info at https://docs.asciidoctor.org/asciidoc/latest/tables/build-a-basic-table/ + +== Admonitions (Notes, Warnings) + +AsciiDoc supports several types of callout boxes, called "admonitions": + +* NOTE +* TIP +* IMPORTANT +* CAUTION +* WARNING + +It is enough to start a paragraph with one of these words followed by a colon (such as `NOTE:`). When it is converted to HTML, those sections will be formatted properly - indented from the main text and showing an icon inline. + +You can add titles to admonitions by making it an admonition block. The structure of an admonition block is like this: + +[source,asciidoc] +---- +.Title of Note +[NOTE] +==== +Text of note +==== +---- + +In this example, the type of admonition is included in square brackets (`[NOTE]`), and the title is prefixed with a period. Four equal signs give the start and end points of the note text (which can include new lines, lists, code examples, etc.). + +NOTE: More info at https://docs.asciidoctor.org/asciidoc/latest/blocks/admonitions/ + +== STEM Notation Support + +We have set up the Ref Guide to be able to support STEM notation whenever it's needed. + +The http://asciimath.org/[AsciiMath] syntax is supported by default, but LaTeX syntax is also available. + +To insert a mathematical formula inline with your text, you can simply write: + +[source,asciidoc] +---- +stem:[a//b] +---- + +MathJax.js will render the formula as proper mathematical notation when a user loads the page. When the above example is converted to HTML, it will look like this to a user: stem:[a//b] + +To insert LaTeX, preface the formula with `latexmath` instead of `stem`: + +[source,asciidoc] +---- +latexmath:[tp \leq 1 - (1 - sim^{rows})^{bands}] +---- + +Long formulas, or formulas which should to be set off from the main text, can use the block syntax prefaced by `stem` or `latexmath`: + +[source,asciidoc] +---- +[stem] +++++ +sqrt(3x-1)+(1+x)^2 < y +++++ +---- + +or for LaTeX: + +[source,asciidoc] +---- +[latexmath] +++++ +[tp \leq 1 - (1 - sim^{rows})^{bands}] +++++ +---- + +NOTE: More info at https://docs.asciidoctor.org/asciidoc/latest/stem/stem diff --git a/fineract-doc/src/docs/en/chapters/documentation/asciidoc-tutorial.adoc b/fineract-doc/src/docs/en/chapters/documentation/asciidoc-tutorial.adoc deleted file mode 100644 index 3af79bf1c..000000000 --- a/fineract-doc/src/docs/en/chapters/documentation/asciidoc-tutorial.adoc +++ /dev/null @@ -1,5 +0,0 @@ -= Tutorial - -See: https://www.vogella.com/tutorials/AsciiDoc/article.html - -TBD diff --git a/fineract-doc/src/docs/en/chapters/documentation/asciidoc.adoc b/fineract-doc/src/docs/en/chapters/documentation/asciidoc.adoc index 2e4f79198..353585991 100644 --- a/fineract-doc/src/docs/en/chapters/documentation/asciidoc.adoc +++ b/fineract-doc/src/docs/en/chapters/documentation/asciidoc.adoc @@ -1,9 +1,3 @@ -= Asciidoc - -See: https://asciidoctor.org/ - -TBD - -include::asciidoc-tutorial.adoc[leveloffset=+1] += AsciiDoc include::asciidoc-cheatsheet.adoc[leveloffset=+1] diff --git a/fineract-doc/src/docs/en/chapters/documentation/diagram-archimate-cheatsheet.adoc b/fineract-doc/src/docs/en/chapters/documentation/diagram-archimate-cheatsheet.adoc new file mode 100644 index 000000000..5f7a5d016 --- /dev/null +++ b/fineract-doc/src/docs/en/chapters/documentation/diagram-archimate-cheatsheet.adoc @@ -0,0 +1,5 @@ += Archimate Cheatsheet + +TBD + +NOTE: https://www.opengroup.org/archimate-forum/archimate-overview and https://plantuml.com/archimate-diagram \ No newline at end of file diff --git a/fineract-doc/src/docs/en/chapters/documentation/diagram-c4-cheatsheet.adoc b/fineract-doc/src/docs/en/chapters/documentation/diagram-c4-cheatsheet.adoc new file mode 100644 index 000000000..8c2691c40 --- /dev/null +++ b/fineract-doc/src/docs/en/chapters/documentation/diagram-c4-cheatsheet.adoc @@ -0,0 +1,5 @@ += C4 Cheatsheet + +TBD + +NOTE: https://c4model.com and https://github.com/plantuml-stdlib/C4-PlantUML \ No newline at end of file diff --git a/fineract-doc/src/docs/en/chapters/documentation/diagram-plantuml-cheatsheet.adoc b/fineract-doc/src/docs/en/chapters/documentation/diagram-plantuml-cheatsheet.adoc new file mode 100644 index 000000000..dc668b9e6 --- /dev/null +++ b/fineract-doc/src/docs/en/chapters/documentation/diagram-plantuml-cheatsheet.adoc @@ -0,0 +1,5 @@ += PlantUML Cheatsheet + +TBD + +NOTE: https://plantuml.com \ No newline at end of file diff --git a/fineract-doc/src/docs/en/chapters/documentation/diagram-vega-cheatsheet.adoc b/fineract-doc/src/docs/en/chapters/documentation/diagram-vega-cheatsheet.adoc new file mode 100644 index 000000000..a7e79a01c --- /dev/null +++ b/fineract-doc/src/docs/en/chapters/documentation/diagram-vega-cheatsheet.adoc @@ -0,0 +1,5 @@ += Vega Cheatsheet + +TBD + +NOTE: https://vega.github.io/vega-lite/ \ No newline at end of file diff --git a/fineract-doc/src/docs/en/chapters/documentation/diagram.adoc b/fineract-doc/src/docs/en/chapters/documentation/diagram.adoc new file mode 100644 index 000000000..b6327330e --- /dev/null +++ b/fineract-doc/src/docs/en/chapters/documentation/diagram.adoc @@ -0,0 +1,13 @@ += Diagrams + +TBD + +NOTE: https://docs.asciidoctor.org/diagram-extension/latest + +include::diagram-plantuml-cheatsheet.adoc[leveloffset=+1] + +include::diagram-c4-cheatsheet.adoc[leveloffset=+1] + +include::diagram-archimate-cheatsheet.adoc[leveloffset=+1] + +include::diagram-vega-cheatsheet.adoc[leveloffset=+1] diff --git a/fineract-doc/src/docs/en/chapters/documentation/index.adoc b/fineract-doc/src/docs/en/chapters/documentation/index.adoc index 7ac4570c4..9b0450878 100644 --- a/fineract-doc/src/docs/en/chapters/documentation/index.adoc +++ b/fineract-doc/src/docs/en/chapters/documentation/index.adoc @@ -1,6 +1,4 @@ -= Documentation - -We try to assemble all Fineract related documentation in this document. We use Asciidoc... += Fineract Documentation Guide TBD @@ -8,7 +6,7 @@ include::layout.adoc[leveloffset=+1] include::asciidoc.adoc[leveloffset=+1] -include::plantuml.adoc[leveloffset=+1] +include::diagram.adoc[leveloffset=+1] include::editor.adoc[leveloffset=+1] diff --git a/fineract-doc/src/docs/en/chapters/documentation/layout.adoc b/fineract-doc/src/docs/en/chapters/documentation/layout.adoc index 66a67da2a..93fc21567 100644 --- a/fineract-doc/src/docs/en/chapters/documentation/layout.adoc +++ b/fineract-doc/src/docs/en/chapters/documentation/layout.adoc @@ -1,19 +1,14 @@ = File and Folder Layout -TBD +The general rules are:: -== Book +* keep things as flat as possible (avoid sub-folders as much as possible) +* DRY (don't repeat yourself): don't copy and paste code pieces, use AsciiDoc's include feature and reference files/-sections from the project folder +* images are located in `fineract-doc/src/docs/en/images` (or sub-folders) +* diagrams are located in `fineract-doc/src/docs/en/diagrams` (or sub-folders) +* specific chapters are located in `fineract-doc/src/docs/en/chapters` +* every chapter has its own folder and at least one `index.adoc` file +* it's recommended to keep the chapters flat (i. e. no sub-folders in the chapter folders) +* it's recommended to create one file per chapter section; like that you can re-arrange sections very easily in the `index.adoc` file -TBD - -== Chapter - -TBD - -== Diagrams - -TBD - -== Images - -TBD +NOTE: These rules are not entirely set in stone and could be modified if necessary. If you see any issues then please report them on the mailto:dev@fineract.apache.org[mailing list] or https://issues.apache.org[open a Jira ticket]. diff --git a/fineract-doc/src/docs/en/chapters/documentation/plantuml-cheatsheet.adoc b/fineract-doc/src/docs/en/chapters/documentation/plantuml-cheatsheet.adoc deleted file mode 100644 index 58c70e86e..000000000 --- a/fineract-doc/src/docs/en/chapters/documentation/plantuml-cheatsheet.adoc +++ /dev/null @@ -1,3 +0,0 @@ -= Cheatsheet - -TBD diff --git a/fineract-doc/src/docs/en/chapters/documentation/plantuml-tutorial.adoc b/fineract-doc/src/docs/en/chapters/documentation/plantuml-tutorial.adoc deleted file mode 100644 index b704924d7..000000000 --- a/fineract-doc/src/docs/en/chapters/documentation/plantuml-tutorial.adoc +++ /dev/null @@ -1,3 +0,0 @@ -= Tutorial - -TBD diff --git a/fineract-doc/src/docs/en/chapters/documentation/plantuml.adoc b/fineract-doc/src/docs/en/chapters/documentation/plantuml.adoc deleted file mode 100644 index 10cd7506c..000000000 --- a/fineract-doc/src/docs/en/chapters/documentation/plantuml.adoc +++ /dev/null @@ -1,9 +0,0 @@ -= PlantUML - -See: https://plantuml.com/ - -TBD - -include::plantuml-tutorial.adoc[leveloffset=+1] - -include::plantuml-cheatsheet.adoc[leveloffset=+1] diff --git a/fineract-doc/src/docs/en/chapters/release/configuration-gradle.adoc b/fineract-doc/src/docs/en/chapters/release/configuration-gradle.adoc index d3df3b5eb..2cec0fcc1 100644 --- a/fineract-doc/src/docs/en/chapters/release/configuration-gradle.adoc +++ b/fineract-doc/src/docs/en/chapters/release/configuration-gradle.adoc @@ -34,5 +34,5 @@ Creating Apache Fineract releases was a very manual and tedious procedure before [source,groovy] ---- -include::{rootdir}/buildSrc/src/main/groovy/org.apache.fineract.release.gradle[lines=32..105] +include::{rootdir}/buildSrc/src/main/groovy/org.apache.fineract.release.gradle[lines=23..89] ---- diff --git a/fineract-doc/src/docs/en/chapters/release/index.adoc b/fineract-doc/src/docs/en/chapters/release/index.adoc index 1c663ab8c..8bbfe89e5 100644 --- a/fineract-doc/src/docs/en/chapters/release/index.adoc +++ b/fineract-doc/src/docs/en/chapters/release/index.adoc @@ -3,7 +3,7 @@ https://cwiki.apache.org/confluence/x/DRwIB[How to Release Apache Fineract] documents the process how we make the source code that is available here in this Git repository into a binary release tar.gz available on http://fineract.apache.org. .Release Schedule -[plantuml, format=svg] +[plantuml, format=svg, width=100%] ---- include::{diagramsdir}/release-schedule.puml[] ---- diff --git a/fineract-doc/src/docs/en/chapters/release/process-step01.adoc b/fineract-doc/src/docs/en/chapters/release/process-step01.adoc index 364106669..929e99cba 100644 --- a/fineract-doc/src/docs/en/chapters/release/process-step01.adoc +++ b/fineract-doc/src/docs/en/chapters/release/process-step01.adoc @@ -12,15 +12,15 @@ The RM should then inform users when the git branch is planned to be created, by [source,text] ---- -include::{rootdir}/buildSrc/src/main/resources/email/release.step01.headsup.subject.ftl[] +include::{rootdir}/buildSrc/src/main/resources/email/release.step01.headsup.subject.ftl[lines=21..] -include::{rootdir}/buildSrc/src/main/resources/email/release.step01.headsup.message.ftl[] +include::{rootdir}/buildSrc/src/main/resources/email/release.step01.headsup.message.ftl[lines=21..] ---- == Gradle Task .Command -[source,bash,subs="attributes"] +[source,bash,subs="attributes+,+macros"] ---- % ./gradlew fineractReleaseStep1 -Pfineract.release.issue=1234 -Pfineract.release.date="Monday, April 25, 2022" -Pfineract.release.version={revnumber} ---- diff --git a/fineract-doc/src/docs/en/chapters/release/process-step02.adoc b/fineract-doc/src/docs/en/chapters/release/process-step02.adoc index 1bbb05fb0..b126d0fa1 100644 --- a/fineract-doc/src/docs/en/chapters/release/process-step02.adoc +++ b/fineract-doc/src/docs/en/chapters/release/process-step02.adoc @@ -10,13 +10,13 @@ project = FINERACT and resolution = fixed and fixVersion is empty ---- Move all unresolved JIRA issues which have this release as Fix Version to the next release -[source,text,subs="attributes"] +[source,text,subs="attributes+,+macros"] ---- project = FINERACT and fixVersion = {revnumber} and status not in ( Resolved, Done, Accepted, Closed ) ---- You can also run the following query to make sure that the issues fixed for the to-be-released version look accurate: -[source,text,subs="attributes"] +[source,text,subs="attributes+,+macros"] ---- project = FINERACT and fixVersion = {revnumber} ---- @@ -26,7 +26,7 @@ Finally, check out the output of the JIRA release note tool to see which tickets == Gradle Task .Command -[source,bash,subs="attributes"] +[source,bash,subs="attributes+,+macros"] ---- % ./gradlew fineractReleaseStep2 -Pfineract.release.version={revnumber} ---- diff --git a/fineract-doc/src/docs/en/chapters/release/process-step03.adoc b/fineract-doc/src/docs/en/chapters/release/process-step03.adoc index 3f142a6bd..daee60517 100644 --- a/fineract-doc/src/docs/en/chapters/release/process-step03.adoc +++ b/fineract-doc/src/docs/en/chapters/release/process-step03.adoc @@ -24,14 +24,14 @@ You do not need to ask committers to hold off any commits until you have branche 1. Create a new release branch with name "$Version" + -[source,bash,subs="attributes"] +[source,bash,subs="attributes+,+macros"] ---- % git checkout -b {revnumber} ---- 1. Push new branch to Apache Fineract repository + -[source,bash,subs="attributes"] +[source,bash,subs="attributes+,+macros"] ---- % git push origin {revnumber} ---- @@ -42,15 +42,15 @@ You do not need to ask committers to hold off any commits until you have branche + [source,text] ---- -include::{rootdir}/buildSrc/src/main/resources/email/release.step03.branch.subject.ftl[] +include::{rootdir}/buildSrc/src/main/resources/email/release.step03.branch.subject.ftl[lines=21..] -include::{rootdir}/buildSrc/src/main/resources/email/release.step03.branch.message.ftl[] +include::{rootdir}/buildSrc/src/main/resources/email/release.step03.branch.message.ftl[lines=21..] ---- == Gradle Task .Command -[source,bash,subs="attributes"] +[source,bash,subs="attributes+,+macros"] ---- % ./gradlew fineractReleaseStep3 -Pfineract.release.date="Monday, May 10, 2022" -Pfineract.release.version={revnumber} ---- diff --git a/fineract-doc/src/docs/en/chapters/release/process-step05.adoc b/fineract-doc/src/docs/en/chapters/release/process-step05.adoc index bafbae123..8288ef1c9 100644 --- a/fineract-doc/src/docs/en/chapters/release/process-step05.adoc +++ b/fineract-doc/src/docs/en/chapters/release/process-step05.adoc @@ -4,7 +4,7 @@ Next, you create a git tag from the HEAD of the release's git branch. -[source,bash,subs="attributes"] +[source,bash,subs="attributes+,+macros"] ---- % git checkout {revnumber} % ./gradlew clean integrationTests <1> @@ -18,7 +18,7 @@ CAUTION: It is important to create so called annotated tags (vs. lightweight) fo == Gradle Task .Command -[source,bash,subs="attributes"] +[source,bash,subs="attributes+,+macros"] ---- % ./gradlew fineractReleaseStep5 -Pfineract.release.version={revnumber} ---- diff --git a/fineract-doc/src/docs/en/chapters/release/process-step06.adoc b/fineract-doc/src/docs/en/chapters/release/process-step06.adoc index e9326faa0..e0a8ceab8 100644 --- a/fineract-doc/src/docs/en/chapters/release/process-step06.adoc +++ b/fineract-doc/src/docs/en/chapters/release/process-step06.adoc @@ -4,7 +4,7 @@ Create source and binary artifacts. Make sure to do some sanity checks. The tar and the release branch should match. -[source,bash,subs="attributes"] +[source,bash,subs="attributes+,+macros"] ---- % cd /fineract-release-preparations <1> % tar -xvf apache-fineract-{revnumber}-src.tar.gz @@ -18,7 +18,7 @@ Create source and binary artifacts. Make sure to do some sanity checks. The tar Make sure code compiles and tests pass on the uncompressed source. -[source,bash,subs="attributes"] +[source,bash,subs="attributes+,+macros"] ---- % cd apache-fineract-{revnumber}-src/fineract-provider <1> % gradlew clean integrationTest <2> diff --git a/fineract-doc/src/docs/en/chapters/release/process-step07.adoc b/fineract-doc/src/docs/en/chapters/release/process-step07.adoc index 169a61cb3..cbc2e6945 100644 --- a/fineract-doc/src/docs/en/chapters/release/process-step07.adoc +++ b/fineract-doc/src/docs/en/chapters/release/process-step07.adoc @@ -4,7 +4,7 @@ All release artifacts must be signed. In order to sign a release you will need a PGP key. You should get your key signed by a few other people. You will also need to receive their keys from a public key server. See the Apache release signing page for more details. Please follow the steps defined in Release Sign. -[source,bash,subs="attributes"] +[source,bash,subs="attributes+,+macros"] ---- % gpg --armor --output apache-fineract-{revnumber}-src.tar.gz.asc --detach-sig apache-fineract-{revnumber}-src.tar.gz % gpg --print-md MD5 apache-fineract-{revnumber}-src.tar.gz > apache-fineract-{revnumber}-src.tar.gz.md5 diff --git a/fineract-doc/src/docs/en/chapters/release/process-step08.adoc b/fineract-doc/src/docs/en/chapters/release/process-step08.adoc index a954973c2..fecf7786b 100644 --- a/fineract-doc/src/docs/en/chapters/release/process-step08.adoc +++ b/fineract-doc/src/docs/en/chapters/release/process-step08.adoc @@ -15,7 +15,7 @@ Finally create a directory with release name ({revnumber} in this example) in ht Upload binary and source archives to ASF's distribution dev (staging) area: -[source,bash,subs="attributes"] +[source,bash,subs="attributes+,+macros"] ---- % svn co https://dist.apache.org/repos/dist/dev/fineract/ fineract-dist-dev % mkdir fineract-dist-dev/{revnumber} @@ -28,7 +28,7 @@ NOTE: You will need your ASF Committer credentials to be able to access the Subv == Gradle Task .Command -[source,bash,subs="attributes"] +[source,bash,subs="attributes+,+macros"] ---- % ./gradlew fineractReleaseStep8 -Pfineract.release.version={revnumber} ---- diff --git a/fineract-doc/src/docs/en/chapters/release/process-step09.adoc b/fineract-doc/src/docs/en/chapters/release/process-step09.adoc index ecb5feeb5..ffebe3f20 100644 --- a/fineract-doc/src/docs/en/chapters/release/process-step09.adoc +++ b/fineract-doc/src/docs/en/chapters/release/process-step09.adoc @@ -18,7 +18,7 @@ Make sure release artifacts are hosted at https://dist.apache.org/repos/dist/dev == Gradle Task .Command -[source,bash,subs="attributes"] +[source,bash,subs="attributes+,+macros"] ---- % ./gradlew fineractReleaseStep9 -Pfineract.release.version={revnumber} ---- diff --git a/fineract-doc/src/docs/en/chapters/release/process-step10.adoc b/fineract-doc/src/docs/en/chapters/release/process-step10.adoc index d27916112..eb36cdc23 100644 --- a/fineract-doc/src/docs/en/chapters/release/process-step10.adoc +++ b/fineract-doc/src/docs/en/chapters/release/process-step10.adoc @@ -6,15 +6,15 @@ Voting has to be done on dev@fineract.apache.org. You can close the vote after v [source,text] ---- -include::{rootdir}/buildSrc/src/main/resources/email/release.step10.vote.subject.ftl[] +include::{rootdir}/buildSrc/src/main/resources/email/release.step10.vote.subject.ftl[lines=21..] -include::{rootdir}/buildSrc/src/main/resources/email/release.step10.vote.message.ftl[] +include::{rootdir}/buildSrc/src/main/resources/email/release.step10.vote.message.ftl[lines=21..] ---- == Gradle Task .Command -[source,bash,subs="attributes"] +[source,bash,subs="attributes+,+macros"] ---- % ./gradlew fineractReleaseStep10 -Pfineract.release.version={revnumber} ---- diff --git a/fineract-doc/src/docs/en/chapters/release/process-step11.adoc b/fineract-doc/src/docs/en/chapters/release/process-step11.adoc index e00de88ec..01134f86b 100644 --- a/fineract-doc/src/docs/en/chapters/release/process-step11.adoc +++ b/fineract-doc/src/docs/en/chapters/release/process-step11.adoc @@ -6,15 +6,15 @@ Upon receiving 3 x +1 from the PMC, or after 72 hours (whichever one comes first [source,text] ---- -include::{rootdir}/buildSrc/src/main/resources/email/release.step11.vote.subject.ftl[] +include::{rootdir}/buildSrc/src/main/resources/email/release.step11.vote.subject.ftl[lines=21..] -include::{rootdir}/buildSrc/src/main/resources/email/release.step11.vote.message.ftl[] +include::{rootdir}/buildSrc/src/main/resources/email/release.step11.vote.message.ftl[lines=21..] ---- == Gradle Task .Command -[source,text,subs="attributes"] +[source,text,subs="attributes+,+macros"] ---- % ./gradlew fineractReleaseStep11 -Pfineract.release.version={revnumber} ---- diff --git a/fineract-doc/src/docs/en/chapters/release/process-step12.adoc b/fineract-doc/src/docs/en/chapters/release/process-step12.adoc index b93fd7f74..979b6e854 100644 --- a/fineract-doc/src/docs/en/chapters/release/process-step12.adoc +++ b/fineract-doc/src/docs/en/chapters/release/process-step12.adoc @@ -4,7 +4,7 @@ In order to release you have to checkout release repository located on https://dist.apache.org/repos/dist/release/fineract and add release artifacts there. -[source,bash,subs="attributes"] +[source,bash,subs="attributes+,+macros"] ---- % svn co https://dist.apache.org/repos/dist/release/fineract fineract-release % mkdir fineract-release/{revnumber}/ @@ -18,7 +18,7 @@ You will now get an automated email from the Apache Reporter Service (no-reply@r == Gradle Task .Command -[source,bash,subs="attributes"] +[source,bash,subs="attributes+,+macros"] ---- % ./gradlew fineractReleaseStep12 -Pfineract.release.version={revnumber} ---- diff --git a/fineract-doc/src/docs/en/chapters/release/process-step13.adoc b/fineract-doc/src/docs/en/chapters/release/process-step13.adoc index 49a2725f8..131f1cbad 100644 --- a/fineract-doc/src/docs/en/chapters/release/process-step13.adoc +++ b/fineract-doc/src/docs/en/chapters/release/process-step13.adoc @@ -4,7 +4,7 @@ As discussed in https://issues.apache.org/jira/browse/FINERACT-1154[FINERACT-1154], now that everything is final, please do the following to remove the release branch (and just keep the tag), and make sure that everything on the release tag is merged to develop and that e.g. git describe works: -[source,bash,subs="attributes"] +[source,bash,subs="attributes+,+macros"] ---- % git checkout develop % git branch -D {revnumber} @@ -21,7 +21,7 @@ As discussed in https://issues.apache.org/jira/browse/FINERACT-1154[FINERACT-115 == Gradle Task .Command -[source,bash,subs="attributes"] +[source,bash,subs="attributes+,+macros"] ---- % ./gradlew fineractReleaseStep13 -Pfineract.release.version={revnumber} ---- diff --git a/fineract-doc/src/docs/en/chapters/release/process-step15.adoc b/fineract-doc/src/docs/en/chapters/release/process-step15.adoc index 5e85ee1cd..8ccc7d1e8 100644 --- a/fineract-doc/src/docs/en/chapters/release/process-step15.adoc +++ b/fineract-doc/src/docs/en/chapters/release/process-step15.adoc @@ -2,19 +2,19 @@ == Description -Send an email to announce@apache.org (sender address must be @apache.org): +Send an email to `announce@apache.org` (sender address must be `@apache.org`): [source,text] ---- -include::{rootdir}/buildSrc/src/main/resources/email/release.step15.announce.subject.ftl[] +include::{rootdir}/buildSrc/src/main/resources/email/release.step15.announce.subject.ftl[lines=21..] -include::{rootdir}/buildSrc/src/main/resources/email/release.step15.announce.message.ftl[] +include::{rootdir}/buildSrc/src/main/resources/email/release.step15.announce.message.ftl[lines=21..] ---- == Gradle Task .Command -[source,bash,subs="attributes"] +[source,bash,subs="attributes+,+macros"] ---- % ./gradlew fineractReleaseStep15 -Pfineract.release.version={revnumber} ---- diff --git a/fineract-doc/src/docs/en/chapters/security/oauth.adoc b/fineract-doc/src/docs/en/chapters/security/oauth.adoc index 8f7909d87..74a9e9c00 100644 --- a/fineract-doc/src/docs/en/chapters/security/oauth.adoc +++ b/fineract-doc/src/docs/en/chapters/security/oauth.adoc @@ -63,4 +63,4 @@ curl --location --request GET \ ---- -NOTE: See also https://demo.fineract.dev/fineract-provider/legacy-docs/apiLive.htm#authentication_oauth +NOTE: See also https://fineract.apache.org/legacy-docs/apiLive.htm#authentication_oauth diff --git a/fineract-doc/src/docs/en/chapters/testing/cucumber.adoc b/fineract-doc/src/docs/en/chapters/testing/cucumber.adoc index 81a578358..90c356bb4 100644 --- a/fineract-doc/src/docs/en/chapters/testing/cucumber.adoc +++ b/fineract-doc/src/docs/en/chapters/testing/cucumber.adoc @@ -1,3 +1,4 @@ +[[testing-cucumber]] = Cucumber TBD diff --git a/fineract-doc/src/docs/en/colophon.adoc b/fineract-doc/src/docs/en/colophon.adoc index 1454f104c..3a0e713da 100644 --- a/fineract-doc/src/docs/en/colophon.adoc +++ b/fineract-doc/src/docs/en/colophon.adoc @@ -34,6 +34,7 @@ | Arnold Galovics | Database architecture section | 2022-03-11 | 1.7.0 | Aleksandar Vidakovic | Module section | 2022-03-23 | 1.7.0 | Aleksandar Vidakovic | Release section, file re-organisation | 2022-04-18 | 1.7.0 +| Aleksandar Vidakovic | AsciiDoc cheatsheet | 2022-05-09 | 1.7.0 |=== toc::[] diff --git a/fineract-doc/src/docs/en/config.adoc b/fineract-doc/src/docs/en/config.adoc index f6f597849..0cc9b3326 100644 --- a/fineract-doc/src/docs/en/config.adoc +++ b/fineract-doc/src/docs/en/config.adoc @@ -8,8 +8,8 @@ :pdf-version: 1.7 :page-layout: base :toc-title: Table of Contents -:toc: macro -:toclevels: 4 +:toc: left +:toclevels: 2 :icons: font :source-highlighter: coderay :experimental: diff --git a/fineract-doc/src/docs/en/index.adoc b/fineract-doc/src/docs/en/index.adoc index 2b77c7112..d030fbd32 100644 --- a/fineract-doc/src/docs/en/index.adoc +++ b/fineract-doc/src/docs/en/index.adoc @@ -1,3 +1,22 @@ +// +// 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. +// + :doctitle: Fineract Platform Documentation include::config.adoc[] diff --git a/fineract-provider/build.gradle b/fineract-provider/build.gradle index d6e38a943..84ac3e8fa 100644 --- a/fineract-provider/build.gradle +++ b/fineract-provider/build.gradle @@ -70,9 +70,9 @@ resolve { prettyPrint = false classpath = sourceSets.main.runtimeClasspath outputDir = file("${buildDir}/classes/java/main/static") - // openApiFile = file("${buildDir}/tmp/swagger/fineract-input.yaml") + openApiFile = file("${buildDir}/tmp/swagger/fineract-input.yaml") sortOutput = true - // dependsOn(prepareInputYaml) + dependsOn(prepareInputYaml) } configurations { diff --git a/fineract-provider/config/swagger/fineract-input.yaml.template b/fineract-provider/config/swagger/fineract-input.yaml.template index 3202c903a..f7abfe93c 100644 --- a/fineract-provider/config/swagger/fineract-input.yaml.template +++ b/fineract-provider/config/swagger/fineract-input.yaml.template @@ -1,13 +1,13 @@ openapi: 3.0.3 info: version: @VERSION@ - title: Apache Fineract + title: Apache Fineract REST API description: |- Apache Fineract is a secure, multi-tenanted microfinance platform. The goal of the Apache Fineract API is to empower developers to build apps on top of the Apache Fineract Platform. - The reference app (username: mifos, password: password) works on the same demo tenant as the interactive links in this documentation. - Until we complete the new REST API documentation you still have the legacy documentation available here. - Please check the Fineract documentation for more information. + The https://cui.fineract.dev[reference app] (username: mifos, password: password) works on the same demo tenant as the interactive links in this documentation. + Until we complete the new REST API documentation you still have the legacy documentation available https://fineract.apache.org/legacy-docs/apiLive.htm[here]. + Please check https://fineract.apache.org/docs/current[the Fineract documentation] for more information. contact: email: dev@fineract.apache.org license: diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/survey/api/SurveyApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/survey/api/SurveyApiResource.java index 2cf9acad4..19bc1aa31 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/survey/api/SurveyApiResource.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/survey/api/SurveyApiResource.java @@ -119,8 +119,7 @@ public class SurveyApiResource { @Consumes({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON }) @Operation(summary = "Create an entry in the survey table", description = "Insert and entry in a survey table (full fill the survey)." - + "\n" + "\n" - + "Refer Link for sample Body: [ https://demo.fineract.dev/fineract-provider/legacy-docs/apiLive.htm#survey_create ] ") + + "\n" + "\n" + "Refer Link for sample Body: [ https://fineract.apache.org/legacy-docs/apiLive.htm#survey_create ] ") @RequestBody(required = true, content = @Content(schema = @Schema(implementation = SurveyApiResourceSwagger.PostSurveySurveyNameApptableIdRequest.class))) @ApiResponses({ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = SurveyApiResourceSwagger.PostSurveySurveyNameApptableIdResponse.class))) }) diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/api/AccountsApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/api/AccountsApiResource.java index 6489e4b60..43cb3da5e 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/api/AccountsApiResource.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/api/AccountsApiResource.java @@ -200,7 +200,7 @@ public class AccountsApiResource { + "requestedDate is requsted date of shares redeem\n" + "\n" + "requestedShares is number of shares to be redeemed\n\n" + "Mandatory Fields: dateFormat,locale,requestedDate,requestedShares\n\n" + "Showing request/response for 'Reject additional shares request on a share account'\n\n" - + "For more info visit this link - https://demo.fineract.dev/fineract-provider/legacy-docs/apiLive.htm#shareaccounts") + + "For more info visit this link - https://fineract.apache.org/legacy-docs/apiLive.htm#shareaccounts") @RequestBody(required = true, content = @Content(schema = @Schema(implementation = AccountsApiResourceSwagger.PostAccountsTypeAccountIdRequest.class))) @ApiResponses({ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = AccountsApiResourceSwagger.PostAccountsTypeAccountIdResponse.class))) }) diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/security/api/SelfAuthenticationApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/security/api/SelfAuthenticationApiResource.java index 784912de2..e5d3cd2a8 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/security/api/SelfAuthenticationApiResource.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/security/api/SelfAuthenticationApiResource.java @@ -53,7 +53,7 @@ public class SelfAuthenticationApiResource { @Consumes({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON }) @Operation(summary = "Verify authentication", description = "Authenticates the credentials provided and returns the set roles and permissions allowed.\n\n" - + "Please visit this link for more info - https://demo.fineract.dev/fineract-provider/legacy-docs/apiLive.htm#selfbasicauth") + + "Please visit this link for more info - https://fineract.apache.org/legacy-docs/apiLive.htm#selfbasicauth") @ApiResponses({ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = SelfAuthenticationApiResourceSwagger.PostSelfAuthenticationResponse.class))) }) public String authenticate(final String apiRequestBodyAsJson) { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/security/api/SelfUserDetailsApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/security/api/SelfUserDetailsApiResource.java index dd893042d..79e431df4 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/security/api/SelfUserDetailsApiResource.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/security/api/SelfUserDetailsApiResource.java @@ -52,7 +52,7 @@ public class SelfUserDetailsApiResource { @GET @Produces({ MediaType.APPLICATION_JSON }) @Operation(summary = "Fetch authenticated user details", description = "Checks the Authentication and returns the set roles and permissions allowed\n\n" - + "For more info visit this link - https://demo.fineract.dev/fineract-provider/legacy-docs/apiLive.htm#selfoauth") + + "For more info visit this link - https://fineract.apache.org/legacy-docs/apiLive.htm#selfoauth") @ApiResponses({ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = SelfUserDetailsApiResourceSwagger.GetSelfUserDetailsResponse.class))) }) public String fetchAuthenticatedUserData() { diff --git a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/classpath/ClasspathDuplicatesStepDefinitions.java b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/classpath/ClasspathDuplicatesStepDefinitions.java index cdc2ca423..39e72fe41 100644 --- a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/classpath/ClasspathDuplicatesStepDefinitions.java +++ b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/classpath/ClasspathDuplicatesStepDefinitions.java @@ -35,10 +35,13 @@ public class ClasspathDuplicatesStepDefinitions implements En { private ClassGraph classGraph; public ClasspathDuplicatesStepDefinitions() { + // tag::given[] Given("A class graph", () -> { this.classGraph = new ClassGraph(); }); + // end::given[] + // tag::when[] When("The user scans the class graph", () -> { // nothing to do here try (ScanResult scanResult = this.classGraph.scan()) { @@ -61,12 +64,15 @@ public class ClasspathDuplicatesStepDefinitions implements En { } } }); + // end::when[] + // tag::then[] Then("There should be no duplicates", () -> { if (!duplicates.isEmpty()) { throw new AssertionFailedError(duplicates.size() + " Classpath duplicates detected:\n" + duplicates); } }); + // end::then[] } private boolean skipJAR(String jarPath) { From 555240c0940ed087cba8d8d3791756c6f16bcbc8 Mon Sep 17 00:00:00 2001 From: Aleksandar Vidakovic Date: Wed, 11 May 2022 01:53:23 +0200 Subject: [PATCH 03/11] FINERACT-1607: Improve documentation on Cucumber usage --- build.gradle | 29 ++++- .../src/docs/en/chapters/release/index.adoc | 2 + .../src/docs/en/chapters/release/publish.adoc | 32 +++++ .../chapters/testing/cucumber-cheatsheet.adoc | 110 +++++++++++++++++- .../testing/cucumber-introduction.adoc | 3 - .../chapters/testing/cucumber-tutorial.adoc | 3 - .../docs/en/chapters/testing/cucumber.adoc | 4 - fineract-doc/src/docs/en/index.adoc | 8 +- fineract-provider/build.gradle | 5 +- .../accounting/accounting.common.feature | 19 +++ .../features/batch/batch.builder.feature | 19 +++ .../commands/commands.provider.feature | 19 +++ .../infrastructure.classpath.feature | 19 +++ .../infrastructure.core.feature | 19 +++ .../infrastructure.sqlbuilder.feature | 19 +++ .../resources/features/mix/mix.report.feature | 19 +++ .../features/module/modules.example.feature | 19 +++ .../features/module/modules.service.feature | 19 +++ .../portfolio/loanproduct.validation.feature | 19 +++ .../template/template.service.feature | 19 +++ gradle.properties | 1 + 21 files changed, 384 insertions(+), 22 deletions(-) create mode 100644 fineract-doc/src/docs/en/chapters/release/publish.adoc delete mode 100644 fineract-doc/src/docs/en/chapters/testing/cucumber-introduction.adoc delete mode 100644 fineract-doc/src/docs/en/chapters/testing/cucumber-tutorial.adoc diff --git a/build.gradle b/build.gradle index 5a1042533..03a22692b 100644 --- a/build.gradle +++ b/build.gradle @@ -96,8 +96,7 @@ allprojects { jgitver { strategy 'PATTERN' - versionPattern '${M}.${m}.${p}-${meta.GIT_SHA1_8}' - // versionPattern '${v}' + versionPattern ( project.hasProperty('fineract.release.version') ? '${v}' : '${M}.${m}.${p}-${meta.GIT_SHA1_8}' ) } repositories { @@ -265,6 +264,7 @@ configure(project.fineractJavaProjects) { apply plugin: 'se.thinkcode.cucumber-runner' apply from: "${rootDir}/buildSrc/src/main/groovy/org.apache.fineract.dependencies.gradle" + group = 'org.apache.fineract' /* define the valid syntax level for source files */ sourceCompatibility = JavaVersion.VERSION_17 @@ -590,10 +590,15 @@ configure(project.fineractJavaProjects) { configure(project.fineractPublishProjects) { apply plugin: 'maven-publish' + apply plugin: 'signing' publishing { publications { mavenJava(MavenPublication) { + groupId 'org.apache.fineract' + artifactId project.name + version "${project.version}-SNAPSHOT" + from components.java versionMapping { @@ -606,7 +611,7 @@ configure(project.fineractPublishProjects) { } pom { - name = 'Fineract' + name = "Fineract: ${project.name}" description = 'A secure, multi-tenanted microfinance platform' url = 'https://fineract.apache.org' licenses { @@ -624,7 +629,23 @@ configure(project.fineractPublishProjects) { } } - // TODO FINERACT-1102: Actually use this to deploy to ASF (Apache Software Foundation) Nexus Maven Repository + repositories { + def releaseUrl = 'https://repository.apache.org/content/repositories/releases' + def stagingUrl = 'https://repository.apache.org/content/repositories/snapshots' + + maven { + name 'apache' + url hasProperty('fineract.release.version') ? releaseUrl : stagingUrl + credentials { + username "${findProperty('fineract.config.username')}" + password "${findProperty('fineract.config.password')}" + } + } + } + } + + signing { + sign publishing.publications.mavenJava } } diff --git a/fineract-doc/src/docs/en/chapters/release/index.adoc b/fineract-doc/src/docs/en/chapters/release/index.adoc index 8bbfe89e5..d8efa23f3 100644 --- a/fineract-doc/src/docs/en/chapters/release/index.adoc +++ b/fineract-doc/src/docs/en/chapters/release/index.adoc @@ -11,3 +11,5 @@ include::{diagramsdir}/release-schedule.puml[] include::configuration.adoc[leveloffset=+1] include::process.adoc[leveloffset=+1] + +include::publish.adoc[leveloffset=+1] diff --git a/fineract-doc/src/docs/en/chapters/release/publish.adoc b/fineract-doc/src/docs/en/chapters/release/publish.adoc new file mode 100644 index 000000000..609bdcbc1 --- /dev/null +++ b/fineract-doc/src/docs/en/chapters/release/publish.adoc @@ -0,0 +1,32 @@ += Publish Release Artifacts + +NOTE: More on releases at the ASF see https://www.apache.org/legal/release-policy.html#distribute-raw-artifact + +== Requirements + +You need to have your GPG keypairs properly set up. The JAR release artifacts (currently only `fineract-client`) are signed with a Gradle plugin just before being uploaded to the Maven repository. Please make sure that the following properties are set in your private `gradle.properties` file in your home folder: + +[source,properties] +---- +signing.keyId=7890ABCD +signing.password=***** +signing.secretKeyRingFile=~/.gnupg/secring.gpg +---- + +This is quite similiar to the Fineract release plugin properties for GPG. In one of the next release we'll merge these two setups to avoid this duplicated configuration. + +== Maven Repository + +We are using the ASF's official https://repository.apache.org[Nexus Maven repository] to publish our snapshot and release artifacts. + +NOTE: Find more information at https://infra.apache.org/publishing-maven-artifacts.html + +== NPM Registry + +For convenience we will be using Github Packages to publish Fineract's Typescript API client. + +TBD + +== Docker Hub + +TBD diff --git a/fineract-doc/src/docs/en/chapters/testing/cucumber-cheatsheet.adoc b/fineract-doc/src/docs/en/chapters/testing/cucumber-cheatsheet.adoc index 4a2888a05..ec246d7f9 100644 --- a/fineract-doc/src/docs/en/chapters/testing/cucumber-cheatsheet.adoc +++ b/fineract-doc/src/docs/en/chapters/testing/cucumber-cheatsheet.adoc @@ -1,3 +1,109 @@ -= Cheatsheet += Cucumber Cheatsheet -TBD \ No newline at end of file +Cucumber is a test framework based on Behavior-Driven Development (BDD). Tests are written in plain text with very basic syntax rules. These rules form a mini language that is called Gherkin. + +A specification resembles spoken language. This makes it ideal for use with non-technical people that have domain specific knowledge. The emphasis of Cucumber lies on finding examples to describe your test cases. The few keywords and language rules are easy to explain to anyone (compared JUnit for example). + +== Keywords + +The Gherkin language has the following keywords: + +* `Feature` +* `Rule` +* `Scenario Outline` or `Scenario Template` +* `Example` or `Scenario` +* `Examples` or `Scenarios` +* `Background` +* `Given` +* `And` +* `But` +* `When` +* `Then` + +There are a couple of additional signs used in Gherkin: + +* `|` is as column delimiters in `Examples` tables +* with `@` you can assign any kind of tags to categorize the specs (or e.g. relate them to certain Jira tickets) +* `#` is used to indicate line comments + +IMPORTANT: The tag `@ignore` is used to skip tests. This is a somewhat arbitrary choice (we could use any other tag to indicate temporarily disabled tests). + +Each non-empty line of a test specification needs to start with one of these keywords. The text blocks that follows the keywords are mapped to so called step definitions that contain the actual test code. + +A typical Cucumber test specification written in Gherkin looks like this: + +[source,gherkin,subs="attributes+,+macros"] +---- +include::{rootdir}/fineract-provider/src/test/resources/features/template/template.service.feature[lines=19..] +---- + +The corresponding step definitions would look like this: + +[source,java,subs="attributes+,+macros"] +---- +include::{rootdir}/fineract-provider/src/test/java/org/apache/fineract/template/service/TemplateServiceStepDefinitions.java[lines=19..] +---- + +NOTE: This example is an actual test specification that you can find in the `fineract-provider` module. + +=== Feature + +This keyword is used to group scenarios and to group related scenarios. All Gherkin specifications must start with the word `Feature`. + +=== Descriptions + +A description is any non-empty line that doesn't start with a keyword. Descriptions can be placed under the keywords: + +* `Feature` +* `Rule` +* `Background` +* `Example`/`Scenario` +* `Scenario Outline` + +=== Rule + +Rule is used to group multiple related scenarios together. + +=== Example/Scenario + +This is the important part of the specification as it should describe the business logic in more detail with the usage of steps (usually `Given`, `When`, `Then`) + +=== Steps + +TBD + +=== Given + +TBD + +=== When + +TBD + +=== Then + +TBD + +=== And, But + +TBD + +=== Background + +TBD + +=== Scenario Outline + +TBD + +=== Examples/Tables + +TBD + +== Outlook + +As a proof of concept we've converted all unit tests in `fineract-provider` into Cucumber tests. The more interesting part starts when we'll attack the integration tests with over 400 mostly business logic related tests. These tests fit very well in Cucumber's test specification structure (a lot of _if-then-else_ or in Gherkin: _Given-When-Then_). Migrating all tests will take a while, but we would already recommend trying to implement tests as Cucumber specifications. It should be relatively easy to convert these tests into the new syntax. + +Hopefully this will motivate even more people from the broader Fineract community to participate in the project by sharing their domain specific knowledge as Cucumber specifications. Specifications are written in Englisch (although not a technical requirement). + +NOTE: Have a look at the specifications in `fineract-provider` for an initial inspiration. For more information please see https://cucumber.io/docs \ No newline at end of file diff --git a/fineract-doc/src/docs/en/chapters/testing/cucumber-introduction.adoc b/fineract-doc/src/docs/en/chapters/testing/cucumber-introduction.adoc deleted file mode 100644 index 42839e88a..000000000 --- a/fineract-doc/src/docs/en/chapters/testing/cucumber-introduction.adoc +++ /dev/null @@ -1,3 +0,0 @@ -= Introduction - -TBD \ No newline at end of file diff --git a/fineract-doc/src/docs/en/chapters/testing/cucumber-tutorial.adoc b/fineract-doc/src/docs/en/chapters/testing/cucumber-tutorial.adoc deleted file mode 100644 index 912dad8a5..000000000 --- a/fineract-doc/src/docs/en/chapters/testing/cucumber-tutorial.adoc +++ /dev/null @@ -1,3 +0,0 @@ -= Tutorial - -TBD \ No newline at end of file diff --git a/fineract-doc/src/docs/en/chapters/testing/cucumber.adoc b/fineract-doc/src/docs/en/chapters/testing/cucumber.adoc index 90c356bb4..272fc8953 100644 --- a/fineract-doc/src/docs/en/chapters/testing/cucumber.adoc +++ b/fineract-doc/src/docs/en/chapters/testing/cucumber.adoc @@ -3,8 +3,4 @@ TBD -include::cucumber-introduction.adoc[leveloffset=+1] - -include::cucumber-tutorial.adoc[leveloffset=+1] - include::cucumber-cheatsheet.adoc[leveloffset=+1] diff --git a/fineract-doc/src/docs/en/index.adoc b/fineract-doc/src/docs/en/index.adoc index d030fbd32..c68bd3dd3 100644 --- a/fineract-doc/src/docs/en/index.adoc +++ b/fineract-doc/src/docs/en/index.adoc @@ -39,16 +39,16 @@ include::chapters/development/index.adoc[leveloffset=+1] include::chapters/security/index.adoc[leveloffset=+1] -include::{rootdir}/fineract-client/build/generated/asciidoc/index.adoc[leveloffset=+1] - -include::chapters/sdk/index.adoc[leveloffset=+1] - include::chapters/testing/index.adoc[leveloffset=+1] include::chapters/documentation/index.adoc[leveloffset=+1] include::chapters/release/index.adoc[leveloffset=+1] +include::chapters/sdk/index.adoc[leveloffset=+1] + +include::{rootdir}/fineract-client/build/generated/asciidoc/index.adoc[leveloffset=+1] + include::faq.adoc[leveloffset=+1] include::glossary.adoc[leveloffset=+1] diff --git a/fineract-provider/build.gradle b/fineract-provider/build.gradle index 84ac3e8fa..1d8f96ab7 100644 --- a/fineract-provider/build.gradle +++ b/fineract-provider/build.gradle @@ -51,11 +51,11 @@ compileJava.doLast { import org.apache.tools.ant.filters.ReplaceTokens task prepareInputYaml { - outputs.file('config/swagger/fineract-input.yaml') + outputs.file("${buildDir}/tmp/swagger/fineract-input.yaml") doLast { copy { - from file('config/swagger/fineract-input.yaml.template') + from file("${projectDir}/config/swagger/fineract-input.yaml.template") into file("${buildDir}/tmp/swagger") rename { String filename -> return 'fineract-input.yaml' } filter(ReplaceTokens, tokens: [VERSION: "${project.version}".toString()]) @@ -323,6 +323,7 @@ jar.dependsOn resolve test.dependsOn resolve checkstyleMain.dependsOn resolve checkstyleTest.dependsOn resolve +rat.dependsOn prepareInputYaml spotbugsTest.dependsOn resolve compileTestJava.dependsOn ':fineract-client:processResources' bootJarMainClassName.dependsOn resolve diff --git a/fineract-provider/src/test/resources/features/accounting/accounting.common.feature b/fineract-provider/src/test/resources/features/accounting/accounting.common.feature index ec58e7a1e..9589b179f 100644 --- a/fineract-provider/src/test/resources/features/accounting/accounting.common.feature +++ b/fineract-provider/src/test/resources/features/accounting/accounting.common.feature @@ -1,3 +1,22 @@ +# +# 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. +# + Feature: Accounting Service @accounting diff --git a/fineract-provider/src/test/resources/features/batch/batch.builder.feature b/fineract-provider/src/test/resources/features/batch/batch.builder.feature index 6c84197e8..62dfec23c 100644 --- a/fineract-provider/src/test/resources/features/batch/batch.builder.feature +++ b/fineract-provider/src/test/resources/features/batch/batch.builder.feature @@ -1,3 +1,22 @@ +# +# 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. +# + Feature: Batch Service @batch diff --git a/fineract-provider/src/test/resources/features/commands/commands.provider.feature b/fineract-provider/src/test/resources/features/commands/commands.provider.feature index 450134514..34fb1518e 100644 --- a/fineract-provider/src/test/resources/features/commands/commands.provider.feature +++ b/fineract-provider/src/test/resources/features/commands/commands.provider.feature @@ -1,3 +1,22 @@ +# +# 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. +# + Feature: Commands Service @template diff --git a/fineract-provider/src/test/resources/features/infrastructure/infrastructure.classpath.feature b/fineract-provider/src/test/resources/features/infrastructure/infrastructure.classpath.feature index 8ee4b9b97..6a03cccfa 100644 --- a/fineract-provider/src/test/resources/features/infrastructure/infrastructure.classpath.feature +++ b/fineract-provider/src/test/resources/features/infrastructure/infrastructure.classpath.feature @@ -1,3 +1,22 @@ +# +# 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. +# + Feature: Classpath duplicates @infrastructure diff --git a/fineract-provider/src/test/resources/features/infrastructure/infrastructure.core.feature b/fineract-provider/src/test/resources/features/infrastructure/infrastructure.core.feature index e78bd0e76..a7ebd8279 100644 --- a/fineract-provider/src/test/resources/features/infrastructure/infrastructure.core.feature +++ b/fineract-provider/src/test/resources/features/infrastructure/infrastructure.core.feature @@ -1,3 +1,22 @@ +# +# 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. +# + Feature: Core Infrastructure @infrastructure diff --git a/fineract-provider/src/test/resources/features/infrastructure/infrastructure.sqlbuilder.feature b/fineract-provider/src/test/resources/features/infrastructure/infrastructure.sqlbuilder.feature index c5fdf8530..23044a0c9 100644 --- a/fineract-provider/src/test/resources/features/infrastructure/infrastructure.sqlbuilder.feature +++ b/fineract-provider/src/test/resources/features/infrastructure/infrastructure.sqlbuilder.feature @@ -1,3 +1,22 @@ +# +# 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. +# + Feature: SQL Builder @sqlbuilder diff --git a/fineract-provider/src/test/resources/features/mix/mix.report.feature b/fineract-provider/src/test/resources/features/mix/mix.report.feature index 54c7d651d..bac3f143b 100644 --- a/fineract-provider/src/test/resources/features/mix/mix.report.feature +++ b/fineract-provider/src/test/resources/features/mix/mix.report.feature @@ -1,3 +1,22 @@ +# +# 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. +# + Feature: MIX XBRL @mix diff --git a/fineract-provider/src/test/resources/features/module/modules.example.feature b/fineract-provider/src/test/resources/features/module/modules.example.feature index ad2be04b8..c4454a4f1 100644 --- a/fineract-provider/src/test/resources/features/module/modules.example.feature +++ b/fineract-provider/src/test/resources/features/module/modules.example.feature @@ -1,3 +1,22 @@ +# +# 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. +# + Feature: Example Modules @modules diff --git a/fineract-provider/src/test/resources/features/module/modules.service.feature b/fineract-provider/src/test/resources/features/module/modules.service.feature index 6aee6ba4f..e2cc191a4 100644 --- a/fineract-provider/src/test/resources/features/module/modules.service.feature +++ b/fineract-provider/src/test/resources/features/module/modules.service.feature @@ -1,3 +1,22 @@ +# +# 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. +# + Feature: Service Modules @modules diff --git a/fineract-provider/src/test/resources/features/portfolio/loanproduct.validation.feature b/fineract-provider/src/test/resources/features/portfolio/loanproduct.validation.feature index 0167830fa..36a7a0f2b 100644 --- a/fineract-provider/src/test/resources/features/portfolio/loanproduct.validation.feature +++ b/fineract-provider/src/test/resources/features/portfolio/loanproduct.validation.feature @@ -1,3 +1,22 @@ +# +# 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. +# + Feature: Loan Product Validation @loanproduct diff --git a/fineract-provider/src/test/resources/features/template/template.service.feature b/fineract-provider/src/test/resources/features/template/template.service.feature index 53152ce00..e0e168d94 100644 --- a/fineract-provider/src/test/resources/features/template/template.service.feature +++ b/fineract-provider/src/test/resources/features/template/template.service.feature @@ -1,3 +1,22 @@ +# +# 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. +# + Feature: Template Service @template diff --git a/gradle.properties b/gradle.properties index 0da3f472a..7d886728a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -20,3 +20,4 @@ org.gradle.jvmargs=-Xmx2g --add-exports jdk.compiler/com.sun.tools.javac.api=ALL buildType=BUILD org.gradle.caching=true org.gradle.parallel=true +org.gradle.logging.level=warn From b42423b941ab2fc28e6996c306ff4bc41a35e28f Mon Sep 17 00:00:00 2001 From: Aleksandar Vidakovic Date: Wed, 11 May 2022 16:38:29 +0200 Subject: [PATCH 04/11] Last minute release plugin fixes --- .../groovy/org.apache.fineract.release.gradle | 9 +++++-- .../fineract/gradle/FineractPlugin.groovy | 12 ++++----- .../gradle/FineractPluginExtension.groovy | 2 +- .../fineract/gradle/service/GpgService.groovy | 26 +++++++++++++------ fineract-war/build.gradle | 17 +++++------- 5 files changed, 37 insertions(+), 29 deletions(-) diff --git a/buildSrc/src/main/groovy/org.apache.fineract.release.gradle b/buildSrc/src/main/groovy/org.apache.fineract.release.gradle index 50bd11a9d..3345ff886 100644 --- a/buildSrc/src/main/groovy/org.apache.fineract.release.gradle +++ b/buildSrc/src/main/groovy/org.apache.fineract.release.gradle @@ -111,7 +111,9 @@ fineract { templateFile: "${projectDir}/buildSrc/src/main/resources/jira/changelog.txt.ftl" ], gpg: [ - file: "${projectDir}/buildSrc/build/libs/buildSrc.jar" + files: [ + "${projectDir}/buildSrc/build/libs/buildSrc.jar" + ] ] ], step1: [ @@ -173,7 +175,10 @@ fineract { order: 7, description: 'Sign the distribution artifacts', gpg: [ - file: "${rootDir}/fineract-war/build/distributions/apache-fineract-binary-${project.version}.tar.gz" + files: [ + "${rootDir}/fineract-war/build/distributions/apache-fineract-${project.version}-src.tar.gz", + "${rootDir}/fineract-war/build/distributions/apache-fineract-${project.version}-binary.tar.gz" + ] ] ], step8: [ diff --git a/buildSrc/src/main/groovy/org/apache/fineract/gradle/FineractPlugin.groovy b/buildSrc/src/main/groovy/org/apache/fineract/gradle/FineractPlugin.groovy index fb308c7a2..305952da8 100644 --- a/buildSrc/src/main/groovy/org/apache/fineract/gradle/FineractPlugin.groovy +++ b/buildSrc/src/main/groovy/org/apache/fineract/gradle/FineractPlugin.groovy @@ -324,7 +324,7 @@ class FineractPlugin implements Plugin { // step 6 project.tasks.register("fineractReleaseStep6") { - dependsOn(":fineract-war:binaryDistTar") + dependsOn(":fineract-war:distTar") } // step 7 @@ -334,13 +334,11 @@ class FineractPlugin implements Plugin { gpgService.sign(step.gpg) - def md5 = gpgService.md5(step.gpg) - def md5File = new File("${step.gpg.file}.md5") - md5File.write md5 + step.gpg.files.findAll { + gpgService.md5(step.gpg) - def sha512 = gpgService.sha512(step.gpg) - def sha512File = new File("${step.gpg.file}.sha512") - sha512File.write sha512 + gpgService.sha512(step.gpg) + } } } diff --git a/buildSrc/src/main/groovy/org/apache/fineract/gradle/FineractPluginExtension.groovy b/buildSrc/src/main/groovy/org/apache/fineract/gradle/FineractPluginExtension.groovy index fad37e071..b38834f33 100644 --- a/buildSrc/src/main/groovy/org/apache/fineract/gradle/FineractPluginExtension.groovy +++ b/buildSrc/src/main/groovy/org/apache/fineract/gradle/FineractPluginExtension.groovy @@ -159,6 +159,6 @@ class FineractPluginExtension { } static class FineractPluginGpgParams { - String file + List files } } diff --git a/buildSrc/src/main/groovy/org/apache/fineract/gradle/service/GpgService.groovy b/buildSrc/src/main/groovy/org/apache/fineract/gradle/service/GpgService.groovy index 7e8c6470f..9750c128a 100644 --- a/buildSrc/src/main/groovy/org/apache/fineract/gradle/service/GpgService.groovy +++ b/buildSrc/src/main/groovy/org/apache/fineract/gradle/service/GpgService.groovy @@ -92,11 +92,13 @@ class GpgService { } void sign(FineractPluginExtension.FineractPluginGpgParams params) { - InputStream is = new FileInputStream(params.file) + params.files.findAll { + InputStream is = new FileInputStream(it) - BCPGOutputStream os = new BCPGOutputStream(new ArmoredOutputStream(new FileOutputStream(params.file + ".asc"))) - sign(is).encode(os) - os.close(); + BCPGOutputStream os = new BCPGOutputStream(new ArmoredOutputStream(new FileOutputStream(it + ".asc"))) + sign(is).encode(os) + os.close(); + } } PGPSignature sign(InputStream is) throws IOException, PGPException, GeneralSecurityException { @@ -115,12 +117,20 @@ class GpgService { } - String md5(FineractPluginExtension.FineractPluginGpgParams params) { - return calc(new FileInputStream(params.file), MessageDigest.getInstance("MD5", BouncyCastleProvider.PROVIDER_NAME)) + 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 + } } - String sha512(FineractPluginExtension.FineractPluginGpgParams params) { - return calc(new FileInputStream(params.file), MessageDigest.getInstance("SHA-512", BouncyCastleProvider.PROVIDER_NAME)) + void sha512(FineractPluginExtension.FineractPluginGpgParams params) { + params.files.findAll { + def result = calc(new FileInputStream(it), MessageDigest.getInstance("SHA-512", BouncyCastleProvider.PROVIDER_NAME)) + def file = new File("${it}.sha512") + file.write result + } } private static String calc(InputStream is, MessageDigest digest) { diff --git a/fineract-war/build.gradle b/fineract-war/build.gradle index aca955e22..1690079ac 100644 --- a/fineract-war/build.gradle +++ b/fineract-war/build.gradle @@ -58,11 +58,7 @@ tasks.withType(Tar) { distributions { binary { - if(project.hasProperty("distVersion")) { - distributionBaseName = "apache-fineract-$distVersion-binary" - } else { - distributionBaseName = "apache-fineract-binary" - } + distributionBaseName = 'apache-fineract-binary' contents { from ("$rootDir/fineract-client/build/libs/") { include 'fineract-client-*.jar' @@ -88,12 +84,7 @@ distributions { } } src { - if(project.hasProperty("distVersion")) { - distributionBaseName = "apache-fineract-$distVersion-src" - } else { - distributionBaseName = "apache-fineract-src" - } - + distributionBaseName = 'apache-fineract-src' contents { from "$rootDir/" exclude '**/build' , '.git', '**/.gradle', '.github', '**/.settings', '**/.project', '**/.classpath', '.idea', 'out', '._.DS_Store', '.DS_Store', 'WebContent', '**/.externalToolbuilders', '.theia', '.gitpod.yml', '.travis.yml', 'LICENSE_RELEASE', 'NOTICE_RELEASE', '**/licenses', '*.class', '**/bin', '*.log', '.dockerignore', '**/.gitkeep' @@ -101,6 +92,10 @@ distributions { rename ('NOTICE_SOURCE', 'NOTICE') } } + doLast { + file("${buildDir}/distributions/apache-fineract-binary-${version}.tar.gz").renameTo("${buildDir}/distributions/apache-fineract-${version}-binary.tar.gz") + file("${buildDir}/distributions/apache-fineract-src-${version}.tar.gz").renameTo("${buildDir}/distributions/apache-fineract-${version}-src.tar.gz") + } } binaryDistZip.enabled false From edc734c946862e6a6818d6b6211c6fbf026316fc Mon Sep 17 00:00:00 2001 From: logoutdhaval <100835289+logoutdhaval@users.noreply.github.com> Date: Fri, 5 Aug 2022 16:36:40 +0530 Subject: [PATCH 05/11] FINERACT-1668: Adding Configuration for reversal transaction (#2454) Co-authored-by: Dhaval Maniyar --- .../domain/ConfigurationDomainService.java | 2 + .../domain/ConfigurationDomainServiceJpa.java | 7 ++ .../DepositAccountDomainServiceJpa.java | 11 ++-- .../savings/domain/FixedDepositAccount.java | 3 + .../domain/RecurringDepositAccount.java | 19 +++--- .../savings/domain/SavingsAccount.java | 5 +- .../SavingsAccountDomainServiceJpa.java | 11 ++-- ...WritePlatformServiceJpaRepositoryImpl.java | 34 +++++----- .../db/changelog/tenant/changelog-tenant.xml | 1 + ...26_reversals_for_reversed_transactions.xml | 37 +++++++++++ .../ClientSavingsIntegrationTest.java | 64 +++++++++++++++++++ .../common/GlobalConfigurationHelper.java | 12 +++- 12 files changed, 162 insertions(+), 44 deletions(-) create mode 100644 fineract-provider/src/main/resources/db/changelog/tenant/parts/0026_reversals_for_reversed_transactions.xml diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainService.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainService.java index 73761fbcc..14b92f646 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainService.java @@ -116,4 +116,6 @@ public interface ConfigurationDomainService { boolean isBusinessDateEnabled(); boolean isCOBDateAdjustmentEnabled(); + + boolean isReversalTransactionAllowed(); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java index 6dd1a1ab8..d77071f55 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java @@ -444,4 +444,11 @@ public class ConfigurationDomainServiceJpa implements ConfigurationDomainService public boolean isCOBDateAdjustmentEnabled() { return getGlobalConfigurationPropertyData(ENABLE_AUTOMATIC_COB_DATE_ADJUSTMENT).isEnabled(); } + + @Override + public boolean isReversalTransactionAllowed() { + final String propertyName = "enable-post-reversal-txns-for-reverse-transactions"; + final GlobalConfigurationPropertyData property = getGlobalConfigurationPropertyData(propertyName); + return property.isEnabled(); + } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/DepositAccountDomainServiceJpa.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/DepositAccountDomainServiceJpa.java index 85f49ef40..1f1d09bc5 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/DepositAccountDomainServiceJpa.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/DepositAccountDomainServiceJpa.java @@ -140,7 +140,6 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService .isSavingsInterestPostingAtCurrentPeriodEnd(); final Integer financialYearBeginningMonth = this.configurationDomainService.retrieveFinancialYearBeginningMonth(); - boolean isAccountTransfer = false; final boolean isPreMatureClosure = false; final MathContext mc = MathContext.DECIMAL64; @@ -348,7 +347,7 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService .isSavingsInterestPostingAtCurrentPeriodEnd(); final Integer financialYearBeginningMonth = this.configurationDomainService.retrieveFinancialYearBeginningMonth(); - + final boolean postReversals = false; boolean isRegularTransaction = false; boolean isAccountTransfer = false; final boolean isPreMatureClosure = false; @@ -364,7 +363,7 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi final DateTimeFormatter fmt = DateTimeFormatter.ofPattern(command.dateFormat()).withLocale(locale); final LocalDate closedDate = command.localDateValueOfParameterNamed(SavingsApiConstants.closedOnDateParamName); Long savingsTransactionId = null; - account.postMaturityInterest(isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, closedDate); + account.postMaturityInterest(isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, closedDate, postReversals); final BigDecimal transactionAmount = account.getAccountBalance(); final Integer onAccountClosureId = command.integerValueOfParameterNamed(onAccountClosureIdParamName); final DepositAccountOnClosureType onClosureType = DepositAccountOnClosureType.fromInt(onAccountClosureId); @@ -385,7 +384,7 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi Integer frequency = CalendarUtils.getInterval(calendar.getRecurrence()); frequency = frequency == -1 ? 1 : frequency; reinvestedDeposit.generateSchedule(frequencyType, frequency, calendar); - reinvestedDeposit.processAccountUponActivation(fmt, user); + reinvestedDeposit.processAccountUponActivation(fmt, user, postReversals); reinvestedDeposit.updateMaturityDateAndAmount(mc, isPreMatureClosure, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth); this.savingsAccountRepository.save(reinvestedDeposit); @@ -520,7 +519,7 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService .isSavingsInterestPostingAtCurrentPeriodEnd(); final Integer financialYearBeginningMonth = this.configurationDomainService.retrieveFinancialYearBeginningMonth(); - + final boolean postReversals = false; boolean isAccountTransfer = false; final boolean isPreMatureClosure = true; boolean isRegularTransaction = false; @@ -537,7 +536,7 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi Long savingsTransactionId = null; // post interest account.postPreMaturityInterest(closedDate, isPreMatureClosure, isSavingsInterestPostingAtCurrentPeriodEnd, - financialYearBeginningMonth); + financialYearBeginningMonth, postReversals); final Integer closureTypeValue = command.integerValueOfParameterNamed(DepositsApiConstants.onAccountClosureIdParamName); DepositAccountOnClosureType closureType = DepositAccountOnClosureType.fromInt(closureTypeValue); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/FixedDepositAccount.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/FixedDepositAccount.java index a306b3a9b..60eec4dc7 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/FixedDepositAccount.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/FixedDepositAccount.java @@ -39,6 +39,7 @@ import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.OneToOne; import javax.persistence.Transient; +import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService; import org.apache.fineract.infrastructure.core.api.JsonCommand; import org.apache.fineract.infrastructure.core.data.ApiParameterError; import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder; @@ -77,6 +78,8 @@ public class FixedDepositAccount extends SavingsAccount { @Transient protected InterestRateChartAssembler chartAssembler; + @Transient + private ConfigurationDomainService configurationDomainService; protected FixedDepositAccount() { // diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/RecurringDepositAccount.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/RecurringDepositAccount.java index 98e1a4823..7d59e1825 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/RecurringDepositAccount.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/RecurringDepositAccount.java @@ -270,11 +270,11 @@ public class RecurringDepositAccount extends SavingsAccount { } } - public void updateMaturityStatus(final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth) { + public void updateMaturityStatus(final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth, + final boolean postReversals) { final List dataValidationErrors = new ArrayList<>(); final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) .resource(RECURRING_DEPOSIT_ACCOUNT_RESOURCE_NAME + SavingsApiConstants.updateMaturityDetailsAction); - final SavingsAccountStatusType currentStatus = SavingsAccountStatusType.fromInt(this.status); if (!SavingsAccountStatusType.ACTIVE.hasStateOf(currentStatus)) { baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("not.in.active.state"); @@ -287,7 +287,7 @@ public class RecurringDepositAccount extends SavingsAccount { if (!this.maturityDate().isAfter(todayDate)) { // update account status this.status = SavingsAccountStatusType.MATURED.getValue(); - postMaturityInterest(isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, todayDate); + postMaturityInterest(isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, todayDate, postReversals); } } @@ -537,7 +537,7 @@ public class RecurringDepositAccount extends SavingsAccount { return Money.of(this.currency, this.minRequiredOpeningBalance); } - protected void processAccountUponActivation(final DateTimeFormatter fmt, final AppUser user) { + protected void processAccountUponActivation(final DateTimeFormatter fmt, final AppUser user, final boolean postReversals) { final Money minRequiredOpeningBalance = Money.of(this.currency, this.minRequiredOpeningBalance); final boolean backdatedTxnsAllowedTill = false; String refNo = null; @@ -549,7 +549,6 @@ public class RecurringDepositAccount extends SavingsAccount { // update existing transactions so derived balance fields are // correct. - boolean postReversals = false; recalculateDailyBalances(Money.zero(this.currency), DateUtils.getBusinessLocalDate(), backdatedTxnsAllowedTill, postReversals); } } @@ -635,7 +634,7 @@ public class RecurringDepositAccount extends SavingsAccount { } public void postMaturityInterest(final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth, - final LocalDate closeDate) { + final LocalDate closeDate, final boolean postReversals) { LocalDate interestPostingUpToDate = maturityDate(); if (interestPostingUpToDate == null) { interestPostingUpToDate = closeDate; @@ -645,7 +644,6 @@ public class RecurringDepositAccount extends SavingsAccount { boolean isInterestTransfer = false; LocalDate postInterestOnDate = null; final boolean backdatedTxnsAllowedTill = false; - boolean postReversals = false; final List postingPeriods = calculateInterestUsing(mc, interestPostingUpToDate.minusDays(1), isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, postInterestOnDate, backdatedTxnsAllowedTill, postReversals); @@ -690,7 +688,7 @@ public class RecurringDepositAccount extends SavingsAccount { } public void postPreMaturityInterest(final LocalDate accountCloseDate, final boolean isPreMatureClosure, - final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth) { + final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth, boolean postReversals) { final Money interestPostedToDate = totalInterestPosted(); // calculate interest before one day of closure date @@ -713,7 +711,6 @@ public class RecurringDepositAccount extends SavingsAccount { } applyWithholdTaxForDepositAccounts(accountCloseDate, recalucateDailyBalance, backdatedTxnsAllowedTill); - boolean postReversals = false; if (recalucateDailyBalance) { // update existing transactions so derived balance fields are // correct. @@ -755,11 +752,11 @@ public class RecurringDepositAccount extends SavingsAccount { return interestOnMaturity; } + @Override public void postInterest(final MathContext mc, final LocalDate postingDate, final boolean isInterestTransfer, final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth, - final LocalDate postInterestAson, final boolean backdatedTxnsAllowedTill) { + final LocalDate postInterestAson, final boolean backdatedTxnsAllowedTill, final boolean postReversals) { final LocalDate interestPostingUpToDate = interestPostingUpToDate(postingDate); - boolean postReversals = false; super.postInterest(mc, interestPostingUpToDate, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, postInterestAson, backdatedTxnsAllowedTill, postReversals); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccount.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccount.java index 90fbb967f..79cba8ee1 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccount.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccount.java @@ -977,7 +977,6 @@ public class SavingsAccount extends AbstractPersistableCustom { protected void recalculateDailyBalances(final Money openingAccountBalance, final LocalDate interestPostingUpToDate, final boolean backdatedTxnsAllowedTill, boolean postReversals) { - Money runningBalance = openingAccountBalance.copy(); List accountTransactionsSorted = null; @@ -2775,6 +2774,7 @@ public class SavingsAccount extends AbstractPersistableCustom { private void payActivationCharges(final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth, final AppUser user, final boolean backdatedTxnsAllowedTill) { boolean isSavingsChargeApplied = false; + boolean postReversals = false; UUID refNo = UUID.randomUUID(); for (SavingsAccountCharge savingsAccountCharge : this.charges()) { if (savingsAccountCharge.isSavingsActivation()) { @@ -2788,7 +2788,6 @@ public class SavingsAccount extends AbstractPersistableCustom { final MathContext mc = MathContext.DECIMAL64; boolean isInterestTransfer = false; LocalDate postInterestAsOnDate = null; - boolean postReversals = false; if (this.isBeforeLastPostingPeriod(getActivationLocalDate(), backdatedTxnsAllowedTill)) { final LocalDate today = DateUtils.getBusinessLocalDate(); this.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, @@ -3551,12 +3550,12 @@ public class SavingsAccount extends AbstractPersistableCustom { this.closedOnDate = DateUtils.getBusinessLocalDate(); this.closedBy = appUser; boolean postInterestAsOnDate = false; + boolean postReversals = false; LocalDate transactionDate = DateUtils.getBusinessLocalDate(); if (this.getSummary().getAccountBalance(this.getCurrency()).isGreaterThanZero()) { SavingsAccountTransaction transaction = SavingsAccountTransaction.escheat(this, transactionDate, appUser, postInterestAsOnDate); this.transactions.add(transaction); } - boolean postReversals = false; recalculateDailyBalances(Money.zero(this.currency), transactionDate, false, postReversals); this.summary.updateSummary(this.currency, this.savingsAccountTransactionSummaryWrapper, this.transactions); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountDomainServiceJpa.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountDomainServiceJpa.java index 555585145..45436ce42 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountDomainServiceJpa.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountDomainServiceJpa.java @@ -92,6 +92,7 @@ public class SavingsAccountDomainServiceJpa implements SavingsAccountDomainServi final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService .isSavingsInterestPostingAtCurrentPeriodEnd(); final Long relaxingDaysConfigForPivotDate = this.configurationDomainService.retrieveRelaxingDaysConfigForPivotDate(); + final boolean postReversals = this.configurationDomainService.isReversalTransactionAllowed(); final Integer financialYearBeginningMonth = this.configurationDomainService.retrieveFinancialYearBeginningMonth(); if (transactionBooleanValues.isRegularTransaction() && !account.allowWithdrawal()) { throw new DepositAccountTransactionNotAllowedException(account.getId(), "withdraw", account.depositAccountType()); @@ -115,7 +116,7 @@ public class SavingsAccountDomainServiceJpa implements SavingsAccountDomainServi final MathContext mc = MathContext.DECIMAL64; final LocalDate today = DateUtils.getBusinessLocalDate(); - boolean postReversals = false; + if (account.isBeforeLastPostingPeriod(transactionDate, backdatedTxnsAllowedTill)) { account.postInterest(mc, today, transactionBooleanValues.isInterestTransfer(), isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, postInterestOnDate, backdatedTxnsAllowedTill, postReversals); @@ -202,7 +203,7 @@ public class SavingsAccountDomainServiceJpa implements SavingsAccountDomainServi final MathContext mc = MathContext.DECIMAL64; final LocalDate today = DateUtils.getBusinessLocalDate(); - boolean postReversals = false; + boolean postReversals = this.configurationDomainService.isReversalTransactionAllowed(); if (account.isBeforeLastPostingPeriod(transactionDate, backdatedTxnsAllowedTill)) { account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, postInterestOnDate, backdatedTxnsAllowedTill, postReversals); @@ -297,6 +298,7 @@ public class SavingsAccountDomainServiceJpa implements SavingsAccountDomainServi .isSavingsInterestPostingAtCurrentPeriodEnd(); final Integer financialYearBeginningMonth = this.configurationDomainService.retrieveFinancialYearBeginningMonth(); final Long relaxingDaysConfigForPivotDate = this.configurationDomainService.retrieveRelaxingDaysConfigForPivotDate(); + final boolean postReversals = true; final Set existingTransactionIds = new HashSet<>(); final Set existingReversedTransactionIds = new HashSet<>(); @@ -314,7 +316,9 @@ public class SavingsAccountDomainServiceJpa implements SavingsAccountDomainServi chargePaidBySet = savingsAccountTransaction.getSavingsAccountChargesPaid(); reversal.getSavingsAccountChargesPaid().addAll(chargePaidBySet); account.undoTransaction(savingsAccountTransaction); - newTransactions.add(reversal); + if (postReversals) { + newTransactions.add(reversal); + } } boolean isInterestTransfer = false; @@ -322,7 +326,6 @@ public class SavingsAccountDomainServiceJpa implements SavingsAccountDomainServi final LocalDate today = DateUtils.getBusinessLocalDate(); final MathContext mc = new MathContext(15, MoneyHelper.getRoundingMode()); for (SavingsAccountTransaction savingsAccountTransaction : savingsAccountTransactions) { - boolean postReversals = true; if (savingsAccountTransaction.isPostInterestCalculationRequired() && account.isBeforeLastPostingPeriod(savingsAccountTransaction.transactionLocalDate(), backdatedTxnsAllowedTill)) { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/DepositAccountWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/DepositAccountWritePlatformServiceJpaRepositoryImpl.java index 3aab8ef0a..1a4bceaf3 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/DepositAccountWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/DepositAccountWritePlatformServiceJpaRepositoryImpl.java @@ -201,7 +201,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService .isSavingsInterestPostingAtCurrentPeriodEnd(); final Integer financialYearBeginningMonth = this.configurationDomainService.retrieveFinancialYearBeginningMonth(); - + final boolean postReversals = false; this.depositAccountTransactionDataValidator.validateActivation(command); final MathContext mc = MathContext.DECIMAL64; final FixedDepositAccount account = (FixedDepositAccount) this.depositAccountAssembler.assembleFrom(savingsId, @@ -239,7 +239,6 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo } final boolean isInterestTransfer = false; final LocalDate postInterestOnDate = null; - final boolean postReversals = false; if (activationChargeAmount.isGreaterThanZero()) { payActivationCharge(account, user); } @@ -309,7 +308,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService .isSavingsInterestPostingAtCurrentPeriodEnd(); final Integer financialYearBeginningMonth = this.configurationDomainService.retrieveFinancialYearBeginningMonth(); - + final boolean postReversals = false; this.depositAccountTransactionDataValidator.validateActivation(command); final RecurringDepositAccount account = (RecurringDepositAccount) this.depositAccountAssembler.assembleFrom(savingsId, @@ -367,11 +366,10 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo account.updateOverduePayments(overdueUptoDate); final boolean isInterestTransfer = false; final LocalDate postInterestOnDate = null; - boolean postReversals = false; if (account.isBeforeLastPostingPeriod(account.getActivationLocalDate(), false)) { final LocalDate today = DateUtils.getBusinessLocalDate(); account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, - postInterestOnDate, false); + postInterestOnDate, false, postReversals); } else { final LocalDate today = DateUtils.getBusinessLocalDate(); account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, @@ -523,7 +521,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo checkClientOrGroupActive(account); final LocalDate today = DateUtils.getBusinessLocalDate(); - boolean postReversals = false; + final boolean postReversals = false; final MathContext mc = new MathContext(15, MoneyHelper.getRoundingMode()); boolean isInterestTransfer = false; LocalDate postInterestOnDate = null; @@ -571,7 +569,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo final MathContext mc = new MathContext(10, MoneyHelper.getRoundingMode()); boolean isInterestTransfer = false; LocalDate postInterestOnDate = null; - boolean postReversals = false; + final boolean postReversals = false; account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, postInterestOnDate, false, postReversals); this.savingAccountRepositoryWrapper.saveAndFlush(account); @@ -646,11 +644,11 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo boolean isInterestTransfer = false; LocalDate postInterestOnDate = null; checkClientOrGroupActive(account); - boolean postReversals = false; + final boolean postReversals = false; if (savingsAccountTransaction.isPostInterestCalculationRequired() && account.isBeforeLastPostingPeriod(savingsAccountTransaction.transactionLocalDate(), false)) { account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, - postInterestOnDate, false); + postInterestOnDate, false, postReversals); } else { account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, postInterestOnDate, false, postReversals); @@ -759,11 +757,11 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo final Long newtransactionId = saveTransactionToGenerateTransactionId(transaction); boolean isInterestTransfer = false; final LocalDate postInterestOnDate = null; - boolean postReversals = false; + final boolean postReversals = false; if (account.isBeforeLastPostingPeriod(transactionDate, false) || account.isBeforeLastPostingPeriod(savingsAccountTransaction.transactionLocalDate(), false)) { account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, - postInterestOnDate, false); + postInterestOnDate, false, postReversals); } else { account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, postInterestOnDate, false, postReversals); @@ -988,7 +986,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo savingsAccount.setStatus(SavingsAccountStatusType.TRANSFER_IN_PROGRESS.getValue()); final MathContext mc = MathContext.DECIMAL64; boolean isInterestTransfer = false; - boolean postReversals = false; + final boolean postReversals = false; savingsAccount.calculateInterestUsing(mc, transferDate, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, postInterestOnDate, false, postReversals); @@ -1020,7 +1018,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo savingsAccount.office(), transferDate, user); savingsAccount.addTransaction(withdrawtransferTransaction); savingsAccount.setStatus(SavingsAccountStatusType.ACTIVE.getValue()); - boolean postReversals = false; + final boolean postReversals = false; final MathContext mc = MathContext.DECIMAL64; boolean isInterestTransfer = false; LocalDate postInterestOnDate = null; @@ -1066,7 +1064,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo savingsAccount.reassignSavingsOfficer(fieldOfficer, transferDate); } boolean isInterestTransfer = false; - boolean postReversals = false; + final boolean postReversals = false; LocalDate postInterestOnDate = null; final MathContext mc = MathContext.DECIMAL64; savingsAccount.calculateInterestUsing(mc, transferDate, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, @@ -1223,7 +1221,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo boolean isInterestTransfer = false; LocalDate postInterestOnDate = null; final MathContext mc = MathContext.DECIMAL64; - boolean postReversals = false; + final boolean postReversals = false; if (account.isBeforeLastPostingPeriod(savingsAccountCharge.getDueLocalDate(), false)) { final LocalDate today = DateUtils.getBusinessLocalDate(); account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, @@ -1363,7 +1361,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo boolean isInterestTransfer = false; LocalDate postInterestOnDate = null; final MathContext mc = MathContext.DECIMAL64; - boolean postReversals = false; + final boolean postReversals = false; if (account.isBeforeLastPostingPeriod(transactionDate, false)) { final LocalDate today = DateUtils.getBusinessLocalDate(); account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, @@ -1394,7 +1392,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService .isSavingsInterestPostingAtCurrentPeriodEnd(); final Integer financialYearBeginningMonth = this.configurationDomainService.retrieveFinancialYearBeginningMonth(); - + final boolean postReversals = false; final SavingsAccount account = this.depositAccountAssembler.assembleFrom(depositAccountId, depositAccountType); final Set existingTransactionIds = new HashSet<>(); final Set existingReversedTransactionIds = new HashSet<>(); @@ -1429,7 +1427,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo } } else if (depositAccountType.isRecurringDeposit()) { ((RecurringDepositAccount) account).updateMaturityStatus(isSavingsInterestPostingAtCurrentPeriodEnd, - financialYearBeginningMonth); + financialYearBeginningMonth, postReversals); } this.savingAccountRepositoryWrapper.saveAndFlush(account); postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds); diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml index 25e775ef4..f8cb165ca 100644 --- a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml +++ b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml @@ -40,4 +40,5 @@ + diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/parts/0026_reversals_for_reversed_transactions.xml b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0026_reversals_for_reversed_transactions.xml new file mode 100644 index 000000000..1fdbf358d --- /dev/null +++ b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0026_reversals_for_reversed_transactions.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientSavingsIntegrationTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientSavingsIntegrationTest.java index d12abc8a3..a98a796a2 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientSavingsIntegrationTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientSavingsIntegrationTest.java @@ -2989,6 +2989,70 @@ public class ClientSavingsIntegrationTest { assertEquals("error.msg.savings.transaction.is.not.allowed", error.get(0).get(CommonConstants.RESPONSE_ERROR_MESSAGE_CODE)); } + @Test + public void testReversalEntriesAfterSystemReversingTransactionWithReversalConfigOn() { + this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec, this.responseSpec); + this.savingsProductHelper = new SavingsProductHelper(); + this.scheduleJobHelper = new SchedulerJobHelper(requestSpec); + GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(this.requestSpec, this.responseSpec, "46", true); + LocalDate transactionDate = LocalDate.now(Utils.getZoneIdOfTenant()).minusDays(5); + LocalDate nextTransactionDate = transactionDate.plusDays(2); + final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd MMMM yyyy"); + String startDate = formatter.format(transactionDate); + String nxtTransaction = formatter.format(nextTransactionDate); + final String jobName = "Post Interest For Savings"; + final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec, startDate); + Assertions.assertNotNull(clientID); + + final Integer savingsId = createSavingsAccountDailyPostingOverdraft(clientID, startDate); + this.savingsAccountHelper.withdrawalFromSavingsAccount(savingsId, "100", startDate, CommonConstants.RESPONSE_RESOURCE_ID); + this.scheduleJobHelper.executeAndAwaitJob(jobName); + this.savingsAccountHelper.withdrawalFromSavingsAccount(savingsId, "100", nxtTransaction, CommonConstants.RESPONSE_RESOURCE_ID); + + List transactions = this.savingsAccountHelper.getSavingsTransactions(savingsId); + boolean reversalFlag = false; + for (int i = 0; i < transactions.size(); i++) { + boolean isReversal = (boolean) transactions.get(i).get("isReversal"); + if (isReversal) { + reversalFlag = true; + break; + } + } + Assertions.assertTrue(reversalFlag); + } + + @Test + public void testReversalEntriesAfterSystemReversingTransactionWithReversalConfigOff() { + this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec, this.responseSpec); + this.savingsProductHelper = new SavingsProductHelper(); + this.scheduleJobHelper = new SchedulerJobHelper(requestSpec); + GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(this.requestSpec, this.responseSpec, "46", false); + LocalDate transactionDate = LocalDate.now(Utils.getZoneIdOfTenant()).minusDays(5); + LocalDate nextTransactionDate = transactionDate.plusDays(2); + final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd MMMM yyyy"); + String startDate = formatter.format(transactionDate); + String nxtTransaction = formatter.format(nextTransactionDate); + final String jobName = "Post Interest For Savings"; + final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec, startDate); + Assertions.assertNotNull(clientID); + + final Integer savingsId = createSavingsAccountDailyPostingOverdraft(clientID, startDate); + this.savingsAccountHelper.withdrawalFromSavingsAccount(savingsId, "100", startDate, CommonConstants.RESPONSE_RESOURCE_ID); + this.scheduleJobHelper.executeAndAwaitJob(jobName); + this.savingsAccountHelper.withdrawalFromSavingsAccount(savingsId, "100", nxtTransaction, CommonConstants.RESPONSE_RESOURCE_ID); + + List transactions = this.savingsAccountHelper.getSavingsTransactions(savingsId); + boolean reversalFlag = false; + for (int i = 0; i < transactions.size(); i++) { + boolean isReversal = (boolean) transactions.get(i).get("isReversal"); + if (isReversal) { + reversalFlag = true; + break; + } + } + Assertions.assertFalse(reversalFlag); + } + private Integer createSavingsAccountDailyPostingOverdraft(final Integer clientID, final String startDate) { final Integer savingsProductID = createSavingsProductDailyPostingOverdraft(); Assertions.assertNotNull(savingsProductID); diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/GlobalConfigurationHelper.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/GlobalConfigurationHelper.java index 12c3e4cc3..231168ce9 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/GlobalConfigurationHelper.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/GlobalConfigurationHelper.java @@ -99,8 +99,8 @@ public class GlobalConfigurationHelper { ArrayList actualGlobalConfigurations = getAllGlobalConfigurations(requestSpec, responseSpec); // There are currently 37 global configurations. - Assertions.assertEquals(40, expectedGlobalConfigurations.size()); - Assertions.assertEquals(40, actualGlobalConfigurations.size()); + Assertions.assertEquals(41, expectedGlobalConfigurations.size()); + Assertions.assertEquals(41, actualGlobalConfigurations.size()); for (int i = 0; i < expectedGlobalConfigurations.size(); i++) { @@ -456,6 +456,14 @@ public class GlobalConfigurationHelper { isAutomaticCOBDateAdjustmentEnabled.put("trapDoor", false); defaults.add(isAutomaticCOBDateAdjustmentEnabled); + HashMap isReversalTransactionAllowed = new HashMap<>(); + isReversalTransactionAllowed.put("id", 46); + isReversalTransactionAllowed.put("name", "enable-post-reversal-txns-for-reverse-transactions"); + isReversalTransactionAllowed.put("value", 0); + isReversalTransactionAllowed.put("enabled", false); + isReversalTransactionAllowed.put("trapDoor", false); + defaults.add(isReversalTransactionAllowed); + return defaults; } From c3b462919fed1206bd8a6d3255d3591f84970f2c Mon Sep 17 00:00:00 2001 From: Dhaval Maniyar Date: Sat, 30 Jul 2022 22:08:59 +0530 Subject: [PATCH 06/11] FINERACT-1646-backdated-transaction-available-balance-fix --- .../savings/data/SavingsAccountData.java | 10 ++++++++ .../data/SavingsAccountSummaryData.java | 11 ++++++-- .../domain/SavingsAccountAssembler.java | 25 +++++++++++++++---- ...ingsAccountInterestPostingServiceImpl.java | 8 ++++-- ...SavingsAccountReadPlatformServiceImpl.java | 2 +- .../service/SavingsSchedularServiceImpl.java | 2 +- .../ClientSavingsIntegrationTest.java | 12 +++++---- 7 files changed, 54 insertions(+), 16 deletions(-) diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountData.java index 8f66a23cf..befcbe8de 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountData.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountData.java @@ -116,6 +116,8 @@ public final class SavingsAccountData implements Serializable { private final BigDecimal minOverdraftForInterestCalculation; private transient List savingsAccountTransactionData = new ArrayList<>(); + private transient SavingsAccountTransactionData lastSavingsAccountTransaction; + private List datatables = null; // import field @@ -1104,4 +1106,12 @@ public final class SavingsAccountData implements Serializable { public Set getExistingReversedTransactionIds() { return this.existingReversedTransactionIds; } + + public SavingsAccountTransactionData getLastSavingsAccountTransaction() { + return lastSavingsAccountTransaction; + } + + public void setLastSavingsAccountTransaction(SavingsAccountTransactionData lastSavingsAccountTransaction) { + this.lastSavingsAccountTransaction = lastSavingsAccountTransaction; + } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountSummaryData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountSummaryData.java index a7eea4d47..9335f8df6 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountSummaryData.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountSummaryData.java @@ -223,16 +223,23 @@ public class SavingsAccountSummaryData implements Serializable { Money interestTotal = Money.of(currency, this.totalInterestPosted); Money withHoldTaxTotal = Money.of(currency, this.totalWithholdTax); Money overdraftInterestTotal = Money.of(currency, this.totalOverdraftInterestDerived); + this.totalDeposits = wrapper.calculateTotalDeposits(currency, savingsAccountTransactions); + this.totalWithdrawals = wrapper.calculateTotalWithdrawals(currency, savingsAccountTransactions); final HashMap map = updateRunningBalanceAndPivotDate(true, savingsAccountTransactions, interestTotal, overdraftInterestTotal, withHoldTaxTotal, currency); interestTotal = map.get("interestTotal"); withHoldTaxTotal = map.get("withHoldTax"); overdraftInterestTotal = map.get("overdraftInterestTotal"); + BigDecimal deltaInterest = interestTotal.minus(this.totalInterestPosted).getAmountDefaultedToNullIfZero(); + BigDecimal deltaWithholdTax = withHoldTaxTotal.minus(this.totalWithholdTax).getAmountDefaultedToNullIfZero(); + BigDecimal deltaOverdraftInterestDerived = overdraftInterestTotal.minus(this.totalOverdraftInterestDerived) + .getAmountDefaultedToNullIfZero(); this.totalInterestPosted = interestTotal.getAmountDefaultedToNullIfZero(); this.totalWithholdTax = withHoldTaxTotal.getAmountDefaultedToNullIfZero(); this.totalOverdraftInterestDerived = overdraftInterestTotal.getAmountDefaultedToNullIfZero(); - this.accountBalance = Money.of(currency, this.accountBalance).plus(this.totalInterestPosted).minus(this.totalWithholdTax) - .minus(this.totalOverdraftInterestDerived).getAmount(); + this.accountBalance = getRunningBalanceOnPivotDate(); + this.accountBalance = Money.of(currency, this.accountBalance).plus(Money.of(currency, this.totalDeposits)).plus(deltaInterest) + .minus(this.totalWithdrawals).minus(deltaWithholdTax).minus(deltaOverdraftInterestDerived).getAmount(); } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountAssembler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountAssembler.java index fa234fcaa..e45626574 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountAssembler.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountAssembler.java @@ -48,6 +48,7 @@ import static org.apache.fineract.portfolio.savings.SavingsApiConstants.withdraw import com.google.gson.JsonElement; import java.math.BigDecimal; import java.time.LocalDate; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Set; @@ -74,6 +75,7 @@ import org.apache.fineract.portfolio.savings.SavingsInterestCalculationType; import org.apache.fineract.portfolio.savings.SavingsPeriodFrequencyType; import org.apache.fineract.portfolio.savings.SavingsPostingInterestPeriodType; import org.apache.fineract.portfolio.savings.data.SavingsAccountData; +import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionData; import org.apache.fineract.portfolio.savings.exception.SavingsProductNotFoundException; import org.apache.fineract.useradministration.domain.AppUser; import org.slf4j.Logger; @@ -381,12 +383,25 @@ public class SavingsAccountAssembler { public SavingsAccountData assembleSavings(final SavingsAccountData account) { // Update last running balance on account level - if (account.getTransactions() != null && account.getTransactions().size() != 0 - && account.getSummary().getInterestPostedTillDate() != null) { - account.getSummary().setRunningBalanceOnPivotDate(account.getTransactions().get(account.getTransactions().size() - 1) - .getRunningBalance(account.getCurrency()).getAmount()); - } + final boolean backdatedTxnsAllowedTill = this.configurationDomainService.retrievePivotDateConfig(); + if (backdatedTxnsAllowedTill && account.getTransactions() != null && account.getSummary().getInterestPostedTillDate() != null) { + List removalList = new ArrayList<>(); + for (int i = 0; i < account.getTransactions().size(); i++) { + SavingsAccountTransactionData savingsAccountTransaction = account.getTransactions().get(i); + removalList.add(savingsAccountTransaction); + if ((savingsAccountTransaction.isInterestPostingAndNotReversed() + || savingsAccountTransaction.isOverdraftInterestAndNotReversed()) + && !savingsAccountTransaction.isReversalTransaction()) { + account.getSummary().setRunningBalanceOnPivotDate(savingsAccountTransaction.getRunningBalance()); + account.setLastSavingsAccountTransaction(savingsAccountTransaction); + break; + } + } + account.getTransactions().removeAll(removalList); + } else { + account.getSummary().setRunningBalanceOnPivotDate(BigDecimal.ZERO); + } account.setHelpers(this.savingsAccountTransactionSummaryWrapper, this.savingsHelper); return account; } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountInterestPostingServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountInterestPostingServiceImpl.java index dc2a87309..0ee01f2ba 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountInterestPostingServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountInterestPostingServiceImpl.java @@ -364,8 +364,12 @@ public class SavingsAccountInterestPostingServiceImpl implements SavingsAccountI } final List listOfTransactionsSorted = new ArrayList<>(); listOfTransactionsSorted.addAll(savingsAccountData.getTransactions()); - final SavingsAccountTransactionDataComparator transactionComparator = new SavingsAccountTransactionDataComparator(); - Collections.sort(listOfTransactionsSorted, transactionComparator); + if (!listOfTransactionsSorted.isEmpty()) { + final SavingsAccountTransactionDataComparator transactionComparator = new SavingsAccountTransactionDataComparator(); + Collections.sort(listOfTransactionsSorted, transactionComparator); + } else { + listOfTransactionsSorted.add(savingsAccountData.getLastSavingsAccountTransaction()); + } return listOfTransactionsSorted.get(0); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java index b53a1bfed..31e8e3f0e 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java @@ -272,7 +272,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead + "where (CASE WHEN sa.interest_posted_till_date is not null THEN tr.transaction_date >= sa.interest_posted_till_date ELSE tr.transaction_date >= sa.activatedon_date END) "; } - sql = sql + " and (sa.interest_posted_till_date is null or sa.interest_posted_till_date < ? ) "; + sql = sql + " and (sa.interest_posted_till_date is null or sa.interest_posted_till_date <= ? ) "; sql = sql + " order by sa.id, tr.transaction_date, tr.created_date, tr.id"; List savingsAccountDataList = this.jdbcTemplate.query(sql, this.savingAccountMapperForInterestPosting, // NOSONAR diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsSchedularServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsSchedularServiceImpl.java index 018fc5b98..5234e5932 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsSchedularServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsSchedularServiceImpl.java @@ -76,7 +76,7 @@ public class SavingsSchedularServiceImpl implements SavingsSchedularService { Long maxSavingsIdInList = 0L; // initialise the executor service with fetched configurations final ExecutorService executorService = Executors.newFixedThreadPool(threadPoolSize); - final boolean backdatedTxnsAllowedTill = false; + final boolean backdatedTxnsAllowedTill = this.configurationDomainService.retrievePivotDateConfig(); long start = System.currentTimeMillis(); diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientSavingsIntegrationTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientSavingsIntegrationTest.java index a98a796a2..3e778bba3 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientSavingsIntegrationTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientSavingsIntegrationTest.java @@ -2972,18 +2972,20 @@ public class ClientSavingsIntegrationTest { configurationForBackdatedTransaction(); - String transactionDate = "1 July 2022"; + LocalDate transactionDate = LocalDate.now(Utils.getZoneIdOfTenant()).minusDays(10); + final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd MMMM yyyy"); + String startDate = formatter.format(transactionDate); - final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec, transactionDate); + final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec, startDate); Assertions.assertNotNull(clientID); - final Integer savingsId = createSavingsAccountDailyPostingOverdraft(clientID, transactionDate); - this.savingsAccountHelper.depositToSavingsAccount(savingsId, "200", transactionDate, CommonConstants.RESPONSE_RESOURCE_ID); + final Integer savingsId = createSavingsAccountDailyPostingOverdraft(clientID, startDate); + this.savingsAccountHelper.depositToSavingsAccount(savingsId, "200", startDate, CommonConstants.RESPONSE_RESOURCE_ID); final String jobName = "Post Interest For Savings"; this.scheduleJobHelper.executeAndAwaitJob(jobName); final ResponseSpecification errorResponse = new ResponseSpecBuilder().expectStatusCode(403).build(); final SavingsAccountHelper validationErrorHelper = new SavingsAccountHelper(this.requestSpec, errorResponse); - List error = (List) validationErrorHelper.depositToSavingsAccount(savingsId, "3000", transactionDate, + List error = (List) validationErrorHelper.depositToSavingsAccount(savingsId, "300", startDate, CommonConstants.RESPONSE_ERROR); assertEquals("error.msg.savings.transaction.is.not.allowed", error.get(0).get(CommonConstants.RESPONSE_ERROR_MESSAGE_CODE)); From 7fe34c3d7d3c3e69f0d32ae508bd5d9f14afbd59 Mon Sep 17 00:00:00 2001 From: Dhaval Maniyar Date: Sat, 20 Aug 2022 16:32:37 +0530 Subject: [PATCH 07/11] FINERACT-1668: Adding Configuration for reversal transaction --- .../db/changelog/tenant/changelog-tenant.xml | 2 +- ...21_reversals_for_reversed_transactions.xml | 37 +++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 fineract-provider/src/main/resources/db/changelog/tenant/parts/0021_reversals_for_reversed_transactions.xml diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml index f8cb165ca..c795749e6 100644 --- a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml +++ b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml @@ -40,5 +40,5 @@ - + diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/parts/0021_reversals_for_reversed_transactions.xml b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0021_reversals_for_reversed_transactions.xml new file mode 100644 index 000000000..1fdbf358d --- /dev/null +++ b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0021_reversals_for_reversed_transactions.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + From 9cabd179d1d646179830bfde79ba53e86cb8d222 Mon Sep 17 00:00:00 2001 From: Aleksandar Vidakovic Date: Sat, 20 Aug 2022 14:52:17 +0200 Subject: [PATCH 08/11] FINERACT-1696: Update README with instructions concerning Mysql UTC behavior --- README.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/README.md b/README.md index 85738369f..04bd20808 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,31 @@ Beware that this database container database keeps its state inside the containe Tomcat v9 is only required if you wish to deploy the Fineract WAR to a separate external servlet container. Note that you do not require to install Tomcat to develop Fineract, or to run it in production if you use the self-contained JAR, which transparently embeds a servlet container using Spring Boot. (Until FINERACT-730, Tomcat 7/8 were also supported, but now Tomcat 9 is required.) +IMPORTANT: if you use MySQL or MariaDB +============ + +Recently (after release 1.7.0) we introduced improved date time handling in Fineract. Date time is from now on stored in UTC and we are enforcing UTC timezone even on the JDBC driver, e. g. for MySQL: + +``` +serverTimezone=UTC&useLegacyDatetimeCode=false&sessionVariables=time_zone=‘-00:00’ +``` + +__DO__: If you do use MySQL as your Fineract databsae then the following configuration is highly recommended: + +* Run the application in UTC (the default command line in our Docker image has the necessary parameters already set) +* Run the MySQL database server in UTC (if you use managed services like AWS RDS then this should be the default anyway, but it would be good to double-check) + +__DON'T__: In case the Fineract instance and the MySQL server are __not__ running in UTC then the following could happen: + +* MySQL is saving date time values differently from PostgreSQL +* Example scenario: if the Fineract instance runs in timezone: GMT+2, and the local date time is 2022-08-11 17:15 ... +* ... then __PostgreSQL saves__ the LocalDateTime as is: __2022-08-11 17:15__ +* ... and __MySQL saves__ the LocalDateTime in UTC: __2022-08-11 15:15__ +* ... but when we __read__ the date time from PostgreSQL __or__ from MySQL, then both systems give us the same values: __2022-08-11 17:15 GMT+2__ + +If a previously used Fineract instance didn't run in UTC (backward compatibility), then all prior dates will be read wrongly by MySQL/MariaDB. This can cause issues when you run the database migration scripts. + +__RECOMMENDATION__: you need to shift all dates in your database by the timezone offset that your Fineract instance used. Instructions how to run for local development ============ From 998b3ff28917fb980b544be6f557c75d83f75d4f Mon Sep 17 00:00:00 2001 From: Manoj <56669674+fynmanoj@users.noreply.github.com> Date: Mon, 22 Aug 2022 12:19:04 +0530 Subject: [PATCH 09/11] delete-duplicate-migration (#2531) --- .../db/changelog/tenant/changelog-tenant.xml | 2 +- ...21_reversals_for_reversed_transactions.xml | 37 ------------------- 2 files changed, 1 insertion(+), 38 deletions(-) delete mode 100644 fineract-provider/src/main/resources/db/changelog/tenant/parts/0021_reversals_for_reversed_transactions.xml diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml index c795749e6..f8cb165ca 100644 --- a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml +++ b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml @@ -40,5 +40,5 @@ - + diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/parts/0021_reversals_for_reversed_transactions.xml b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0021_reversals_for_reversed_transactions.xml deleted file mode 100644 index 1fdbf358d..000000000 --- a/fineract-provider/src/main/resources/db/changelog/tenant/parts/0021_reversals_for_reversed_transactions.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - - - - - From 9000b6e82634536261bfc86c78cdc45ba7d6733e Mon Sep 17 00:00:00 2001 From: Dhaval Maniyar Date: Sat, 30 Jul 2022 22:08:59 +0530 Subject: [PATCH 10/11] FINERACT-1646-backdated-transaction-available-balance-fix --- .../savings/domain/SavingsAccountAssembler.java | 9 ++------- .../service/SavingsAccountReadPlatformServiceImpl.java | 1 - 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountAssembler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountAssembler.java index e45626574..99cd32057 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountAssembler.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountAssembler.java @@ -390,13 +390,8 @@ public class SavingsAccountAssembler { for (int i = 0; i < account.getTransactions().size(); i++) { SavingsAccountTransactionData savingsAccountTransaction = account.getTransactions().get(i); removalList.add(savingsAccountTransaction); - if ((savingsAccountTransaction.isInterestPostingAndNotReversed() - || savingsAccountTransaction.isOverdraftInterestAndNotReversed()) - && !savingsAccountTransaction.isReversalTransaction()) { - account.getSummary().setRunningBalanceOnPivotDate(savingsAccountTransaction.getRunningBalance()); - account.setLastSavingsAccountTransaction(savingsAccountTransaction); - break; - } + account.getSummary().setRunningBalanceOnPivotDate(savingsAccountTransaction.getRunningBalance()); + account.setLastSavingsAccountTransaction(savingsAccountTransaction); } account.getTransactions().removeAll(removalList); } else { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java index 31e8e3f0e..10c8a1cc2 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java @@ -264,7 +264,6 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead public List retrieveAllSavingsDataForInterestPosting(final boolean backdatedTxnsAllowedTill, final int pageSize, final Integer status, final Long maxSavingsId) { LocalDate yesterday = DateUtils.getBusinessLocalDate().minusDays(1); - String sql = "select " + this.savingAccountMapperForInterestPosting.schema() + "join (select a.id from m_savings_account a where a.id > ? and a.status_enum = ? limit ?) b on b.id = sa.id "; if (backdatedTxnsAllowedTill) { From 27489a6850a709a482781325f644e0ae94eda235 Mon Sep 17 00:00:00 2001 From: Aleksandar Vidakovic Date: Mon, 22 Aug 2022 12:30:41 +0200 Subject: [PATCH 11/11] Prepare release 1.8.0 --- fineract-doc/.asciidoctorconfig | 2 +- .../src/docs/en/chapters/release/index.adoc | 2 ++ .../src/docs/en/chapters/release/maintenance.adoc | 13 +++++++++++++ fineract-doc/src/docs/en/config.adoc | 2 +- 4 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 fineract-doc/src/docs/en/chapters/release/maintenance.adoc diff --git a/fineract-doc/.asciidoctorconfig b/fineract-doc/.asciidoctorconfig index 26a82e248..b8b621676 100644 --- a/fineract-doc/.asciidoctorconfig +++ b/fineract-doc/.asciidoctorconfig @@ -1,5 +1,5 @@ :years: 2015-2022 -:revnumber: 1.7.0 +:revnumber: 1.8.0 :draft: true :rootdir: {asciidoctorconfigdir}/.. :generated: {asciidoctorconfigdir}/build/generated/asciidoc diff --git a/fineract-doc/src/docs/en/chapters/release/index.adoc b/fineract-doc/src/docs/en/chapters/release/index.adoc index d8efa23f3..67b670d2c 100644 --- a/fineract-doc/src/docs/en/chapters/release/index.adoc +++ b/fineract-doc/src/docs/en/chapters/release/index.adoc @@ -12,4 +12,6 @@ include::configuration.adoc[leveloffset=+1] include::process.adoc[leveloffset=+1] +include::maintenance.adoc[leveloffset=+1] + include::publish.adoc[leveloffset=+1] diff --git a/fineract-doc/src/docs/en/chapters/release/maintenance.adoc b/fineract-doc/src/docs/en/chapters/release/maintenance.adoc new file mode 100644 index 000000000..8cf8abd2a --- /dev/null +++ b/fineract-doc/src/docs/en/chapters/release/maintenance.adoc @@ -0,0 +1,13 @@ += Maintenance Release Process + +IMPORTANT: This is a first attempt to introduce maintenance releases. Some details might change as soon as we get more experience with the process and feedback from the community. The numbers here are still more or less arbitrary, and we'll adapt as necessary. + +== Rules + +- hotfix releases are reserved for critical (BLOCKER) bugs and security issues. Probably we'll have some kind of voting process in place, e. g. "minimum 3 x +1 votes from PMC members" +- we will support (for now to start) two minor versions back counting from the last release; this would mean that once 1.8.0 is out we would support 1.8.x and 1.7.x, but not 1.6.x and older; this rule is tentative, we'll see then what we do in the future when we have more feedback. +- guaranteed backward compatibility with the last minor release; i. e. "1.6.1" is a drop-in replacement for "1.6.0" +- NO new features, tables, data, REST endpoints +- NO major (or "minor" framework upgrades); i. e. if we used Spring Boot "2.6.1" in version "1.6.0" of Fineract we can upgrade dependencies to "2.6.10" (unless it breaks something of course), but not to "2.7.2" of Spring Boot + +NOTE: The rest of the release process is the same as for normal releases. In the future we might have smaller time windows for reviews. \ No newline at end of file diff --git a/fineract-doc/src/docs/en/config.adoc b/fineract-doc/src/docs/en/config.adoc index 0cc9b3326..dd47f4ec7 100644 --- a/fineract-doc/src/docs/en/config.adoc +++ b/fineract-doc/src/docs/en/config.adoc @@ -5,7 +5,7 @@ :compress: :pdf-page-size: LETTER :experimental: -:pdf-version: 1.7 +:pdf-version: 1.8 :page-layout: base :toc-title: Table of Contents :toc: left