package mapping import ( "errors" "testing" "time" "simenandre.no/accounted-multicase-intgr/internal/accounted" "simenandre.no/accounted-multicase-intgr/internal/multicase" ) func config() *Config { return &Config{ Accounts: map[string]string{"3000": "3001", "1460": "1460", "4301": "4000"}, CustomerLedgerAccount: "1510", VATCodes: map[string]VATCode{ "3": {Rate: 25, Account: "2611"}, "6": {Rate: 0}, }, RoundingAccount: "3740", MaxRoundingOre: 100, SourceType: "manual", } } func voucher(lines ...multicase.Line) multicase.Voucher { return multicase.Voucher{ Type: "11", TypeText: "Faktura", Number: "100", Text: "Demo kunde", Date: time.Date(2026, 10, 1, 0, 0, 0, 0, time.UTC), Lines: lines, } } func TestSplitNet(t *testing.T) { tests := []struct { gross, want int64 rate float64 }{ {270019, 216015, 25}, {-270019, -216015, 25}, {12500, 10000, 25}, {1, 1, 25}, {11200, 10000, 12}, {5000, 5000, 0}, } for _, tt := range tests { if got := SplitNet(tt.gross, tt.rate); got != tt.want { t.Errorf("SplitNet(%d, %v) = %d, want %d", tt.gross, tt.rate, got, tt.want) } } } func TestBuildSplitsVATAndMapsCustomerLedger(t *testing.T) { e, err := config().Build(voucher( multicase.Line{Account: "3000", Amount: -270019, VATCode: "3", CustomerNo: "20001"}, multicase.Line{Account: "20001", Amount: 270019, CustomerNo: "20001"}, )) if err != nil { t.Fatal(err) } want := []accounted.Line{ {Account: "3001", Credit: 216015}, {Account: "2611", Credit: 54004}, {Account: "1510", Debit: 270019}, } if len(e.Lines) != len(want) { t.Fatalf("lines = %+v", e.Lines) } for i, w := range want { g := e.Lines[i] if g.Account != w.Account || g.Debit != w.Debit || g.Credit != w.Credit { t.Errorf("line %d = %+v, want %+v", i, g, w) } } if e.SourceID != "multicase:11:100" || e.EntryDate != "2026-10-01" || e.Description != "Faktura 100: Demo kunde" { t.Errorf("header = %+v", e) } } func TestBuildReportsUnmapped(t *testing.T) { _, err := config().Build(voucher( multicase.Line{Account: "9999", Amount: -100}, multicase.Line{Account: "1460", Amount: 100, VATCode: "42"}, )) var u *UnmappedError if !errors.As(err, &u) || len(u.Accounts) != 1 || u.Accounts[0] != "9999" || len(u.VATCodes) != 1 || u.VATCodes[0] != "42" { t.Fatalf("err = %v", err) } } func TestBuildRounding(t *testing.T) { e, err := config().Build(voucher( multicase.Line{Account: "1460", Amount: 1978734}, multicase.Line{Account: "4301", Amount: -1832162}, multicase.Line{Account: "4301", Amount: -146573}, )) if err != nil { t.Fatal(err) } last := e.Lines[len(e.Lines)-1] if last.Account != "3740" || last.Debit != 1 { t.Errorf("rounding line = %+v", last) } _, err = config().Build(voucher( multicase.Line{Account: "1460", Amount: 1000}, multicase.Line{Account: "4301", Amount: -500}, )) if err == nil { t.Error("expected an error for a voucher off by more than max_rounding_ore") } } func TestBuildSkipsZeroLines(t *testing.T) { _, err := config().Build(voucher(multicase.Line{Account: "20001", Amount: 0, CustomerNo: "20001"})) if !errors.Is(err, ErrEmpty) { t.Fatalf("err = %v, want ErrEmpty", err) } }