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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
|
package lulu
// TODO: should we remove the redundant tax/subtotal fields or remain
// true to their retarded api?
import (
"encoding/json"
"fmt"
"github.com/shopspring/decimal"
)
// printJobCostReq is the json body of a /print-job-cost-calculations/ request.
type printJobCostReq struct {
LineItems []printJobCostLineItem `json:"line_items"`
ShipAddr ShippingAddress `json:"shipping_address"`
ShipOpt ShippingLevel `json:"shipping_option"`
}
type printJobCostLineItem struct {
NPages uint `json:"page_count"`
Mfg PkgId `json:"pod_package_id"`
Quantity uint `json:"quantity"`
}
// PrintJobCost is the response from /print-job-cost-calculations/.
type PrintJobCost struct {
Addr, SuggestedAddr ShippingAddress
AddrWarning ShippingAddressWarning
Currency string
Fees []Fee
FulfillmentCost FulfillmentCost
LineItemCosts []LineItemCost
ShipCost FulfillmentCost
TotalCostExclTax, TotalCostInclTax, TotalDiscount, TotalTax decimal.Decimal
}
type ShippingAddressWarning struct {
Type string `json:"type"` // eg "validation_warning"
Path string `json:"path"` // eg "external"
Code string `json:"code"` // eg "REPLACED"
Msg string `json:"message"` // eg "street1: Holstenstr. 40 -> Holstenstraße 40"
}
type Fee struct {
Currency string `json:"currency"`
Type string `json:"fee_type"`
Sku string `json:"sku"`
TaxRate decimal.Decimal `json:"tax_rate"`
TotalCostExclTax decimal.Decimal `json:"total_cost_excl_tax"`
TotalCostInclTax decimal.Decimal `json:"total_cost_incl_tax"`
TotalTax decimal.Decimal `json:"total_tax"`
}
type FulfillmentCost struct {
TaxRate decimal.Decimal `json:"tax_rate"`
TotalCostExclTax decimal.Decimal `json:"total_cost_excl_tax"`
TotalCostInclTax decimal.Decimal `json:"total_cost_incl_tax"`
TotalTax decimal.Decimal `json:"total_tax"`
}
type LineItemCost struct {
CostExclDiscounts decimal.Decimal `json:"cost_excl_discounts"`
Discounts []Discount `json:"discounts"`
Quantity uint `json:"quantity"`
TaxRate decimal.Decimal `json:"tax_rate"`
TotalCostExclDiscounts decimal.Decimal `json:"total_cost_excl_discounts"`
TotalCostExclTax decimal.Decimal `json:"total_cost_excl_tax"`
TotalCostInclTax decimal.Decimal `json:"total_cost_incl_tax"`
TotalTax decimal.Decimal `json:"total_tax"`
UnitTierCost *decimal.Decimal `json:"unit_tier_cost"`
}
type Discount struct {
Amount decimal.Decimal `json:"amount"`
Descr string `json:"description"`
}
// printJobCostResp is the json body of a /print-job-cost-calculations/ response.
type printJobCostResp struct {
Addr struct {
City string `json:"city"`
CountryCode string `json:"country_code"`
IsBusiness bool `json:"is_business"`
Name string `json:"name"`
Phone string `json:"phone_number"`
PostCode string `json:"postcode"`
StateCode string `json:"state_code"`
Street1 string `json:"street1"`
Street2 string `json:"street2"`
Warning ShippingAddressWarning `json:"warnings"`
Suggested suggestedShippingAddress `json:"suggested_address"`
} `json:"shipping_address"`
Currency string `json:"currency"`
Fees []Fee `json:"fees"`
FulfillmentCost FulfillmentCost `json:"fulfillment_cost"`
LineItemCosts []LineItemCost `json:"line_item_costs"`
ShipCost FulfillmentCost `json:"shipping_cost"`
TotalCostExclTax decimal.Decimal `json:"total_cost_excl_tax"`
TotalCostInclTax decimal.Decimal `json:"total_cost_incl_tax"`
TotalDiscount decimal.Decimal `json:"total_discount_amount"`
TotalTax decimal.Decimal `json:"total_tax"`
}
// Some blockhead that Lulu hired thought it would be a good idea to use
// a string in one place and a number in another for the postal code.
// That's why this special struct is needed.
type suggestedShippingAddress struct {
CountryCode string `json:"country_code"`
StateCode string `json:"state_code"`
PostCode uint `json:"postcode"`
City string `json:"city"`
Street1 string `json:"street1"`
Street2 string `json:"street2"`
}
func (c *PrintJobCost) UnmarshalJSON(data []byte) error {
// Lulu decided to put warnings and suggested_address INSIDE
// shipping_address in the response. So we unmarshal into an
// alias struct and then map onto our own structs. The
// alternative is to add everything to our ShippingAddress
// struct, but that would be confusing to users of this library:
// ShippingAddress would have semantics because some fields would
// be "optional".
var resp printJobCostResp
if err := json.Unmarshal(data, &resp); err != nil {
return err
}
addr := resp.Addr
c.Addr = ShippingAddress{
City: addr.City,
CountryCode: addr.CountryCode,
IsBusiness: addr.IsBusiness,
Name: addr.Name,
Phone: addr.Phone,
PostCode: addr.PostCode,
StateCode: addr.StateCode,
Street1: addr.Street1,
Street2: addr.Street2,
}
sugad := addr.Suggested
c.SuggestedAddr = ShippingAddress{
CountryCode: sugad.CountryCode,
StateCode: sugad.StateCode,
PostCode: fmt.Sprint(sugad.PostCode), // idiot
City: sugad.City,
Street1: sugad.Street1,
Street2: sugad.Street2,
}
c.AddrWarning = addr.Warning
c.Currency = resp.Currency
c.Fees = resp.Fees
c.FulfillmentCost = resp.FulfillmentCost
c.LineItemCosts = resp.LineItemCosts
c.ShipCost = resp.ShipCost
c.TotalCostExclTax = resp.TotalCostExclTax
c.TotalCostInclTax = resp.TotalCostInclTax
c.TotalDiscount = resp.TotalDiscount
c.TotalTax = resp.TotalTax
return nil
}
|