Browse Source

Merge pull request #1 from Shopify/master

Update from master
Vinicius Brasil 6 years ago
parent
commit
0b38ce5ae7

+ 6 - 0
CHANGELOG

@@ -1,3 +1,9 @@
+== Version 4.11.0
+
+* Added `ShopifyAPI::InventoryItem`
+* Added `ShopifyAPI::InventoryLevel`
+* Added `#inventory_levels` method to `ShopifyAPI::Location`
+
 == Version 4.10.0
 
 * Added `ShopifyAPI::AccessScope`

+ 8 - 8
lib/shopify_api/resources/asset.rb

@@ -42,7 +42,7 @@ module ShopifyAPI
       prefix_options, query_options = split_options(prefix_options) if query_options.nil?
       "#{prefix(prefix_options)}#{collection_name}.#{format.extension}#{query_string(query_options)}"
     end
-    
+
     # find an asset by key:
     #   ShopifyAPI::Asset.find('layout/theme.liquid', :params => {:theme_id => 99})
     def self.find(*args)
@@ -57,7 +57,7 @@ module ShopifyAPI
         resource
       end
     end
-    
+
     # For text assets, Shopify returns the data in the 'value' attribute.
     # For binary assets, the data is base-64-encoded and returned in the
     # 'attachment' attribute. This accessor returns the data in both cases.
@@ -65,28 +65,28 @@ module ShopifyAPI
       attributes['value'] ||
       (attributes['attachment'] ? Base64.decode64(attributes['attachment']) : nil)
     end
-    
+
     def attach(data)
       self.attachment = Base64.encode64(data)
     end
-    
+
     def destroy
       connection.delete(element_path(prefix_options.merge(:asset => {:key => key})), self.class.headers)
     end
-    
+
     def new?
       false
     end
-    
+
     def method_missing(method_symbol, *arguments) #:nodoc:
       if %w{value= attachment= src= source_key=}.include?(method_symbol)
         wipe_value_attributes
       end
       super
     end
-    
+
     private
-    
+
     def wipe_value_attributes
       %w{value attachment src source_key}.each do |attr|
         attributes.delete(attr)

+ 1 - 1
lib/shopify_api/resources/billing_address.rb

@@ -1,4 +1,4 @@
 module ShopifyAPI
   class BillingAddress < Base
-  end         
+  end
 end

+ 3 - 3
lib/shopify_api/resources/custom_collection.rb

@@ -6,14 +6,14 @@ module ShopifyAPI
     def products
       Product.find(:all, :params => {:collection_id => self.id})
     end
-    
+
     def add_product(product)
       Collect.create(:collection_id => self.id, :product_id => product.id)
     end
-    
+
     def remove_product(product)
       collect = Collect.find(:first, :params => {:collection_id => self.id, :product_id => product.id})
       collect.destroy if collect
     end
-  end                                                                 
+  end
 end

+ 2 - 2
lib/shopify_api/resources/image.rb

@@ -1,13 +1,13 @@
 module ShopifyAPI
   class Image < Base
     init_prefix :product
-    
+
     # generate a method for each possible image variant
     [:pico, :icon, :thumb, :small, :compact, :medium, :large, :grande, :original].each do |m|
       reg_exp_match = "/\\1_#{m}.\\2"
       define_method(m) { src.gsub(/\/(.*)\.(\w{2,4})/, reg_exp_match) }
     end
-    
+
     def attach_image(data, filename = nil)
       attributes['attachment'] = Base64.encode64(data)
       attributes['filename'] = filename unless filename.nil?

+ 6 - 0
lib/shopify_api/resources/inventory_item.rb

@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+module ShopifyAPI
+  class InventoryItem < Base
+  end
+end

+ 55 - 0
lib/shopify_api/resources/inventory_level.rb

@@ -0,0 +1,55 @@
+# frozen_string_literal: true
+
+module ShopifyAPI
+  class InventoryLevel < Base
+
+    # The default path structure in ActiveResource for delete would result in:
+    # /admin/inventory_levels/#{ inventory_level.id }.json?#{ params }, but since
+    # InventroyLevels are a second class resource made up of a Where and a What
+    # (Location and InventoryItem), it does not have a resource ID. Here we
+    # redefine element_path to remove the id so HTTP DELETE requests go to
+    # /admin/inventory_levels.json?#{ params } instead.
+    #
+    def self.element_path(prefix_options = {}, query_options = nil)
+      prefix_options, query_options = split_options(prefix_options) if query_options.nil?
+      "#{prefix(prefix_options)}#{collection_name}.#{format.extension}#{query_string(query_options)}"
+    end
+
+    def destroy
+      load_attributes_from_response(
+        self.class.delete('/', location_id: location_id, inventory_item_id: inventory_item_id)
+      )
+    end
+
+    def connect(relocate_if_necessary: nil)
+      body = { location_id: location_id, inventory_item_id: inventory_item_id }
+      body[:relocate_if_necessary] = relocate_if_necessary unless relocate_if_necessary.nil?
+      load_attributes_from_response(
+        self.class.post(:connect, {}, body.to_json)
+      )
+    end
+
+    def set(new_available, disconnect_if_necessary: nil)
+      body = {
+        location_id: location_id,
+        inventory_item_id: inventory_item_id,
+        available: new_available
+      }
+      body[:disconnect_if_necessary] = disconnect_if_necessary unless disconnect_if_necessary.nil?
+      load_attributes_from_response(
+        self.class.post(:set, {}, body.to_json)
+      )
+    end
+
+    def adjust(available_adjustment)
+      body = {
+        location_id: location_id,
+        inventory_item_id: inventory_item_id,
+        available_adjustment: available_adjustment
+      }
+      load_attributes_from_response(
+        self.class.post(:adjust, {}, body.to_json)
+      )
+    end
+  end
+end

+ 4 - 0
lib/shopify_api/resources/location.rb

@@ -1,4 +1,8 @@
 module ShopifyAPI
   class Location < Base
+
+    def inventory_levels
+      ShopifyAPI::InventoryLevel.find(:all, from: "/admin/locations/#{id}/inventory_levels.json")
+    end
   end
 end

+ 7 - 2
lib/shopify_api/resources/order.rb

@@ -3,8 +3,13 @@ module ShopifyAPI
     include Events
     include Metafields
 
-    def close;  load_attributes_from_response(post(:close, {}, only_id)); end
-    def open;   load_attributes_from_response(post(:open, {}, only_id)); end
+    def close
+      load_attributes_from_response(post(:close, {}, only_id))
+    end
+
+    def open
+      load_attributes_from_response(post(:open, {}, only_id))
+    end
 
     def cancel(options = {})
       load_attributes_from_response(post(:cancel, {}, options.to_json))

+ 4 - 4
lib/shopify_api/resources/product.rb

@@ -13,19 +13,19 @@ module ShopifyAPI
         format % prices.min
       end
     end
-    
+
     def collections
       CustomCollection.find(:all, :params => {:product_id => self.id})
     end
-    
+
     def smart_collections
       SmartCollection.find(:all, :params => {:product_id => self.id})
     end
-    
+
     def add_to_collection(collection)
       collection.add_product(self)
     end
-    
+
     def remove_from_collection(collection)
       collection.remove_product(self)
     end

+ 1 - 1
lib/shopify_api/resources/shipping_line.rb

@@ -1,4 +1,4 @@
 module ShopifyAPI
   class ShippingLine < Base
-  end  
+  end
 end

+ 4 - 4
lib/shopify_api/resources/shop.rb

@@ -1,5 +1,5 @@
 module ShopifyAPI
-  # Shop object. Use Shop.current to receive 
+  # Shop object. Use Shop.current to receive
   # the shop.
   class Shop < Base
     def self.current(options={})
@@ -11,13 +11,13 @@ module ShopifyAPI
     end
 
     def add_metafield(metafield)
-      raise ArgumentError, "You can only add metafields to resource that has been saved" if new?      
+      raise ArgumentError, "You can only add metafields to resource that has been saved" if new?
       metafield.save
       metafield
     end
-    
+
     def events
       Event.find(:all)
     end
-  end               
+  end
 end

+ 1 - 1
lib/shopify_api/version.rb

@@ -1,3 +1,3 @@
 module ShopifyAPI
-  VERSION = "4.10.0"
+  VERSION = "4.11.0"
 end

+ 7 - 0
test/fixtures/inventory_level.json

@@ -0,0 +1,7 @@
+{
+  "inventory_level" : {
+      "inventory_item_id": 808950810,
+      "location_id": 905684977,
+      "available": 1
+  }
+}

+ 24 - 0
test/fixtures/inventory_levels.json

@@ -0,0 +1,24 @@
+{
+  "inventory_levels": [
+    {
+      "inventory_item_id": 39072856,
+      "location_id": 487838322,
+      "available": 27
+    },
+    {
+      "inventory_item_id": 808950810,
+      "location_id": 905684977,
+      "available": 1
+    },
+    {
+      "inventory_item_id": 808950810,
+      "location_id": 487838322,
+      "available": 9
+    },
+    {
+      "inventory_item_id": 39072856,
+      "location_id": 905684977,
+      "available": 3
+    }
+  ]
+}

+ 59 - 0
test/inventory_level_test.rb

@@ -0,0 +1,59 @@
+require 'test_helper'
+
+class InventoryLevelTest < Test::Unit::TestCase
+  def setup
+    super
+    @inventory_level_response = ActiveSupport::JSON.decode load_fixture('inventory_level')
+    @inventory_level = ShopifyAPI::InventoryLevel.new(@inventory_level_response['inventory_level'])
+  end
+
+  test ".find with inventory_item_ids and location_ids returns expected inventory levels" do
+    params = { inventory_item_ids: [808950810, 39072856], location_ids: [905684977, 487838322] }
+    fake "inventory_levels.json?#{params.to_param}", extension: false, method: :get,
+          status: 200, body: load_fixture('inventory_levels')
+    inventory_levels = ShopifyAPI::InventoryLevel.find(:all, params: params)
+
+    assert inventory_levels.all? { |item|
+      params[:location_ids].include?(item.location_id) &&
+      params[:inventory_item_ids].include?(item.inventory_item_id)
+    }, message: 'Response contained inventory_items or locations not requested.'
+  end
+
+  test '#adjust with adjustment value returns inventory_level with available increased by adjustment value' do
+    adjustment = 5
+    updated_available = @inventory_level.available + adjustment
+    @inventory_level_response[:available] = updated_available
+
+    fake 'inventory_levels/adjust', method: :post, body: ActiveSupport::JSON.encode(@inventory_level_response)
+    @inventory_level.adjust(adjustment)
+    assert_equal updated_available, @inventory_level.available
+  end
+
+  test '#connect saves an inventory_level associated with inventory_item and location_id' do
+    params = { inventory_item_id: 808950810, location_id: 99999999 }
+    response = params.clone
+    response[:available] = 0
+
+    fake 'inventory_levels/connect', method: :post, body: ActiveSupport::JSON.encode(response)
+    inventory_level = ShopifyAPI::InventoryLevel.new(params)
+    inventory_level.connect
+    assert_equal 0, inventory_level.available, message: 'expected newly connected location to have 0 inventory'
+  end
+
+  test '#destroy removes inventory_level and returns nil' do
+    params = { inventory_item_id: @inventory_level.inventory_item_id, location_id: @inventory_level.location_id }
+    fake "inventory_levels.json?#{params.to_param}", extension: false, method: :delete, status: 204, body: nil
+    assert_nil @inventory_level.destroy
+  end
+
+  test '#set with available value returns inventory_level with available as the available value' do
+    available = 13
+    response = @inventory_level_response.clone
+    response['inventory_level']['available'] = available
+
+    fake 'inventory_levels/set', method: :post, body: ActiveSupport::JSON.encode(response)
+    @inventory_level.set(available)
+
+    assert_equal available, @inventory_level.available
+  end
+end

+ 14 - 0
test/location_test.rb

@@ -0,0 +1,14 @@
+require 'test_helper'
+
+class LocationTest < Test::Unit::TestCase
+  test '#inventory_levels returns all inventory_levels associated with this location' do
+    location = ShopifyAPI::Location.new(id: 487838322)
+    expected_body = JSON.parse(load_fixture('inventory_levels'))
+    expected_body['inventory_levels'].delete_if {|level| level['location_id'] != location.id }
+    fake "locations/#{location.id}/inventory_levels", method: :get, status: 200, body: JSON(expected_body).to_s
+    inventory_levels = location.inventory_levels
+
+    assert inventory_levels.all? { |item| item.location_id == location.id },
+           message: 'Response contained locations other than the current location.'
+  end
+end