all repos

clerk @ 7638389

missing tooling for ledger/hledger
5 files changed, 78 insertions(+), 7 deletions(-)
linter: add "more than one posting has omitted amount" rule
Author: Oleksandr Smirnov olexsmir@gmail.com
Committed at: 2026-06-22 18:00:20 +0300
Authored at: 2026-06-20 17:43:03 +0300
Change ID: wqlznlrlqxpvpprmmvkzrvppyormxzqu
Parent: fbfbb0b
M internal/linter/linter_test.go
···
        9
        9
         )

      
        10
        10
         

      
        11
        11
         var tests = map[string][]Rule{

      
        12
        
        -	"correct":           Rules,

      
        13
        
        -	"empty-postings":    {&EmptyPostings{}},

      
        14
        
        -	"parse-error":       {&ParseError{}},

      
        15
        
        -	"omitted-precision": {&OmittedPrecision{}},

      
        16
        
        -	"missing-commodity": {&MissingCommodity{}},

      
        17
        
        -	"missing-status":    {&MissingStatus{}},

      
        18
        
        -	"account-depth":     {&AccountDepthLimit{MaxDepth: 3}},

      
        
        12
        +	"correct":                  Rules,

      
        
        13
        +	"empty-postings":           {&EmptyPostings{}},

      
        
        14
        +	"parse-error":              {&ParseError{}},

      
        
        15
        +	"omitted-precision":        {&OmittedPrecision{}},

      
        
        16
        +	"missing-commodity":        {&MissingCommodity{}},

      
        
        17
        +	"missing-status":           {&MissingStatus{}},

      
        
        18
        +	"account-depth":            {&AccountDepthLimit{MaxDepth: 3}},

      
        
        19
        +	"multiple-omitted-amounts": {&MultipleOmittedAmounts{}},

      
        19
        20
         }

      
        20
        21
         

      
        21
        22
         func TestLinter(t *testing.T) {

      
A internal/linter/rule_multiple_omitted_amounts.go
···
        
        1
        +package linter

      
        
        2
        +

      
        
        3
        +import "olexsmir.xyz/clerk/journal/ast"

      
        
        4
        +

      
        
        5
        +// MultipleOmittedAmounts flags entries where more than one posting has an ommited amount.

      
        
        6
        +type MultipleOmittedAmounts struct{}

      
        
        7
        +

      
        
        8
        +func (MultipleOmittedAmounts) ID() RuleID         { return "multiple-omitted-amounts" }

      
        
        9
        +func (MultipleOmittedAmounts) Severity() Severity { return SeverityError }

      
        
        10
        +func (m *MultipleOmittedAmounts) CheckEntry(entry ast.Entry) []Find {

      
        
        11
        +	switch e := entry.(type) {

      
        
        12
        +	case *ast.Transaction:

      
        
        13
        +		return m.check(e.Postings)

      
        
        14
        +	case *ast.PeriodicTransaction:

      
        
        15
        +		return m.check(e.Postings)

      
        
        16
        +	case *ast.AutomatedTransaction:

      
        
        17
        +		return m.check(e.Postings)

      
        
        18
        +	default:

      
        
        19
        +		return nil

      
        
        20
        +	}

      
        
        21
        +}

      
        
        22
        +

      
        
        23
        +func (m *MultipleOmittedAmounts) check(postings []*ast.Posting) []Find {

      
        
        24
        +	var finds []Find

      
        
        25
        +	for _, p := range postings {

      
        
        26
        +		// skipping postings with balance assertion, since those are often used as reconciliation entries

      
        
        27
        +		if p.Amount == nil && p.Balance == nil {

      
        
        28
        +			finds = append(finds, Find{

      
        
        29
        +				Code:     m.ID(),

      
        
        30
        +				Severity: m.Severity(),

      
        
        31
        +				Message:  "more than one posting has omitted amount",

      
        
        32
        +				Span:     p.Span,

      
        
        33
        +			})

      
        
        34
        +		}

      
        
        35
        +	}

      
        
        36
        +	if len(finds) > 1 {

      
        
        37
        +		return finds

      
        
        38
        +	}

      
        
        39
        +	return nil

      
        
        40
        +}

      
M internal/linter/rules.go
···
        30
        30
         	&MissingCommodity{},

      
        31
        31
         	&MissingStatus{},

      
        32
        32
         	&AccountDepthLimit{MaxDepth: 4},

      
        
        33
        +	&MultipleOmittedAmounts{},

      
        33
        34
         }

      
A internal/linter/testdata/multiple-omitted-amounts.golden
···
        
        1
        +multiple-omitted-amounts.journal:2:1: multiple-omitted-amounts: more than one posting has omitted amount

      
        
        2
        +multiple-omitted-amounts.journal:3:1: multiple-omitted-amounts: more than one posting has omitted amount

      
        
        3
        +multiple-omitted-amounts.journal:7:1: multiple-omitted-amounts: more than one posting has omitted amount

      
        
        4
        +multiple-omitted-amounts.journal:8:1: multiple-omitted-amounts: more than one posting has omitted amount

      
        
        5
        +multiple-omitted-amounts.journal:9:1: multiple-omitted-amounts: more than one posting has omitted amount

      
        
        6
        +multiple-omitted-amounts.journal:11:1: multiple-omitted-amounts: more than one posting has omitted amount

      
A internal/linter/testdata/multiple-omitted-amounts.input
···
        
        1
        +2025-01-01 "two omitted amounts"

      
        
        2
        +    Expenses:Food

      
        
        3
        +    Expenses:Fun

      
        
        4
        +    Assets:Cash  100 USD

      
        
        5
        +

      
        
        6
        +2025-01-02 "three omitted amounts"

      
        
        7
        +    Expenses:Food

      
        
        8
        +    Expenses:Fun

      
        
        9
        +    Expenses:Play

      
        
        10
        +    Assets:Cash  100 USD

      
        
        11
        +    Equity:Opening-Balances

      
        
        12
        +

      
        
        13
        +2025-01-03 "one omitted amount (ok)"

      
        
        14
        +    Expenses:Food  50 USD

      
        
        15
        +    Assets:Cash

      
        
        16
        +

      
        
        17
        +2025-01-04 "all have amounts (ok)"

      
        
        18
        +    Expenses:Food  50 USD

      
        
        19
        +    Assets:Cash  50 USD

      
        
        20
        +

      
        
        21
        +2025-01-05 * reconcile or some s..stuff

      
        
        22
        +  Assets:Bank  = 1.69 UAH

      
        
        23
        +  Assets:Cash  = 2.00 UAH