6 files changed,
106 insertions(+),
0 deletions(-)
Author:
Oleksandr Smirnov
olexsmir@gmail.com
Committed at:
2026-06-27 12:41:04 +0300
Authored at:
2026-06-26 15:44:13 +0300
Change ID:
kvywvuqrzylyryumvmoswknlomlzyqqy
Parent:
1b934a8
A
internal/linter/rule_orderdate.go
··· 1 +package linter 2 + 3 +import ( 4 + "fmt" 5 + 6 + "olexsmir.xyz/clerk/journal/ast" 7 +) 8 + 9 +// OrderDate checks that transactions are in chronological order by date. 10 +type OrderDate struct{} 11 + 12 +func (OrderDate) ID() RuleID { return "orderdate" } 13 +func (OrderDate) Severity() Severity { return SeverityWarning } 14 +func (r *OrderDate) CheckJournal(j *ast.Journal) []Find { 15 + var finds []Find 16 + var anchor *ast.Date 17 + for _, entry := range j.Entries { 18 + txn, ok := entry.(*ast.Transaction) 19 + if !ok { 20 + continue 21 + } 22 + 23 + d := txn.Date 24 + if anchor != nil { 25 + cmp := compareDate(d, *anchor) 26 + if cmp < 0 { 27 + finds = append(finds, Find{ 28 + Code: r.ID(), 29 + Severity: r.Severity(), 30 + Message: fmt.Sprintf("transaction is out of chronological order (date %s before %s)", 31 + dateString(d), dateString(*anchor)), 32 + Span: d.Span, 33 + }) 34 + continue 35 + } 36 + } 37 + 38 + anchor = &d 39 + } 40 + 41 + return finds 42 +} 43 + 44 +// compareDate returns -1 if a < b, 0 if equal, 1 if a > b. 45 +func compareDate(a, b ast.Date) int { 46 + if a.Year != b.Year { 47 + if a.Year < b.Year { 48 + return -1 49 + } 50 + return 1 51 + } 52 + if a.Month != b.Month { 53 + if a.Month < b.Month { 54 + return -1 55 + } 56 + return 1 57 + } 58 + if a.Day != b.Day { 59 + if a.Day < b.Day { 60 + return -1 61 + } 62 + return 1 63 + } 64 + return 0 65 +} 66 + 67 +func dateString(d ast.Date) string { 68 + return fmt.Sprintf("%d-%02d-%02d", d.Year, d.Month, d.Day) 69 +}
A
internal/linter/testdata/orderdate.golden
··· 1 +orderdate.journal:5:1: orderdate: transaction is out of chronological order (date 2026-01-10 before 2026-01-15) 2 +orderdate.journal:9:1: orderdate: transaction is out of chronological order (date 2026-01-12 before 2026-01-15) 3 +orderdate.journal:17:1: orderdate: transaction is out of chronological order (date 2026-01-20 before 2026-02-01) 4 +orderdate.journal:25:1: orderdate: transaction is out of chronological order (date 2025-12-01 before 2026-03-01)
A
internal/linter/testdata/orderdate.input
··· 1 +2026-01-15 * "Transaction 1" 2 + Expenses:Food $10 3 + Assets:Cash 4 + 5 +2026-01-10 * "Transaction 2 - out of order (before Txn 1)" 6 + Expenses:Food $20 7 + Assets:Cash 8 + 9 +2026-01-12 * "Transaction 3 - would slip through old prev logic (12 < 15 but 12 > 10)" 10 + Expenses:Food $8 11 + Assets:Cash 12 + 13 +2026-02-01 * "Transaction 4" 14 + Expenses:Food $15 15 + Assets:Cash 16 + 17 +2026-01-20 * "Transaction 5 - out of order (before Txn 4)" 18 + Expenses:Food $5 19 + Assets:Cash 20 + 21 +2026-03-01 * "Transaction 6" 22 + Expenses:Food $25 23 + Assets:Cash 24 + 25 +2025-12-01 * "Transaction 7 - entirely before everything" 26 + Expenses:Food $30 27 + Assets:Cash