Haseeb-001 commited on
Commit
39e62fd
·
verified ·
1 Parent(s): 808ab5b

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +208 -0
app.py ADDED
@@ -0,0 +1,208 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import streamlit as st
3
+ import pandas as pd
4
+ import numpy as np
5
+ from io import BytesIO
6
+ import tempfile
7
+
8
+ # Optional: PDF extraction if needed
9
+ try:
10
+ import pdfplumber
11
+ except ImportError:
12
+ pdfplumber = None
13
+
14
+ # FAISS for potential vector similarity (for future enhancement)
15
+ import faiss
16
+
17
+ # Groq API for LLM integration
18
+ from groq import Groq
19
+
20
+ # -------------------------------
21
+ # Initialize Groq Client
22
+ # -------------------------------
23
+ client = Groq(api_key=os.environ.get("GROQ_API_KEY"))
24
+
25
+ # -------------------------------
26
+ # Utility Functions
27
+ # -------------------------------
28
+
29
+ def load_ledger(file):
30
+ """
31
+ Load ledger from CSV, JSON, or PDF.
32
+ """
33
+ file_ext = os.path.splitext(file.name)[1].lower()
34
+ if file_ext == ".csv":
35
+ df = pd.read_csv(file)
36
+ elif file_ext == ".json":
37
+ df = pd.read_json(file)
38
+ elif file_ext == ".pdf":
39
+ if pdfplumber is None:
40
+ st.error("Please install pdfplumber to process PDF files.")
41
+ return None
42
+ with pdfplumber.open(file) as pdf:
43
+ page = pdf.pages[0] # Assumes table on first page
44
+ table = page.extract_table()
45
+ df = pd.DataFrame(table[1:], columns=table[0])
46
+ else:
47
+ st.error("Unsupported file type!")
48
+ return None
49
+ return df
50
+
51
+ def preprocess_ledger(df):
52
+ """
53
+ Standardize date format and convert credit/debit to float.
54
+ """
55
+ df['date'] = pd.to_datetime(df['date'], errors='coerce')
56
+ df['credit'] = pd.to_numeric(df['credit'], errors='coerce').fillna(0.0)
57
+ df['debit'] = pd.to_numeric(df['debit'], errors='coerce').fillna(0.0)
58
+ return df
59
+
60
+ def generate_suggestion(row):
61
+ """
62
+ Generate a reconciliation suggestion using Groq API.
63
+ """
64
+ prompt = (
65
+ f"Ledger entry mismatch detected.\n"
66
+ f"- Date: {row['date'].date() if pd.notnull(row['date']) else 'Unknown'}\n"
67
+ f"- Credit: {row['credit']}\n"
68
+ f"- Debit: {row['debit']}\n\n"
69
+ "Please provide reconciliation suggestions in simple bullet points."
70
+ )
71
+ try:
72
+ response = client.chat.completions.create(
73
+ messages=[{"role": "user", "content": prompt}],
74
+ model="llama-3.3-70b-versatile",
75
+ stream=False,
76
+ )
77
+ suggestion = response.choices[0].message.content
78
+ except Exception as e:
79
+ suggestion = f"Error generating suggestion: {e}"
80
+ return suggestion
81
+
82
+ def compare_ledgers(df_a, df_b):
83
+ """
84
+ Compare two ledger DataFrames row-by-row based on date, credit, and debit.
85
+ """
86
+ results = []
87
+ df_b_copy = df_b.copy()
88
+
89
+ # Compare each entry in Ledger A with Ledger B
90
+ for idx, row in df_a.iterrows():
91
+ # Match based on same date and nearly identical credit & debit amounts.
92
+ match = df_b_copy[
93
+ (df_b_copy['date'] == row['date']) &
94
+ (np.isclose(df_b_copy['credit'], row['credit'])) &
95
+ (np.isclose(df_b_copy['debit'], row['debit']))
96
+ ]
97
+ if not match.empty:
98
+ status = "✅ Matched"
99
+ suggestion = ""
100
+ # Remove matched entry to prevent duplicate matching.
101
+ df_b_copy = df_b_copy.drop(match.index[0])
102
+ else:
103
+ status = "❌ Mismatch"
104
+ suggestion = generate_suggestion(row)
105
+ results.append({
106
+ "date": row['date'],
107
+ "credit": row['credit'],
108
+ "debit": row['debit'],
109
+ "description": row.get("description", ""),
110
+ "status": status,
111
+ "suggestion": suggestion
112
+ })
113
+
114
+ # Any remaining entries in Ledger B are extra entries.
115
+ for idx, row in df_b_copy.iterrows():
116
+ results.append({
117
+ "date": row['date'],
118
+ "credit": row['credit'],
119
+ "debit": row['debit'],
120
+ "description": row.get("description", ""),
121
+ "status": "❌ Mismatch (Extra in Ledger B)",
122
+ "suggestion": "Review extra entry in Ledger B."
123
+ })
124
+
125
+ result_df = pd.DataFrame(results)
126
+ return result_df
127
+
128
+ def calculate_totals(df_a, df_b):
129
+ """
130
+ Calculate totals and differences for credits and debits.
131
+ """
132
+ totals = {
133
+ "ledger_a_credit": df_a['credit'].sum(),
134
+ "ledger_a_debit": df_a['debit'].sum(),
135
+ "ledger_b_credit": df_b['credit'].sum(),
136
+ "ledger_b_debit": df_b['debit'].sum(),
137
+ "credit_difference": df_a['credit'].sum() - df_b['credit'].sum(),
138
+ "debit_difference": df_a['debit'].sum() - df_b['debit'].sum(),
139
+ }
140
+ return totals
141
+
142
+ def generate_excel_report(df):
143
+ """
144
+ Generate an Excel report from the reconciliation DataFrame.
145
+ """
146
+ output = BytesIO()
147
+ with pd.ExcelWriter(output, engine='xlsxwriter') as writer:
148
+ df.to_excel(writer, index=False, sheet_name="Reconciliation")
149
+ processed_data = output.getvalue()
150
+ return processed_data
151
+
152
+ # -------------------------------
153
+ # Streamlit User Interface
154
+ # -------------------------------
155
+
156
+ def main():
157
+ st.title("��� Finance Ledger Reconciliation App")
158
+ st.markdown("Upload the ledger files to compare two opposite party records and get reconciliation suggestions.")
159
+
160
+ col1, col2 = st.columns(2)
161
+ with col1:
162
+ ledger_a_file = st.file_uploader("Upload Ledger A (CSV/JSON/PDF)", type=["csv", "json", "pdf"], key="ledger_a")
163
+ with col2:
164
+ ledger_b_file = st.file_uploader("Upload Ledger B (CSV/JSON/PDF)", type=["csv", "json", "pdf"], key="ledger_b")
165
+
166
+ if ledger_a_file and ledger_b_file:
167
+ df_a = load_ledger(ledger_a_file)
168
+ df_b = load_ledger(ledger_b_file)
169
+
170
+ if df_a is not None and df_b is not None:
171
+ st.subheader("Original Ledgers Preview")
172
+ st.markdown("**Ledger A:**")
173
+ st.write(df_a.head())
174
+ st.markdown("**Ledger B:**")
175
+ st.write(df_b.head())
176
+
177
+ # Preprocess the data
178
+ df_a = preprocess_ledger(df_a)
179
+ df_b = preprocess_ledger(df_b)
180
+
181
+ st.subheader("Processed Ledgers Preview")
182
+ st.markdown("**Ledger A:**")
183
+ st.write(df_a.head())
184
+ st.markdown("**Ledger B:**")
185
+ st.write(df_b.head())
186
+
187
+ # Compare ledgers and calculate differences
188
+ with st.spinner("Comparing ledgers..."):
189
+ result_df = compare_ledgers(df_a, df_b)
190
+ totals = calculate_totals(df_a, df_b)
191
+
192
+ st.subheader("Reconciliation Results")
193
+ st.write(result_df)
194
+
195
+ st.markdown("### Totals & Differences")
196
+ st.write(totals)
197
+
198
+ # Download report button (Excel file)
199
+ excel_data = generate_excel_report(result_df)
200
+ st.download_button(label="Download Report as Excel",
201
+ data=excel_data,
202
+ file_name="reconciliation_report.xlsx",
203
+ mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
204
+
205
+ st.success("Reconciliation completed successfully!")
206
+
207
+ if __name__ == '__main__':
208
+ main()