VAT XMLs first working version

This commit is contained in:
Joerg Lehmann 2024-01-09 19:15:31 +01:00
parent 9684daedb6
commit fcc4d96660
2 changed files with 175 additions and 39 deletions

View File

@ -100,7 +100,7 @@ func calculateVAT(vat_code string, amount float64) float64 {
vat_type := string(vat_code[0]) vat_type := string(vat_code[0])
vat_perc, err := strconv.Atoi(vat_code[1:]) vat_perc, err := strconv.Atoi(vat_code[1:])
if err != nil { if err != nil {
fmt.Printf("ERROR: %v\n", err) fmt.Fprintf(os.Stderr, "ERROR: %v\n", err)
return 0.0 return 0.0
} }
vat_perc_f64 := float64(vat_perc) vat_perc_f64 := float64(vat_perc)
@ -109,7 +109,7 @@ func calculateVAT(vat_code string, amount float64) float64 {
} else if vat_type == "I" { } else if vat_type == "I" {
return roundRappen(amount / (1000.0 + vat_perc_f64) * vat_perc_f64) return roundRappen(amount / (1000.0 + vat_perc_f64) * vat_perc_f64)
} else { } else {
fmt.Printf("WARNING: Invalid Vat Type: %s\n", vat_type) fmt.Fprintf(os.Stderr, "WARNING: Invalid Vat Type: %s\n", vat_type)
return 0.0 return 0.0
} }
} }
@ -119,7 +119,7 @@ func addTransaction(document_number string, date string, text string, account_nu
mydate, error := time.Parse(dateString, date) mydate, error := time.Parse(dateString, date)
if error != nil { if error != nil {
fmt.Printf("ERROR: %v\n", error) fmt.Fprintf(os.Stderr, "ERROR: %v\n", error)
return return
} }
@ -128,7 +128,7 @@ func addTransaction(document_number string, date string, text string, account_nu
balanceYear = date[6:10] balanceYear = date[6:10]
} else { } else {
if date[6:10] != balanceYear { 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) fmt.Fprintf(os.Stderr, "WARNING: transaction with Document Number %s is not in same year as first transaction: %s and will be ignored\n", document_number, balanceYear)
return return
} }
} }
@ -146,7 +146,7 @@ func addTransaction(document_number string, date string, text string, account_nu
if s, err := strconv.ParseFloat(amount, 64); err == nil { if s, err := strconv.ParseFloat(amount, 64); err == nil {
myItem.Amount = roundRappen(s) myItem.Amount = roundRappen(s)
} else { } else {
fmt.Printf("WARNING: Document %s, cannot convert Amount to Float64: %s and will be ignored\n", document_number, amount) fmt.Fprintf(os.Stderr, "WARNING: Document %s, cannot convert Amount to Float64: %s and will be ignored\n", document_number, amount)
return return
} }
myItem.Vat_code = vat_code myItem.Vat_code = vat_code
@ -209,7 +209,7 @@ func readAccountData(filename string) {
line := scanner.Text() line := scanner.Text()
matched, _ := regexp.MatchString(`^[0-9][0-9][0-9][0-9],[ALEI]:.*`, line) matched, _ := regexp.MatchString(`^[0-9][0-9][0-9][0-9],[ALEI]:.*`, line)
if !matched { if !matched {
fmt.Printf("WARNING: Line %v in Accountfile %s: line not four digits followed by a comma followed by ALEI (one of those), Colon and description: %s and will be ignored.\n", line_number, filename, line) fmt.Fprintf(os.Stderr, "WARNING: Line %v in Accountfile %s: line not four digits followed by a comma followed by ALEI (one of those), Colon and description: %s and will be ignored.\n", line_number, filename, line)
continue continue
} }
@ -218,7 +218,7 @@ func readAccountData(filename string) {
account_type_and_description := token[1] account_type_and_description := token[1]
if accountExists(account_number) { if accountExists(account_number) {
fmt.Printf("WARNING: Line %v in Accountfile %s: Account Number %s already exists and will be ignored\n", line_number, filename, account_number) fmt.Fprintf(os.Stderr, "WARNING: Line %v in Accountfile %s: Account Number %s already exists and will be ignored\n", line_number, filename, account_number)
continue continue
} }
@ -240,7 +240,7 @@ func floatToString(f float64, sep string) string {
s = "-.-" s = "-.-"
} else { } else {
s = strings.ReplaceAll(p.Sprintf("%.2f", f), ",", sep) s = strings.ReplaceAll(p.Sprintf("%.2f", f), ",", sep)
//fmt.Printf("--- s: @%s@\n", s) //fmt.Fprintf(os.Stderr,"--- s: @%s@\n", s)
} }
return s return s
} }
@ -249,7 +249,7 @@ func str2float64(f string) float64 {
if s, err := strconv.ParseFloat(f, 64); err == nil { if s, err := strconv.ParseFloat(f, 64); err == nil {
return roundRappen(s) return roundRappen(s)
} else { } else {
fmt.Printf("WARNING: cannot convert Amount to Float64: %s and will be ignored\n", f) fmt.Fprintf(os.Stderr, "WARNING: cannot convert Amount to Float64: %s and will be ignored\n", f)
return 0 return 0
} }
} }
@ -277,11 +277,11 @@ func readTransactionData(filename string) {
vat_code := token[6] vat_code := token[6]
if !accountExists(account_number_debit) { if !accountExists(account_number_debit) {
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) fmt.Fprintf(os.Stderr, "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 continue
} }
if !accountExists(account_number_credit) { if !accountExists(account_number_credit) {
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) fmt.Fprintf(os.Stderr, "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 continue
} }
@ -294,7 +294,7 @@ func readTransactionData(filename string) {
account_number := token[0] account_number := token[0]
balance := token[1] balance := token[1]
if !accountExists(account_number) { if !accountExists(account_number) {
fmt.Printf("WARNING: Line %v in Transactionfile %s: Account Number %s does not exist and will be ignored\n", line_number, filename, account_number) fmt.Fprintf(os.Stderr, "WARNING: Line %v in Transactionfile %s: Account Number %s does not exist and will be ignored\n", line_number, filename, account_number)
continue continue
} }
@ -306,7 +306,7 @@ func readTransactionData(filename string) {
account_balance[account_number] = myBalance account_balance[account_number] = myBalance
} else { } else {
fmt.Printf("WARNING: Line %v in Transactionfile %s is not of the form <document number>,<date>,<text>,<account number debit>,<account number credit>,<amount>,<vat code> and will be ignored.\n", line_number, filename, line) fmt.Fprintf(os.Stderr, "WARNING: Line %v in Transactionfile %s is not of the form <document number>,<date>,<text>,<account number debit>,<account number credit>,<amount>,<vat code> and will be ignored.\n", line_number, filename, line)
continue continue
} }
line_number++ line_number++
@ -367,7 +367,7 @@ func printSection(section string) {
case "I": case "I":
title = "ERTRAG" title = "ERTRAG"
default: default:
fmt.Printf("WARNING: invalid section: %s\n", section) fmt.Fprintf(os.Stderr, "WARNING: invalid section: %s\n", section)
return return
} }
@ -462,9 +462,9 @@ func createBalanceSheet() {
printSection("I") printSection("I")
err := pdf.OutputFileAndClose("output.pdf") err := pdf.OutputFileAndClose("output.pdf")
if err == nil { if err == nil {
fmt.Printf("INFO: Successfully created Balance Sheet in file output.pdf\n") fmt.Fprintf(os.Stderr, "INFO: Successfully created Balance Sheet in file output.pdf\n")
} else { } else {
fmt.Printf("ERROR: %v\n", err) fmt.Fprintf(os.Stderr, "ERROR: %v\n", err)
} }
} }
@ -482,26 +482,163 @@ func calculateProfit() float64 {
} }
res = roundRappen(res) res = roundRappen(res)
fmt.Printf("INFO: Calculated Profit: %v\n", res) fmt.Fprintf(os.Stderr, "INFO: Calculated Profit: %v\n", res)
return res return res
} }
func transactionInQuarter(date time.Time, whatquarter string) bool {
month := date.Month()
switch month {
case 1, 2, 3:
return whatquarter == "mwst1"
case 4, 5, 6:
return whatquarter == "mwst2"
case 7, 8, 9:
return whatquarter == "mwst3"
case 10, 11, 12:
return whatquarter == "mwst4"
}
return false
}
func getTotalConsideration(whatquarter string) float64 {
//"43517.04"
var res float64 = 0.0
for key := range transactions {
if transactionInQuarter(transactions[key].Date, whatquarter) {
for _, item := range transactions[key].Items {
if strings.HasPrefix(item.Vat_code, "V") {
res = res + item.Amount + item.Amount_Vat
}
}
}
}
res = roundRappen(res)
fmt.Fprintf(os.Stderr, "INFO: Calculated Total Consideration: %v\n", res)
return res
}
func getVat(vat_code string) string {
// strip first letter, divide by 10
// ie.: V77 => 7.7, V80 => 8.0
if s, err := strconv.ParseFloat(vat_code[1:], 64); err == nil {
return fmt.Sprintf("%.2f", s/10.0)
} else {
fmt.Fprintf(os.Stderr, "WARNING: Invalid VAT Code %s\n", vat_code)
return vat_code
}
}
func getDataTaxRates(whatquarter string) map[string]string {
//map[string]string{"7.70": "43517.04", "8.0": "Test only"}
//var m = map[string]string{
// "7.70": "43517.04",
// "8.0": "Test only",
// }
var m = map[string]string{}
var mn = map[string]float64{}
for key := range transactions {
if transactionInQuarter(transactions[key].Date, whatquarter) {
for _, item := range transactions[key].Items {
if strings.HasPrefix(item.Vat_code, "V") {
vat := getVat(item.Vat_code)
if val, found := mn[vat]; found {
mn[vat] = val + item.Amount + item.Amount_Vat
} else {
mn[vat] = item.Amount + item.Amount_Vat
}
}
}
}
}
for key, value := range mn {
m[key] = floatToString(roundRappen(value), "")
}
fmt.Fprintf(os.Stderr, "INFO: getDataTaxRates: %v\n", m)
return m
}
func getTaxInvestments(whatquarter string) float64 {
// "174.09"
var res float64 = 0.0
for key := range transactions {
if transactionInQuarter(transactions[key].Date, whatquarter) {
for _, item := range transactions[key].Items {
if strings.HasPrefix(item.Vat_code, "I") {
res = res + item.Amount_Vat
}
}
}
}
res = roundRappen(res)
fmt.Fprintf(os.Stderr, "INFO: Calculated Tax Investments: %v\n", res)
return res
}
func getPayableTax(whatquarter string) float64 {
// "3176.72"
var res float64 = 0.0
for key := range transactions {
if transactionInQuarter(transactions[key].Date, whatquarter) {
for _, item := range transactions[key].Items {
res = res + item.Amount_Vat
}
}
}
res = roundRappen(res)
fmt.Fprintf(os.Stderr, "INFO: Calculated Payable Tax: %v\n", res)
return 0 - res
}
func outputMwst(whatquarter string) { func outputMwst(whatquarter string) {
fmt.Printf("Create Mwst Report for %s\n", whatquarter) fmt.Fprintf(os.Stderr, "INFO: Create Mwst Report for %s\n", whatquarter)
tm := time.Now() tm := time.Now()
gmt_location := time.FixedZone("GMT", 0) gmt_location := time.FixedZone("GMT", 0)
const refidPrefix = "ef1q2024_"
var refid string
var data Data var data Data
data.GenerationTime = fmt.Sprintf("%s", tm.In(gmt_location).Format("2006-01-02T15:04:05Z")) data.GenerationTime = fmt.Sprintf("%s", tm.In(gmt_location).Format("2006-01-02T15:04:05Z"))
data.ReportingPeriodFrom = "2022-01-01" switch whatquarter {
data.ReportingPeriodFrom = "2022-03-31" case "mwst1":
data.BusinessReferenceId = "ef1q2024_20220101_20220331_1" data.ReportingPeriodFrom = balanceYear + "-01-01"
data.TotalConsideration = "43517.04" data.ReportingPeriodTill = balanceYear + "-03-31"
data.DataTaxRates = map[string]string{"7.70": "43517.04", "8.0": "Test only"} refid = refidPrefix + balanceYear + "0101_" + balanceYear + "0331_1"
data.InputTaxInvestments = "174.09" case "mwst2":
data.PayableTax = "3176.72" data.ReportingPeriodFrom = balanceYear + "-04-01"
data.ReportingPeriodTill = balanceYear + "-06-30"
refid = refidPrefix + balanceYear + "0401_" + balanceYear + "0630_1"
case "mwst3":
data.ReportingPeriodFrom = balanceYear + "-07-01"
data.ReportingPeriodTill = balanceYear + "-09-30"
refid = refidPrefix + balanceYear + "0701_" + balanceYear + "0930_1"
case "mwst4":
data.ReportingPeriodFrom = balanceYear + "-10-01"
data.ReportingPeriodTill = balanceYear + "-12-31"
refid = refidPrefix + balanceYear + "1001_" + balanceYear + "1231_1"
}
data.BusinessReferenceId = refid
data.TotalConsideration = floatToString(getTotalConsideration(whatquarter), "")
data.DataTaxRates = getDataTaxRates(whatquarter)
data.InputTaxInvestments = floatToString(getTaxInvestments(whatquarter), "")
data.PayableTax = floatToString(getPayableTax(whatquarter), "")
tmp, err := template.ParseFiles("vat_xml.tmpl") tmp, err := template.ParseFiles("vat_xml.tmpl")
@ -517,9 +654,9 @@ func outputMwst(whatquarter string) {
} }
func usage() { func usage() {
fmt.Printf("usage: bookkeeper <action> <accounts file> <transactions file>\n") fmt.Fprintf(os.Stderr, "usage: bookkeeper <action> <accounts file> <transactions file>\n")
fmt.Printf("\n") fmt.Fprintf(os.Stderr, "\n")
fmt.Printf("Valid actions: check, balance, journal, mwst1, mwst2, mwst3, mwst4, new_year\n") fmt.Fprintf(os.Stderr, "Valid actions: check, balance, journal, mwst1, mwst2, mwst3, mwst4, new_year\n")
os.Exit(1) os.Exit(1)
} }
@ -532,23 +669,23 @@ func main() {
readTransactionData(os.Args[3]) readTransactionData(os.Args[3])
profit = calculateProfit() profit = calculateProfit()
//fmt.Printf("accounts: %#v\n", accounts) //fmt.Fprintf(os.Stderr,"accounts: %#v\n", accounts)
//fmt.Printf("transactions: %#v\n", transactions) //fmt.Fprintf(os.Stderr,"transactions: %#v\n", transactions)
//fmt.Printf("account_balance: %#v\n", account_balance) //fmt.Fprintf(os.Stderr,"account_balance: %#v\n", account_balance)
switch action := os.Args[1]; action { switch action := os.Args[1]; action {
case "check": case "check":
fmt.Println("Check Data") fmt.Fprintln(os.Stderr, "Check Data")
case "balance": case "balance":
fmt.Println("Create Balance Sheet") fmt.Fprintln(os.Stderr, "INFO: Create Balance Sheet")
createBalanceSheet() createBalanceSheet()
case "journal": case "journal":
fmt.Println("Create Journal") fmt.Fprintln(os.Stderr, "INFO: Create Journal")
case "mwst1", "mwst2", "mwst3", "mwst4": case "mwst1", "mwst2", "mwst3", "mwst4":
fmt.Println("Create Mwst Quarterly Report") fmt.Fprintln(os.Stderr, "INFO: Create Mwst Quarterly Report")
outputMwst(action) outputMwst(action)
case "new_year": case "new_year":
fmt.Println("Create New Year") fmt.Fprintln(os.Stderr, "INFO: Create New Year")
default: default:
usage() usage()
} }

View File

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="UTF-8"?>
<eCH-0217:VATDeclaration xsi:schemaLocation="http://www.ech.ch/xmlns/eCH-0217/1/eCH-0217-1-0.xsd" xmlns:eCH-0058="http://www.ech.ch/xmlns/eCH-0058/5" xmlns:eCH-0097="http://www.ech.ch/xmlns/eCH-0097/3" xmlns:eCH-0217="http://www.ech.ch/xmlns/eCH-0217/1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<eCH-0217:VATDeclaration xsi:schemaLocation="http://www.ech.ch/xmlns/eCH-0217/1 eCH-0217-1-0.xsd" xmlns:eCH-0217="http://www.ech.ch/xmlns/eCH-0217/1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:eCH-0097="http://www.ech.ch/xmlns/eCH-0097/3" xmlns:eCH-0058="http://www.ech.ch/xmlns/eCH-0058/5">
<eCH-0217:generalInformation> <eCH-0217:generalInformation>
<eCH-0217:uid> <eCH-0217:uid>
<eCH-0097:uidOrganisationIdCategorie>CHE</eCH-0097:uidOrganisationIdCategorie> <eCH-0097:uidOrganisationIdCategorie>CHE</eCH-0097:uidOrganisationIdCategorie>