diff --git a/README.md b/README.md index cefc3e20..ffca94c4 100644 --- a/README.md +++ b/README.md @@ -482,12 +482,13 @@ default. - fulfillmentOrder - `cancel(id, params)` - `close(id[, message])` + - `fulfillments(id)` - `get(id)` + - `hold(id, params)` - `list([params])` - `locationsForMove(id)` - `move(id, locationId)` - `setFulfillmentOrdersDeadline(params)` - - `fulfillments(id)` - fulfillmentRequest - `accept(fulfillmentOrderId[, message])` - `create(fulfillmentOrderId, params)` diff --git a/resources/fulfillment-order.js b/resources/fulfillment-order.js index 460fe10d..04a5a8ff 100644 --- a/resources/fulfillment-order.js +++ b/resources/fulfillment-order.js @@ -127,4 +127,21 @@ FulfillmentOrder.prototype.fulfillments = function fulfillments(id) { return this.shopify.request(url, 'GET', 'fulfillments'); }; +/** + * Halts all fulfillment work on a fulfillment order with + * status OPEN and changes the status of the fulfillment order to ON_HOLD. + * + * @param {Number} id Fulfillment Order ID + * @param {Object} params An object containing the reason for the fulfillment + hold and additional optional information + * @return {Promise} Promise that resolves with the result + * @public + */ +FulfillmentOrder.prototype.hold = function hold(id, params) { + const url = this.buildUrl(`${id}/hold`); + return this.shopify + .request(url, 'POST', undefined, { fulfillment_hold: params }) + .then((body) => body[this.key]); +}; + module.exports = FulfillmentOrder; diff --git a/test/fixtures/fulfillment-order/req/hold.json b/test/fixtures/fulfillment-order/req/hold.json new file mode 100644 index 00000000..d7ee489d --- /dev/null +++ b/test/fixtures/fulfillment-order/req/hold.json @@ -0,0 +1,7 @@ +{ + "fulfillment_hold": { + "reason": "inventory_out_of_stock", + "reason_notes": "Not enough inventory to complete this work.", + "fulfillment_order_line_items": [{ "id": 1058737493, "quantity": 1 }] + } +} diff --git a/test/fixtures/fulfillment-order/req/index.js b/test/fixtures/fulfillment-order/req/index.js index 54a1d09a..6ed476d7 100644 --- a/test/fixtures/fulfillment-order/req/index.js +++ b/test/fixtures/fulfillment-order/req/index.js @@ -2,5 +2,6 @@ exports.cancel = require('./cancel'); exports.close = require('./close'); +exports.hold = require('./hold'); exports.move = require('./move'); exports.setFulfillmentOrdersDeadline = require('./set-fulfillment-orders-deadline'); diff --git a/test/fixtures/fulfillment-order/res/hold.json b/test/fixtures/fulfillment-order/res/hold.json new file mode 100644 index 00000000..31844af5 --- /dev/null +++ b/test/fixtures/fulfillment-order/res/hold.json @@ -0,0 +1,61 @@ +{ + "fulfillment_order": { + "id": 1046000789, + "shop_id": 548380009, + "order_id": 450789469, + "assigned_location_id": 24826418, + "request_status": "unsubmitted", + "status": "on_hold", + "fulfill_at": null, + "supported_actions": ["release_hold"], + "destination": { + "id": 1046000789, + "address1": "Chestnut Street 92", + "address2": "", + "city": "Louisville", + "company": null, + "country": "United States", + "email": "bob.norman@mail.example.com", + "first_name": "Bob", + "last_name": "Norman", + "phone": "+1(502)-459-2181", + "province": "Kentucky", + "zip": "40202" + }, + "line_items": [ + { + "id": 1058737493, + "shop_id": 548380009, + "fulfillment_order_id": 1046000789, + "quantity": 1, + "line_item_id": 518995019, + "inventory_item_id": 49148385, + "fulfillable_quantity": 1, + "variant_id": 49148385 + } + ], + "international_duties": null, + "fulfillment_holds": [ + { + "reason": "inventory_out_of_stock", + "reason_notes": "Not enough inventory to complete this work." + } + ], + "fulfill_by": null, + "created_at": "2024-07-24T06:26:32-04:00", + "updated_at": "2024-07-24T06:26:33-04:00", + "delivery_method": null, + "assigned_location": { + "address1": null, + "address2": null, + "city": null, + "country_code": "DE", + "location_id": 24826418, + "name": "Apple Api Shipwire", + "phone": null, + "province": null, + "zip": null + }, + "merchant_requests": [] + } +} diff --git a/test/fixtures/fulfillment-order/res/index.js b/test/fixtures/fulfillment-order/res/index.js index 56f1bc0e..21cdfab6 100644 --- a/test/fixtures/fulfillment-order/res/index.js +++ b/test/fixtures/fulfillment-order/res/index.js @@ -7,3 +7,4 @@ exports.close = require('./close'); exports.list = require('./list'); exports.move = require('./move'); exports.get = require('./get'); +exports.hold = require('./hold'); diff --git a/test/fulfillment-order.test.js b/test/fulfillment-order.test.js index b3e9626f..dfef4e40 100644 --- a/test/fulfillment-order.test.js +++ b/test/fulfillment-order.test.js @@ -134,4 +134,19 @@ describe('Shopify#fulfillmentOrder', () => { .fulfillments(1046000823) .then((data) => expect(data).to.deep.equal(output.fulfillments)); }); + + it('applies a fulfillment hold on an open fulfillment order', () => { + const input = fixtures.req.hold; + const output = fixtures.res.hold; + + scope + .post('/admin/fulfillment_orders/1046000789/hold.json', input) + .reply(200, output); + + return shopify.fulfillmentOrder + .hold(1046000789, input.fulfillment_hold) + .then((data) => { + expect(data).to.deep.equal(output.fulfillment_order); + }); + }); }); diff --git a/types/index.d.ts b/types/index.d.ts index 44c1d9e9..e1677ed2 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -389,6 +389,10 @@ declare class Shopify { fulfillments: ( id: number ) => Promise>; + hold: ( + id: number, + params: Shopify.IFulfillmentHold + ) => Promise; }; fulfillmentRequest: { accept: ( @@ -3567,4 +3571,21 @@ declare namespace Shopify { }; updated_at: string; } + + interface IFulfillmentHoldFulfillmentOrderLineItem { + id: number; + quantity: number; + } + + interface IFulfillmentHold { + reason: + | 'awaiting_payment' + | 'high_risk_of_fraud' + | 'incorrect_address' + | 'inventory_out_of_stock' + | 'other'; + reason_notes?: string; + notify_merchant?: boolean; + fulfillment_order_line_items?: IFulfillmentHoldFulfillmentOrderLineItem[]; + } }