Skip to content

Commit 2a1469f

Browse files
committed
Support parsing advance credit notes
1 parent d5960e7 commit 2a1469f

File tree

2 files changed

+44
-0
lines changed

2 files changed

+44
-0
lines changed

invoice.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,11 @@ func (inv *Inv) parsePrepaymentTotals(goblInv *bill.Invoice) error {
550550
{inv.DomesticReverseChargeNetSale, "", "10", tax.KeyReverseCharge, nil},
551551
}
552552

553+
// For credit notes (e.g. KOR_ZAL), KSeF P_13/P_14/P_15 values are negative.
554+
// GOBL expects positive totals, so we invert them. Invoice.Invert() cannot
555+
// be used with the bypass tag, so we invert each amount individually.
556+
isCreditNote := goblInv.Type == bill.InvoiceTypeCreditNote
557+
553558
var rates []*tax.RateTotal
554559
var netSum, taxSum num.Amount
555560

@@ -562,6 +567,9 @@ func (inv *Inv) parsePrepaymentTotals(goblInv *bill.Invoice) error {
562567
if err != nil {
563568
return fmt.Errorf("parsing prepayment net for category %s: %w", e.category, err)
564569
}
570+
if isCreditNote {
571+
netAmt = netAmt.Invert()
572+
}
565573

566574
rt := &tax.RateTotal{
567575
Key: e.key,
@@ -577,6 +585,9 @@ func (inv *Inv) parsePrepaymentTotals(goblInv *bill.Invoice) error {
577585
if err != nil {
578586
return fmt.Errorf("parsing prepayment tax for category %s: %w", e.category, err)
579587
}
588+
if isCreditNote {
589+
taxAmt = taxAmt.Invert()
590+
}
580591
rt.Amount = taxAmt
581592
taxSum = taxSum.MatchPrecision(taxAmt)
582593
taxSum = taxSum.Add(taxAmt)
@@ -613,6 +624,9 @@ func (inv *Inv) parsePrepaymentTotals(goblInv *bill.Invoice) error {
613624
if err != nil {
614625
return fmt.Errorf("parsing total amount due: %w", err)
615626
}
627+
if isCreditNote {
628+
payable = payable.Invert()
629+
}
616630
totals.Payable = payable
617631
} else {
618632
totals.Payable = totals.TotalWithTax

invoice_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1047,6 +1047,36 @@ func TestPrepaymentEndToEnd(t *testing.T) {
10471047
assert.Equal(t, cbc.Code("ZAL-001"), inv.Preceding[0].Code)
10481048
})
10491049

1050+
t.Run("KOR_ZAL inverts negative totals for credit note", func(t *testing.T) {
1051+
doc := testPrepaymentDoc()
1052+
doc.Inv.InvoiceType = "KOR_ZAL"
1053+
doc.Inv.CorrectedInv = []*ksef.CorrectedInv{
1054+
{SequentialNumber: "ZAL-001", IssueDate: "2026-01-15"},
1055+
}
1056+
// KSeF credit notes have negative P_13/P_14/P_15 values
1057+
doc.Inv.StandardRateNetSale = "-1000.00"
1058+
doc.Inv.StandardRateTax = "-230.00"
1059+
doc.Inv.TotalAmountDue = "-1230.00"
1060+
1061+
inv, err := doc.ToGOBL()
1062+
require.NoError(t, err)
1063+
1064+
assert.True(t, inv.HasTags(tax.TagBypass))
1065+
assert.Equal(t, bill.InvoiceTypeCreditNote, inv.Type)
1066+
1067+
// Totals should be positive (GOBL convention)
1068+
assert.Equal(t, "1000.00", inv.Totals.Sum.String())
1069+
assert.Equal(t, "230.00", inv.Totals.Tax.String())
1070+
assert.Equal(t, "1230.00", inv.Totals.TotalWithTax.String())
1071+
assert.Equal(t, "1230.00", inv.Totals.Payable.String())
1072+
1073+
// Tax rate amounts should also be positive
1074+
require.NotNil(t, inv.Totals.Taxes)
1075+
rate := inv.Totals.Taxes.Categories[0].Rates[0]
1076+
assert.Equal(t, "1000.00", rate.Base.String())
1077+
assert.Equal(t, "230.00", rate.Amount.String())
1078+
})
1079+
10501080
t.Run("zero rate prepayment sets correct totals", func(t *testing.T) {
10511081
doc := testPrepaymentDoc()
10521082
doc.Inv.StandardRateNetSale = ""

0 commit comments

Comments
 (0)