feat: better PDF

This commit is contained in:
2025-08-24 00:24:40 +02:00
parent 0267e6e578
commit 5b664234e9
18 changed files with 540 additions and 228 deletions

View File

@@ -0,0 +1,68 @@
package invoice
import (
"crypto/rand"
"fmt"
"time"
)
func generateReference() string {
// 1) Build the 26-digit payload from time + random suffix
now := time.Now().UTC()
ts := now.Format("20060102150405") // YYYYMMDDHHMMSS -> 14 chars
ns := now.Nanosecond() // 0..999999999
// format nanoseconds as 9 digits, zero padded
nsStr := fmt.Sprintf("%09d", ns)
// 3-digit random suffix (000..999) to reach 26 digits
randBytes := make([]byte, 2) // we'll read 2 bytes and map to 0..999
rand.Read(randBytes)
// convert to number 0..65535 then mod 1000
rnum := int(randBytes[0])<<8 | int(randBytes[1])
rnum = rnum % 1000
randStr := fmt.Sprintf("%03d", rnum)
// compose 26-digit payload: 14 + 9 + 3 = 26
payload := ts + nsStr + randStr
// Safety: ensure payload is 26 digits (should always be true)
if len(payload) != 26 {
panic(fmt.Errorf("internal error: payload length %d != 26", len(payload)))
}
// 2) Compute Modulo-10 recursive check digit
check := mod10RecursiveChecksum(payload)
// 3) Build full 27-digit reference
full := payload + fmt.Sprintf("%d", check)
// 4) Format with grouping: 2-5-5-5-5-5
formatted := fmt.Sprintf("%s %s %s %s %s %s",
full[0:2],
full[2:7],
full[7:12],
full[12:17],
full[17:22],
full[22:27],
)
return formatted
}
// mod10RecursiveChecksum computes the Mod10 recursive check digit for a numeric string.
// Implements the table-based recursive mod10 described in Swiss Annex B / ISR implementations.
func mod10RecursiveChecksum(digits string) int {
// table as used in ISR/QR implementations
arrTable := [10]int{0, 9, 4, 6, 8, 2, 7, 1, 3, 5}
carry := 0
for i := 0; i < len(digits); i++ {
ch := digits[i]
// assume digits are ASCII '0'..'9'
n := int(ch - '0')
// update carry
idx := (carry + n) % 10
carry = arrTable[idx]
}
// final check digit
return (10 - carry) % 10
}