diff --git a/app/routes/reports.py b/app/routes/reports.py
index 5dd4624..d4ddad2 100644
--- a/app/routes/reports.py
+++ b/app/routes/reports.py
@@ -744,7 +744,7 @@ def export_project_excel():
if project.id not in projects_map:
projects_map[project.id] = {
'name': project.name,
- 'client': project.client.name if project.client else '',
+ 'client': project.client if project.client else '',
'total_hours': 0,
'billable_hours': 0,
'hourly_rate': float(project.hourly_rate) if project.hourly_rate else 0,
diff --git a/app/templates/projects/view.html b/app/templates/projects/view.html
index a887b82..c8874e2 100644
--- a/app/templates/projects/view.html
+++ b/app/templates/projects/view.html
@@ -10,7 +10,7 @@
{{ project.code_display }}
{% endif %}
-
{{ project.client.name }}
+ {{ project.client }}
{% if current_user.is_admin or has_any_permission(['edit_projects', 'archive_projects']) %}
diff --git a/app/utils/excel_export.py b/app/utils/excel_export.py
index 6d3905f..c1aeb8a 100644
--- a/app/utils/excel_export.py
+++ b/app/utils/excel_export.py
@@ -54,7 +54,7 @@ def create_time_entries_excel(entries, filename_prefix='timetracker_export'):
entry.id,
entry.user.display_name if entry.user else 'Unknown',
entry.project.name if entry.project else 'N/A',
- entry.project.client.name if (entry.project and entry.project.client) else 'N/A',
+ entry.project.client if (entry.project and entry.project.client) else 'N/A',
entry.task.name if entry.task else 'N/A',
entry.start_time.isoformat() if entry.start_time else '',
entry.end_time.isoformat() if entry.end_time else '',
diff --git a/tests/test_excel_export.py b/tests/test_excel_export.py
new file mode 100644
index 0000000..29a970a
--- /dev/null
+++ b/tests/test_excel_export.py
@@ -0,0 +1,144 @@
+"""
+Tests for Excel export functionality
+"""
+import pytest
+from datetime import datetime, timedelta
+from app.models import TimeEntry, Task
+
+
+@pytest.mark.unit
+@pytest.mark.routes
+def test_create_time_entries_excel_with_client(app, user, project, test_client):
+ """Test that Excel export handles project.client correctly as a string property"""
+ from app.utils.excel_export import create_time_entries_excel
+
+ # Create a time entry with project that has a client
+ start_time = datetime.utcnow() - timedelta(hours=2)
+ end_time = datetime.utcnow()
+
+ entry = TimeEntry(
+ user_id=user.id,
+ project_id=project.id,
+ start_time=start_time,
+ end_time=end_time,
+ notes='Test entry for Excel export',
+ tags='test,export',
+ source='manual',
+ billable=True
+ )
+
+ # Calculate duration manually since we're not going through the full commit cycle
+ entry.duration_seconds = (end_time - start_time).total_seconds()
+
+ # Test that project.client is a string property, not an object
+ assert hasattr(project, 'client')
+ assert isinstance(project.client, str)
+ assert project.client == test_client.name
+
+ # Test Excel export function
+ output, filename = create_time_entries_excel([entry])
+
+ # Verify the output was created successfully
+ assert output is not None
+ assert filename is not None
+ assert filename.endswith('.xlsx')
+
+ # Verify the file content can be read
+ output.seek(0)
+ content = output.read()
+ assert len(content) > 0
+
+
+@pytest.mark.unit
+@pytest.mark.routes
+def test_create_time_entries_excel_with_task(app, user, project, task):
+ """Test that Excel export handles entries with tasks correctly"""
+ from app.utils.excel_export import create_time_entries_excel
+
+ # Create a time entry with a task
+ start_time = datetime.utcnow() - timedelta(hours=1)
+ end_time = datetime.utcnow()
+
+ entry = TimeEntry(
+ user_id=user.id,
+ project_id=project.id,
+ task_id=task.id,
+ start_time=start_time,
+ end_time=end_time,
+ notes='Test entry with task',
+ billable=True
+ )
+
+ # Calculate duration
+ entry.duration_seconds = (end_time - start_time).total_seconds()
+
+ # Test Excel export function
+ output, filename = create_time_entries_excel([entry])
+
+ # Verify the output was created successfully
+ assert output is not None
+ assert filename is not None
+
+ # Verify the file content can be read
+ output.seek(0)
+ content = output.read()
+ assert len(content) > 0
+
+
+@pytest.mark.unit
+@pytest.mark.routes
+def test_create_time_entries_excel_multiple_entries(app, multiple_time_entries):
+ """Test Excel export with multiple time entries"""
+ from app.utils.excel_export import create_time_entries_excel
+
+ # Test Excel export function with multiple entries
+ output, filename = create_time_entries_excel(multiple_time_entries)
+
+ # Verify the output was created successfully
+ assert output is not None
+ assert filename is not None
+
+ # Verify the file content can be read
+ output.seek(0)
+ content = output.read()
+ assert len(content) > 0
+
+
+@pytest.mark.unit
+@pytest.mark.routes
+def test_project_report_excel_export(app, time_entry):
+ """Test project report Excel export with project.client"""
+ from app.utils.excel_export import create_project_report_excel
+
+ # Create project data structure
+ project = time_entry.project
+ projects_data = [{
+ 'name': project.name,
+ 'client': project.client, # Should be a string
+ 'total_hours': 8.0,
+ 'billable_hours': 7.5,
+ 'hourly_rate': 75.00,
+ 'billable_amount': 562.50,
+ 'total_costs': 0,
+ 'total_value': 562.50
+ }]
+
+ # Test that project.client is a string
+ assert isinstance(project.client, str)
+
+ # Test Excel export function
+ output, filename = create_project_report_excel(
+ projects_data,
+ start_date='2024-01-01',
+ end_date='2024-12-31'
+ )
+
+ # Verify the output was created successfully
+ assert output is not None
+ assert filename is not None
+
+ # Verify the file content can be read
+ output.seek(0)
+ content = output.read()
+ assert len(content) > 0
+