...
The following query shows how to get a breakdown of repayment transaction transactions for a branch for a quarter. This is a typical star schema type query. The 'fact' table is joined to the 'dimension' tables that are used for filtering and grouping.
Repayment transactions cover normal repayments, adjustments, reversals, reschedules and write-offs.
Panel |
---|
select aa.account_action_name, sum(r.principal_amount + r.interest_amount) as amount from fact_loan_repayments r where d.year_quarter = '2010-Q1' group by aa.account_action_name |
This simple query does quite a lot. Its also a trivial matter to change 'branch' for 'loan officer', 'center', 'group' and other built in ways to filter and group tables that are used for filtering and grouping data. There are other 'fact' tables in the data warehouse that cover such things as disbursals and savings. Same It's the same approach to querying for all of them (consistency of query pattern is a strength of this type of data warehouse).
Finally, here's a query to get a breakdown of the number and amount of disbursals per loan_officer by branch by month for 2009 (loan officers can move branch).
Panel |
---|
select p.display_name as loan_officer, o.display_name as branch, d.month_name, sum(disbursal_count) as disbursals, sum(disbursal_amount) as disbursal_amount from fact_loan_disbursals disb where d.year4 = 2009 group by p.display_name, o.display_name, d.month_number |
...
Because it's quite complicated, the data warehouse pre-calculates information about each loan that is in arrears each day so that you get a quick and easy look at arrears related data.
The . Data is snapshot by day. The query below gets arrears information that is past a mifos configure configured 'lateness' number of days as of end 2010-03-31. It does it for a loan officer and breaks the results down into pre-determined weekly bands. Replacing 'weekly' by 'monthly' give the same data in monthly bands.
Panel |
---|
select wb.past_due_description, from hist_loan_arrears hla where endperiod.date_value = date('2010-03-31') group by wb.past_due_description |
...
In the mifos standard reports being developed ... there's a little of the vanilla reporting above, quite a bit of the loan arrears reporting but mostly ite it's customer and account based. It's currently This type of reporting against the data warehouse is a little more involved than the vanilla but the there are 2 main positives are that this 1) .
- it's still better than querying the mifos application
...
- database
- most importantly,
...
- it caters for customer hierarchy changes (loan officer movements, group and client transfers etc) as well as customer and account status changes. Basically,historical reporting like comparing one time period to another is made possible.
The main tables (dim_customer, dim_loan and dim_savings) each have links link directly to a full mifos hierarchy... branch, loan officer, center, group etc... but the thing which is different from normal mifos tables is that one customer, for example a client, will have a number of entries in dim_customer. Each time its the hierarchy changes (e.g. client or group transfer, loan officer assignment) or it has there is a status change... a new entry is added. The entry are is valid for a period of time which is denoted by a pair of date fields called valid_from and valid_to. If you want to find the unique version of the client that is relevent on 2010-01-01 you need to add the following to a query...
Panel |
---|
and valid_from <= date('2010-01-01') |
It took me a little while to get used to this. Also some of the queries felt a little complicated but still better than working with the mifos application database structure. Below are a couple of examples from the standard mifos reports. If you have the Pentaho Report Designer you can get the .prpt files from git://mifos.git.sourceforge.net / mifos/bi and look at the queries.
a) as of 2010-01-01, how many centers did loan officer XXX manager
Panel |
---|
select count(distinct(customer_id)) as centers_managed from dim_personnel p
dim_customer c on c.loan_officer_key = p.personnel_key where p.display_name = 'XXX' and /* get loan office entry that is valid for date*/ <= date(${client_summary_end_date}) anddate('2010-01-01') > date('2010-01-01') and c.customer_status = 'CenterStatus-Active'
c.valid_from <= date(${client_summary_end_date}) and date('2010-01-01') /* get center entry that is valid for date*/ > date('2010-01-01') |
b) as of 2010-01-01, how many client clients with active loans did loan officer XXX manage
Panel |
---|
( select count(distinct(c.customer_id)) as clients_with_loans from dim_personnel p
dim_customer c on c.loan_officer_key = p.personnel_key
dim_loan l on l.customer_key = c.customer_key where p. personneldisplay_ id = ${client_summary_personnel_id} andname = 'XXX' <= date(${client_summary_end_date}) and date('2010-01-01') > date('2010-01-01') and c.customer_level_id = 1 and1 /* client... without this groups would get picked up as well*/ and l.loan_status in ('AccountState-ActiveInGoodStanding', 'AccountState-ActiveInBadStanding')
l.valid_from <= date(${client_summary_end_date}) and date('2010-01-01') > date('2010-01-01') |
c) as of 2010-01-01, what were the repayments on active loans that loan officer XXX dealt with. Here's where I struggle a bit. It's a little struggle technically but really its a bigger struggle to make sure you are asking the right question. The query below only look looks at non-adjusted repayments for active loans managed by a loan office at a certain point in time... .
...
which is okay if that's what the question is.
Panel |
---|
select ifnull(sum(principal_amount),0.0) as principal_paid, ifnull(sum(interest_amount),0.0) as interest_paid from fact_loan_repayments paid
dim_loan l on l.loan_account_key = paid.loan_account_key
dim_personnel p on p.personnel_key = paid.loan_officer_key
dim _date d on d.date_key = paid.action_date_key where_account_action aa on aa.account_action_key = paid.account_action_key where p.personnel_id = ${accounts_summary_personnel_id} and= 'XXX' <= date(${accounts_summary_end_date}) and date('2010-01-01') > date('2010-01-01') and paid.action_date <= date('2010-01-01') and l.loan_status in ('AccountState-ActiveInGoodStanding', 'AccountState-ActiveInBadStanding')
l.valid_from <= date(${accounts_summary_end_date}) and date('2010-01-01') > date('2010-01-01') |