ZeeAI1 commited on
Commit
d3256dd
·
verified ·
1 Parent(s): 6813994

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +128 -124
app.py CHANGED
@@ -3,7 +3,7 @@ import json
3
  import uuid
4
  import datetime
5
  import logging
6
- from flask import Flask, request, render_template_string, jsonify
7
 
8
  # Setup logging
9
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
@@ -18,9 +18,6 @@ except ImportError:
18
  AutoModelForCausalLM = None
19
  AutoTokenizer = None
20
 
21
- # Initialize Flask app
22
- app = Flask(__name__)
23
-
24
  # Initialize AI model (distilbert as placeholder; replace with fine-tuned model or Mistral-7B)
25
  model_name = "distilbert-base-uncased" # Lightweight model for demo
26
  if TRANSFORMERS_AVAILABLE:
@@ -37,7 +34,7 @@ else:
37
  model = None
38
 
39
  # Database setup
40
- conn = sqlite3.connect("erp.db", check_same_thread=False)
41
  cursor = conn.cursor()
42
 
43
  # Create tables
@@ -86,14 +83,20 @@ def initialize_chart_of_accounts():
86
  ("1.2", "Current Assets", "Asset", "1", True, False),
87
  ("1.2.1", "Cash", "Asset", "1.2", True, True),
88
  ("1.2.2", "Laptop", "Asset", "1.2", True, True),
 
 
89
  ("2", "Liabilities", "Liability", None, True, False),
90
  ("2.1", "Accounts Payable", "Liability", "2", True, True),
 
91
  ("3", "Equity", "Equity", None, True, False),
92
- ("3.1", "Owner's Capital", "Equity", "3", True, True),
 
93
  ("4", "Revenue", "Revenue", None, True, False),
94
- ("4.1", "Sales", "Revenue", "4", True, True),
95
  ("5", "Expenses", "Expense", None, True, False),
96
- ("5.1", "Operating Expenses", "Expense", "5", True, True)
 
 
97
  ]
98
  cursor.executemany("""
99
  INSERT OR REPLACE INTO chart_of_accounts
@@ -103,7 +106,7 @@ def initialize_chart_of_accounts():
103
  conn.commit()
104
  logging.info("Chart of accounts initialized.")
105
 
106
- # Parse prompt using AI model (or fallback)
107
  def parse_prompt(prompt):
108
  if model and tokenizer:
109
  try:
@@ -111,7 +114,7 @@ def parse_prompt(prompt):
111
  Parse the following accounting prompt into a JSON object with:
112
  - debit: {{account, type, amount}}
113
  - credit: {{account, type, amount}}
114
- - payment_method: 'cash' or 'credit' or null
115
  Prompt: {prompt}
116
  """
117
  inputs = tokenizer(input_text, return_tensors="pt")
@@ -121,34 +124,77 @@ def parse_prompt(prompt):
121
  except Exception as e:
122
  logging.warning(f"Model parsing failed: {e}. Using fallback parser.")
123
 
124
- # Fallback parsing for common scenarios
125
- prompt_lower = prompt.lower()
126
  amount = None
127
- for word in prompt_lower.split():
128
- if word.startswith("$"):
129
- try:
130
- amount = float(word[1:])
131
- break
132
- except:
133
- pass
134
-
 
135
  if not amount:
136
  logging.error("No amount found in prompt.")
137
  return None
138
 
139
- if "laptop" in prompt_lower:
140
- debit_account = "Laptop"
141
- debit_type = "Asset"
142
- if "cash" in prompt_lower:
143
- credit_account = "Cash"
144
- credit_type = "Asset"
145
- payment_method = "cash"
146
- elif "credit" in prompt_lower:
147
- credit_account = "Accounts Payable"
148
- credit_type = "Liability"
149
- payment_method = "credit"
150
- else:
151
- return {"debit": {"account": "Laptop", "type": "Asset", "amount": amount}, "credit": None, "payment_method": None}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
152
  return {
153
  "debit": {"account": debit_account, "type": debit_type, "amount": amount},
154
  "credit": {"account": credit_account, "type": credit_type, "amount": amount},
@@ -169,20 +215,23 @@ def generate_journal_entry(prompt, follow_up_response=None):
169
 
170
  # Handle ambiguous payment method
171
  if not payment_method and not follow_up_response:
172
- return {"status": "clarify", "message": "Wonderful, did you buy on credit? (Yes/No)", "original_prompt": prompt}
173
 
174
  # Determine credit account
175
  credit_account = None
176
  credit_type = None
177
- if follow_up_response and follow_up_response.lower() == "yes":
178
- credit_account = "Accounts Payable"
179
- credit_type = "Liability"
 
 
 
 
 
180
  elif payment_method == "cash":
181
- credit_account = parsed["credit"]["account"]
182
- credit_type = parsed["credit"]["type"]
183
  elif payment_method == "credit":
184
- credit_account = "Accounts Payable"
185
- credit_type = "Liability"
186
  else:
187
  return "Invalid payment method specified."
188
 
@@ -249,91 +298,46 @@ def generate_t_account(account_name):
249
 
250
  return t_account
251
 
252
- # HTML template for web interface
253
- HTML_TEMPLATE = """
254
- <!DOCTYPE html>
255
- <html>
256
- <head>
257
- <title>AI ERP System</title>
258
- <style>
259
- body { font-family: Arial, sans-serif; margin: 20px; }
260
- h1 { color: #333; }
261
- textarea, input { width: 100%; margin: 10px 0; }
262
- pre { background: #f4f4f4; padding: 10px; white-space: pre-wrap; }
263
- .error { color: red; }
264
- </style>
265
- </head>
266
- <body>
267
- <h1>AI ERP System</h1>
268
- <h2>Enter Transaction Prompt</h2>
269
- <form method="POST" action="/process_prompt">
270
- <textarea name="prompt" rows="4" placeholder="e.g., Bought a laptop for $200 on cash"></textarea>
271
- <input type="submit" value="Submit Prompt">
272
- </form>
273
- {% if result %}
274
- <h2>Result</h2>
275
- {% if result.status == 'clarify' %}
276
- <p>{{ result.message }}</p>
277
- <form method="POST" action="/process_follow_up">
278
- <input type="hidden" name="original_prompt" value="{{ result.original_prompt }}">
279
- <input type="text" name="follow_up" placeholder="Yes/No">
280
- <input type="submit" value="Submit Response">
281
- </form>
282
- {% else %}
283
- <pre>{{ result }}</pre>
284
- {% endif %}
285
- {% endif %}
286
- <h2>View T-Account</h2>
287
- <form method="POST" action="/t_account">
288
- <input type="text" name="account_name" placeholder="e.g., Laptop">
289
- <input type="submit" value="Generate T-Account">
290
- </form>
291
- {% if t_account %}
292
- <h2>T-Account</h2>
293
- <pre>{{ t_account }}</pre>
294
- {% endif %}
295
- </body>
296
- </html>
297
- """
298
-
299
- # Flask routes
300
- @app.route("/", methods=["GET", "POST"])
301
- def index():
302
  initialize_chart_of_accounts()
303
- return render_template_string(HTML_TEMPLATE, result=None, t_account=None)
304
-
305
- @app.route("/process_prompt", methods=["POST"])
306
- def process_prompt():
307
- prompt = request.form.get("prompt")
308
- if not prompt:
309
- return render_template_string(HTML_TEMPLATE, result="No prompt provided.", t_account=None)
310
 
311
- result = generate_journal_entry(prompt)
312
- return render_template_string(HTML_TEMPLATE, result=result, t_account=None)
313
-
314
- @app.route("/process_follow_up", methods=["POST"])
315
- def process_follow_up():
316
- original_prompt = request.form.get("original_prompt")
317
- follow_up = request.form.get("follow_up")
318
- if not original_prompt or not follow_up:
319
- return render_template_string(HTML_TEMPLATE, result="Missing prompt or response.", t_account=None)
320
-
321
- result = generate_journal_entry(original_prompt, follow_up)
322
- return render_template_string(HTML_TEMPLATE, result=result, t_account=None)
323
-
324
- @app.route("/t_account", methods=["POST"])
325
- def t_account():
326
- account_name = request.form.get("account_name")
327
- if not account_name:
328
- return render_template_string(HTML_TEMPLATE, result=None, t_account="No account name provided.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
329
 
330
- t_account = generate_t_account(account_name)
331
- return render_template_string(HTML_TEMPLATE, result=None, t_account=t_account)
332
 
333
- # Run the app
334
  if __name__ == "__main__":
335
- # Initialize chart of accounts
336
- initialize_chart_of_accounts()
337
-
338
- # Run Flask app (use port 7860 for Hugging Face Spaces)
339
- app.run(host="0.0.0.0", port=7860, debug=False)
 
3
  import uuid
4
  import datetime
5
  import logging
6
+ import re
7
 
8
  # Setup logging
9
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
 
18
  AutoModelForCausalLM = None
19
  AutoTokenizer = None
20
 
 
 
 
21
  # Initialize AI model (distilbert as placeholder; replace with fine-tuned model or Mistral-7B)
22
  model_name = "distilbert-base-uncased" # Lightweight model for demo
23
  if TRANSFORMERS_AVAILABLE:
 
34
  model = None
35
 
36
  # Database setup
37
+ conn = sqlite3.connect("erp.db")
38
  cursor = conn.cursor()
39
 
40
  # Create tables
 
83
  ("1.2", "Current Assets", "Asset", "1", True, False),
84
  ("1.2.1", "Cash", "Asset", "1.2", True, True),
85
  ("1.2.2", "Laptop", "Asset", "1.2", True, True),
86
+ ("1.2.3", "Inventory", "Asset", "1.2", True, True),
87
+ ("1.2.4", "Accounts Receivable", "Asset", "1.2", True, True),
88
  ("2", "Liabilities", "Liability", None, True, False),
89
  ("2.1", "Accounts Payable", "Liability", "2", True, True),
90
+ ("2.2", "Loan Payable", "Liability", "2", True, True),
91
  ("3", "Equity", "Equity", None, True, False),
92
+ ("3.1", "Owner's Equity", "Equity", "3", True, True),
93
+ ("3.2", "Drawings", "Equity", "3", True, True),
94
  ("4", "Revenue", "Revenue", None, True, False),
95
+ ("4.1", "Sales Revenue", "Revenue", "4", True, True),
96
  ("5", "Expenses", "Expense", None, True, False),
97
+ ("5.1", "Rent Expense", "Expense", "5", True, True),
98
+ ("5.2", "Salary Expense", "Expense", "5", True, True),
99
+ ("5.3", "Office Supplies", "Expense", "5", True, True)
100
  ]
101
  cursor.executemany("""
102
  INSERT OR REPLACE INTO chart_of_accounts
 
106
  conn.commit()
107
  logging.info("Chart of accounts initialized.")
108
 
109
+ # Enhanced fallback parser
110
  def parse_prompt(prompt):
111
  if model and tokenizer:
112
  try:
 
114
  Parse the following accounting prompt into a JSON object with:
115
  - debit: {{account, type, amount}}
116
  - credit: {{account, type, amount}}
117
+ - payment_method: 'cash', 'credit', or null
118
  Prompt: {prompt}
119
  """
120
  inputs = tokenizer(input_text, return_tensors="pt")
 
124
  except Exception as e:
125
  logging.warning(f"Model parsing failed: {e}. Using fallback parser.")
126
 
127
+ # Fallback parser
128
+ prompt_lower = prompt.lower().strip()
129
  amount = None
130
+ # Extract amount
131
+ match = re.search(r'\$[\d,.]+', prompt_lower)
132
+ if match:
133
+ try:
134
+ amount = float(match.group().replace('$', '').replace(',', ''))
135
+ except ValueError:
136
+ logging.error("Invalid amount format.")
137
+ return None
138
+
139
  if not amount:
140
  logging.error("No amount found in prompt.")
141
  return None
142
 
143
+ # Map keywords to accounts and types
144
+ account_mappings = {
145
+ "laptop": ("Laptop", "Asset"),
146
+ "inventory": ("Inventory", "Asset"),
147
+ "machinery": ("Machinery", "Asset"),
148
+ "building": ("Building", "Asset"),
149
+ "plant": ("Plant", "Asset"),
150
+ "office supplies": ("Office Supplies", "Expense"),
151
+ "cash": ("Cash", "Asset"),
152
+ "receivable": ("Accounts Receivable", "Asset"),
153
+ "sold goods": ("Sales Revenue", "Revenue"),
154
+ "sales": ("Sales Revenue", "Revenue"),
155
+ "rent": ("Rent Expense", "Expense"),
156
+ "salary": ("Salary Expense", "Expense"),
157
+ "paid": ("Cash", "Asset"),
158
+ "bought": ("Laptop", "Asset"), # Default to Laptop; refine below
159
+ "purchased": ("Laptop", "Asset")
160
+ }
161
+
162
+ debit_account = None
163
+ debit_type = None
164
+ credit_account = None
165
+ credit_type = None
166
+ payment_method = None
167
+
168
+ # Determine debit account
169
+ for keyword, (account, acc_type) in account_mappings.items():
170
+ if keyword in prompt_lower:
171
+ if keyword in ["bought", "purchased"]:
172
+ # Check for specific asset/expense
173
+ for asset in ["laptop", "inventory", "machinery", "building", "plant", "office supplies"]:
174
+ if asset in prompt_lower:
175
+ debit_account, debit_type = account_mappings[asset]
176
+ break
177
+ if not debit_account:
178
+ debit_account, debit_type = account, acc_type
179
+ elif keyword in ["rent", "salary", "office supplies"]:
180
+ debit_account, debit_type = account, acc_type
181
+ elif keyword in ["sold goods", "sales"]:
182
+ debit_account, debit_type = "Accounts Receivable", "Asset"
183
+ credit_account, credit_type = account, acc_type
184
+ break
185
+
186
+ # Determine credit account and payment method
187
+ if "cash" in prompt_lower:
188
+ credit_account, credit_type = "Cash", "Asset"
189
+ payment_method = "cash"
190
+ elif "credit" in prompt_lower:
191
+ credit_account, credit_type = "Accounts Payable", "Liability"
192
+ payment_method = "credit"
193
+ elif debit_account and not credit_account:
194
+ # Ambiguous payment method
195
+ return {"debit": {"account": debit_account, "type": debit_type, "amount": amount}, "credit": None, "payment_method": None}
196
+
197
+ if debit_account and credit_account:
198
  return {
199
  "debit": {"account": debit_account, "type": debit_type, "amount": amount},
200
  "credit": {"account": credit_account, "type": credit_type, "amount": amount},
 
215
 
216
  # Handle ambiguous payment method
217
  if not payment_method and not follow_up_response:
218
+ return {"status": "clarify", "message": "Was this bought on cash or credit? (cash/credit)", "original_prompt": prompt}
219
 
220
  # Determine credit account
221
  credit_account = None
222
  credit_type = None
223
+ if follow_up_response:
224
+ follow_up_lower = follow_up_response.lower()
225
+ if follow_up_lower == "cash":
226
+ credit_account, credit_type = "Cash", "Asset"
227
+ elif follow_up_lower == "credit":
228
+ credit_account, credit_type = "Accounts Payable", "Liability"
229
+ else:
230
+ return "Invalid response. Please specify 'cash' or 'credit'."
231
  elif payment_method == "cash":
232
+ credit_account, credit_type = parsed["credit"]["account"], parsed["credit"]["type"]
 
233
  elif payment_method == "credit":
234
+ credit_account, credit_type = "Accounts Payable", "Liability"
 
235
  else:
236
  return "Invalid payment method specified."
237
 
 
298
 
299
  return t_account
300
 
301
+ # Main CLI loop
302
+ def main():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
303
  initialize_chart_of_accounts()
304
+ print("AI ERP System: Enter accounting prompts or 't-account <account_name>' to view T-accounts. Type 'exit' to quit.")
 
 
 
 
 
 
305
 
306
+ pending_prompt = None
307
+ while True:
308
+ try:
309
+ prompt = input("> ").strip()
310
+ if prompt.lower() == "exit":
311
+ break
312
+ if prompt.lower().startswith("t-account "):
313
+ account_name = prompt[10:].strip()
314
+ if account_name:
315
+ print(generate_t_account(account_name))
316
+ else:
317
+ print("Please specify an account name.")
318
+ continue
319
+
320
+ # Handle follow-up response
321
+ if pending_prompt:
322
+ result = generate_journal_entry(pending_prompt, prompt)
323
+ pending_prompt = None
324
+ else:
325
+ result = generate_journal_entry(prompt)
326
+
327
+ if isinstance(result, dict) and result["status"] == "clarify":
328
+ print(result["message"])
329
+ pending_prompt = result["original_prompt"]
330
+ else:
331
+ print(result)
332
+
333
+ except KeyboardInterrupt:
334
+ print("\nExiting...")
335
+ break
336
+ except Exception as e:
337
+ logging.error(f"Error processing prompt: {e}")
338
+ print("An error occurred. Please try again.")
339
 
340
+ conn.close()
 
341
 
 
342
  if __name__ == "__main__":
343
+ main()