mirror of
https://github.com/DRYTRIX/TimeTracker.git
synced 2026-01-06 03:30:25 -06:00
Major Features: - Add project costs feature with full CRUD operations - Implement toast notification system for better user feedback - Enhance analytics dashboard with improved visualizations - Add OIDC authentication improvements and debug tools Improvements: - Enhance reports with new filtering and export capabilities - Update command palette with additional shortcuts - Improve mobile responsiveness across all pages - Refactor UI components for consistency Removals: - Remove license server integration and related dependencies - Clean up unused license-related templates and utilities Technical Changes: - Add new migration 018 for project_costs table - Update models: Project, Settings, User with new relationships - Refactor routes: admin, analytics, auth, invoices, projects, reports - Update static assets: CSS improvements, new JS modules - Enhance templates: analytics, admin, projects, reports Documentation: - Add comprehensive documentation for project costs feature - Document toast notification system with visual guides - Update README with new feature descriptions - Add migration instructions and quick start guides - Document OIDC improvements and Kanban enhancements Files Changed: - Modified: 56 files (core app, models, routes, templates, static assets) - Deleted: 6 files (license server integration) - Added: 28 files (new features, documentation, migrations)
125 lines
4.2 KiB
Python
125 lines
4.2 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Migration script to add project_costs table to the database.
|
|
This script adds support for tracking project expenses beyond hourly work.
|
|
|
|
Usage:
|
|
python migrate-add-project-costs.py
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import psycopg2
|
|
from psycopg2 import sql
|
|
|
|
def get_db_connection():
|
|
"""Get database connection from environment variables"""
|
|
return psycopg2.connect(
|
|
host=os.getenv('DB_HOST', 'localhost'),
|
|
port=os.getenv('DB_PORT', '5432'),
|
|
database=os.getenv('DB_NAME', 'timetracker'),
|
|
user=os.getenv('DB_USER', 'timetracker'),
|
|
password=os.getenv('DB_PASSWORD', 'timetracker')
|
|
)
|
|
|
|
def table_exists(cursor, table_name):
|
|
"""Check if a table exists"""
|
|
cursor.execute("""
|
|
SELECT EXISTS (
|
|
SELECT FROM information_schema.tables
|
|
WHERE table_schema = 'public'
|
|
AND table_name = %s
|
|
);
|
|
""", (table_name,))
|
|
return cursor.fetchone()[0]
|
|
|
|
def migrate():
|
|
"""Run the migration"""
|
|
print("Starting project_costs migration...")
|
|
|
|
try:
|
|
conn = get_db_connection()
|
|
conn.autocommit = False
|
|
cursor = conn.cursor()
|
|
|
|
# Check if table already exists
|
|
if table_exists(cursor, 'project_costs'):
|
|
print("✓ Table 'project_costs' already exists. Skipping migration.")
|
|
return True
|
|
|
|
print("Creating project_costs table...")
|
|
|
|
# Create table
|
|
cursor.execute("""
|
|
CREATE TABLE project_costs (
|
|
id SERIAL PRIMARY KEY,
|
|
project_id INTEGER NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
|
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
description VARCHAR(500) NOT NULL,
|
|
category VARCHAR(50) NOT NULL,
|
|
amount NUMERIC(10, 2) NOT NULL,
|
|
currency_code VARCHAR(3) NOT NULL DEFAULT 'EUR',
|
|
billable BOOLEAN NOT NULL DEFAULT TRUE,
|
|
invoiced BOOLEAN NOT NULL DEFAULT FALSE,
|
|
invoice_id INTEGER REFERENCES invoices(id) ON DELETE SET NULL,
|
|
cost_date DATE NOT NULL,
|
|
notes TEXT,
|
|
receipt_path VARCHAR(500),
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
""")
|
|
print("✓ Created project_costs table")
|
|
|
|
# Create indexes
|
|
print("Creating indexes...")
|
|
cursor.execute("CREATE INDEX ix_project_costs_project_id ON project_costs(project_id);")
|
|
cursor.execute("CREATE INDEX ix_project_costs_user_id ON project_costs(user_id);")
|
|
cursor.execute("CREATE INDEX ix_project_costs_cost_date ON project_costs(cost_date);")
|
|
cursor.execute("CREATE INDEX ix_project_costs_invoice_id ON project_costs(invoice_id);")
|
|
print("✓ Created indexes")
|
|
|
|
# Add comments
|
|
print("Adding table comments...")
|
|
cursor.execute("""
|
|
COMMENT ON TABLE project_costs IS
|
|
'Tracks project expenses beyond hourly work (travel, materials, services, etc.)';
|
|
""")
|
|
cursor.execute("""
|
|
COMMENT ON COLUMN project_costs.category IS
|
|
'Category of cost: travel, materials, services, equipment, software, other';
|
|
""")
|
|
cursor.execute("""
|
|
COMMENT ON COLUMN project_costs.billable IS
|
|
'Whether this cost should be billed to the client';
|
|
""")
|
|
cursor.execute("""
|
|
COMMENT ON COLUMN project_costs.invoiced IS
|
|
'Whether this cost has been included in an invoice';
|
|
""")
|
|
print("✓ Added comments")
|
|
|
|
# Commit the transaction
|
|
conn.commit()
|
|
print("✓ Migration completed successfully!")
|
|
|
|
cursor.close()
|
|
conn.close()
|
|
return True
|
|
|
|
except psycopg2.Error as e:
|
|
print(f"✗ Database error: {e}")
|
|
if conn:
|
|
conn.rollback()
|
|
return False
|
|
except Exception as e:
|
|
print(f"✗ Error: {e}")
|
|
if conn:
|
|
conn.rollback()
|
|
return False
|
|
|
|
if __name__ == '__main__':
|
|
success = migrate()
|
|
sys.exit(0 if success else 1)
|
|
|