FINERACT-2386: Journal entry aggregation capabilities

This commit is contained in:
Adam Saghy
2025-10-03 11:07:12 +02:00
parent 297fa8e707
commit 89e9cfcd7d
35 changed files with 2531 additions and 3 deletions
@@ -71,6 +71,11 @@ fineract.correlation.header-name=${FINERACT_LOGGING_HTTP_CORRELATION_ID_HEADER_N
fineract.job.stuck-retry-threshold=${FINERACT_JOB_STUCK_RETRY_THRESHOLD:5}
fineract.ip-tracking.enabled=${FINERACT_CLIENT_IP_TRACKING_ENABLED:false}
fineract.job.loan-cob-enabled=${FINERACT_JOB_LOAN_COB_ENABLED:true}
# Aggregation job configuration
fineract.job.journal-entry-aggregation.exclude-recent-N-days=${FINERACT_JOB_JOURNAL_ENTRY_AGGREGATION_EXCLUDE_RECENT_N_DAYS:1}
fineract.job.journal-entry-aggregation.enabled=${FINERACT_JOB_JOURNAL_ENTRY_AGGREGATION_ENABLED:true}
#this property if enabled, will create aggregated entry for all data on first run, instead of one entry per submitted_on_date
fineract.job.journal-entry-aggregation.chunk-size=${FINERACT_JOB_JOURNAL_ENTRY_AGGREGATION_CHUNK_SIZE:2000}
fineract.partitioned-job.partitioned-job-properties[0].job-name=LOAN_COB
fineract.partitioned-job.partitioned-job-properties[0].chunk-size=${LOAN_COB_CHUNK_SIZE:100}
@@ -218,4 +218,7 @@
<include file="parts/0197_add_deleted_and_closed_to_capitalized_income_balance.xml" relativeToChangelogFile="true" />
<include file="parts/0198_add_classification_id_to_acc_product_mapping.xml" relativeToChangelogFile="true" />
<include file="parts/0199_write_off_reason_mapping_loan.xml" relativeToChangelogFile="true" />
<include file="parts/0200_add_journal_entry_aggregation_tables.xml" relativeToChangelogFile="true"/>
<include file="parts/0201_add_journal_entry_aggregation_job.xml" relativeToChangelogFile="true"/>
<include file="parts/0202_trial_balance_summary_with_asset_owner_journal_entry_aggregation.xml" relativeToChangelogFile="true" />
</databaseChangeLog>
@@ -0,0 +1,246 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
<changeSet id="1" author="fineract" context="postgresql">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="m_journal_entry_aggregation_summary"/>
</not>
</preConditions>
<createTable tableName="m_journal_entry_aggregation_summary">
<column autoIncrement="true" name="id" type="BIGINT">
<constraints nullable="false" primaryKey="true" primaryKeyName="pk_journal_entry_aggregation_summary"/>
</column>
<column name="gl_account_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="product_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="office_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="entity_type_enum" type="SMALLINT">
<constraints nullable="false"/>
</column>
<column name="aggregated_on_date" type="DATE">
<constraints nullable="false"/>
</column>
<column name="submitted_on_date" type="DATE">
<constraints nullable="false"/>
</column>
<column name="external_owner_id" type="BIGINT"/>
<column name="debit_amount" type="DECIMAL(19, 6)"/>
<column name="credit_amount" type="DECIMAL(19, 6)"/>
<column defaultValueBoolean="false" name="manual_entry" type="boolean">
<constraints nullable="false"/>
</column>
<column name="job_execution_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="created_by" type="BIGINT"/>
<column name="created_on_utc" type="timestamp with time zone"/>
<column name="last_modified_by" type="BIGINT"/>
<column name="last_modified_on_utc" type="timestamp with time zone"/>
</createTable>
<addForeignKeyConstraint baseColumnNames="created_by" baseTableName="m_journal_entry_aggregation_summary" constraintName="FK_GL_JOURNAL_ENTRY_AGGREGATION_SUMMARY_ON_CREATED_BY" referencedColumnNames="id" referencedTableName="m_appuser"/>
<addForeignKeyConstraint baseColumnNames="last_modified_by" baseTableName="m_journal_entry_aggregation_summary" constraintName="FK_GL_JOURNAL_ENTRY_AGGREGATION_SUMMARY_ON_LAST_MODIFIED_BY" referencedColumnNames="id" referencedTableName="m_appuser"/>
<addForeignKeyConstraint baseColumnNames="office_id" baseTableName="m_journal_entry_aggregation_summary" constraintName="FK_GL_JOURNAL_ENTRY_AGGREGATION_SUMMARY_ON_OFFICE_ID" referencedColumnNames="id" referencedTableName="m_office"/>
<addForeignKeyConstraint baseColumnNames="gl_account_id" baseTableName="m_journal_entry_aggregation_summary" constraintName="FK_GL_JOURNAL_ENTRY_AGGREGATION_SUMMARY_ON_GL_ACCOUNT_ID" referencedColumnNames="id" referencedTableName="acc_gl_account"/>
<addForeignKeyConstraint baseColumnNames="job_execution_id" baseTableName="m_journal_entry_aggregation_summary" constraintName="FK_GL_JOURNAL_ENTRY_AGGREGATION_SUMMARY_ON_JOB_EXECUTION_ID" referencedColumnNames="job_execution_id" referencedTableName="batch_job_execution"/>
</changeSet>
<changeSet id="2" author="fineract" runInTransaction="false" context="postgresql">
<preConditions onFail="MARK_RAN">
<not>
<indexExists tableName="m_journal_entry_aggregation_summary" columnNames="submitted_on_date,gl_account_id"/>
</not>
</preConditions>
<sql>
create index concurrently idx_journal_entry_aggregation_summary on m_journal_entry_aggregation_summary(submitted_on_date,gl_account_id);
</sql>
</changeSet>
<changeSet id="3" author="fineract" context="postgresql">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="m_journal_entry_aggregation_tracking"/>
</not>
</preConditions>
<createTable tableName="m_journal_entry_aggregation_tracking">
<column autoIncrement="true" name="id" type="BIGINT">
<constraints nullable="false" primaryKey="true" primaryKeyName="pk_journal_entry_aggregation_tracking"/>
</column>
<column name="aggregated_on_date_from" type="DATE">
<constraints nullable="false"/>
</column>
<column name="aggregated_on_date_to" type="DATE">
<constraints nullable="false"/>
</column>
<column name="submitted_on_date" type="DATE">
<constraints nullable="false"/>
</column>
<column name="job_execution_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="created_by" type="BIGINT"/>
<column name="created_on_utc" type="timestamp with time zone"/>
<column name="last_modified_by" type="BIGINT"/>
<column name="last_modified_on_utc" type="timestamp with time zone"/>
</createTable>
<addForeignKeyConstraint baseColumnNames="job_execution_id" baseTableName="m_journal_entry_aggregation_tracking" constraintName="FK_GL_JOURNAL_ENTRY_AGGREGATION_TRACKING_ON_JOB_EXECUTION_ID" referencedColumnNames="job_execution_id" referencedTableName="batch_job_execution"/>
<addForeignKeyConstraint baseColumnNames="created_by" baseTableName="m_journal_entry_aggregation_tracking" constraintName="FK_GL_JOURNAL_ENTRY_AGGREGATION_TRACKING_ON_CREATED_BY" referencedColumnNames="id" referencedTableName="m_appuser"/>
<addForeignKeyConstraint baseColumnNames="last_modified_by" baseTableName="m_journal_entry_aggregation_tracking" constraintName="FK_GL_JOURNAL_ENTRY_AGGREGATION_TRACKING_ON_LAST_MODIFIED_BY" referencedColumnNames="id" referencedTableName="m_appuser"/>
</changeSet>
<changeSet id="4" author="fineract" runInTransaction="false" context="postgresql">
<preConditions onFail="MARK_RAN">
<not>
<indexExists tableName="m_journal_entry_aggregation_tracking" columnNames="submitted_on_date"/>
</not>
</preConditions>
<sql>
create index concurrently idx_m_journal_entry_aggregation_tracking on m_journal_entry_aggregation_tracking(submitted_on_date);
</sql>
</changeSet>
<changeSet id="5" author="fineract" runInTransaction="false" context="postgresql">
<preConditions onFail="MARK_RAN">
<not>
<indexExists tableName="m_journal_entry_aggregation_tracking" columnNames="aggregated_on_date_to"/>
</not>
</preConditions>
<sql>
create unique index concurrently idx2_m_journal_entry_aggregation_tracking on m_journal_entry_aggregation_tracking(aggregated_on_date_to);
</sql>
</changeSet>
<changeSet id="6" author="fineract" context="mysql">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="m_journal_entry_aggregation_summary"/>
</not>
</preConditions>
<createTable tableName="m_journal_entry_aggregation_summary">
<column autoIncrement="true" name="id" type="BIGINT">
<constraints nullable="false" primaryKey="true" primaryKeyName="pk_journal_entry_aggregation_summary"/>
</column>
<column name="gl_account_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="product_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="office_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="entity_type_enum" type="SMALLINT">
<constraints nullable="false"/>
</column>
<column name="aggregated_on_date" type="DATE">
<constraints nullable="false"/>
</column>
<column name="submitted_on_date" type="DATE">
<constraints nullable="false"/>
</column>
<column name="external_owner_id" type="BIGINT"/>
<column name="debit_amount" type="DECIMAL(19, 6)"/>
<column name="credit_amount" type="DECIMAL(19, 6)"/>
<column defaultValueBoolean="false" name="manual_entry" type="boolean">
<constraints nullable="false"/>
</column>
<column name="job_execution_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="created_by" type="BIGINT"/>
<column name="created_on_utc" type="DATETIME"/>
<column name="last_modified_by" type="BIGINT"/>
<column name="last_modified_on_utc" type="DATETIME"/>
</createTable>
<addForeignKeyConstraint baseColumnNames="created_by" baseTableName="m_journal_entry_aggregation_summary" constraintName="FK_GL_JOURNAL_ENTRY_AGGREGATION_SUMMARY_ON_CREATED_BY" referencedColumnNames="id" referencedTableName="m_appuser"/>
<addForeignKeyConstraint baseColumnNames="last_modified_by" baseTableName="m_journal_entry_aggregation_summary" constraintName="FK_GL_JOURNAL_ENTRY_AGGREGATION_SUMMARY_ON_LAST_MODIFIED_BY" referencedColumnNames="id" referencedTableName="m_appuser"/>
<addForeignKeyConstraint baseColumnNames="office_id" baseTableName="m_journal_entry_aggregation_summary" constraintName="FK_GL_JOURNAL_ENTRY_AGGREGATION_SUMMARY_ON_OFFICE_ID" referencedColumnNames="id" referencedTableName="m_office"/>
<addForeignKeyConstraint baseColumnNames="gl_account_id" baseTableName="m_journal_entry_aggregation_summary" constraintName="FK_GL_JOURNAL_ENTRY_AGGREGATION_SUMMARY_ON_GL_ACCOUNT_ID" referencedColumnNames="id" referencedTableName="acc_gl_account"/>
<addForeignKeyConstraint baseColumnNames="job_execution_id" baseTableName="m_journal_entry_aggregation_summary" constraintName="FK_GL_JOURNAL_ENTRY_AGGREGATION_SUMMARY_ON_JOB_EXECUTION_ID" referencedColumnNames="JOB_EXECUTION_ID" referencedTableName="BATCH_JOB_EXECUTION"/>
</changeSet>
<changeSet id="7" author="fineract" runInTransaction="false" context="mysql">
<preConditions onFail="MARK_RAN">
<not>
<indexExists tableName="m_journal_entry_aggregation_summary" columnNames="submitted_on_date,gl_account_id"/>
</not>
</preConditions>
<sql>
create index idx_journal_entry_aggregation_summary on m_journal_entry_aggregation_summary(submitted_on_date,gl_account_id);
</sql>
</changeSet>
<changeSet id="8" author="fineract" context="mysql">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="m_journal_entry_aggregation_tracking"/>
</not>
</preConditions>
<createTable tableName="m_journal_entry_aggregation_tracking">
<column autoIncrement="true" name="id" type="BIGINT">
<constraints nullable="false" primaryKey="true" primaryKeyName="pk_journal_entry_aggregation_tracking"/>
</column>
<column name="aggregated_on_date_from" type="DATE">
<constraints nullable="false"/>
</column>
<column name="aggregated_on_date_to" type="DATE">
<constraints nullable="false"/>
</column>
<column name="submitted_on_date" type="DATE">
<constraints nullable="false"/>
</column>
<column name="job_execution_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="created_by" type="BIGINT"/>
<column name="created_on_utc" type="DATETIME"/>
<column name="last_modified_by" type="BIGINT"/>
<column name="last_modified_on_utc" type="DATETIME"/>
</createTable>
<addForeignKeyConstraint baseColumnNames="job_execution_id" baseTableName="m_journal_entry_aggregation_tracking" constraintName="FK_GL_JOURNAL_ENTRY_AGGREGATION_TRACKING_ON_JOB_EXECUTION_ID" referencedColumnNames="JOB_EXECUTION_ID" referencedTableName="BATCH_JOB_EXECUTION"/>
<addForeignKeyConstraint baseColumnNames="created_by" baseTableName="m_journal_entry_aggregation_tracking" constraintName="FK_GL_JOURNAL_ENTRY_AGGREGATION_TRACKING_ON_CREATED_BY" referencedColumnNames="id" referencedTableName="m_appuser"/>
<addForeignKeyConstraint baseColumnNames="last_modified_by" baseTableName="m_journal_entry_aggregation_tracking" constraintName="FK_GL_JOURNAL_ENTRY_AGGREGATION_TRACKING_ON_LAST_MODIFIED_BY" referencedColumnNames="id" referencedTableName="m_appuser"/>
</changeSet>
<changeSet id="9" author="fineract" runInTransaction="false" context="mysql">
<preConditions onFail="MARK_RAN">
<not>
<indexExists tableName="m_journal_entry_aggregation_tracking" columnNames="submitted_on_date"/>
</not>
</preConditions>
<sql>
create index idx_m_journal_entry_aggregation_tracking on m_journal_entry_aggregation_tracking(submitted_on_date);
</sql>
</changeSet>
<changeSet id="10" author="fineract" runInTransaction="false" context="mysql">
<preConditions onFail="MARK_RAN">
<not>
<indexExists tableName="m_journal_entry_aggregation_tracking" columnNames="aggregated_on_date_to"/>
</not>
</preConditions>
<sql>
create unique index idx2_m_journal_entry_aggregation_tracking on m_journal_entry_aggregation_tracking(aggregated_on_date_to);
</sql>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.3.xsd">
<property name="current_datetime" value="NOW()"/>
<changeSet id="1" author="fineract">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(1) from job where short_name = 'JRNL_ENTRY_AGG'</sqlCheck>
</preConditions>
<insert tableName="job">
<column name="name" value="Journal Entry Aggregation"/>
<column name="display_name" value="Journal Entry Aggregation"/>
<column name="cron_expression" value="0 0 6 * * ?"/>
<column name="create_time" valueDate="${current_datetime}"/>
<column name="task_priority" valueNumeric="5"/>
<column name="group_name"/>
<column name="previous_run_start_time"/>
<column name="job_key" value="Journal Entry Aggregation _ DEFAULT"/>
<column name="initializing_errorlog"/>
<column name="is_active" valueBoolean="false"/>
<column name="currently_running" valueBoolean="false"/>
<column name="updates_allowed" valueBoolean="true"/>
<column name="scheduler_group" valueNumeric="0"/>
<column name="is_misfired" valueBoolean="false"/>
<column name="node_id" valueNumeric="1"/>
<column name="is_mismatched_job" valueBoolean="true"/>
<column name="short_name" value="JRNL_AGG"/>
</insert>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,180 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.3.xsd">
<changeSet author="fineract" id="trial-balance-summary-with-asset-owner-update-1">
<update tableName="stretchy_report">
<column name="report_sql" value="WITH retained_earning AS (
SELECT DISTINCT '${endDate}' AS postingdate,
lp.name AS product,
gl_code AS glacct,
COALESCE((SELECT name FROM acc_gl_account WHERE gl_code = e.gl_code), '') AS description,
COALESCE(e.owner_external_id, 'self') AS assetowner,
SUM(opening_balance_amount) AS beginningbalance,
0 AS debitmovement,
0 AS creditmovement,
SUM(opening_balance_amount) AS endingbalance
FROM acc_gl_journal_entry_annual_summary e, m_product_loan lp
WHERE e.office_id = ${officeId}
AND lp.id = product_id
AND date_part('year', e.year_end_date::date) &lt; date_part('year', '${endDate}'::date)
GROUP BY gl_code, lp.name, office_id, owner_external_id
),
aggregated_date AS (
SELECT MAX(aggregated_on_date_to) as latest
FROM m_journal_entry_aggregation_tracking
),
summary_snapshot_baseline_data AS (
--using m_journal_entry_aggregation_summary to fetch aggregated journal entry data
SELECT lp.NAME AS productname,
acc_gl_account.gl_code AS glcode,
acc_gl_account.NAME AS glname,
CASE WHEN ags.external_owner_id IS NULL THEN 0 ELSE ags.external_owner_id END AS assetowner,
SUM(ags.debit_amount) AS debitamount,
SUM(ags.credit_amount) AS creditamount
FROM acc_gl_account
JOIN m_journal_entry_aggregation_summary ags ON acc_gl_account.id = ags.gl_account_id
JOIN m_product_loan lp ON lp.id = ags.product_id
WHERE ags.entity_type_enum = 1
AND ags.manual_entry = false
AND ags.submitted_on_date &lt;= (select latest from aggregated_date)
AND (ags.office_id = ${officeId})
GROUP BY productname, glcode, glname, assetowner
),
post_snapshot_delta_data AS (
-- still using acc_gl_journal_entry for dates after last aggregated data and before the cob date
SELECT lp.NAME AS productname,
acc_gl_account.gl_code AS glcode,
acc_gl_account.NAME AS glname,
CASE WHEN aw.owner_id IS NULL THEN 0 ELSE aw.owner_id END AS assetowner,
SUM(CASE WHEN acc_gl_journal_entry.type_enum = 2 THEN amount ELSE 0 END) AS debitamount,
SUM(CASE WHEN acc_gl_journal_entry.type_enum = 1 THEN amount ELSE 0 END) AS creditamount
FROM acc_gl_account
JOIN acc_gl_journal_entry ON acc_gl_account.id = acc_gl_journal_entry.account_id
JOIN m_loan m ON m.id = acc_gl_journal_entry.entity_id
JOIN m_product_loan lp ON lp.id = m.product_id
LEFT JOIN m_external_asset_owner_journal_entry_mapping aw ON aw.journal_entry_id = acc_gl_journal_entry.id
WHERE acc_gl_journal_entry.entity_type_enum = 1
AND acc_gl_journal_entry.manual_entry = false
AND acc_gl_journal_entry.submitted_on_date &gt; (select latest from aggregated_date)
AND acc_gl_journal_entry.submitted_on_date &lt; '${endDate}'
AND (acc_gl_journal_entry.office_id = ${officeId})
GROUP BY productname, glcode, glname, assetowner
),
-- fetch all data before cob date
merged_historical_data AS (
SELECT summary_snapshot_baseline_data.productname,
summary_snapshot_baseline_data.glcode,
summary_snapshot_baseline_data.glname,
summary_snapshot_baseline_data.assetowner,
COALESCE(summary_snapshot_baseline_data.debitamount, 0) + COALESCE(post_snapshot_delta_data.debitamount, 0) AS debitamount,
COALESCE(summary_snapshot_baseline_data.creditamount, 0) + COALESCE(post_snapshot_delta_data.creditamount, 0) AS creditamount
FROM summary_snapshot_baseline_data
FULL OUTER JOIN post_snapshot_delta_data ON summary_snapshot_baseline_data.glcode = post_snapshot_delta_data.glcode
AND summary_snapshot_baseline_data.productname = post_snapshot_delta_data.productname
AND summary_snapshot_baseline_data.assetowner = post_snapshot_delta_data.assetowner
),
-- fetch data for cob date
current_cob_data AS (
SELECT lp.name AS productname,
account_id,
acc_gl_account.gl_code AS glcode,
acc_gl_account.name AS glname,
CASE WHEN aw.owner_id IS NULL THEN 0 ELSE aw.owner_id END AS assetowner,
SUM(CASE WHEN acc_gl_journal_entry.type_enum = 2 THEN amount ELSE 0 END) AS debitamount,
SUM(CASE WHEN acc_gl_journal_entry.type_enum = 1 THEN amount ELSE 0 END) AS creditamount
FROM acc_gl_journal_entry
JOIN acc_gl_account ON acc_gl_account.id = acc_gl_journal_entry.account_id
JOIN m_loan m ON m.id = acc_gl_journal_entry.entity_id
JOIN m_product_loan lp ON lp.id = m.product_id
LEFT JOIN m_external_asset_owner_journal_entry_mapping aw ON aw.journal_entry_id = acc_gl_journal_entry.id
WHERE acc_gl_journal_entry.entity_type_enum = 1
AND acc_gl_journal_entry.manual_entry = false
AND acc_gl_journal_entry.submitted_on_date = '${endDate}'
AND (acc_gl_journal_entry.office_id = ${officeId})
GROUP BY productname, account_id, glcode, glname, assetowner
)
SELECT *
FROM (
SELECT *
FROM retained_earning
WHERE glacct = (SELECT gl_code FROM acc_gl_account WHERE name = 'Retained Earnings Prior Year')
UNION
SELECT txnreport.postingdate,
txnreport.product,
txnreport.glacct,
txnreport.description,
txnreport.assetowner,
(COALESCE(txnreport.beginningbalance, 0) + COALESCE(summary.beginningbalance, 0)) AS beginningbalance,
txnreport.debitmovement AS debitmovement,
txnreport.creditmovement AS creditmovement,
(COALESCE(txnreport.endingbalance, 0) + COALESCE(summary.beginningbalance, 0)) AS endingbalance
FROM (
SELECT *
FROM (
SELECT DISTINCT '${endDate}' AS postingdate,
loan.pname AS product,
loan.gl_code AS glacct,
loan.glname AS description,
COALESCE((SELECT external_id FROM m_external_asset_owner WHERE id = loan.assetowner), 'self') AS
assetowner,
loan.openingbalance AS beginningbalance,
(loan.debitamount * 1) AS debitmovement,
(loan.creditamount * -1) AS creditmovement,
(loan.openingbalance + loan.debitamount - loan.creditamount) AS endingbalance
FROM (
SELECT DISTINCT COALESCE(g.pname, current_cob_data.productname) pname,
COALESCE(g.gl_code, current_cob_data.glcode) gl_code,
COALESCE(g.glname, current_cob_data.glname) glname,
COALESCE(merged_historical_data.assetowner, current_cob_data.assetowner) assetowner,
COALESCE(merged_historical_data.debitamount, 0) - COALESCE(merged_historical_data.creditamount, 0) AS openingbalance,
COALESCE(current_cob_data.debitamount, 0) AS debitamount,
COALESCE(current_cob_data.creditamount, 0) AS creditamount
FROM (
SELECT DISTINCT ag.gl_code, ag.id, pl.NAME AS pname, ag.NAME AS glname
FROM acc_gl_account ag
JOIN acc_product_mapping am ON am.gl_account_id = ag.id AND am.product_type = 1
JOIN m_product_loan pl ON pl.id = am.product_id
) g
LEFT JOIN merged_historical_data ON g.gl_code = merged_historical_data.glcode AND merged_historical_data.productname = g.pname
FULL OUTER JOIN current_cob_data ON g.gl_code = current_cob_data.glcode
AND current_cob_data.productname = g.pname
AND merged_historical_data.assetowner = current_cob_data.assetowner
) loan
) a
) AS txnreport
LEFT JOIN retained_earning summary ON txnreport.glacct = summary.glacct
AND txnreport.assetowner = summary.assetowner
AND summary.product = txnreport.product
) report
WHERE report.endingbalance != 0
OR report.debitmovement != 0
OR report.creditmovement != 0
ORDER BY glacct"/>
<where>report_name='Trial Balance Summary Report with Asset Owner'</where>
</update>
</changeSet>
</databaseChangeLog>