Ccy.Amt validation: %.2f to conform to spec
fixes https://github.com/stapelberg/qrbill/issues/8
This commit is contained in:
26
qrbill.go
26
qrbill.go
@@ -28,8 +28,11 @@ package qrbill
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"fmt"
|
||||||
"image"
|
"image"
|
||||||
|
"log"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/makiuchi-d/gozxing/qrcode/decoder"
|
"github.com/makiuchi-d/gozxing/qrcode/decoder"
|
||||||
@@ -128,11 +131,28 @@ func (a QRCHCcyAmt) Validate() QRCHCcyAmt {
|
|||||||
c := a
|
c := a
|
||||||
|
|
||||||
if c.Amt != "" {
|
if c.Amt != "" {
|
||||||
|
parsed, err := strconv.ParseFloat(c.Amt, 64)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("ParseFloat(%q): %v", c.Amt, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The Swiss Payment Standards 2019 Swiss Implementation Guidelines
|
||||||
|
// QR-bill Version 2.3 explains:
|
||||||
|
//
|
||||||
|
// The amount element is to be entered without leading
|
||||||
|
// zeroes, including decimal separators and two decimal
|
||||||
|
// places.
|
||||||
|
// Decimal, maximum 12-digits permitted, including decimal
|
||||||
|
// separators. Only decimal points (".") are permitted as
|
||||||
|
// decimal separators. The amount must be between CHF/
|
||||||
|
// EUR 0.01 and 999,999,999.99
|
||||||
|
//
|
||||||
|
// (Notably, the validator is less strict and also permits values
|
||||||
|
// without decimal separators or with only one decimal place.)
|
||||||
|
//
|
||||||
// Some banking apps are picky regarding integer numbers (e.g. 50) and
|
// Some banking apps are picky regarding integer numbers (e.g. 50) and
|
||||||
// require a separator plus two digits (e.g. 50.00).
|
// require a separator plus two digits (e.g. 50.00).
|
||||||
if !strings.Contains(c.Amt, ".") {
|
c.Amt = fmt.Sprintf("%.2f", parsed)
|
||||||
c.Amt += ".00"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return c
|
return c
|
||||||
|
|||||||
110
qrbill_test.go
Normal file
110
qrbill_test.go
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
package qrbill_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stapelberg/qrbill"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAmountValidation(t *testing.T) {
|
||||||
|
for _, tt := range []struct {
|
||||||
|
amount string
|
||||||
|
wantAmount string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
// ensure empty amount values are not modified
|
||||||
|
amount: "",
|
||||||
|
wantAmount: "",
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
amount: "50",
|
||||||
|
wantAmount: "50.00",
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
amount: "50.3",
|
||||||
|
wantAmount: "50.30",
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
amount: "50.32",
|
||||||
|
wantAmount: "50.32",
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
amount: "50.32",
|
||||||
|
wantAmount: "50.32",
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
amount: "50.000",
|
||||||
|
wantAmount: "50.00",
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
amount: "50.-",
|
||||||
|
wantAmount: "0.00", // result of invalid input
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
amount: ".30",
|
||||||
|
wantAmount: "0.30",
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
amount: ".3",
|
||||||
|
wantAmount: "0.30",
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
// minimum amount mentioned in the Implementation Guidelines
|
||||||
|
amount: "0.01",
|
||||||
|
wantAmount: "0.01",
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
// maximum amount mentioned in the Implementation Guidelines
|
||||||
|
amount: "999999999.99",
|
||||||
|
wantAmount: "999999999.99",
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(tt.amount, func(t *testing.T) {
|
||||||
|
qrch := &qrbill.QRCH{
|
||||||
|
CdtrInf: qrbill.QRCHCdtrInf{
|
||||||
|
IBAN: "CH0209000000870913543",
|
||||||
|
Cdtr: qrbill.Address{
|
||||||
|
AdrTp: qrbill.AddressTypeCombined,
|
||||||
|
Name: "Legalize it",
|
||||||
|
StrtNmOrAdrLine1: "Quellenstrasse 25",
|
||||||
|
BldgNbOrAdrLine2: "8005 Zürich",
|
||||||
|
Ctry: "CH",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CcyAmt: qrbill.QRCHCcyAmt{
|
||||||
|
Amt: tt.amount,
|
||||||
|
Ccy: "CHF",
|
||||||
|
},
|
||||||
|
UltmtDbtr: qrbill.Address{
|
||||||
|
AdrTp: qrbill.AddressTypeCombined,
|
||||||
|
Name: "Michael Stapelberg",
|
||||||
|
StrtNmOrAdrLine1: "Stauffacherstr 42",
|
||||||
|
BldgNbOrAdrLine2: "8004 Zürich",
|
||||||
|
Ctry: "CH",
|
||||||
|
},
|
||||||
|
RmtInf: qrbill.QRCHRmtInf{
|
||||||
|
Tp: "NON", // Reference type
|
||||||
|
Ref: "", // Reference
|
||||||
|
AddInf: qrbill.QRCHRmtInfAddInf{
|
||||||
|
Ustrd: "test",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
validated := qrch.Validate()
|
||||||
|
if got, want := validated.CcyAmt.Amt, tt.wantAmount; got != want {
|
||||||
|
t.Errorf("CcyAmt.Amt = %q, want %q", got, want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user