mirror of
https://github.com/DRYTRIX/TimeTracker.git
synced 2026-05-22 06:10:24 -05:00
935f30e4d6
Implement comprehensive client notes system allowing users to add internal notes about clients that are never visible to clients themselves. Notes support importance flagging, full CRUD operations, and proper access controls. Key Changes: - Add ClientNote model with user/client relationships - Create Alembic migration (025) for client_notes table - Implement full REST API with 9 endpoints - Add client_notes blueprint with CRUD routes - Create UI templates (edit page + notes section on client view) - Add importance toggle with AJAX functionality - Implement permission system (users edit own, admins edit all) Features: - Internal-only notes with rich text support - Mark notes as important for quick identification - Author tracking with timestamps - Cascade delete when client is removed - Mobile-responsive design - i18n support for all user-facing text Testing: - 24 comprehensive model tests - 23 route/integration tests - Full coverage of CRUD operations and permissions Documentation: - Complete feature guide in docs/CLIENT_NOTES_FEATURE.md - API documentation with examples - Troubleshooting section - Updated main docs index Database: - Migration revision 025 (depends on 024) - Fixed PostgreSQL boolean default value issue - 4 indexes for query performance - CASCADE delete constraint on client_id This feature addresses the need for teams to track important information about clients internally without exposing sensitive notes to client-facing interfaces or documents.
72 lines
2.7 KiB
Python
72 lines
2.7 KiB
Python
"""Add client notes table for internal notes about clients
|
||
|
||
Revision ID: 025
|
||
Revises: 024
|
||
Create Date: 2025-10-24 00:00:00
|
||
|
||
"""
|
||
from alembic import op
|
||
import sqlalchemy as sa
|
||
|
||
|
||
# revision identifiers, used by Alembic.
|
||
revision = '025'
|
||
down_revision = '024'
|
||
branch_labels = None
|
||
depends_on = None
|
||
|
||
|
||
def upgrade() -> None:
|
||
"""Create client_notes table"""
|
||
bind = op.get_bind()
|
||
inspector = sa.inspect(bind)
|
||
|
||
# Check if client_notes table already exists
|
||
if 'client_notes' not in inspector.get_table_names():
|
||
op.create_table('client_notes',
|
||
sa.Column('id', sa.Integer(), nullable=False),
|
||
sa.Column('content', sa.Text(), nullable=False),
|
||
sa.Column('client_id', sa.Integer(), nullable=False),
|
||
sa.Column('user_id', sa.Integer(), nullable=False),
|
||
sa.Column('is_important', sa.Boolean(), nullable=False, server_default=sa.text('false')),
|
||
sa.Column('created_at', sa.DateTime(), nullable=False),
|
||
sa.Column('updated_at', sa.DateTime(), nullable=False),
|
||
sa.ForeignKeyConstraint(['client_id'], ['clients.id'], ondelete='CASCADE'),
|
||
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
|
||
sa.PrimaryKeyConstraint('id')
|
||
)
|
||
|
||
# Create indexes for better performance
|
||
op.create_index('ix_client_notes_client_id', 'client_notes', ['client_id'], unique=False)
|
||
op.create_index('ix_client_notes_user_id', 'client_notes', ['user_id'], unique=False)
|
||
op.create_index('ix_client_notes_created_at', 'client_notes', ['created_at'], unique=False)
|
||
op.create_index('ix_client_notes_is_important', 'client_notes', ['is_important'], unique=False)
|
||
|
||
print("✓ Created client_notes table")
|
||
else:
|
||
print("ℹ client_notes table already exists")
|
||
|
||
|
||
def downgrade() -> None:
|
||
"""Drop client_notes table"""
|
||
bind = op.get_bind()
|
||
inspector = sa.inspect(bind)
|
||
|
||
# Check if client_notes table exists before trying to drop it
|
||
if 'client_notes' in inspector.get_table_names():
|
||
try:
|
||
# Drop indexes first
|
||
op.drop_index('ix_client_notes_is_important', table_name='client_notes')
|
||
op.drop_index('ix_client_notes_created_at', table_name='client_notes')
|
||
op.drop_index('ix_client_notes_user_id', table_name='client_notes')
|
||
op.drop_index('ix_client_notes_client_id', table_name='client_notes')
|
||
|
||
# Drop the table
|
||
op.drop_table('client_notes')
|
||
print("✓ Dropped client_notes table")
|
||
except Exception as e:
|
||
print(f"⚠ Warning dropping client_notes table: {e}")
|
||
else:
|
||
print("ℹ client_notes table does not exist")
|
||
|