-
Notifications
You must be signed in to change notification settings - Fork 1
/
rates.go
124 lines (102 loc) · 2.83 KB
/
rates.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
package vat
import (
"encoding/json"
"net/http"
"strings"
"sync"
"time"
)
// RatePeriod represents a time and the various activate rates at that time.
type RatePeriod struct {
EffectiveFrom time.Time
Rates map[string]float32
}
// CountryRates holds the various differing VAT rate periods for a given country
type CountryRates struct {
CountryCode string `json:"country_code"`
Periods []RatePeriod
}
var mutex = &sync.Mutex{} // protect countriesRates
var countriesRates []CountryRates
// GetRateOn returns the effective VAT rate on a given date
func (cr *CountryRates) GetRateOn(t time.Time, level string) (float32, error) {
var activePeriod RatePeriod
// find active period for the given time
for _, p := range cr.Periods {
if t.After(p.EffectiveFrom) && (activePeriod.EffectiveFrom.IsZero() ||
p.EffectiveFrom.After(activePeriod.EffectiveFrom)) {
activePeriod = p
}
}
activeRate, ok := activePeriod.Rates[level]
if !ok {
return 0.00, ErrInvalidRateLevel
}
return activeRate, nil
}
// GetRate returns the currently active rate
func (cr *CountryRates) GetRate(level string) (float32, error) {
now := time.Now()
return cr.GetRateOn(now, level)
}
// GetCountryRates gets the CountryRates struct for a country by its ISO-3166-1-alpha2 country code.
func GetCountryRates(countryCode string) (CountryRates, error) {
var rate CountryRates
rates, err := GetRates()
if err != nil {
return rate, err
}
for _, r := range rates {
if r.CountryCode == countryCode {
return r, nil
}
}
return rate, ErrInvalidCountryCode
}
// GetRates returns the in-memory VAT rates
func GetRates() ([]CountryRates, error) {
var err error
mutex.Lock()
if countriesRates == nil {
countriesRates, err = FetchRates()
}
mutex.Unlock()
return countriesRates, err
}
// FetchRates fetches the latest VAT rates from ibericode/vat-rates and updates the in-memory rates
func FetchRates() ([]CountryRates, error) {
client := http.Client{
Timeout: serviceTimeout,
}
r, err := client.Get("https://raw.githubusercontent.com/ibericode/vat-rates/master/vat-rates.json")
if err != nil {
return nil, err
}
apiResponse := &struct {
Details string
Version float32
Items map[string][]struct {
EffectiveFrom string `json:"effective_from"`
Rates map[string]float32
}
}{}
err = json.NewDecoder(r.Body).Decode(&apiResponse)
if err != nil {
return nil, err
}
var rates []CountryRates
for code, periods := range apiResponse.Items {
rate := CountryRates{CountryCode: code}
for _, period := range periods {
from := strings.Replace(period.EffectiveFrom, "0000-", "2000-", 1)
fromTime, _ := time.Parse("2006-01-02", from)
rperiod := RatePeriod{
EffectiveFrom: fromTime,
Rates: period.Rates,
}
rate.Periods = append(rate.Periods, rperiod)
}
rates = append(rates, rate)
}
return rates, err
}