initial commit

This commit is contained in:
2025-12-20 14:52:47 -06:00
commit 71397f0b8a
8 changed files with 1033 additions and 0 deletions

125
reports.py Normal file
View File

@@ -0,0 +1,125 @@
import sqlite3
import os
from datetime import datetime
def connect_db():
"""
Connects to the SQLite database file and returns the connection object.
If the file does not exist, it prints an an error message.
"""
db_file = 'time_tracker.db'
if not os.path.exists(db_file):
print(f"Error: Database file '{db_file}' not found. Please run the database creation script first.")
return None
return sqlite3.connect(db_file)
def unbilled_time_report():
"""
Generates a report of unbilled time entries for a selected client.
"""
conn = connect_db()
if not conn:
return
try:
cursor = conn.cursor()
# Display active clients for selection
cursor.execute('SELECT client_id, client_name FROM clients WHERE active = 1 ORDER BY client_name')
active_clients = cursor.fetchall()
if not active_clients:
print("No active clients found to generate a report for.")
return
print("\n--- Select a Client for Unbilled Time Report ---")
for client_id, client_name in active_clients:
print(f"{client_id}: {client_name}")
print("0: Exit")
print("--------------------------------------------------\n")
while True:
try:
choice = input("Enter the ID of the client (or 0 to exit): ")
if choice == '0':
print("Exiting report generation.")
return
client_id = int(choice)
if any(c[0] == client_id for c in active_clients):
break
else:
print("Invalid client ID. Please enter a valid ID from the list.")
except ValueError:
print("Invalid input. Please enter a number.")
# Fetch client details and billing rate
cursor.execute('SELECT client_name, billing_rate FROM clients WHERE client_id = ?', (client_id,))
client_name, billing_rate = cursor.fetchone()
# Fetch unbilled time entries for the selected client, including description
cursor.execute('''
SELECT date, hours, description, project
FROM time_tracking
WHERE client_id = ? AND invoiced = 0
ORDER BY date
''', (client_id,))
time_entries = cursor.fetchall()
if not time_entries:
print(f"\nNo unbilled time entries found for {client_name}.")
return
# Calculate daily summary, descriptions, and total cost
daily_summary = {}
grand_total_cost = 0.0
for entry_date, hours, description, project in time_entries:
if entry_date not in daily_summary:
daily_summary[entry_date] = {'hours': 0.0, 'cost': 0.0, 'entries': []}
daily_summary[entry_date]['hours'] += hours
daily_summary[entry_date]['cost'] += hours * billing_rate
daily_summary[entry_date]['entries'].append({'description': description, 'project': project})
grand_total_cost += hours * billing_rate
# Print the formatted report
print(f"\n--- Unbilled Time Report for {client_name} ---")
print(f"Total Unbilled Cost: ${grand_total_cost:.2f}\n")
for daily_date, summary in daily_summary.items():
print(f"Date: {daily_date} | Hours: {summary['hours']:.2f} | Cost: ${summary['cost']:.2f}")
for entry in summary['entries']:
print(f" - Project: {entry['project']}, Description: {entry['description']}")
print("-" * 32)
print(f"\nReport generated on {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
except sqlite3.Error as e:
print(f"An error occurred: {e}")
finally:
if conn:
conn.close()
def main_menu():
"""
Displays the main menu and handles user choices.
"""
while True:
print("\n--- Reports Menu ---")
print("1: Unbilled Time")
print("0: Exit")
print("--------------------\n")
choice = input("Enter your choice: ")
if choice == '1':
unbilled_time_report()
elif choice == '0':
print("Exiting reports script. Goodbye!")
break
else:
print("Invalid choice. Please try again.")
if __name__ == "__main__":
main_menu()