File size: 17,582 Bytes
0672540
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
acc078a
 
 
 
 
 
 
 
 
0672540
 
 
 
 
1e87919
f48b9a1
1e87919
acc078a
 
ca5766c
f3a03d8
0672540
b600303
 
1e87919
acc078a
 
0672540
 
1e87919
0672540
 
 
acc078a
 
 
0672540
acc078a
a15a6d1
acc078a
 
 
 
 
0672540
1e87919
 
0672540
a15a6d1
 
f3a03d8
a15a6d1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2ad24bd
a15a6d1
acc078a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2ad24bd
0672540
2ad24bd
 
 
 
 
 
 
 
9b62bd4
0672540
1e87919
 
 
 
 
acc078a
 
 
 
a15a6d1
 
1e87919
 
 
 
 
acc078a
 
 
 
a15a6d1
 
1e87919
 
0672540
 
4abf268
0672540
f48b9a1
0672540
 
 
 
 
 
 
4abf268
0672540
 
 
 
b600303
 
2ad24bd
 
 
 
 
 
 
 
 
a15a6d1
2ad24bd
 
 
b600303
 
 
 
 
 
 
 
 
 
0672540
 
1e87919
0672540
1e87919
0672540
1e87919
acc078a
1e87919
 
0672540
acc078a
 
 
 
 
 
 
 
 
a15a6d1
acc078a
 
 
2ad24bd
4abf268
 
 
f3a03d8
0672540
 
 
 
 
acc078a
9627998
0672540
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a15a6d1
0672540
 
 
 
f3a03d8
acc078a
1e87919
acc078a
 
 
 
 
 
 
f3a03d8
1e87919
acc078a
 
 
 
1e87919
 
acc078a
 
 
 
 
0672540
 
e5a42bb
0672540
 
e5a42bb
b600303
 
 
ca5766c
 
 
b600303
 
8fe9118
 
0672540
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
import streamlit as st
import pandas as pd
import base64

# Display user Error, Warning or Success Message
def fn_display_user_messages(lv_text, lv_type, mv_processing_message):
    """Display user Info, Error, Warning or Success Message"""
    
    if lv_type == "Success":
        with mv_processing_message.container(): 
            st.success(lv_text)
    elif lv_type == "Error":
        with mv_processing_message.container(): 
            st.error(lv_text)
    elif lv_type == "Warning":
        with mv_processing_message.container(): 
            st.warning(lv_text)
    else:
        with mv_processing_message.container(): 
            st.info(lv_text)

# Removing the length of the suffix from the end
def remove_suffix(input_string, suffix):
    """Removing the length of the suffix from the end"""
    
    if input_string.endswith(suffix):
        return input_string[:-len(suffix)]
    else:
        raise("Invalid Input")

# Function to generate balance and due date history
def fn_generate_data(mv_setup_df, mv_txn_df, mv_balance_df, mv_due_date_history_df):
    """Function to generate balance and due date history"""

    lv_total_tenure_outstanding = 0
    lv_total_posted_txns = 0
    lv_total_due_amt = 0
    lv_total_payment = 0
    lv_total_waived = 0
    lv_total_adj_minus = 0
    lv_total_payment_appropriated = 0
    lv_total_refund = 0
    lv_index = 0
    lv_txn_details = []
    mv_txn_details_by_bill = [] 
    lv_has_excess_payment = False
    lv_has_charged_off = False
    lv_payment_excess = 0

    for lv_txn_index, lv_txn_row in mv_txn_df.iterrows():
        
        lv_setup_record_type = mv_setup_df.loc[mv_setup_df['TXN_CODE'] == lv_txn_row['TXN_TCD_CODE']]['TYPE'].values
        lv_balance_code = lv_txn_row['TXN_TCD_CODE']
        lv_posted = 0
        lv_adj_plus = 0
        lv_adj_minus = 0
        lv_waive = 0
        lv_paid = 0
        lv_payment_excess_paid = 0
        lv_refund =0

        if lv_balance_code.endswith("CHGOFF"):
            lv_has_charged_off = True
            print("Account is Charged OFF")
            break

        if(lv_balance_code != 'PAYMENT'):
            lv_txn_details.append(lv_txn_row)
        
        if(lv_setup_record_type == 'REFUND'):
            lv_refund = lv_txn_row['TXN_AMT']
            lv_total_refund += lv_refund

            if lv_balance_code in mv_balance_df['BALANCE_CODE'].values:
                lv_temp_row_id = mv_balance_df.index[mv_balance_df['BALANCE_CODE'] == lv_balance_code][0]
                mv_balance_df.at[lv_temp_row_id, 'POSTED'] = 0
                mv_balance_df.at[lv_temp_row_id, 'PAID'] = 0
                mv_balance_df.at[lv_temp_row_id, 'ADJ_PLUS'] = 0
                mv_balance_df.at[lv_temp_row_id, 'ADJ_MINUS'] = 0
                mv_balance_df.at[lv_temp_row_id, 'WAIVE'] = 0
                mv_balance_df.at[lv_temp_row_id, 'PAID_WITH_EXCESS'] = 0
                mv_balance_df.at[lv_temp_row_id, 'REFUND'] += lv_refund
                mv_balance_df.at[lv_temp_row_id, 'OUTSTANDING'] = mv_balance_df.at[lv_temp_row_id, 'POSTED'] + mv_balance_df.at[lv_temp_row_id, 'ADJ_PLUS'] + mv_balance_df.at[lv_temp_row_id, 'REFUND']  - mv_balance_df.at[lv_temp_row_id, 'ADJ_MINUS'] - mv_balance_df.at[lv_temp_row_id,'WAIVE'] - mv_balance_df.at[lv_temp_row_id, 'PAID'] - mv_balance_df.at[lv_temp_row_id,'PAID_WITH_EXCESS']
            else:
                mv_balance_df = pd.concat([mv_balance_df, pd.DataFrame(
                                                                {   'BALANCE_CODE': lv_balance_code,
                                                                    'POSTED': [0],
                                                                    'PAID': [0],
                                                                    'ADJ_PLUS': [0],
                                                                    'ADJ_MINUS': [0],
                                                                    'WAIVE': [0],
                                                                    'PAID_WITH_EXCESS': [0],
                                                                    'REFUND':[lv_refund],
                                                                    'OUTSTANDING': [lv_refund]
                                                                })],
                                        ignore_index=True)
        
        if(len(lv_setup_record_type) == 0 and lv_balance_code != 'PAYMENT_EXCESS'):
            if(lv_txn_row['TXN_AMT']>0) :
                if lv_balance_code.endswith("ADJ_PLUS"):
                    lv_balance_code = remove_suffix(lv_balance_code, "_ADJ_PLUS")
                    lv_adj_plus = lv_txn_row['TXN_AMT']
                    lv_total_tenure_outstanding += lv_adj_plus
                    lv_total_posted_txns += lv_adj_plus
                elif lv_balance_code.endswith("ADJ_MINUS"):
                    lv_balance_code = remove_suffix(lv_balance_code, "_ADJ_MINUS")
                    lv_adj_minus = lv_txn_row['TXN_AMT']
                    lv_total_adj_minus += lv_adj_minus
                elif lv_balance_code.endswith("WAIVE"):
                    lv_balance_code = remove_suffix(lv_balance_code, "_WAIVE")
                    lv_waive = lv_txn_row['TXN_AMT']
                    lv_total_waived += lv_waive
                else:
                    lv_posted = lv_txn_row['TXN_AMT']
                    lv_total_tenure_outstanding += lv_posted
                    lv_total_posted_txns += lv_posted              
            elif(lv_txn_row['TXN_AMT']<0):
                lv_temp_paid = lv_txn_row['TXN_AMT']
                
                if(lv_txn_row['PAID_WITH_EXCESS'] == 'Y'):
                    lv_payment_excess += lv_temp_paid
                    lv_payment_excess_paid = lv_temp_paid*-1
                else:
                    lv_paid = lv_temp_paid
                
                lv_total_payment_appropriated += (lv_temp_paid*-1)

            if (lv_txn_row['TXN_AMT'] != 0 and lv_balance_code != 'PAYMENT'):
                if lv_balance_code in mv_balance_df['BALANCE_CODE'].values:
                    lv_temp_row_id = mv_balance_df.index[mv_balance_df['BALANCE_CODE'] == lv_balance_code][0]
                    mv_balance_df.at[lv_temp_row_id, 'POSTED'] += lv_posted
                    mv_balance_df.at[lv_temp_row_id, 'PAID'] += lv_paid*-1
                    mv_balance_df.at[lv_temp_row_id, 'ADJ_PLUS'] += lv_adj_plus
                    mv_balance_df.at[lv_temp_row_id, 'ADJ_MINUS'] += lv_adj_minus
                    mv_balance_df.at[lv_temp_row_id, 'WAIVE'] += lv_waive
                    mv_balance_df.at[lv_temp_row_id, 'PAID_WITH_EXCESS'] += lv_payment_excess_paid
                    mv_balance_df.at[lv_temp_row_id, 'REFUND'] += lv_refund
                    mv_balance_df.at[lv_temp_row_id, 'OUTSTANDING'] = mv_balance_df.at[lv_temp_row_id, 'POSTED'] + mv_balance_df.at[lv_temp_row_id, 'ADJ_PLUS'] + mv_balance_df.at[lv_temp_row_id, 'REFUND']  - mv_balance_df.at[lv_temp_row_id, 'ADJ_MINUS'] - mv_balance_df.at[lv_temp_row_id,'WAIVE'] - mv_balance_df.at[lv_temp_row_id, 'PAID'] - mv_balance_df.at[lv_temp_row_id,'PAID_WITH_EXCESS']
                else:
                    mv_balance_df = pd.concat([mv_balance_df, pd.DataFrame(
                                                                    {   'BALANCE_CODE': [lv_balance_code],
                                                                        'POSTED': [lv_posted],
                                                                        'PAID': [lv_paid],
                                                                        'ADJ_PLUS': [lv_adj_plus],
                                                                        'ADJ_MINUS': [lv_adj_minus],
                                                                        'WAIVE': [lv_waive],
                                                                        'PAID_WITH_EXCESS': [lv_payment_excess_paid],
                                                                        'REFUND':[lv_refund],
                                                                        'OUTSTANDING': [lv_posted + lv_adj_plus + lv_refund - lv_paid - lv_adj_minus - lv_waive]
                                                                    })],
                                            ignore_index=True)

        elif(lv_setup_record_type == "BILL"):
            lv_due_generation_dt = lv_txn_row['TXN_DT']
            lv_due_amount = lv_txn_row['TXN_AMT']
            lv_total_due_amt += lv_due_amount
            mv_due_date_history_df = pd.concat([
                                                    mv_due_date_history_df,
                                                    pd.DataFrame(
                                                                    {
                                                                        'DUE_GENERATION_DATE': [lv_due_generation_dt],
                                                                        'DUE_AMOUNT': [lv_due_amount],
                                                                        'ADDITIONAL_CHARGES': [round(lv_total_tenure_outstanding - lv_due_amount,2)],
                                                                        'TOTAL_OUTSTANDING': [lv_total_tenure_outstanding],
                                                                    }
                                                    )
                                                ], ignore_index=True
                                                )
            
            temp_mv_balance_df = mv_balance_df.copy()
            if(lv_payment_excess >0):
                temp_mv_balance_df = pd.concat([temp_mv_balance_df, pd.DataFrame(
                                                                            {   'BALANCE_CODE': "CREDIT",
                                                                                'POSTED': [0],
                                                                                'PAID': [0],
                                                                                'ADJ_PLUS': [0],
                                                                                'ADJ_MINUS': [0],
                                                                                'WAIVE': [0],
                                                                                'PAID_WITH_EXCESS': [lv_payment_excess],
                                                                                'REFUND':[0],
                                                                                'OUTSTANDING': [lv_payment_excess*-1]
                                                                            })],
                                                    ignore_index=True)
            temp_mv_balance_df.loc['Total']=temp_mv_balance_df.sum()
            temp_mv_balance_df.loc[temp_mv_balance_df.index[-1], 'BALANCE_CODE'] = ''

            mv_txn_details_by_bill.append({
                'DUE_GENERATION_DATE': lv_due_generation_dt,
                'TXN_DETAILS': lv_txn_details,
                'BALANCE_DETAILS': temp_mv_balance_df
            })

            lv_txn_details = []
            lv_index += 1
            lv_total_tenure_outstanding = 0
            
        elif(lv_setup_record_type == "IGNORE"):
            print("Ignore txns record - "+lv_balance_code)
        elif(lv_setup_record_type == "EXCESS"):
            lv_has_excess_payment = True
            lv_payment_excess += lv_txn_row['TXN_AMT']*-1
        elif(lv_setup_record_type == "PAYMENT"):
            lv_total_payment += lv_txn_row['TXN_AMT']

    if(lv_payment_excess >0):
        mv_balance_df = pd.concat([mv_balance_df, pd.DataFrame(
                                                                    {   'BALANCE_CODE': "CREDIT",
                                                                        'POSTED': [0],
                                                                        'PAID': [0],
                                                                        'ADJ_PLUS': [0],
                                                                        'ADJ_MINUS': [0],
                                                                        'WAIVE': [0],
                                                                        'PAID_WITH_EXCESS': [lv_payment_excess],
                                                                        'REFUND':[0],
                                                                        'OUTSTANDING': [lv_payment_excess*-1]
                                                                    })],
                                            ignore_index=True)

    mv_balance_df.loc['Total']= mv_balance_df.sum()
    mv_balance_df.loc[mv_balance_df.index[-1], 'BALANCE_CODE'] = ''
    
    return mv_balance_df,mv_due_date_history_df, mv_txn_details_by_bill, lv_total_posted_txns, lv_total_payment, lv_total_due_amt, lv_total_payment_appropriated, lv_total_adj_minus, lv_total_waived, lv_total_refund, lv_has_excess_payment, lv_has_charged_off

# Main Program
def main():
    
    # -- Streamlit Settings
    st.set_page_config("Billing Summarizer",layout="wide")
    st.header("Billing Summarizer πŸ’")
    st.text("")
    st.text("")
    st.text("")

    # -- Display Processing Details
    mv_processing_message = st.empty()
    st.text("")
    st.text("")

    # -- Input Upload
    with st.sidebar:
        st.header("Upload Files")

        st.text("")
        lv_setup_csv = st.file_uploader("Setup File", type=["csv"])
        
        st.text("")
        st.text("")
        lv_txn_csv = st.file_uploader("Txn File", type=["csv"])

    # -- Generate Bill Summary
    if st.button("Generate Bill Summary"):
        if lv_setup_csv != None and lv_txn_csv != None:
            fn_display_user_messages("Processing Input Files","Success",mv_processing_message)
            
            mv_setup_df = pd.read_csv(lv_setup_csv, sep=',')
            mv_txn_df = pd.read_csv(lv_txn_csv, sep=',')

            mv_balance_df = pd.DataFrame(columns=['BALANCE_CODE','POSTED','PAID','ADJ_PLUS','ADJ_MINUS','WAIVE','PAID_WITH_EXCESS','REFUND','OUTSTANDING'])
            mv_due_date_history_df = pd.DataFrame(columns=['DUE_GENERATION_DATE','DUE_AMOUNT','ADDITIONAL_CHARGES','TOTAL_OUTSTANDING'])

            with st.spinner("Generating response..."):

                mv_balance_df,mv_due_date_history_df, mv_txn_details_by_bill, lv_total_posted_txns, lv_total_payment, lv_total_due_amt, lv_total_payment_appropriated, lv_total_adj_minus, lv_total_waived, lv_total_refund, lv_has_excess_payment, lv_has_charged_off = fn_generate_data(mv_setup_df, mv_txn_df, mv_balance_df, mv_due_date_history_df)
                lv_summary = f"""
                                        #### Summary:
                                        - Total Txns Posted          =  **{round(lv_total_posted_txns,2)}**.
                                        - Total Due Amounts          =  **{round(lv_total_due_amt,2)}**.
                                        - Total Payment Received     =  **{round(lv_total_payment,2)}**.
                                        - Total Payment Appropriated =  **{round(lv_total_payment_appropriated,2)}**.
                                        - Total Adjust Minus         =  **{round(lv_total_adj_minus,2)}**.
                                        - Total Waived               =  **{round(lv_total_waived,2)}**.
                                        - Difference of Payments     =  **{round(round(lv_total_payment,2) - round(lv_total_payment_appropriated,2),2)}**.
                                        - Estimated Payoff Amount    =  **{round(round(lv_total_posted_txns,2) - round(lv_total_payment,2) - round(lv_total_adj_minus,2) - round(lv_total_waived,2) + round(lv_total_refund,2),2)}**.
                                        """

                with st.container(border=True):
                    if lv_has_excess_payment:
                         lv_summary += f"""
                                        - Account has Excess Payment Transaction.
                                        """
                    if lv_has_charged_off:
                        lv_summary += f"""
                                        - Account is Charged off. Consider below as chargeoff balances
                                        """
                    st.markdown(lv_summary)

                with st.expander("Balance Details"):
                    st.dataframe(mv_balance_df, use_container_width=True)

                with st.expander("Due Date History"):
                    st.dataframe(mv_due_date_history_df, use_container_width=True)
                
                with st.container(border=True):
                    st.subheader("Txn & Balance details By Due Date")
                    st.text("")
                    st.text("")
                    
                    for row in mv_txn_details_by_bill:
                        with st.expander("Due Dt - "+str(row['DUE_GENERATION_DATE'])):
                            st.dataframe(row['BALANCE_DETAILS'], use_container_width=True)
                            st.dataframe(row['TXN_DETAILS'], use_container_width=True)
                                
            fn_display_user_messages("File Processing Completed Successfully","Success",mv_processing_message)
        else:
            fn_display_user_messages("Upload Input Files","Error",mv_processing_message)

# Loading Main
if __name__ == "__main__":
    main()