magento 0.9.1 → 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,10 +2,13 @@
2
2
 
3
3
  require 'time'
4
4
  require 'dry/inflector'
5
+ require 'active_support/core_ext/string/inflections'
6
+ require 'active_support/core_ext/hash/keys'
5
7
 
6
8
  require_relative 'magento/errors'
7
9
  require_relative 'magento/request'
8
10
  require_relative 'magento/model_mapper'
11
+ require_relative 'magento/params'
9
12
  require_relative 'magento/polymorphic_model'
10
13
  require_relative 'magento/model'
11
14
  require_relative 'magento/record_collection'
@@ -17,8 +20,10 @@ require_relative 'magento/customer'
17
20
  require_relative 'magento/order'
18
21
  require_relative 'magento/invoice'
19
22
  require_relative 'magento/guest_cart'
23
+ require_relative 'magento/sales_rule'
20
24
 
21
25
  Dir[File.expand_path('magento/shared/*.rb', __dir__)].map { |f| require f }
26
+ Dir[File.expand_path('magento/params/*.rb', __dir__)].map { |f| require f }
22
27
 
23
28
  module Magento
24
29
  class << self
@@ -1,17 +1,47 @@
1
1
  module Magento
2
2
  class Customer < Model
3
+ self.endpoint = 'customers/search'
4
+
3
5
  def fullname
4
6
  "#{@firstname} #{@lastname}"
5
7
  end
6
8
 
9
+ def update(attributes)
10
+ raise "id not present" if @id.nil?
11
+
12
+ attributes.each { |key, value| send("#{key}=", value) }
13
+ save
14
+ end
15
+
7
16
  class << self
8
17
  alias_method :find_by_id, :find
9
18
 
19
+ def update(id, attributes)
20
+ hash = request.put("customers/#{id}", { customer: attributes }).parse
21
+
22
+ block_given? ? yield(hash) : build(hash)
23
+ end
24
+
25
+ def create(attributes)
26
+ attributes.transform_keys!(&:to_sym)
27
+ password = attributes.delete :password
28
+ hash = request.post("customers", {
29
+ customer: attributes,
30
+ password: password
31
+ }).parse
32
+ build(hash)
33
+ end
34
+
10
35
  def find_by_token(token)
11
36
  user_request = Request.new(token: token)
12
37
  customer_hash = user_request.get('customers/me').parse
13
38
  build(customer_hash)
14
39
  end
40
+
41
+ def find(id)
42
+ hash = request.get("customers/#{id}").parse
43
+ build(hash)
44
+ end
15
45
  end
16
46
  end
17
47
  end
@@ -7,13 +7,17 @@ module Magento
7
7
  include Magento::ModelParser
8
8
 
9
9
  def save
10
- self.class.update(send(self.class.primary_key), to_h)
10
+ self.class.update(send(self.class.primary_key), to_h) do |hash|
11
+ update_attributes(hash)
12
+ end
11
13
  end
12
14
 
13
15
  def update(attrs)
14
16
  raise "#{self.class.name} not saved" if send(self.class.primary_key).nil?
15
17
 
16
- self.class.update(send(self.class.primary_key), attrs)
18
+ self.class.update(send(self.class.primary_key), attrs) do |hash|
19
+ update_attributes(hash)
20
+ end
17
21
  end
18
22
 
19
23
  def delete
@@ -24,10 +28,15 @@ module Magento
24
28
  @id || send(self.class.primary_key)
25
29
  end
26
30
 
31
+ protected def update_attributes(hash)
32
+ ModelMapper.map_hash(self, hash)
33
+ end
34
+
27
35
  class << self
28
36
  extend Forwardable
29
37
 
30
- def_delegators :query, :all, :page, :per, :page_size, :order, :select, :where
38
+ def_delegators :query, :all, :page, :per, :page_size, :order, :select,
39
+ :where, :first, :find_by, :count
31
40
 
32
41
  def find(id)
33
42
  hash = request.get("#{api_resource}/#{id}").parse
@@ -47,7 +56,8 @@ module Magento
47
56
  def update(id, attributes)
48
57
  body = { entity_key => attributes }
49
58
  hash = request.put("#{api_resource}/#{id}", body).parse
50
- build(hash)
59
+
60
+ block_given? ? yield(hash) : build(hash)
51
61
  end
52
62
 
53
63
  def api_resource
@@ -17,9 +17,12 @@ module Magento
17
17
  end
18
18
 
19
19
  def self.map_hash(model, values)
20
- object = model.new
20
+ object = model.is_a?(Class) ? model.new : model
21
21
  values.each do |key, value|
22
- object.singleton_class.instance_eval { attr_accessor key }
22
+ unless object.respond_to?(key) && object.respond_to?("#{key}=")
23
+ object.singleton_class.instance_eval { attr_accessor key }
24
+ end
25
+
23
26
  if value.is_a?(Hash)
24
27
  class_name = Magento.inflector.camelize(Magento.inflector.singularize(key))
25
28
  value = map_hash(Object.const_get("Magento::#{class_name}"), value)
@@ -12,7 +12,9 @@ module Magento
12
12
  def update(attrs)
13
13
  raise "'entity_id' not found" if @entity_id.nil?
14
14
 
15
- self.class.update(@entity_id, attrs)
15
+ self.class.update(@entity_id, attrs) do |hash|
16
+ update_attributes(hash)
17
+ end
16
18
  end
17
19
 
18
20
  def cancel
@@ -63,7 +65,7 @@ module Magento
63
65
  def update(entity_id, attributes)
64
66
  attributes[:entity_id] = entity_id
65
67
  hash = request.put('orders/create', { entity_key => attributes }).parse
66
- build(hash)
68
+ block_given? ? yield(hash) : build(hash)
67
69
  end
68
70
 
69
71
  # @return {Boolean}
@@ -0,0 +1,9 @@
1
+ require 'dry/struct'
2
+
3
+ module Magento
4
+ module Params
5
+ module Type
6
+ include Dry.Types()
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Magento
4
+ module Params
5
+ class CreateCategoria < Dry::Struct
6
+ attribute :name, Type::String
7
+ attribute :parent_id, Type::String.optional
8
+ attribute :path, Type::String.optional
9
+ attribute :is_active, Type::Bool.default(true)
10
+
11
+ def to_h
12
+ {
13
+ "name": name,
14
+ "parent_id": parent_id,
15
+ "is_active": is_active,
16
+ "path": path
17
+ }
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Magento
4
+ module Params
5
+ class CreateCustomAttribute < Dry::Struct
6
+ attribute :code, Type::String
7
+ attribute :value, Type::String
8
+
9
+ def to_h
10
+ {
11
+ "attribute_code": code,
12
+ "value": value
13
+ }
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'open-uri'
4
+ require 'mini_magick'
5
+
6
+ module Magento
7
+ module Params
8
+ class CreateImage < Dry::Struct
9
+ VARIANTS = {
10
+ 'large' => { size: '800x800', type: :image },
11
+ 'medium' => { size: '300x300', type: :small_image },
12
+ 'small' => { size: '100x100', type: :thumbnail }
13
+ }.freeze
14
+
15
+ attribute :title, Type::String
16
+ attribute :path, Type::String
17
+ attribute :position, Type::Integer
18
+ attribute :size, Type::String.default('large').enum(*VARIANTS.keys)
19
+ attribute :disabled, Type::Bool.default(false)
20
+ attribute :main, Type::Bool.default(false)
21
+
22
+ def to_h
23
+ {
24
+ "disabled": disabled,
25
+ "media_type": 'image',
26
+ "label": title,
27
+ "position": position,
28
+ "content": {
29
+ "base64_encoded_data": base64,
30
+ "type": mini_type,
31
+ "name": filename
32
+ },
33
+ "types": main ? [VARIANTS[size][:type]] : []
34
+ }
35
+ end
36
+
37
+ def variants
38
+ VARIANTS.keys.map do |size|
39
+ CreateImage.new(attributes.merge(size: size, disabled: size != :large))
40
+ end
41
+ end
42
+
43
+ private
44
+
45
+ def base64
46
+ Base64.strict_encode64(File.open(file.path).read).to_s
47
+ end
48
+
49
+ def file
50
+ @file ||= MiniMagick::Image.open(path).tap do |b|
51
+ b.resize VARIANTS[size][:size]
52
+ b.strip
53
+ end
54
+ end
55
+
56
+ def filename
57
+ title.parameterize
58
+ end
59
+
60
+ def mini_type
61
+ file.mime_type
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,147 @@
1
+ require_relative 'create_image'
2
+ require_relative 'create_custom_attribute'
3
+
4
+ module Magento
5
+ module Params
6
+ # Example
7
+ #
8
+ # params = Magento::Params::CreateProduct.new(
9
+ # sku: '556-teste-builder',
10
+ # name: 'REFRIGERANTE PET COCA-COLA 1,5L ORIGINAL',
11
+ # description: 'Descrição do produto',
12
+ # brand: 'Coca-Cola',
13
+ # price: 4.99,
14
+ # special_price: 3.49,
15
+ # quantity: 2,
16
+ # weight: 0.3,
17
+ # attribute_set_id: 4,
18
+ # images: [
19
+ # *Magento::Params::CreateImage.new(
20
+ # path: 'https://urltoimage.com/image.jpg',
21
+ # title: 'REFRIGERANTE PET COCA-COLA 1,5L ORIGINAL',
22
+ # position: 0,
23
+ # main: true
24
+ # ).variants, # it's generate all variants thumbnail => '100x100', small_image => '300x300' and image => '800x800'
25
+ # Magento::Params::CreateImage.new(
26
+ # path: '/path/to/image.jpg',
27
+ # title: 'REFRIGERANTE PET COCA-COLA 1,5L ORIGINAL',
28
+ # position: 1
29
+ # )
30
+ # ]
31
+ # )
32
+ #
33
+ # Magento::Product.create(params.to_h)
34
+ #
35
+ class CreateProduct < Dry::Struct
36
+ ProductTypes = Type::String.default('simple'.freeze).enum(
37
+ 'simple',
38
+ 'bundle',
39
+ 'configurable',
40
+ 'downloadable',
41
+ 'grouped',
42
+ 'Virtual'
43
+ )
44
+
45
+ Visibilities = Type::String.default('catalog_and_search'.freeze).enum(
46
+ 'not_visible_individually' => 1,
47
+ 'catalog' => 1,
48
+ 'search' => 3,
49
+ 'catalog_and_search' => 4
50
+ )
51
+
52
+ Statuses = Type::String.default('enabled'.freeze).enum('enabled' => 1, 'disabled' => 2)
53
+
54
+ attribute :sku, Type::String
55
+ attribute :name, Type::String
56
+ attribute :description, Type::String
57
+ attribute :brand, Type::String
58
+ attribute :price, Type::Coercible::Float
59
+ attribute :special_price, Type::Float.optional.default(nil)
60
+ attribute :attribute_set_id, Type::Integer
61
+ attribute :status, Statuses
62
+ attribute :visibility, Visibilities
63
+ attribute :type_id, ProductTypes
64
+ attribute :weight, Type::Coercible::Float
65
+ attribute :quantity, Type::Coercible::Float
66
+ attribute :featured, Type::String.default('0'.freeze).enum('0', '1')
67
+ attribute :is_qty_decimal, Type::Bool.default(false)
68
+ attribute :manage_stock, Type::Bool.default(true)
69
+ attribute :category_ids, Type::Array.of(Type::Integer).default([].freeze)
70
+ attribute :images, Type::Array.of(Type::Instance(CreateImage)).default([].freeze)
71
+ attribute :website_ids, Type::Array.of(Type::Integer).default([0].freeze)
72
+ attribute :custom_attributes, Type::Array.default([], shared: true) do
73
+ attribute :attribute_code, Type::String
74
+ attribute :value, Type::Coercible::String
75
+ end
76
+
77
+ alias orig_custom_attributes custom_attributes
78
+
79
+ def to_h
80
+ {
81
+ sku: sku,
82
+ name: name.titlecase,
83
+ price: price,
84
+ status: Statuses.mapping[status],
85
+ visibility: Visibilities.mapping[visibility],
86
+ type_id: type_id,
87
+ weight: weight,
88
+ attribute_set_id: attribute_set_id,
89
+ extension_attributes: {
90
+ website_ids: website_ids,
91
+ category_links: categories,
92
+ stock_item: stock
93
+ },
94
+ media_gallery_entries: images.map(&:to_h),
95
+ custom_attributes: custom_attributes.map(&:to_h)
96
+ }
97
+ end
98
+
99
+ def stock
100
+ {
101
+ qty: quantity,
102
+ is_in_stock: quantity.to_i > 0,
103
+ is_qty_decimal: is_qty_decimal,
104
+ show_default_notification_message: false,
105
+ use_config_min_qty: true,
106
+ min_qty: 1,
107
+ use_config_min_sale_qty: 0,
108
+ min_sale_qty: 0,
109
+ use_config_max_sale_qty: true,
110
+ max_sale_qty: 0,
111
+ use_config_backorders: true,
112
+ backorders: 0,
113
+ use_config_notify_stock_qty: true,
114
+ notify_stock_qty: 0,
115
+ use_config_qty_increments: true,
116
+ qty_increments: 0,
117
+ use_config_enable_qty_inc: true,
118
+ enable_qty_increments: true,
119
+ use_config_manage_stock: true,
120
+ manage_stock: manage_stock,
121
+ low_stock_date: 'string',
122
+ is_decimal_divided: is_qty_decimal,
123
+ stock_status_changed_auto: 0
124
+ }
125
+ end
126
+
127
+ def custom_attributes
128
+ default_attributes = [
129
+ CustomAttribute.new(attribute_code: 'description', value: description),
130
+ CustomAttribute.new(attribute_code: 'url_key', value: name.parameterize ),
131
+ CustomAttribute.new(attribute_code: 'product_brand', value: brand ),
132
+ CustomAttribute.new(attribute_code: 'featured', value: featured)
133
+ ]
134
+
135
+ if special_price.to_f > 0
136
+ default_attributes << CustomAttribute.new(attribute_code: 'special_price', value: special_price.to_s)
137
+ end
138
+
139
+ default_attributes + orig_custom_attributes
140
+ end
141
+
142
+ def categories
143
+ category_ids.map { |c| { "category_id": c, "position": 0 } }
144
+ end
145
+ end
146
+ end
147
+ end
@@ -2,6 +2,20 @@ module Magento
2
2
  class Product < Model
3
3
  self.primary_key = :sku
4
4
 
5
+ def method_missing(m)
6
+ attr(m) || super
7
+ end
8
+
9
+ # returns custom_attribute value by custom_attribute code
10
+ # return nil if custom_attribute is not present
11
+ def attr(attribute_code)
12
+ @custom_attributes&.find { |a| a.attribute_code == attribute_code.to_s }&.value
13
+ end
14
+
15
+ def respond_to?(attribute_code)
16
+ super || @custom_attributes&.any? { |a| a.attribute_code == attribute_code.to_s }
17
+ end
18
+
5
19
  class << self
6
20
  alias_method :find_by_sku, :find
7
21
  end
@@ -89,6 +89,18 @@ module Magento
89
89
  RecordCollection.from_magento_response(result, model: model, iterable_field: field)
90
90
  end
91
91
 
92
+ def first
93
+ page_size(1).page(1).all.first
94
+ end
95
+
96
+ def find_by(attributes)
97
+ where(attributes).first
98
+ end
99
+
100
+ def count
101
+ select(:id).page_size(1).page(1).all.total_count
102
+ end
103
+
92
104
  private
93
105
 
94
106
  attr_accessor :current_page, :filter_groups, :request, :sort_orders, :model, :fields
@@ -119,10 +131,11 @@ module Magento
119
131
 
120
132
  def parse_filter(key)
121
133
  patter = /(.*)_([a-z]+)$/
122
- raise 'Invalid format' unless key.match(patter)
123
- raise 'Condition not accepted' unless ACCEPTED_CONDITIONS.include?(key.match(patter)[2])
134
+ match = key.match(patter)
135
+
136
+ return match.to_a[1..2] if match && ACCEPTED_CONDITIONS.include?(match[2])
124
137
 
125
- key.match(patter).to_a[1..2]
138
+ [key, 'eq']
126
139
  end
127
140
 
128
141
  def parse_value_filter(condition, value)