summaryrefslogtreecommitdiff
path: root/internal/reports
diff options
context:
space:
mode:
authorT <t@tjp.lol>2025-08-13 16:48:58 -0600
committerT <t@tjp.lol>2025-08-13 16:49:20 -0600
commit7d0d21ba8663ab7ff777a06f4b113337fa717ff3 (patch)
treef06d8691fd13bee6ebccfc17a08c599bd5710705 /internal/reports
parent5c3554c7e49abe263faf54c61e435ba1d5202d27 (diff)
go module in git.tjp.lol
Diffstat (limited to 'internal/reports')
-rw-r--r--internal/reports/api.go3
-rw-r--r--internal/reports/invoice.go2
-rw-r--r--internal/reports/pdf.go20
-rw-r--r--internal/reports/pdf_test.go7
-rw-r--r--internal/reports/timesheet.go67
-rw-r--r--internal/reports/timesheet_test.go17
-rw-r--r--internal/reports/unified.go5
-rw-r--r--internal/reports/unified_test.go148
8 files changed, 134 insertions, 135 deletions
diff --git a/internal/reports/api.go b/internal/reports/api.go
index 0db8672..90b066b 100644
--- a/internal/reports/api.go
+++ b/internal/reports/api.go
@@ -7,7 +7,7 @@ import (
"path/filepath"
"time"
- "punchcard/internal/queries"
+ "git.tjp.lol/punchcard/internal/queries"
)
type ReportParams struct {
@@ -497,4 +497,3 @@ func findProjectByName(ctx context.Context, q *queries.Queries, projectName stri
}
return projects[0], nil
}
-
diff --git a/internal/reports/invoice.go b/internal/reports/invoice.go
index 73235d5..4ac5eb4 100644
--- a/internal/reports/invoice.go
+++ b/internal/reports/invoice.go
@@ -5,7 +5,7 @@ import (
"fmt"
"time"
- "punchcard/internal/queries"
+ "git.tjp.lol/punchcard/internal/queries"
)
type InvoiceData struct {
diff --git a/internal/reports/pdf.go b/internal/reports/pdf.go
index 8434f07..b83436f 100644
--- a/internal/reports/pdf.go
+++ b/internal/reports/pdf.go
@@ -9,8 +9,8 @@ import (
"path/filepath"
"time"
- "punchcard/internal/queries"
- "punchcard/templates"
+ "git.tjp.lol/punchcard/internal/queries"
+ "git.tjp.lol/punchcard/templates"
)
// RecordInvoice records the invoice in the database after successful generation
@@ -229,16 +229,16 @@ type UnifiedJSONData struct {
ContractorName string `json:"contractor_name"`
ContractorLabel string `json:"contractor_label"`
ContractorEmail string `json:"contractor_email"`
-
+
// Invoice-specific fields
InvoiceNumber string `json:"invoice_number"`
LineItems []LineItem `json:"line_items"`
TotalAmount float64 `json:"total_amount"`
-
+
// Timesheet-specific fields
- Entries []TimesheetEntry `json:"entries"`
- Timezone string `json:"timezone"`
-
+ Entries []TimesheetEntry `json:"entries"`
+ Timezone string `json:"timezone"`
+
// Shared field with same value
TotalHours float64 `json:"total_hours"`
}
@@ -268,7 +268,7 @@ func GenerateUnifiedPDF(unifiedData *UnifiedReportData, outputPath string) error
ContractorLabel: unifiedData.InvoiceData.ContractorLabel,
ContractorEmail: unifiedData.InvoiceData.ContractorEmail,
TotalHours: unifiedData.InvoiceData.TotalHours, // Should match timesheet total
-
+
// Invoice-specific fields
InvoiceNumber: fmt.Sprintf("%04d-%02d-%03d",
unifiedData.InvoiceData.DateRange.Start.Year(),
@@ -277,7 +277,7 @@ func GenerateUnifiedPDF(unifiedData *UnifiedReportData, outputPath string) error
),
LineItems: unifiedData.InvoiceData.LineItems,
TotalAmount: unifiedData.InvoiceData.TotalAmount,
-
+
// Timesheet-specific fields
Entries: unifiedData.TimesheetData.Entries,
Timezone: unifiedData.TimesheetData.Timezone,
@@ -296,7 +296,7 @@ func GenerateUnifiedPDF(unifiedData *UnifiedReportData, outputPath string) error
// Create unified template by combining invoice and timesheet templates
unifiedTemplate := templates.InvoiceTemplate + "\n\n#pagebreak()\n\n" + templates.TimesheetTemplate
-
+
// Write Typst template file
typstFile := filepath.Join(tempDir, "unified.typ")
if err := os.WriteFile(typstFile, []byte(unifiedTemplate), 0o644); err != nil {
diff --git a/internal/reports/pdf_test.go b/internal/reports/pdf_test.go
index b31112e..1f2e968 100644
--- a/internal/reports/pdf_test.go
+++ b/internal/reports/pdf_test.go
@@ -4,9 +4,10 @@ import (
"os"
"os/exec"
"path/filepath"
- "punchcard/templates"
"testing"
"time"
+
+ "git.tjp.lol/punchcard/templates"
)
// Helper function for tests
@@ -39,13 +40,13 @@ func TestTypstTemplateCompilation(t *testing.T) {
}
dataFile := filepath.Join(tempDir, "data.json")
- if err := os.WriteFile(dataFile, testData, 0644); err != nil {
+ if err := os.WriteFile(dataFile, testData, 0o644); err != nil {
t.Fatalf("Failed to write test data file: %v", err)
}
// Write Typst template to temp directory
typstFile := filepath.Join(tempDir, "invoice.typ")
- if err := os.WriteFile(typstFile, []byte(templates.InvoiceTemplate), 0644); err != nil {
+ if err := os.WriteFile(typstFile, []byte(templates.InvoiceTemplate), 0o644); err != nil {
t.Fatalf("Failed to write Typst template: %v", err)
}
diff --git a/internal/reports/timesheet.go b/internal/reports/timesheet.go
index a40d8ae..d9ad4b8 100644
--- a/internal/reports/timesheet.go
+++ b/internal/reports/timesheet.go
@@ -5,7 +5,7 @@ import (
"fmt"
"time"
- "punchcard/internal/queries"
+ "git.tjp.lol/punchcard/internal/queries"
)
type TimesheetData struct {
@@ -33,14 +33,14 @@ type TimesheetEntry struct {
}
type timesheetEntryData struct {
- TimeEntryID int64
- StartTime time.Time
- EndTime sql.NullTime
- Description sql.NullString
- ClientID int64
- ClientName string
- ProjectID sql.NullInt64
- ProjectName sql.NullString
+ TimeEntryID int64
+ StartTime time.Time
+ EndTime sql.NullTime
+ Description sql.NullString
+ ClientID int64
+ ClientName string
+ ProjectID sql.NullInt64
+ ProjectName sql.NullString
DurationSeconds int64
}
@@ -59,28 +59,28 @@ func GenerateTimesheetData(
case []queries.GetTimesheetDataByClientRow:
for _, entry := range e {
timeEntries = append(timeEntries, timesheetEntryData{
- TimeEntryID: entry.TimeEntryID,
- StartTime: entry.StartTime,
- EndTime: entry.EndTime,
- Description: entry.Description,
- ClientID: entry.ClientID,
- ClientName: entry.ClientName,
- ProjectID: entry.ProjectID,
- ProjectName: entry.ProjectName,
+ TimeEntryID: entry.TimeEntryID,
+ StartTime: entry.StartTime,
+ EndTime: entry.EndTime,
+ Description: entry.Description,
+ ClientID: entry.ClientID,
+ ClientName: entry.ClientName,
+ ProjectID: entry.ProjectID,
+ ProjectName: entry.ProjectName,
DurationSeconds: entry.DurationSeconds,
})
}
case []queries.GetTimesheetDataByProjectRow:
for _, entry := range e {
timeEntries = append(timeEntries, timesheetEntryData{
- TimeEntryID: entry.TimeEntryID,
- StartTime: entry.StartTime,
- EndTime: entry.EndTime,
- Description: entry.Description,
- ClientID: entry.ClientID,
- ClientName: entry.ClientName,
- ProjectID: sql.NullInt64{Int64: entry.ProjectID, Valid: true},
- ProjectName: sql.NullString{String: entry.ProjectName, Valid: true},
+ TimeEntryID: entry.TimeEntryID,
+ StartTime: entry.StartTime,
+ EndTime: entry.EndTime,
+ Description: entry.Description,
+ ClientID: entry.ClientID,
+ ClientName: entry.ClientName,
+ ProjectID: sql.NullInt64{Int64: entry.ProjectID, Valid: true},
+ ProjectName: sql.NullString{String: entry.ProjectName, Valid: true},
DurationSeconds: entry.DurationSeconds,
})
}
@@ -134,27 +134,27 @@ func convertToTimesheetEntries(entries []timesheetEntryData, loc *time.Location)
// Convert UTC times to specified timezone
localStartTime := entry.StartTime.In(loc)
localEndTime := entry.EndTime.Time.In(loc)
-
+
// Format date as YYYY-MM-DD
date := localStartTime.Format("2006-01-02")
-
+
// Format times as HH:MM
startTime := localStartTime.Format("15:04")
endTime := localEndTime.Format("15:04")
-
+
// Format duration as HH:MM
duration := formatDuration(entry.DurationSeconds)
-
+
// Calculate hours as decimal, rounded to nearest minute
- totalMinutes := (entry.DurationSeconds + 30) / 60 // Round to nearest minute
+ totalMinutes := (entry.DurationSeconds + 30) / 60 // Round to nearest minute
hours := float64(totalMinutes) / 60.0
-
+
// Get project name
projectName := ""
if entry.ProjectName.Valid {
projectName = entry.ProjectName.String
}
-
+
// Get description
description := ""
if entry.Description.Valid {
@@ -177,9 +177,8 @@ func convertToTimesheetEntries(entries []timesheetEntryData, loc *time.Location)
func formatDuration(seconds int64) string {
// Round to nearest minute
- totalMinutes := (seconds + 30) / 60 // Add 30 seconds for rounding
+ totalMinutes := (seconds + 30) / 60 // Add 30 seconds for rounding
hours := totalMinutes / 60
minutes := totalMinutes % 60
return fmt.Sprintf("%d:%02d", hours, minutes)
}
-
diff --git a/internal/reports/timesheet_test.go b/internal/reports/timesheet_test.go
index 8c0ac52..ed35c86 100644
--- a/internal/reports/timesheet_test.go
+++ b/internal/reports/timesheet_test.go
@@ -8,8 +8,8 @@ import (
"testing"
"time"
- "punchcard/internal/queries"
- "punchcard/templates"
+ "git.tjp.lol/punchcard/internal/queries"
+ "git.tjp.lol/punchcard/templates"
)
func TestGenerateTimesheetData(t *testing.T) {
@@ -217,7 +217,7 @@ func TestConvertToTimesheetEntries(t *testing.T) {
entries: []timesheetEntryData{
{
TimeEntryID: 2,
- StartTime: mustParseTime("2025-07-10T18:00:00Z"), // 6:00 PM UTC
+ StartTime: mustParseTime("2025-07-10T18:00:00Z"), // 6:00 PM UTC
EndTime: sql.NullTime{Time: mustParseTime("2025-07-10T19:00:00Z"), Valid: true}, // 7:00 PM UTC
Description: sql.NullString{String: "Meeting", Valid: true},
DurationSeconds: 3600, // 1 hour
@@ -373,13 +373,13 @@ func TestTimesheetTypstTemplateCompilation(t *testing.T) {
}`
dataFile := filepath.Join(tempDir, "data.json")
- if err := os.WriteFile(dataFile, []byte(testData), 0644); err != nil {
+ if err := os.WriteFile(dataFile, []byte(testData), 0o644); err != nil {
t.Fatalf("Failed to write test data file: %v", err)
}
// Write Typst template to temp directory
typstFile := filepath.Join(tempDir, "timesheet.typ")
- if err := os.WriteFile(typstFile, []byte(templates.TimesheetTemplate), 0644); err != nil {
+ if err := os.WriteFile(typstFile, []byte(templates.TimesheetTemplate), 0o644); err != nil {
t.Fatalf("Failed to write Typst template: %v", err)
}
@@ -502,12 +502,12 @@ func TestGenerateDefaultTimesheetFilename(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := GenerateDefaultTimesheetFilename(tt.clientName, tt.projectName, dateRange)
-
+
// Check that the filename starts with the expected pattern
if len(result) < len(tt.want) || result[:len(tt.want)] != tt.want {
t.Errorf("GenerateDefaultTimesheetFilename() prefix = %s, want prefix %s", result, tt.want)
}
-
+
// Check that it ends with .pdf
if result[len(result)-4:] != ".pdf" {
t.Errorf("GenerateDefaultTimesheetFilename() should end with .pdf, got %s", result)
@@ -538,4 +538,5 @@ func abs(x float64) float64 {
return -x
}
return x
-} \ No newline at end of file
+}
+
diff --git a/internal/reports/unified.go b/internal/reports/unified.go
index a6eb8c4..07507ae 100644
--- a/internal/reports/unified.go
+++ b/internal/reports/unified.go
@@ -4,7 +4,7 @@ import (
"fmt"
"time"
- "punchcard/internal/queries"
+ "git.tjp.lol/punchcard/internal/queries"
)
type UnifiedReportData struct {
@@ -80,4 +80,5 @@ func GenerateUnifiedReportData(
InvoiceData: invoiceData,
TimesheetData: timesheetData,
}, nil
-} \ No newline at end of file
+}
+
diff --git a/internal/reports/unified_test.go b/internal/reports/unified_test.go
index 64d0b3f..cf18350 100644
--- a/internal/reports/unified_test.go
+++ b/internal/reports/unified_test.go
@@ -5,10 +5,9 @@ import (
"testing"
"time"
- "punchcard/internal/queries"
+ "git.tjp.lol/punchcard/internal/queries"
)
-
func TestGenerateUnifiedReportData(t *testing.T) {
tests := []struct {
name string
@@ -29,30 +28,30 @@ func TestGenerateUnifiedReportData(t *testing.T) {
name: "client entries with UTC timezone",
entries: []queries.GetInvoiceDataByClientRow{
{
- TimeEntryID: 1,
- StartTime: mustParseTime("2025-07-10T14:55:00Z"),
- EndTime: sql.NullTime{Time: mustParseTime("2025-07-10T18:05:00Z"), Valid: true},
- Description: sql.NullString{String: "GL closing", Valid: true},
- ClientID: 1,
- ClientName: "Test Client",
- ProjectID: sql.NullInt64{Int64: 1, Valid: true},
- ProjectName: sql.NullString{String: "Test Project", Valid: true},
- DurationSeconds: 11400, // 3:10
+ TimeEntryID: 1,
+ StartTime: mustParseTime("2025-07-10T14:55:00Z"),
+ EndTime: sql.NullTime{Time: mustParseTime("2025-07-10T18:05:00Z"), Valid: true},
+ Description: sql.NullString{String: "GL closing", Valid: true},
+ ClientID: 1,
+ ClientName: "Test Client",
+ ProjectID: sql.NullInt64{Int64: 1, Valid: true},
+ ProjectName: sql.NullString{String: "Test Project", Valid: true},
+ DurationSeconds: 11400, // 3:10
EntryBillableRate: sql.NullInt64{Int64: 150, Valid: true},
ClientBillableRate: sql.NullInt64{Int64: 150, Valid: true},
ProjectBillableRate: sql.NullInt64{Int64: 150, Valid: true},
RateSource: "entry",
},
{
- TimeEntryID: 2,
- StartTime: mustParseTime("2025-07-10T18:42:00Z"),
- EndTime: sql.NullTime{Time: mustParseTime("2025-07-10T20:04:00Z"), Valid: true},
- Description: sql.NullString{String: "GL closing", Valid: true},
- ClientID: 1,
- ClientName: "Test Client",
- ProjectID: sql.NullInt64{Int64: 1, Valid: true},
- ProjectName: sql.NullString{String: "Test Project", Valid: true},
- DurationSeconds: 4920, // 1:22
+ TimeEntryID: 2,
+ StartTime: mustParseTime("2025-07-10T18:42:00Z"),
+ EndTime: sql.NullTime{Time: mustParseTime("2025-07-10T20:04:00Z"), Valid: true},
+ Description: sql.NullString{String: "GL closing", Valid: true},
+ ClientID: 1,
+ ClientName: "Test Client",
+ ProjectID: sql.NullInt64{Int64: 1, Valid: true},
+ ProjectName: sql.NullString{String: "Test Project", Valid: true},
+ DurationSeconds: 4920, // 1:22
EntryBillableRate: sql.NullInt64{Int64: 150, Valid: true},
ClientBillableRate: sql.NullInt64{Int64: 150, Valid: true},
ProjectBillableRate: sql.NullInt64{Int64: 150, Valid: true},
@@ -66,13 +65,13 @@ func TestGenerateUnifiedReportData(t *testing.T) {
Label: "Software Development",
Email: "travis@example.com",
},
- invoiceNumber: 123,
+ invoiceNumber: 123,
dateRange: DateRange{
Start: mustParseTime("2025-07-01T00:00:00Z"),
End: mustParseTime("2025-07-31T23:59:59Z"),
},
timezone: time.UTC,
- wantEntries: 1, // Both entries have same rate so grouped together
+ wantEntries: 1, // Both entries have same rate so grouped together
wantHours: 4.5333, // 16320 seconds / 3600
wantTotalAmount: 6.80, // 4.5333 * 1.50
},
@@ -80,14 +79,14 @@ func TestGenerateUnifiedReportData(t *testing.T) {
name: "project entries with local timezone",
entries: []queries.GetInvoiceDataByProjectRow{
{
- TimeEntryID: 3,
- StartTime: mustParseTime("2025-07-11T13:55:00Z"),
- EndTime: sql.NullTime{Time: mustParseTime("2025-07-11T18:35:00Z"), Valid: true},
- Description: sql.NullString{String: "Development work", Valid: true},
- ClientID: 1,
- ClientName: "Test Client",
- ProjectID: 1,
- ProjectName: "Test Project",
+ TimeEntryID: 3,
+ StartTime: mustParseTime("2025-07-11T13:55:00Z"),
+ EndTime: sql.NullTime{Time: mustParseTime("2025-07-11T18:35:00Z"), Valid: true},
+ Description: sql.NullString{String: "Development work", Valid: true},
+ ClientID: 1,
+ ClientName: "Test Client",
+ ProjectID: 1,
+ ProjectName: "Test Project",
DurationSeconds: 16800, // 4:40
EntryBillableRate: sql.NullInt64{Int64: 125, Valid: true},
ClientBillableRate: sql.NullInt64{Int64: 125, Valid: true},
@@ -117,15 +116,15 @@ func TestGenerateUnifiedReportData(t *testing.T) {
name: "entries with different timezone",
entries: []queries.GetInvoiceDataByClientRow{
{
- TimeEntryID: 4,
- StartTime: mustParseTime("2025-07-15T00:09:00Z"),
- EndTime: sql.NullTime{Time: mustParseTime("2025-07-15T00:13:00Z"), Valid: true},
- Description: sql.NullString{String: "Quick fix", Valid: true},
- ClientID: 1,
- ClientName: "Test Client",
- ProjectID: sql.NullInt64{Int64: 1, Valid: true},
- ProjectName: sql.NullString{String: "Test Project", Valid: true},
- DurationSeconds: 240, // 4 minutes
+ TimeEntryID: 4,
+ StartTime: mustParseTime("2025-07-15T00:09:00Z"),
+ EndTime: sql.NullTime{Time: mustParseTime("2025-07-15T00:13:00Z"), Valid: true},
+ Description: sql.NullString{String: "Quick fix", Valid: true},
+ ClientID: 1,
+ ClientName: "Test Client",
+ ProjectID: sql.NullInt64{Int64: 1, Valid: true},
+ ProjectName: sql.NullString{String: "Test Project", Valid: true},
+ DurationSeconds: 240, // 4 minutes
EntryBillableRate: sql.NullInt64{Int64: 200, Valid: true},
ClientBillableRate: sql.NullInt64{Int64: 200, Valid: true},
ProjectBillableRate: sql.NullInt64{Int64: 200, Valid: true},
@@ -278,14 +277,14 @@ func TestUnifiedReportDataConsistency(t *testing.T) {
// Test that unified report produces consistent data between invoice and timesheet components
entries := []queries.GetInvoiceDataByClientRow{
{
- TimeEntryID: 1,
- StartTime: mustParseTime("2025-07-10T14:55:00Z"),
- EndTime: sql.NullTime{Time: mustParseTime("2025-07-10T18:05:00Z"), Valid: true},
- Description: sql.NullString{String: "Development work", Valid: true},
- ClientID: 1,
- ClientName: "Test Client",
- ProjectID: sql.NullInt64{Int64: 1, Valid: true},
- ProjectName: sql.NullString{String: "Test Project", Valid: true},
+ TimeEntryID: 1,
+ StartTime: mustParseTime("2025-07-10T14:55:00Z"),
+ EndTime: sql.NullTime{Time: mustParseTime("2025-07-10T18:05:00Z"), Valid: true},
+ Description: sql.NullString{String: "Development work", Valid: true},
+ ClientID: 1,
+ ClientName: "Test Client",
+ ProjectID: sql.NullInt64{Int64: 1, Valid: true},
+ ProjectName: sql.NullString{String: "Test Project", Valid: true},
DurationSeconds: 11400, // 3:10
EntryBillableRate: sql.NullInt64{Int64: 150, Valid: true},
ClientBillableRate: sql.NullInt64{Int64: 150, Valid: true},
@@ -293,14 +292,14 @@ func TestUnifiedReportDataConsistency(t *testing.T) {
RateSource: "entry",
},
{
- TimeEntryID: 2,
- StartTime: mustParseTime("2025-07-10T18:42:00Z"),
- EndTime: sql.NullTime{Time: mustParseTime("2025-07-10T20:04:00Z"), Valid: true},
- Description: sql.NullString{String: "Code review", Valid: true},
- ClientID: 1,
- ClientName: "Test Client",
- ProjectID: sql.NullInt64{Int64: 1, Valid: true},
- ProjectName: sql.NullString{String: "Test Project", Valid: true},
+ TimeEntryID: 2,
+ StartTime: mustParseTime("2025-07-10T18:42:00Z"),
+ EndTime: sql.NullTime{Time: mustParseTime("2025-07-10T20:04:00Z"), Valid: true},
+ Description: sql.NullString{String: "Code review", Valid: true},
+ ClientID: 1,
+ ClientName: "Test Client",
+ ProjectID: sql.NullInt64{Int64: 1, Valid: true},
+ ProjectName: sql.NullString{String: "Test Project", Valid: true},
DurationSeconds: 4920, // 1:22
EntryBillableRate: sql.NullInt64{Int64: 150, Valid: true},
ClientBillableRate: sql.NullInt64{Int64: 150, Valid: true},
@@ -330,7 +329,6 @@ func TestUnifiedReportDataConsistency(t *testing.T) {
dateRange,
time.UTC,
)
-
if err != nil {
t.Fatalf("GenerateUnifiedReportData() error = %v", err)
}
@@ -385,15 +383,15 @@ func TestUnifiedReportEntryTypeConversion(t *testing.T) {
name: "client entries conversion",
entries: []queries.GetInvoiceDataByClientRow{
{
- TimeEntryID: 1,
- StartTime: mustParseTime("2025-07-10T14:55:00Z"),
- EndTime: sql.NullTime{Time: mustParseTime("2025-07-10T18:05:00Z"), Valid: true},
- Description: sql.NullString{String: "Work", Valid: true},
- ClientID: 1,
- ClientName: "Test Client",
- ProjectID: sql.NullInt64{Int64: 1, Valid: true},
- ProjectName: sql.NullString{String: "Test Project", Valid: true},
- DurationSeconds: 11400,
+ TimeEntryID: 1,
+ StartTime: mustParseTime("2025-07-10T14:55:00Z"),
+ EndTime: sql.NullTime{Time: mustParseTime("2025-07-10T18:05:00Z"), Valid: true},
+ Description: sql.NullString{String: "Work", Valid: true},
+ ClientID: 1,
+ ClientName: "Test Client",
+ ProjectID: sql.NullInt64{Int64: 1, Valid: true},
+ ProjectName: sql.NullString{String: "Test Project", Valid: true},
+ DurationSeconds: 11400,
EntryBillableRate: sql.NullInt64{Int64: 150, Valid: true},
ClientBillableRate: sql.NullInt64{Int64: 150, Valid: true},
ProjectBillableRate: sql.NullInt64{Int64: 150, Valid: true},
@@ -406,14 +404,14 @@ func TestUnifiedReportEntryTypeConversion(t *testing.T) {
name: "project entries conversion",
entries: []queries.GetInvoiceDataByProjectRow{
{
- TimeEntryID: 2,
- StartTime: mustParseTime("2025-07-11T13:55:00Z"),
- EndTime: sql.NullTime{Time: mustParseTime("2025-07-11T18:35:00Z"), Valid: true},
- Description: sql.NullString{String: "Work", Valid: true},
- ClientID: 1,
- ClientName: "Test Client",
- ProjectID: 1,
- ProjectName: "Test Project",
+ TimeEntryID: 2,
+ StartTime: mustParseTime("2025-07-11T13:55:00Z"),
+ EndTime: sql.NullTime{Time: mustParseTime("2025-07-11T18:35:00Z"), Valid: true},
+ Description: sql.NullString{String: "Work", Valid: true},
+ ClientID: 1,
+ ClientName: "Test Client",
+ ProjectID: 1,
+ ProjectName: "Test Project",
DurationSeconds: 16800,
EntryBillableRate: sql.NullInt64{Int64: 125, Valid: true},
ClientBillableRate: sql.NullInt64{Int64: 125, Valid: true},
@@ -526,7 +524,6 @@ func TestUnifiedReportEmptyEntries(t *testing.T) {
dateRange,
time.UTC,
)
-
if err != nil {
t.Errorf("GenerateUnifiedReportData() error = %v", err)
return
@@ -559,4 +556,5 @@ func TestUnifiedReportEmptyEntries(t *testing.T) {
}
})
}
-} \ No newline at end of file
+}
+