Skip to content

Commit

Permalink
Fixed report generation error and display issues
Browse files Browse the repository at this point in the history
  • Loading branch information
raymond0208 committed Jan 2, 2025
1 parent 64c7655 commit c952820
Show file tree
Hide file tree
Showing 12 changed files with 204 additions and 101 deletions.
29 changes: 10 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
# CashCatalyst - Cash Flow Management System
# CashCatalyst: AI-Powered Cash Flow Management 🚀💰

CashCatalyst is a modern web application for tracking and analyzing cash flow, built with Flask and modern web technologies. It provides comprehensive tools for managing transactions, analyzing cash flow patterns, and generating financial insights.
For individuals or lean teams managing the finances of a business, cash flow is fundamental and has the most direct and CashCatalyst is your ultimate companion for mastering cash flow management. Whether you're an entrepreneur, small business owner, or part of a lean finance team, our tool harnesses cutting-edge AI to transform the way you handle your business's lifeblood - cash.

## 🎯 Why CashCatalyst?

AI-Driven Insights: Get expert analysis and predictions for your cash flow scenarios.
Automatic Reporting: Generate professional cash flow statements with a click.
User-Friendly Interface: Easily record and manage your cash activities.
Collaborative: Share and work together with your team seamlessly.

## Features

Expand Down Expand Up @@ -38,21 +45,12 @@ CashCatalyst is a modern web application for tracking and analyzing cash flow, b
- Automated cash flow statement generation

### 5. Data Import/Export
![Data Upload](screenshots/Data_upload.png)
- Upload transactions from CSV/Excel files
- Export transactions to CSV/Excel
- Bulk transaction processing
- Data validation and preview

## Technology Stack

- **Backend**: Python Flask
- **Database**: SQLite with SQLAlchemy ORM
- **Frontend**: HTML5, CSS3, JavaScript
- **UI Framework**: Bootstrap 5
- **Charts**: Chart.js
- **Icons**: Font Awesome
- **Data Processing**: Pandas, NumPy
- **Visualization**: Matplotlib

## Installation

Expand Down Expand Up @@ -116,13 +114,6 @@ The application will be available at `http://localhost:5000`
- Export data for reporting
- Manage transaction categories

## Security Features

- Password hashing using Werkzeug
- User session management
- CSRF protection
- Secure form handling
- User-specific data isolation

## Contributing

Expand Down
Binary file modified instance/cash_flow.db
Binary file not shown.
177 changes: 120 additions & 57 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ def edit_transaction(transaction_id):
transaction.type = request.form['type']
db.session.commit()
flash('Transaction Updated Successfully', 'success')
return redirect(url_for('home'))
return redirect(url_for('cash_activities'))
return render_template('edit_transaction.html', transaction=transaction)

@app.route('/delete/<int:transaction_id>', methods=['POST'])
Expand Down Expand Up @@ -345,78 +345,141 @@ def forecast():
'error': str(e)
}), 500

@app.route('/generate_cashflow_statement')
@app.route('/generate_cashflow_statement', methods=['GET'])
@login_required
def generate_cashflow_statement():
def generate_cashflow_statement_route():
try:
# Get all transactions for the current user
transactions = Transaction.query.filter_by(user_id=current_user.id).order_by(Transaction.date).all()

if not transactions:
flash('No transactions found to generate statement', 'warning')
return redirect(url_for('ai_analysis'))
app.logger.warning("No transactions found when generating cash flow statement")
return jsonify({'error': 'No transactions found'}), 400

initial_balance_record = InitialBalance.query.filter_by(user_id=current_user.id).first()
initial_balance = initial_balance_record.balance if initial_balance_record else 0.0

start_date = transactions[0].date
end_date = transactions[-1].date

transaction_data = "\n".join([f"Date: {t.date}, Description: {t.description}, Amount: {t.amount}, Type: {t.type}" for t in transactions])

app.logger.info(f"Generating cash flow statement for period {start_date} to {end_date}")

# Create Excel workbook
# Initialize FinancialAnalytics
api_key = os.getenv('ANTHROPIC_API_KEY')
if not api_key:
app.logger.error("ANTHROPIC_API_KEY not found in environment variables")
return jsonify({'error': 'API key not found'}), 500

analytics = FinancialAnalytics(api_key=api_key)

# Generate cash flow statement using AI
statement_data, ending_balance = analytics.generate_cashflow_statement(
initial_balance=initial_balance,
start_date=start_date,
end_date=end_date,
transaction_data=transaction_data
)

if not statement_data:
app.logger.error("generate_cashflow_statement returned None")
return jsonify({'error': 'Failed to generate statement: No data returned'}), 500

app.logger.info("Cash flow statement generated successfully")
app.logger.debug(f"Statement data: {statement_data}")

# Create a new workbook and select the active sheet
wb = Workbook()
ws = wb.active
ws.title = "Cash Flow Statement"


# Set column widths
ws.column_dimensions['A'].width = 40
ws.column_dimensions['B'].width = 15

# Add title
ws['A1'] = f"Cash Flow Statement - {start_date} to {end_date}"
ws['A1'].font = Font(bold=True, size=14)
ws.merge_cells('A1:B1')

# Add headers
headers = ['Date', 'Description', 'Amount', 'Type', 'Category']
for col, header in enumerate(headers, 1):
cell = ws.cell(row=1, column=col)
cell.value = header
headers = ['Category', 'Amount']
for col, header in enumerate(headers, start=1):
cell = ws.cell(row=3, column=col, value=header)
cell.font = Font(bold=True)
cell.fill = PatternFill(start_color="CCCCCC", end_color="CCCCCC", fill_type="solid")

# Add transactions
for row, t in enumerate(transactions, 2):
ws.cell(row=row, column=1, value=t.date.strftime('%Y-%m-%d'))
ws.cell(row=row, column=2, value=t.description)
ws.cell(row=row, column=3, value=t.amount)
ws.cell(row=row, column=4, value=t.type)

# Determine category based on type
if 'cfo' in t.type.lower():
category = 'Operating'
elif 'cfi' in t.type.lower():
category = 'Investing'
else:
category = 'Financing'
ws.cell(row=row, column=5, value=category)

# Add totals
total_row = len(transactions) + 3
ws.cell(row=total_row, column=1, value='Totals')
ws.cell(row=total_row, column=1).font = Font(bold=True)

# Calculate and add category totals
categories = ['Operating', 'Investing', 'Financing']
for col, category in enumerate(categories, 3):
total = sum(t.amount for t in transactions if category.lower() in t.type.lower())
cell = ws.cell(row=total_row, column=col)
cell.value = total
cell.font = Font(bold=True)

# Auto-adjust column widths
for col in range(1, 6):
ws.column_dimensions[get_column_letter(col)].auto_size = True

# Save to BytesIO
excel_file = BytesIO()
wb.save(excel_file)
excel_file.seek(0)

cell.fill = PatternFill(start_color="D3D3D3", end_color="D3D3D3", fill_type="solid")

# Helper function to add a row with formatting
def add_row(row, category, amount, is_total=False, indent=0):
ws.cell(row=row, column=1, value=category).alignment = Alignment(indent=indent)
ws.cell(row=row, column=2, value=amount)
if is_total:
for col in range(1, 3):
ws.cell(row=row, column=col).font = Font(bold=True)

# Add Beginning Cash Balance
row = 4
add_row(row, "Beginning Cash Balance", initial_balance, True)
row += 2

# Process and add data for each category
categories = ['CFO', 'CFI', 'CFF']
for category in categories:
ws.cell(row=row, column=1, value=f"Cash Flow from {'Operating' if category == 'CFO' else 'Investing' if category == 'CFI' else 'Financing'} Activities ({category})")
ws.cell(row=row, column=1).font = Font(bold=True)
ws.cell(row=row, column=1).fill = PatternFill(start_color="D3D3D3", end_color="D3D3D3", fill_type="solid")
row += 1

category_total = 0
for item in statement_data:
if item['Category'] == category:
add_row(row, item['Subcategory'], item['Amount'], indent=1)
category_total += item['Amount']
row += 1

add_row(row, f"Net Cash from {'Operating' if category == 'CFO' else 'Investing' if category == 'CFI' else 'Financing'}", category_total, True)
row += 2

# Add Total Net Cash Flow
total_net_cash_flow = sum(item['Amount'] for item in statement_data)
add_row(row, "Total Net Cash Flow", total_net_cash_flow, True)
row += 2

# Add Ending Cash Balance
ending_balance = initial_balance + total_net_cash_flow
add_row(row, "Ending Cash Balance", ending_balance, True)

# Apply currency formatting to amount column
for row in ws['B4:B' + str(ws.max_row)]:
for cell in row:
cell.number_format = '#,##0.00'

# Apply right alignment to amount column
for row in ws['B1:B' + str(ws.max_row)]:
for cell in row:
cell.alignment = Alignment(horizontal='right')

# Add borders
thin_border = Border(left=Side(style='thin'), right=Side(style='thin'), top=Side(style='thin'), bottom=Side(style='thin'))
for row in ws.iter_rows(min_row=1, max_row=ws.max_row, min_col=1, max_col=2):
for cell in row:
cell.border = thin_border

# Save to BytesIO object
output = BytesIO()
wb.save(output)
output.seek(0)

app.logger.info("Excel file created successfully")
return send_file(
excel_file,
output,
mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
as_attachment=True,
download_name='cash_flow_statement.xlsx'
)

except Exception as e:
flash(f'Error generating statement: {str(e)}', 'danger')
return redirect(url_for('ai_analysis'))
app.logger.error(f"Failed to generate cash flow statement: {str(e)}")
app.logger.error(traceback.format_exc())
return jsonify({'error': f'Failed to generate cash flow statement: {str(e)}'}), 500

# Generate monthly chart function
def generate_monthly_balance_chart():
Expand Down
Binary file added screenshots/Data_upload.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshots/Homepage.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified screenshots/ai_analysis.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified screenshots/cash_activities.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified screenshots/cash_overview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
27 changes: 21 additions & 6 deletions src/anthropic_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,12 +155,27 @@ def generate_cashflow_statement(self, initial_balance, start_date, end_date, tra
Transaction Data:
{transaction_data}
Please provide the following:
1. A categorized breakdown of cash flows (CFO, CFI, CFF) with subcategories.
Note: Classify bank interest payments under Cash Flow from Financing (CFF), not Cash Flow from Operations (CFO).
2. Total net cash flow for each category (CFO, CFI, CFF).
3. Overall net cash flow.
4. Ending cash balance.
Please follow these strict categorization rules:
Cash Flow from Operations (CFO):
- Cash from customers
- Salary and supplier payments
- Income tax payments
- Other operating activities
Cash Flow from Investing (CFI):
- Purchase/sale of property and equipment
- Purchase/sale of investments
- Other investing activities
Cash Flow from Financing (CFF):
- Bank loans and repayments
- Interest payments
- Share issuance
- Dividend payments
- Other financing activities
Important: Salary and supplier payments MUST be categorized under CFO, not CFF.
Present the data in a clear, structured format. Use a simple list format with each item on a new line, like this:
Expand Down
3 changes: 3 additions & 0 deletions templates/ai_analysis.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ <h3>AI Financial Analysis</h3>
<div class="analysis-controls mb-4">
<button id="generate-analysis" class="btn btn-primary">Generate Advanced Analysis</button>
<button id="generate-cashflow-statement" class="btn btn-secondary">Generate Cash Flow Statement</button>
<div class="text-muted mt-2">
<small><i class="fas fa-info-circle"></i> Note: The cash flow statement is generated based on the transactions currently in your database. Additional options for custom date ranges and filtering will be available in future updates.</small>
</div>
</div>

<!-- Loading Indicator -->
Expand Down
46 changes: 38 additions & 8 deletions templates/cash_activities.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,15 @@
{% endif %}
{% endwith %}

<!-- Balance by Date Section -->
<div class="balance-section mt-4">
<h3>Balance by Date</h3>
<form id="balance-by-date-form" action="{{ url_for('balance_by_date') }}" method="post">
<!-- Form to set the initial balance -->
<div class="initial-balance-section mt-4">
<form method="POST" action="{{ url_for('set_initial_balance') }}">
<h3>Set Initial Cash Balance</h3>
<div class="mb-3">
<input type="date" name="date" class="form-control" required>
<input type="number" step="0.01" class="form-control" id="initial_balance" name="initial_balance" required>
</div>
<button type="submit" class="btn btn-primary">Calculate Balance</button>
<button type="submit" class="btn btn-secondary">Set Initial Balance</button>
</form>
<div id="balance-by-date-result" class="mt-3"></div>
</div>

<!-- Form to record transaction -->
Expand Down Expand Up @@ -91,7 +90,7 @@ <h3>Transaction Records</h3>
</tr>
</thead>
<tbody>
{% for transaction in transactions %}
{% for transaction in transactions.items %}
<tr class='table-row' draggable="true">
<td>{{ transaction.date }}</td>
<td>{{ transaction.description }}</td>
Expand All @@ -107,6 +106,37 @@ <h3>Transaction Records</h3>
{% endfor %}
</tbody>
</table>

<!-- Pagination Controls -->
{% if transactions.pages > 1 %}
<nav aria-label="Transaction pagination">
<ul class="pagination justify-content-center">
{% if transactions.has_prev %}
<li class="page-item">
<a class="page-link" href="{{ url_for('cash_activities', page=transactions.prev_num) }}">&laquo; Previous</a>
</li>
{% endif %}

{% for page_num in transactions.iter_pages(left_edge=2, left_current=2, right_current=2, right_edge=2) %}
{% if page_num %}
<li class="page-item {% if page_num == transactions.page %}active{% endif %}">
<a class="page-link" href="{{ url_for('cash_activities', page=page_num) }}">{{ page_num }}</a>
</li>
{% else %}
<li class="page-item disabled">
<span class="page-link">...</span>
</li>
{% endif %}
{% endfor %}

{% if transactions.has_next %}
<li class="page-item">
<a class="page-link" href="{{ url_for('cash_activities', page=transactions.next_num) }}">Next &raquo;</a>
</li>
{% endif %}
</ul>
</nav>
{% endif %}
</div>

<!-- Export Buttons -->
Expand Down
Loading

0 comments on commit c952820

Please sign in to comment.