aboutsummaryrefslogtreecommitdiffstats
path: root/cost.go
diff options
context:
space:
mode:
Diffstat (limited to 'cost.go')
-rw-r--r--cost.go165
1 files changed, 165 insertions, 0 deletions
diff --git a/cost.go b/cost.go
new file mode 100644
index 0000000..202fcde
--- /dev/null
+++ b/cost.go
@@ -0,0 +1,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
+}