From 4370a8133c8e9c870d7d2c659f27ac655896eec0 Mon Sep 17 00:00:00 2001 From: Sam Anthony Date: Thu, 21 May 2026 14:40:50 -0400 Subject: shorten client method names; implement order cancelation --- cmd/lulu/cost.go | 2 +- cmd/lulu/job.go | 2 +- cmd/lulu/jobs.go | 2 +- cost_test.go | 22 ++++---- lulu.go | 66 +++++++++++++++++++---- print.go | 2 +- print_test.go | 74 ++++++++++++++++--------- testdata/costreq.json | 23 ++++++++ testdata/costresp.json | 95 ++++++++++++++++++++++++++++++++ testdata/getprintjobsresp.json | 119 ----------------------------------------- testdata/jobsresp.json | 119 +++++++++++++++++++++++++++++++++++++++++ testdata/printjobcostreq.json | 23 -------- testdata/printjobcostresp.json | 95 -------------------------------- 13 files changed, 358 insertions(+), 286 deletions(-) create mode 100644 testdata/costreq.json create mode 100644 testdata/costresp.json delete mode 100644 testdata/getprintjobsresp.json create mode 100644 testdata/jobsresp.json delete mode 100644 testdata/printjobcostreq.json delete mode 100644 testdata/printjobcostresp.json diff --git a/cmd/lulu/cost.go b/cmd/lulu/cost.go index 66e1cb5..1826a5c 100644 --- a/cmd/lulu/cost.go +++ b/cmd/lulu/cost.go @@ -37,7 +37,7 @@ func (cmd *CostCmd) Run(cli *kong.Kong, clnt *lulu.Client) error { for i := range cmd.Items { items[i] = cmd.Items[i].PrintJobCostLineItem } - cost, addrVal, err := clnt.PrintJobCost(items, cmd.ShippingAddress.Addr(), cmd.Ship) + cost, addrVal, err := clnt.Cost(items, cmd.ShippingAddress.Addr(), cmd.Ship) if err != nil { return err } diff --git a/cmd/lulu/job.go b/cmd/lulu/job.go index 49948f0..e58ec19 100644 --- a/cmd/lulu/job.go +++ b/cmd/lulu/job.go @@ -14,7 +14,7 @@ type JobCmd struct { } func (cmd JobCmd) Run(clnt *lulu.Client) error { - job, err := clnt.GetPrintJob(cmd.Id) + job, err := clnt.Job(cmd.Id) if err != nil { return err } diff --git a/cmd/lulu/jobs.go b/cmd/lulu/jobs.go index f56c3e0..19deb35 100644 --- a/cmd/lulu/jobs.go +++ b/cmd/lulu/jobs.go @@ -19,7 +19,7 @@ type JobsCmd struct { } func (cmd JobsCmd) Run(clnt *lulu.Client) error { - jobs, err := clnt.GetPrintJobs(cmd.queries()...) + jobs, err := clnt.Jobs(cmd.queries()...) if err != nil { return err } diff --git a/cost_test.go b/cost_test.go index 446440d..c2f18ec 100644 --- a/cost_test.go +++ b/cost_test.go @@ -10,14 +10,14 @@ import ( ) var ( - //go:embed testdata/printjobcostreq.json - printJobCostReqJson string + //go:embed testdata/costreq.json + costReqJson string - //go:embed testdata/printjobcostresp.json - printJobCostRespJson string + //go:embed testdata/costresp.json + costRespJson string ) -var printJobCostReqSample = printJobCostReq{ +var costReqSample = printJobCostReq{ []PrintJobCostLineItem{ { 32, @@ -129,16 +129,16 @@ var printJobCostRespSample = printJobCostResp{ } func TestMarshalPrintJobCostReq(t *testing.T) { - requireMarshalJsonEq(t, printJobCostReqJson, printJobCostReqSample) + requireMarshalJsonEq(t, costReqJson, costReqSample) } func TestUnmarshalPrintJobCostResp(t *testing.T) { - requireUnmarshalJsonEq(t, printJobCostRespSample, printJobCostRespJson) + requireUnmarshalJsonEq(t, printJobCostRespSample, costRespJson) } -func TestPrintJobCost(t *testing.T) { +func TestCost(t *testing.T) { c := newClient(t) - items := printJobCostReqSample.LineItems + items := costReqSample.LineItems addr := ShippingAddress{ City: "Lübeck", Country: "DE", @@ -147,8 +147,8 @@ func TestPrintJobCost(t *testing.T) { Street1: "Holstenstr. 40", Phone: MustParsePhoneNumber("844-212-0689"), } - shiplvl := printJobCostReqSample.ShipOpt - cost, av, err := c.PrintJobCost(items, addr, shiplvl) + shiplvl := costReqSample.ShipOpt + cost, av, err := c.Cost(items, addr, shiplvl) require.NoError(t, err) require.Equal(t, printJobCostRespSample.AddressValidation, av) requireCurrency(t, cost.Currency) diff --git a/lulu.go b/lulu.go index 212a895..53ce2fd 100644 --- a/lulu.go +++ b/lulu.go @@ -217,11 +217,11 @@ func (c *Client) GetCoverValidation(id uint) (CoverValidation, error) { return rec, nil } -// PrintJobCost calculates the cost of a hypothetical print order without +// Cost calculates the cost of a hypothetical print order without // actually creating a print job. // // https://api.lulu.com/docs/#tag/Print-Job-Cost-Calculations/operation/Print-Job-cost-calculations_create -func (c *Client) PrintJobCost(items []PrintJobCostLineItem, addr ShippingAddress, shipOpt ShippingLevel) (PrintJobCost, ShippingAddressValidation, error) { +func (c *Client) Cost(items []PrintJobCostLineItem, addr ShippingAddress, shipOpt ShippingLevel) (PrintJobCost, ShippingAddressValidation, error) { reqAddr := printJobCostReqShipAddr{ City: addr.City, Country: addr.Country, @@ -240,17 +240,17 @@ func (c *Client) PrintJobCost(items []PrintJobCostLineItem, addr ShippingAddress return resp.cost(), resp.AddressValidation, nil } -// GetPrintJobs retrieves a list of all print jobs that have been -// created, filtered by a set of optional query parameters. +// Jobs retrieves a list of all print jobs that have been created, +// filtered by a set of optional query parameters. // // https://api.lulu.com/docs/#tag/Print-Jobs/operation/Print-Jobs_list -func (c *Client) GetPrintJobs(queries ...PrintJobQuery) ([]PrintJob, error) { +func (c *Client) Jobs(queries ...PrintJobQuery) ([]PrintJob, error) { var q printJobQueries q.apply(queries...) qvals := q.vals() verify := func(v any) error { - resp := v.(*getPrintJobsResp) + resp := v.(*jobsResp) if int(resp.Count) != len(resp.Results) { return fmt.Errorf("count (%d) != len(results) (%d)", resp.Count, len(resp.Results)) } else if len(resp.Results) == 0 && resp.Next != "" { @@ -262,7 +262,7 @@ func (c *Client) GetPrintJobs(queries ...PrintJobQuery) ([]PrintJob, error) { var jobs []PrintJob for page := 1; ; page++ { qvals.Set("page", fmt.Sprint(page)) - var resp getPrintJobsResp + var resp jobsResp if err := c.getQueryDecodeVerify(printJobsPath, qvals, &resp, verify); err != nil { return jobs, pkgErr(err) } @@ -275,10 +275,10 @@ func (c *Client) GetPrintJobs(queries ...PrintJobQuery) ([]PrintJob, error) { } } -// GetPrintJob retrieves the print job with the given ID. +// Job retrieves the print job with the given ID. // // https://api.lulu.com/docs/#tag/Print-Jobs/operation/Print-Jobs_read -func (c *Client) GetPrintJob(id uint64) (PrintJob, error) { +func (c *Client) Job(id uint64) (PrintJob, error) { var job PrintJob path, err := url.JoinPath(printJobsPath, fmt.Sprint(id)) if err != nil { @@ -361,6 +361,25 @@ func verifyProductionDelay(delay time.Duration) error { return nil } +// Cancel cancels the print job with the given ID. +// +// https://api.lulu.com/docs/#tag/Print-Jobs/operation/Print-Jobs_status_cancel +func (c *Client) Cancel(id uint64) error { + path, err := url.JoinPath(printJobsPath, fmt.Sprint(id), "status") + if err != nil { + return pkgErr(err) + } + req := map[string]OrderStatus{"name": OrderCanceled} + var status PrintJobStatus + if err := c.putDecode(path, req, &status); err != nil { + return pkgErr(err) + } + if status.Status != OrderCanceled { + return pkgErr(fmt.Errorf("cancel job %d: expected %s, got %s", id, OrderCanceled, status.Status)) + } + return nil +} + // getDecode sends a GET request and unmarshals the response. func (c *Client) getDecode(path string, v any) error { verify := func(v any) error { return nil } @@ -421,6 +440,35 @@ func (c *Client) post(path string, payload any) (*http.Response, error) { return c.c.Post(url, "application/json", bytes.NewBuffer(body)) } +// putDecode sends a PUT request and unmarshals the response. +func (c *Client) putDecode(path string, payload any, v any) error { + body, err := json.Marshal(payload) + if err != nil { + return errEncReq{payload, path, err} + } + url, err := url.JoinPath(ApiUrl, path) + if err != nil { + return err + } + + debugf("PUT %s request: `%s`\n", url, body) + + req, err := http.NewRequest(http.MethodPut, url, bytes.NewBuffer(body)) + if err != nil { + return err + } + req.Header.Set("Content-Type", "application/json") + resp, err := c.c.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return errRespStatus{resp} + } + return decodeResponse(resp, v) +} + func decodeResponse(resp *http.Response, v any) error { body, err := io.ReadAll(resp.Body) if err != nil { diff --git a/print.go b/print.go index 22880ed..8d48d17 100644 --- a/print.go +++ b/print.go @@ -70,7 +70,7 @@ type Reprintable struct { Title string `json:"title"` } -type getPrintJobsResp struct { +type jobsResp struct { Count uint `json:"count"` Next string `json:"next"` Prev string `json:"previous"` diff --git a/print_test.go b/print_test.go index b5d87f4..79381a1 100644 --- a/print_test.go +++ b/print_test.go @@ -9,8 +9,8 @@ import ( "github.com/stretchr/testify/require" ) -//go:embed testdata/getprintjobsresp.json -var getPrintJobsRespJson string +//go:embed testdata/jobsresp.json +var jobsRespJson string var printJobSample = PrintJob{ Contact: MustParseEmailAddress("test@test.com"), @@ -85,23 +85,47 @@ var printJobSample = PrintJob{ }, } -func TestUnmarshalGetPrintJobsResp(t *testing.T) { - want := getPrintJobsResp{ +func TestUnmarshaljobsResp(t *testing.T) { + want := jobsResp{ Count: 1, Next: "https://api.lulu.com/resources/?page=1&page_size=1", Prev: "https://api.lulu.com/resources/?page=1&page_size=1", Results: []PrintJob{printJobSample}, } - requireUnmarshalJsonEq(t, want, getPrintJobsRespJson) + requireUnmarshalJsonEq(t, want, jobsRespJson) } -func TestGetPrintJobs(t *testing.T) { +func TestJobs(t *testing.T) { t.Fail() // TODO: create a few print jobs and retrieve them c := newClient(t) - _, err := c.GetPrintJobs() + _, err := c.Jobs() require.NoError(t, err) } +func TestJob(t *testing.T) { + contact := MustParseEmailAddress("test@test.com") + jobEid := "demo-time" + productionDelay := 120 * time.Minute + addr := shipAddrSample + shipOpt := Mail + items := []Printable{printableSample} + + c := newClient(t) + job1, err := c.Print(contact, jobEid, productionDelay, addr, shipOpt, items) + require.NoError(t, err) + require.NotZero(t, job1.Id) + + job2, err := c.Job(job1.Id) + require.NoError(t, err) + + // Ignore timestamp because job gets marked as 'modified' between + // creation and retrieval time even if it hasn't actually + // changed. + job2.Modified = job1.Modified + + require.Equal(t, job1, job2) +} + //go:embed testdata/printreq.json var printReqJson string @@ -203,7 +227,7 @@ func TestReprint(t *testing.T) { // Wait for it to be assigned a printable_id tpoll(t, func() bool { - job1, err = c.GetPrintJob(id1) + job1, err = c.Job(id1) require.NoError(t, err) require.Len(t, job1.LineItems, 1) return job1.LineItems[0].Status.Status != ItemCreated @@ -231,30 +255,30 @@ func TestReprint(t *testing.T) { require.Equal(t, item1.Mfg, item2.Mfg) } -func TestGetPrintJob(t *testing.T) { +func TestCancel(t *testing.T) { + // Print + c := newClient(t) contact := MustParseEmailAddress("test@test.com") - jobEid := "demo-time" + jobEid := "cancel-test" productionDelay := 120 * time.Minute addr := shipAddrSample shipOpt := Mail - items := []Printable{printableSample} - - c := newClient(t) - job1, err := c.Print(contact, jobEid, productionDelay, addr, shipOpt, items) + item := printableSample + job, err := c.Print(contact, jobEid, productionDelay, addr, shipOpt, []Printable{item}) require.NoError(t, err) - require.NotZero(t, job1.Id) + id := job.Id + require.NotZero(t, id) - job2, err := c.GetPrintJob(job1.Id) + // Job exists + _, err = c.Job(id) require.NoError(t, err) - // Ignore timestamp because job gets marked as 'modified' between - // creation and retrieval time even if it hasn't actually - // changed. - job2.Modified = job1.Modified - - require.Equal(t, job1, job2) -} + // Cancel + err = c.Cancel(id) + require.NoError(t, err) -func TestCancel(t *testing.T) { - t.Fail() // TODO + // Job canceled + job, err = c.Job(id) + require.NoError(t, err) + require.Equal(t, OrderCanceled, job.Status.Status) } diff --git a/testdata/costreq.json b/testdata/costreq.json new file mode 100644 index 0000000..d521f77 --- /dev/null +++ b/testdata/costreq.json @@ -0,0 +1,23 @@ +{ + "line_items": [ + { + "page_count": 32, + "pod_package_id": "0600X0900.BW.STD.PB.060UW444.MXX", + "quantity": 20 + }, + { + "page_count": 324, + "pod_package_id": "0425X0687.BW.STD.PB.060UW444.GXX", + "quantity": 200 + } + ], + "shipping_address": { + "city": "Lübeck", + "country_code": "DE", + "postcode": "23552", + "state_code": "", + "street1": "Holstenstr. 40", + "phone_number": "844-212-0689" + }, + "shipping_option": "EXPRESS" +} \ No newline at end of file diff --git a/testdata/costresp.json b/testdata/costresp.json new file mode 100644 index 0000000..f633183 --- /dev/null +++ b/testdata/costresp.json @@ -0,0 +1,95 @@ +{ + "shipping_address": { + "city": "Lübeck", + "postcode": "23552", + "street1": "Holstenstr. 40", + "phone_number": "844-212-0689", + "state": "", + "country": "DE", + "is_business": false, + "first_name": ".", + "last_name": ".", + "warnings": [ + { + "type": "validation_warning", + "code": "REPLACED", + "path": "external", + "message": "street1: Holstenstr. 40 -> Holstenstraße 40" + } + ], + "suggested_address": { + "country_code": "DE", + "state_code": null, + "postcode": "23552", + "city": "Lübeck", + "street1": "Holstenstraße 40", + "street2": null + } + }, + "fees": [ + { + "currency": "USD", + "fee_type": "HANDLING_FEE", + "sku": "HANDLING_FEE", + "tax_rate": "0.060000", + "total_cost_excl_tax": "14.00", + "total_cost_incl_tax": "14.84", + "total_tax": "0.84" + }, + { + "currency": "USD", + "fee_type": "FULFILLMENT_FEE", + "sku": "FULFILLMENT_FEE", + "tax_rate": "0.060000", + "total_cost_excl_tax": "0.75", + "total_cost_incl_tax": "0.80", + "total_tax": "0.05" + } + ], + "line_item_costs": [ + { + "cost_excl_discounts": "4.95", + "total_tax": "6.93", + "tax_rate": "0.070000", + "quantity": 20, + "total_cost_excl_tax": "99.00", + "total_cost_excl_discounts": "99.00", + "total_cost_incl_tax": "105.93", + "discounts": [], + "unit_tier_cost": "4.95" + }, + { + "cost_excl_discounts": "16.19", + "total_tax": "215.33", + "tax_rate": "0.070000", + "quantity": 200, + "total_cost_excl_tax": "3076.10", + "total_cost_excl_discounts": "3238.00", + "total_cost_incl_tax": "3291.43", + "discounts": [ + { + "amount": "161.90", + "description": "Volume Discount 5%" + } + ], + "unit_tier_cost": "16.19" + } + ], + "shipping_cost": { + "total_cost_excl_tax": "1085.50", + "total_cost_incl_tax": "1161.49", + "total_tax": "75.99", + "tax_rate": "0.19" + }, + "fulfillment_cost": { + "total_cost_excl_tax": "1.05", + "total_cost_incl_tax": "1.12", + "total_tax": "0.07", + "tax_rate": "0.19" + }, + "total_tax": "298.32", + "total_cost_excl_tax": "4261.65", + "total_cost_incl_tax": "4559.97", + "total_discount_amount": "161.90", + "currency": "CAD" +} diff --git a/testdata/getprintjobsresp.json b/testdata/getprintjobsresp.json deleted file mode 100644 index 3f43d2c..0000000 --- a/testdata/getprintjobsresp.json +++ /dev/null @@ -1,119 +0,0 @@ -{ - "count": 1, - "next": "https://api.lulu.com/resources/?page=1&page_size=1", - "previous": "https://api.lulu.com/resources/?page=1&page_size=1", - "results": [ - { - "contact_email": "test@test.com", - "costs": { - "line_item_costs": null, - "shipping_cost": { - "total_cost_excl_tax": "132.74", - "total_cost_incl_tax": "132.74", - "total_tax": "0.00", - "tax_rate": "0.000000" - }, - "fulfillment_cost": { - "total_cost_excl_tax": "0.75", - "total_cost_incl_tax": "0.81", - "total_tax": "0.06", - "tax_rate": "0.080000" - }, - "fees": [ - { - "currency": "USD", - "fee_type": "HANDLING_FEE", - "sku": "HANDLING_FEE_6", - "tax_rate": "0.088750", - "total_cost_excl_tax": "4.00", - "total_cost_incl_tax": "4.36", - "total_tax": "0.36" - } - ], - "total_cost_excl_tax": "123.45", - "total_cost_incl_tax": "678.00", - "total_tax": "0.123400" - }, - "date_created": "2017-08-07T08:47:26.485456Z", - "date_modified": "2017-08-07T08:47:26.485490Z", - "estimated_shipping_dates": { - "arrival_max": "2017-08-12", - "arrival_min": "2017-08-10", - "dispatch_max": "2017-08-09", - "dispatch_min": "2017-08-07" - }, - "external_id": "demo-time", - "id": 1, - "line_items": [ - { - "external_id": "item-reference-1", - "id": 1, - "printable_id": null, - "printable_normalization": { - "cover": { - "job_id": null, - "normalized_file": null, - "page_count": null, - "source_file": null, - "source_md5_sum": "e78512c777e7f5841fe8f1992cefb898", - "source_url": "https://www.dropbox.com/sh/p3zh22vzsaegiri/AADP367j0bTWlt8fCu-_tm2ia/161025/139056_cover.pdf?dl=1" - }, - "interior": { - "job_id": null, - "normalized_file": null, - "page_count": null, - "source_file": null, - "source_md5_sum": "7f8af20c296747689756f8e310135d79", - "source_url": "https://www.dropbox.com/sh/p3zh22vzsaegiri/AACOUn3LFKsITDzylh13bQpsa/161025/thesis2.pdf?dl=1" - }, - "pod_package_id": "0600X0900.BW.STD.PB.060UW444.MXX" - }, - "quantity": 20, - "status": { - "messages": { - "info": "Line-item is currently being validated" - }, - "name": "CREATED" - }, - "title": "My Book" - } - ], - "production_delay": 120, - "production_due_time": null, - "shipping_address": { - "city": "Lübeck", - "country_code": "DE", - "is_business": false, - "name": "Hans Dampf", - "phone_number": "844-212-0689", - "postcode": "23552", - "state_code": "", - "street1": "Holstenstr. 40", - "street2": "", - "warnings": [ - { - "type": "validation_warning", - "path": "external", - "code": "REPLACED", - "message": "street1: Holstenstr. 40 -> Holstenstraße 40" - } - ], - "suggested_address": { - "country_code": "DE", - "state_code": null, - "postcode": "23552", - "city": "Lübeck", - "street1": "Holstenstraße 40", - "street2": null - } - }, - "shipping_level": "MAIL", - "shipping_option_level": "MAIL", - "status": { - "changed": "2017-08-07T08:47:26.480493Z", - "message": "Print-job is currently being validated", - "name": "CREATED" - } - } - ] -} diff --git a/testdata/jobsresp.json b/testdata/jobsresp.json new file mode 100644 index 0000000..3f43d2c --- /dev/null +++ b/testdata/jobsresp.json @@ -0,0 +1,119 @@ +{ + "count": 1, + "next": "https://api.lulu.com/resources/?page=1&page_size=1", + "previous": "https://api.lulu.com/resources/?page=1&page_size=1", + "results": [ + { + "contact_email": "test@test.com", + "costs": { + "line_item_costs": null, + "shipping_cost": { + "total_cost_excl_tax": "132.74", + "total_cost_incl_tax": "132.74", + "total_tax": "0.00", + "tax_rate": "0.000000" + }, + "fulfillment_cost": { + "total_cost_excl_tax": "0.75", + "total_cost_incl_tax": "0.81", + "total_tax": "0.06", + "tax_rate": "0.080000" + }, + "fees": [ + { + "currency": "USD", + "fee_type": "HANDLING_FEE", + "sku": "HANDLING_FEE_6", + "tax_rate": "0.088750", + "total_cost_excl_tax": "4.00", + "total_cost_incl_tax": "4.36", + "total_tax": "0.36" + } + ], + "total_cost_excl_tax": "123.45", + "total_cost_incl_tax": "678.00", + "total_tax": "0.123400" + }, + "date_created": "2017-08-07T08:47:26.485456Z", + "date_modified": "2017-08-07T08:47:26.485490Z", + "estimated_shipping_dates": { + "arrival_max": "2017-08-12", + "arrival_min": "2017-08-10", + "dispatch_max": "2017-08-09", + "dispatch_min": "2017-08-07" + }, + "external_id": "demo-time", + "id": 1, + "line_items": [ + { + "external_id": "item-reference-1", + "id": 1, + "printable_id": null, + "printable_normalization": { + "cover": { + "job_id": null, + "normalized_file": null, + "page_count": null, + "source_file": null, + "source_md5_sum": "e78512c777e7f5841fe8f1992cefb898", + "source_url": "https://www.dropbox.com/sh/p3zh22vzsaegiri/AADP367j0bTWlt8fCu-_tm2ia/161025/139056_cover.pdf?dl=1" + }, + "interior": { + "job_id": null, + "normalized_file": null, + "page_count": null, + "source_file": null, + "source_md5_sum": "7f8af20c296747689756f8e310135d79", + "source_url": "https://www.dropbox.com/sh/p3zh22vzsaegiri/AACOUn3LFKsITDzylh13bQpsa/161025/thesis2.pdf?dl=1" + }, + "pod_package_id": "0600X0900.BW.STD.PB.060UW444.MXX" + }, + "quantity": 20, + "status": { + "messages": { + "info": "Line-item is currently being validated" + }, + "name": "CREATED" + }, + "title": "My Book" + } + ], + "production_delay": 120, + "production_due_time": null, + "shipping_address": { + "city": "Lübeck", + "country_code": "DE", + "is_business": false, + "name": "Hans Dampf", + "phone_number": "844-212-0689", + "postcode": "23552", + "state_code": "", + "street1": "Holstenstr. 40", + "street2": "", + "warnings": [ + { + "type": "validation_warning", + "path": "external", + "code": "REPLACED", + "message": "street1: Holstenstr. 40 -> Holstenstraße 40" + } + ], + "suggested_address": { + "country_code": "DE", + "state_code": null, + "postcode": "23552", + "city": "Lübeck", + "street1": "Holstenstraße 40", + "street2": null + } + }, + "shipping_level": "MAIL", + "shipping_option_level": "MAIL", + "status": { + "changed": "2017-08-07T08:47:26.480493Z", + "message": "Print-job is currently being validated", + "name": "CREATED" + } + } + ] +} diff --git a/testdata/printjobcostreq.json b/testdata/printjobcostreq.json deleted file mode 100644 index d521f77..0000000 --- a/testdata/printjobcostreq.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "line_items": [ - { - "page_count": 32, - "pod_package_id": "0600X0900.BW.STD.PB.060UW444.MXX", - "quantity": 20 - }, - { - "page_count": 324, - "pod_package_id": "0425X0687.BW.STD.PB.060UW444.GXX", - "quantity": 200 - } - ], - "shipping_address": { - "city": "Lübeck", - "country_code": "DE", - "postcode": "23552", - "state_code": "", - "street1": "Holstenstr. 40", - "phone_number": "844-212-0689" - }, - "shipping_option": "EXPRESS" -} \ No newline at end of file diff --git a/testdata/printjobcostresp.json b/testdata/printjobcostresp.json deleted file mode 100644 index f633183..0000000 --- a/testdata/printjobcostresp.json +++ /dev/null @@ -1,95 +0,0 @@ -{ - "shipping_address": { - "city": "Lübeck", - "postcode": "23552", - "street1": "Holstenstr. 40", - "phone_number": "844-212-0689", - "state": "", - "country": "DE", - "is_business": false, - "first_name": ".", - "last_name": ".", - "warnings": [ - { - "type": "validation_warning", - "code": "REPLACED", - "path": "external", - "message": "street1: Holstenstr. 40 -> Holstenstraße 40" - } - ], - "suggested_address": { - "country_code": "DE", - "state_code": null, - "postcode": "23552", - "city": "Lübeck", - "street1": "Holstenstraße 40", - "street2": null - } - }, - "fees": [ - { - "currency": "USD", - "fee_type": "HANDLING_FEE", - "sku": "HANDLING_FEE", - "tax_rate": "0.060000", - "total_cost_excl_tax": "14.00", - "total_cost_incl_tax": "14.84", - "total_tax": "0.84" - }, - { - "currency": "USD", - "fee_type": "FULFILLMENT_FEE", - "sku": "FULFILLMENT_FEE", - "tax_rate": "0.060000", - "total_cost_excl_tax": "0.75", - "total_cost_incl_tax": "0.80", - "total_tax": "0.05" - } - ], - "line_item_costs": [ - { - "cost_excl_discounts": "4.95", - "total_tax": "6.93", - "tax_rate": "0.070000", - "quantity": 20, - "total_cost_excl_tax": "99.00", - "total_cost_excl_discounts": "99.00", - "total_cost_incl_tax": "105.93", - "discounts": [], - "unit_tier_cost": "4.95" - }, - { - "cost_excl_discounts": "16.19", - "total_tax": "215.33", - "tax_rate": "0.070000", - "quantity": 200, - "total_cost_excl_tax": "3076.10", - "total_cost_excl_discounts": "3238.00", - "total_cost_incl_tax": "3291.43", - "discounts": [ - { - "amount": "161.90", - "description": "Volume Discount 5%" - } - ], - "unit_tier_cost": "16.19" - } - ], - "shipping_cost": { - "total_cost_excl_tax": "1085.50", - "total_cost_incl_tax": "1161.49", - "total_tax": "75.99", - "tax_rate": "0.19" - }, - "fulfillment_cost": { - "total_cost_excl_tax": "1.05", - "total_cost_incl_tax": "1.12", - "total_tax": "0.07", - "tax_rate": "0.19" - }, - "total_tax": "298.32", - "total_cost_excl_tax": "4261.65", - "total_cost_incl_tax": "4559.97", - "total_discount_amount": "161.90", - "currency": "CAD" -} -- cgit v1.2.3