diff --git a/bookkeeper.go b/bookkeeper.go index b28549b..a65be18 100644 --- a/bookkeeper.go +++ b/bookkeeper.go @@ -4,6 +4,8 @@ import ( "bufio" "fmt" "github.com/go-pdf/fpdf" + "golang.org/x/text/language" + "golang.org/x/text/message" "log" "os" "regexp" @@ -27,19 +29,31 @@ type Transaction struct { Items []TransactionItem } +type Balance struct { + balance_start float64 + balance_end float64 +} + /* global variable declaration */ var pdf *fpdf.Fpdf var yPos float64 var accounts = make(map[string]string) var transactions = make(map[uint16]Transaction) -var account_balance = make(map[string]string) -var yearEndDate = "YEARENDDATE" +var account_balance = make(map[string]Balance) +var balanceYear = "UNDEFINED" const reportTitle = "Jahresrechnung - nbit Informatik GmbH" const defaultFontSize = 9 +const smallFontSize = 7 const marginTop = 10 +const smallLineSpacing = 4 const lineSpacing = 5 -const tabstopLeft = 20 +const tabstopLeft = 25 +const tabstopRight = 100 +const widthAmount = 28 +const dashCorrectionXleft = 1.2 +const dashCorrectionXright = -1.3 +const dashCorrectionY = 3 //func round5rappen(f float64) float64 { // return (math.Round(f*20) / 20) @@ -60,6 +74,16 @@ func addTransaction(document_number string, date string, text string, account_nu return } + // we use the first transaction to set the year, all transactions not in this year will be invalid + if balanceYear == "UNDEFINED" { + balanceYear = date[6:10] + } else { + if date[6:10] != balanceYear { + fmt.Printf("WARNING: transaction with Document Number %s is not in same year as first transaction: %s and will be ignored\n", document_number, balanceYear) + return + } + } + var myItem TransactionItem document_number_i, err := strconv.ParseInt(document_number, 0, 16) @@ -73,7 +97,8 @@ func addTransaction(document_number string, date string, text string, account_nu if s, err := strconv.ParseFloat(amount, 64); err == nil { myItem.Amount = s } else { - fmt.Printf("Cannot convert Amount to Float64: %s\n", amount) + fmt.Printf("WARNING: Document %s, cannot convert Amount to Float64: %s and will be ignored\n", document_number, amount) + return } myItem.Vat_code = vat_code @@ -103,8 +128,8 @@ func readAccountData(filename string) { line := scanner.Text() matched, _ := regexp.MatchString(`^[0-9][0-9][0-9][0-9],.*`, line) if !matched { - fmt.Printf("Line %v in Accountfile %s: line not four digits followed by a comma: %s.\n", line_number, filename, line) - os.Exit(3) + fmt.Printf("WARNING: Line %v in Accountfile %s: line not four digits followed by a comma: %s and will be ignored.\n", line_number, filename, line) + continue } token := strings.SplitN(line, ",", 2) @@ -112,8 +137,8 @@ func readAccountData(filename string) { account_description := token[1] if accountExists(account_number) { - fmt.Printf("Line %v in Accountfile %s: Account Number %s already exists\n", line_number, filename, account_number) - os.Exit(4) + fmt.Printf("WARNING: Line %v in Accountfile %s: Account Number %s already exists and will be ignored\n", line_number, filename, account_number) + continue } @@ -127,6 +152,22 @@ func readAccountData(filename string) { } } +func floatToString(f float64, sep string) string { + p := message.NewPrinter(language.English) + s := strings.ReplaceAll(p.Sprintf("%.2f", f), ",", sep) + //fmt.Printf("--- s: @%s@\n", s) + return s +} + +func str2float64(f string) float64 { + if s, err := strconv.ParseFloat(f, 64); err == nil { + return (s) + } else { + fmt.Printf("WARNING: cannot convert Amount to Float64: %s and will be ignored\n", f) + return 0 + } +} + func readTransactionData(filename string) { file, err := os.Open(filename) if err != nil { @@ -150,12 +191,12 @@ func readTransactionData(filename string) { vat_code := token[6] if !accountExists(account_number_debit) { - fmt.Printf("Line %v in Transactionfile %s: Account Number Debit %s does not exist\n", line_number, filename, account_number_debit) - os.Exit(4) + fmt.Printf("WARNING: Line %v in Transactionfile %s: Account Number Debit %s does not exist, and will be ignored\n", line_number, filename, account_number_debit) + continue } if !accountExists(account_number_credit) { - fmt.Printf("Line %v in Transactionfile %s: Account Number Credit %s does not exist\n", line_number, filename, account_number_credit) - os.Exit(4) + fmt.Printf("WARNING: Line %v in Transactionfile %s: Account Number Credit %s does not exist and will be ignored\n", line_number, filename, account_number_credit) + continue } addTransaction(document_number, date, text, account_number_debit, account_number_credit, amount, vat_code) @@ -167,15 +208,20 @@ func readTransactionData(filename string) { account_number := token[0] balance := token[1] if !accountExists(account_number) { - fmt.Printf("Line %v in Transactionfile %s: Account Number %s does not exist\n", line_number, filename, account_number) - os.Exit(4) + fmt.Printf("WARNING: Line %v in Transactionfile %s: Account Number %s does not exist and will be ignored\n", line_number, filename, account_number) + continue } - account_balance[account_number] = balance + f := str2float64(balance) + + var myBalance Balance + myBalance.balance_start = f + myBalance.balance_end = f + account_balance[account_number] = myBalance } else { - fmt.Printf("Line %v in Transactionfile %s is not of the form ,,,,,,.\n", line_number, filename, line) - os.Exit(3) + fmt.Printf("WARNING: Line %v in Transactionfile %s is not of the form ,,,,,, and will be ignored.\n", line_number, filename, line) + continue } line_number++ } @@ -212,29 +258,62 @@ func printPageHeader() { pdf.SetFont("Dejavusans-Bold", "", defaultFontSize) writeText(tabstopLeft, yPos, 0, reportTitle) yPos = yPos + lineSpacing - pdf.SetFont("Dejavusans", "", defaultFontSize) - writeText(tabstopLeft, yPos, 0, "per "+yearEndDate) + pdf.SetFont("Dejavusans", "", smallFontSize) + writeText(tabstopLeft, yPos, 0, "per 31.12."+balanceYear) + yPos = yPos + lineSpacing + smallLineSpacing } -func printAssets() { -} +func printSection(section string) { + var title string + var chars string + switch section { + case "assets": + title = "AKTIVEN" + chars = "1" + case "liabilities": + title = "PASSIVEN" + chars = "2" + case "expense": + title = "AUFWAND" + chars = "568" + case "income": + title = "ERTRAG" + chars = "4" + default: + fmt.Printf("WARNING: invalid section: %s\n", section) + return + } -func printLiabilities() { -} + var total float64 = 0.0 + pdf.SetFont("Dejavusans-Bold", "", smallFontSize) + writeText(tabstopLeft, yPos, 0, title) + writeText(tabstopRight, yPos, widthAmount, "31.12."+balanceYear, "TR") + yPos = yPos + smallLineSpacing + pdf.SetFont("Dejavusans", "", smallFontSize) + pdf.SetDashPattern([]float64{0.2, 0.2}, 0) + for key, element := range account_balance { + if strings.Contains(chars, key[0:1]) { + writeText(tabstopLeft, yPos, 0, accounts[key]) + writeText(tabstopRight, yPos, widthAmount, floatToString(element.balance_end, "'"), "TR") + pdf.Line(tabstopLeft+dashCorrectionXleft, yPos+dashCorrectionY, tabstopRight+widthAmount+dashCorrectionXright, yPos+dashCorrectionY) + total = total + element.balance_end + yPos = yPos + smallLineSpacing + } + } + pdf.SetFont("Dejavusans-Bold", "", smallFontSize) + writeText(tabstopLeft, yPos, 0, "TOTAL "+title) + writeText(tabstopRight, yPos, widthAmount, floatToString(total, "'"), "TR") + yPos = yPos + smallLineSpacing + smallLineSpacing -func printExpenses() { -} - -func printIncome() { } func createBalanceSheet() { setupBalanceSheet() printPageHeader() - printAssets() - printLiabilities() - printExpenses() - printIncome() + printSection("assets") + printSection("liabilities") + printSection("expense") + printSection("income") err := pdf.OutputFileAndClose("output.pdf") if err == nil { fmt.Printf("Successfully created Balance Sheet in file output.pdf\n") diff --git a/go.mod b/go.mod index 5668938..79c36a3 100644 --- a/go.mod +++ b/go.mod @@ -4,4 +4,5 @@ go 1.20 require ( github.com/go-pdf/fpdf v0.9.0 // indirect + golang.org/x/text v0.14.0 // indirect ) diff --git a/go.sum b/go.sum index da576af..7a3b4de 100644 --- a/go.sum +++ b/go.sum @@ -12,3 +12,5 @@ github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfF github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=