atkhayar-dev-spree-multi-vendor 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +10 -0
  3. data/LICENSE +26 -0
  4. data/README.md +181 -0
  5. data/Rakefile +21 -0
  6. data/app/controllers/spree/admin/vendor_settings_controller.rb +34 -0
  7. data/app/controllers/spree/admin/vendors_controller.rb +68 -0
  8. data/app/controllers/spree/api/v1/vendors_controller.rb +62 -0
  9. data/app/controllers/spree/api/v2/platform/vendors_controller.rb +32 -0
  10. data/app/controllers/spree/api/v2/storefront/vendors_controller.rb +33 -0
  11. data/app/controllers/spree_multi_vendor/spree/admin/base_controller_decorator.rb +10 -0
  12. data/app/controllers/spree_multi_vendor/spree/admin/products_controller_decorator.rb +79 -0
  13. data/app/controllers/spree_multi_vendor/spree/admin/resource_controller_decorator.rb +8 -0
  14. data/app/controllers/spree_multi_vendor/spree/admin/shipping_methods_controller_decorator.rb +7 -0
  15. data/app/controllers/spree_multi_vendor/spree/admin/stock_locations_controller_decorator.rb +7 -0
  16. data/app/controllers/spree_multi_vendor/spree/admin/users_controller_decorator.rb +13 -0
  17. data/app/controllers/spree_multi_vendor/spree/admin/variants_controller_decorator.rb +7 -0
  18. data/app/controllers/spree_multi_vendor/spree/api/base_controller_decorator.rb +17 -0
  19. data/app/controllers/spree_multi_vendor/spree/api/v1/products_controller_decorator.rb +9 -0
  20. data/app/controllers/spree_multi_vendor/spree/api/v1/users_controller_decorator.rb +13 -0
  21. data/app/controllers/spree_multi_vendor/spree/api/v2/platform/products_controller_decorator.rb +65 -0
  22. data/app/finders/spree_multi_vendor/products_finder_decorator.rb +31 -0
  23. data/app/helpers/spree/admin/vendor_helper.rb +15 -0
  24. data/app/helpers/spree/api/vendor_helper.rb +13 -0
  25. data/app/helpers/spree_multi_vendor/spree/api/api_helpers_decorator.rb +19 -0
  26. data/app/mailers/spree/vendor_mailer.rb +15 -0
  27. data/app/models/concerns/spree/vendor_concern.rb +11 -0
  28. data/app/models/spree/order_commission.rb +11 -0
  29. data/app/models/spree/vendor.rb +77 -0
  30. data/app/models/spree/vendor_ability.rb +155 -0
  31. data/app/models/spree/vendor_image.rb +18 -0
  32. data/app/models/spree/vendor_order_totals.rb +28 -0
  33. data/app/models/spree/vendor_user.rb +16 -0
  34. data/app/models/spree_multi_vendor/configuration.rb +10 -0
  35. data/app/models/spree_multi_vendor/spree/ability_decorator.rb +9 -0
  36. data/app/models/spree_multi_vendor/spree/base_decorator.rb +13 -0
  37. data/app/models/spree_multi_vendor/spree/line_item_decorator.rb +48 -0
  38. data/app/models/spree_multi_vendor/spree/order_decorator.rb +107 -0
  39. data/app/models/spree_multi_vendor/spree/shipment_decorator.rb +8 -0
  40. data/app/models/spree_multi_vendor/spree/stock/package_decorator.rb +14 -0
  41. data/app/models/spree_multi_vendor/spree/user_decorator.rb +8 -0
  42. data/app/models/spree_multi_vendor/spree/variant_decorator.rb +40 -0
  43. data/app/overrides/spree/admin/orders/_form.rb +13 -0
  44. data/app/overrides/spree/admin/orders/_line_items.rb +6 -0
  45. data/app/overrides/spree/admin/orders/_line_items_edit_form.rb +6 -0
  46. data/app/overrides/spree/admin/orders/index.rb +6 -0
  47. data/app/overrides/spree/admin/products/form.rb +29 -0
  48. data/app/overrides/spree/admin/products/new.rb +16 -0
  49. data/app/overrides/spree/admin/shared/_main_menu.rb +23 -0
  50. data/app/overrides/spree/admin/shared/_order_summary.rb +35 -0
  51. data/app/overrides/spree/admin/users/_form.rb +13 -0
  52. data/app/serializers/spree/api/v2/platform/vendor_serializer.rb +12 -0
  53. data/app/serializers/spree/v2/storefront/vendor_image_serializer.rb +11 -0
  54. data/app/serializers/spree/v2/storefront/vendor_order_totals_serializer.rb +20 -0
  55. data/app/serializers/spree/v2/storefront/vendor_serializer.rb +15 -0
  56. data/app/serializers/spree_multi_vendor/cart_serializer_decorator.rb +19 -0
  57. data/app/serializers/spree_multi_vendor/serializer_decorator.rb +23 -0
  58. data/app/services/spree/orders/generate_commissions.rb +34 -0
  59. data/app/views/spree/admin/translations/vendor.html.erb +21 -0
  60. data/app/views/spree/admin/vendor_settings/edit.html.erb +47 -0
  61. data/app/views/spree/admin/vendors/_form.html.erb +36 -0
  62. data/app/views/spree/admin/vendors/edit.html.erb +11 -0
  63. data/app/views/spree/admin/vendors/index.html.erb +83 -0
  64. data/app/views/spree/admin/vendors/new.html.erb +11 -0
  65. data/app/views/spree/api/v1/vendors/index.rabl +3 -0
  66. data/app/views/spree/api/v1/vendors/show.rabl +2 -0
  67. data/app/views/spree/vendor_mailer/vendor_notification_email.html.erb +81 -0
  68. data/app/views/spree/vendor_mailer/vendor_notification_email.text.erb +36 -0
  69. data/bin/rails +7 -0
  70. data/config/initializers/spree.rb +31 -0
  71. data/config/initializers/spree_permitted_attributes.rb +12 -0
  72. data/config/locales/en.yml +34 -0
  73. data/config/locales/es.yml +36 -0
  74. data/config/locales/fr.yml +33 -0
  75. data/config/routes.rb +29 -0
  76. data/db/default/vendors.rb +13 -0
  77. data/db/migrate/20170406102250_create_spree_vendors.rb +9 -0
  78. data/db/migrate/20170406102944_add_vendor_id_to_spree_models.rb +10 -0
  79. data/db/migrate/20170410111150_add_state_to_vendors.rb +6 -0
  80. data/db/migrate/20170412124925_add_deleted_at_to_spree_vendors.rb +6 -0
  81. data/db/migrate/20170413094447_create_spree_vendor_users.rb +11 -0
  82. data/db/migrate/20190212161426_add_slug_to_spree_vendors.rb +6 -0
  83. data/db/migrate/20190214120226_add_about_us_to_spree_vendors.rb +5 -0
  84. data/db/migrate/20190214142526_add_contact_us_to_spree_vendors.rb +5 -0
  85. data/db/migrate/20190305120337_add_commission_to_spree_vendors.rb +5 -0
  86. data/db/migrate/20190308091546_add_priority_to_vendor.rb +8 -0
  87. data/db/migrate/20190312130754_create_spree_order_commissions.rb +14 -0
  88. data/db/migrate/20190322133128_add_notification_email_to_vendors.rb +5 -0
  89. data/db/migrate/20200416030721_add_translations_to_vendor.rb +19 -0
  90. data/db/migrate/20210728120649_change_vendorized_models_vendor_id_type.rb +10 -0
  91. data/db/migrate/20210728120910_change_vendor_id_and_user_id_type_for_spree_vendor_users.rb +14 -0
  92. data/db/migrate/20210728121024_change_vendor_id_and_order_id_type_for_spree_order_commissions.rb +14 -0
  93. data/db/migrate/20221123095256_add_code_to_vendors.rb +6 -0
  94. data/lib/generators/spree_multi_vendor/install/install_generator.rb +21 -0
  95. data/lib/generators/spree_multi_vendor/mailers_preview/mailers_preview_generator.rb +22 -0
  96. data/lib/generators/spree_multi_vendor/mailers_preview/templates/mailers/previews/vendor_notification_preview.rb +5 -0
  97. data/lib/spree_multi_vendor/engine.rb +24 -0
  98. data/lib/spree_multi_vendor/factories.rb +7 -0
  99. data/lib/spree_multi_vendor/version.rb +11 -0
  100. data/lib/spree_multi_vendor.rb +16 -0
  101. data/lib/tasks/vendors.rake +8 -0
  102. data/spec/factories/spree/vendor_factory.rb +28 -0
  103. data/spec/factories/spree/vendor_image_factory.rb +5 -0
  104. data/spec/factories/spree/vendor_user_factory.rb +6 -0
  105. data/spec/features/spree/admin/orders_spec.rb +86 -0
  106. data/spec/features/spree/admin/product_spec.rb +177 -0
  107. data/spec/features/spree/admin/shipping_method_spec.rb +72 -0
  108. data/spec/features/spree/admin/stock_location_spec.rb +90 -0
  109. data/spec/features/spree/admin/vendor_settings_spec.rb +74 -0
  110. data/spec/features/spree/admin/vendor_spec.rb +125 -0
  111. data/spec/fixtures/thinking-cat.jpg +0 -0
  112. data/spec/mailers/vendor_mailer_spec.rb +45 -0
  113. data/spec/models/spree/line_item_spec.rb +32 -0
  114. data/spec/models/spree/order_commission_spec.rb +8 -0
  115. data/spec/models/spree/order_spec.rb +193 -0
  116. data/spec/models/spree/product_spec.rb +19 -0
  117. data/spec/models/spree/variant_spec.rb +104 -0
  118. data/spec/models/spree/vendor_order_totals_spec.rb +33 -0
  119. data/spec/models/spree/vendor_spec.rb +57 -0
  120. data/spec/models/spree/vendor_user_spec.rb +12 -0
  121. data/spec/requests/spree/api/v1/products_spec.rb +221 -0
  122. data/spec/requests/spree/api/v1/users_spec.rb +31 -0
  123. data/spec/requests/spree/api/v1/vendors_spec.rb +210 -0
  124. data/spec/requests/spree/api/v2/storefront/cart_spec.rb +85 -0
  125. data/spec/requests/spree/api/v2/storefront/products_spec.rb +96 -0
  126. data/spec/requests/spree/api/v2/storefront/vendors_spec.rb +118 -0
  127. data/spec/serializers/spree/v2/storefront/stock_location_serializer_spec.rb +32 -0
  128. data/spec/serializers/spree/v2/storefront/vendor_image_serializer_spec.rb +18 -0
  129. data/spec/serializers/spree/v2/storefront/vendor_order_totals_serializer_spec.rb +54 -0
  130. data/spec/serializers/spree/v2/storefront/vendor_serializer_spec.rb +34 -0
  131. data/spec/spec_helper.rb +13 -0
  132. data/spec/support/shoulda_matchers.rb +6 -0
  133. data/spree_multi_vendor.gemspec +42 -0
  134. metadata +308 -0
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::LineItem do
4
+ let(:vendor) { create(:vendor) }
5
+
6
+ describe '#vendor' do
7
+ context 'vendorized product' do
8
+ let(:product) { create(:product, vendor: vendor) }
9
+ let(:line_item) { build(:line_item, product: product, variant: product.default_variant) }
10
+
11
+ it { expect(line_item.vendor).not_to be_nil }
12
+ it { expect(line_item.vendor).to eq(product.vendor) }
13
+ end
14
+
15
+ context 'vendorized variant' do
16
+ let(:product) { create(:product) }
17
+ let(:variant) { create(:variant, vendor: vendor, product: product) }
18
+ let(:line_item) { build(:line_item, product: product, variant: variant) }
19
+
20
+ before do
21
+ SpreeMultiVendor::Config[:vendorized_models] = ['variant']
22
+ end
23
+
24
+ after do
25
+ SpreeMultiVendor::Config[:vendorized_models] = SpreeMultiVendor::Configuration::DEFAULT_VENDORIZED_MODELS
26
+ end
27
+
28
+ it { expect(line_item.vendor).not_to be_nil }
29
+ it { expect(line_item.vendor).to eq(variant.vendor) }
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,8 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::OrderCommission do
4
+ describe 'associations' do
5
+ it { is_expected.to belong_to(:order) }
6
+ it { is_expected.to belong_to(:vendor) }
7
+ end
8
+ end
@@ -0,0 +1,193 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::Order do
4
+ let(:vendor) { create(:vendor, name: 'Vendor 1') }
5
+ let(:vendor_2) { create(:vendor, name: 'Vendor 2') }
6
+
7
+ let(:order) { create(:order_with_line_items, state: 'payment', line_items_count: 6, line_items_price: 100, shipment_cost: 50) }
8
+
9
+ before do
10
+ order.line_items.each_with_index do |li, idx|
11
+ product = li.product
12
+ product.vendor = idx.odd? ? vendor : vendor_2
13
+ product.save
14
+ end
15
+ end
16
+
17
+ context 'associations' do
18
+ it { is_expected.to have_many(:commissions) }
19
+ end
20
+
21
+ context 'complete with spree_multi_vendor' do
22
+ before { allow(order).to receive_messages payment_required?: false }
23
+
24
+ it 'generates commission records' do
25
+ expect do
26
+ order.next!
27
+ order.reload
28
+ end.to change { Spree::OrderCommission.count }.by(2).and change { vendor.commissions.count }.by(1)
29
+ .and change { vendor_2.commissions.count }.by(1)
30
+
31
+ order.line_items.includes(product: :vendor).group_by { |li| li.product.vendor }.each do |v, line_items|
32
+ commission_ammount = v.commission_rate * line_items.pluck(:pre_tax_amount).sum.to_f / 100
33
+
34
+ expect(v.commissions.sum(:amount)).to eq(commission_ammount)
35
+ end
36
+ end
37
+
38
+ it 'sends mails only once per vendor' do
39
+ mail_double_1 = double(:mail_double_1)
40
+ mail_double_2 = double(:mail_double_1)
41
+
42
+ expect(Spree::VendorMailer).to receive(:vendor_notification_email)
43
+ .with(be_an(Integer), vendor.id) { mail_double_1 }
44
+ expect(mail_double_1).to receive(:deliver_later)
45
+ expect(Spree::VendorMailer).to receive(:vendor_notification_email)
46
+ .with(be_an(Integer), vendor_2.id) { mail_double_2 }
47
+ expect(mail_double_2).to receive(:deliver_later)
48
+
49
+ order.next!
50
+ end
51
+ end
52
+
53
+ context 'vendor methods' do
54
+ let!(:shipment) { create(:shipment, order: order, cost: 20, pre_tax_amount: 20, stock_location: vendor.stock_locations.first) }
55
+ let!(:promotion) { create(:promotion_with_item_adjustment, adjustment_rate: 10, code: '10off') }
56
+
57
+ before do
58
+ order.coupon_code = '10off'
59
+ Spree::PromotionHandler::Coupon.new(order).apply
60
+ order.reload
61
+ end
62
+
63
+ describe '#vendor_line_items' do
64
+ it { expect(order.vendor_line_items(vendor)).to eq(order.line_items.for_vendor(vendor)) }
65
+ it { expect(order.vendor_line_items(vendor).count).to eq(3) }
66
+ end
67
+
68
+ describe '#vendor_shipments' do
69
+ it { expect(order.vendor_shipments(vendor).to_a).to eq([shipment]) }
70
+ it { expect(order.vendor_shipments(vendor).count).to eq(1) }
71
+ end
72
+
73
+ describe '#vendor_subtotal' do
74
+ it { expect(order.vendor_subtotal(vendor)).to eq(320) }
75
+ end
76
+
77
+ describe '#display_subtotal' do
78
+ it { expect(order.display_vendor_subtotal(vendor).to_s).to eq('$320.00') }
79
+ end
80
+
81
+ describe '#vendor_pre_tax_item_amount' do
82
+ it { expect(order.vendor_pre_tax_item_amount(vendor)).to eq(300) }
83
+ end
84
+
85
+ describe '#displayvendor_pre_tax_item_amount' do
86
+ it { expect(order.display_vendor_pre_tax_item_amount(vendor).to_s).to eq('$300.00') }
87
+ end
88
+
89
+ describe '#vendor_pre_tax_total' do
90
+ it { expect(order.vendor_pre_tax_total(vendor)).to eq(320) }
91
+ end
92
+
93
+ describe '#displayvendor_pre_tax_total' do
94
+ it { expect(order.display_vendor_pre_tax_total(vendor).to_s).to eq('$320.00') }
95
+ end
96
+
97
+ context 'ship total' do
98
+ before do
99
+ order.vendor_shipments(vendor).update_all(adjustment_total: 5)
100
+ end
101
+
102
+ describe '#vendor_ship_total' do
103
+ it { expect(order.vendor_ship_total(vendor)).to eq(25) }
104
+ end
105
+
106
+ describe '#display_vendor_ship_total' do
107
+ it { expect(order.display_vendor_ship_total(vendor).to_s).to eq('$25.00') }
108
+ end
109
+
110
+ describe '#vendor_pre_tax_ship_amount' do
111
+ it { expect(order.vendor_pre_tax_ship_amount(vendor)).to eq(20) }
112
+ end
113
+
114
+ describe '#display_vendor_pre_tax_ship_amount' do
115
+ it { expect(order.display_vendor_pre_tax_ship_amount(vendor).to_s).to eq('$20.00') }
116
+ end
117
+ end
118
+
119
+ describe '#vendor_promo_total' do
120
+ it { expect(order.vendor_promo_total(vendor)).to eq(-30) }
121
+ end
122
+
123
+ describe '#display_vendor_promo_total' do
124
+ it { expect(order.display_vendor_promo_total(vendor).to_s).to eq('-$30.00') }
125
+ end
126
+
127
+ context 'additional tax' do
128
+ before do
129
+ order.vendor_line_items(vendor).update_all(additional_tax_total: 10)
130
+ order.vendor_shipments(vendor).update_all(additional_tax_total: 1)
131
+ end
132
+
133
+ describe '#vendor_additional_tax_total' do
134
+ it { expect(order.vendor_additional_tax_total(vendor)).to eq(31) }
135
+ end
136
+
137
+ describe '#display_vendor_additional_tax_total' do
138
+ it { expect(order.display_vendor_additional_tax_total(vendor).to_s).to eq('$31.00') }
139
+ end
140
+ end
141
+
142
+ context 'included tax' do
143
+ before do
144
+ order.vendor_line_items(vendor).update_all(included_tax_total: 5)
145
+ order.vendor_shipments(vendor).update_all(included_tax_total: 1)
146
+ end
147
+
148
+ describe '#vendor_included_tax_total' do
149
+ it { expect(order.vendor_included_tax_total(vendor)).to eq(16) }
150
+ end
151
+
152
+ describe '#display_vendor_included_tax_total' do
153
+ it { expect(order.display_vendor_included_tax_total(vendor).to_s).to eq('$16.00') }
154
+ end
155
+ end
156
+
157
+ describe '#vendor_total' do
158
+ it { expect(order.vendor_total(vendor)).to eq(290) }
159
+ end
160
+
161
+ describe '#display_vendor_total' do
162
+ it { expect(order.display_vendor_total(vendor).to_s).to eq('$290.00') }
163
+
164
+ context 'respects currency' do
165
+ before { order.currency = 'GBP' }
166
+
167
+ it { expect(order.display_vendor_total(vendor).to_s).to eq('£290.00') }
168
+ end
169
+ end
170
+
171
+ describe '#vendor_item_count' do
172
+ before { order.vendor_line_items(vendor).last.update(quantity: 2) }
173
+
174
+ it { expect(order.vendor_item_count(vendor)).to eq(4) }
175
+ end
176
+
177
+ describe '#vendor_ids' do
178
+ it { expect(order.vendor_ids).to match_array([vendor.id, vendor_2.id]) }
179
+
180
+ context 'when a line item does not belong to any vendor' do
181
+ before { order.vendor_line_items(vendor_2).each { |li| li.product.update_columns(vendor_id: nil) } }
182
+ it { expect(order.vendor_ids).to match_array([vendor.id]) }
183
+ end
184
+ end
185
+
186
+ describe '#vendor_totals' do
187
+ it { expect(order.vendor_totals).to be_kind_of(Array) }
188
+ it { expect(order.vendor_totals).not_to be_empty }
189
+ it { expect(order.vendor_totals.first.order).to eq(order) }
190
+ it { expect(order.vendor_totals.map(&:vendor)).to match_array([vendor, vendor_2]) }
191
+ end
192
+ end
193
+ end
@@ -0,0 +1,19 @@
1
+ require 'timecop'
2
+
3
+ describe Spree::Product do
4
+ let(:vendor) { create(:vendor) }
5
+ let(:product) { create(:product, vendor: vendor) }
6
+
7
+ context '.whitelisted_ransackable_associations' do
8
+ it { expect(described_class.whitelisted_ransackable_associations).to include('vendor') }
9
+ it { expect(described_class.whitelisted_ransackable_associations.count).to be > 1 }
10
+ end
11
+
12
+ it 'touches vendor after update' do
13
+ time = Time.current + 1.hour
14
+ product
15
+ Timecop.freeze(time) do
16
+ expect { product.touch }.to change { vendor.reload.updated_at }
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,104 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::Variant do
4
+ let(:vendor) { create :vendor }
5
+ let(:other_vendor) { create :vendor, name: 'Other vendor' }
6
+ let(:product) { create(:product, vendor: vendor) }
7
+ subject { build(:variant, product: product) }
8
+
9
+ context 'after create' do
10
+ it "propagate to stock items only for Vendor's Stock Locations" do
11
+ expect { create(:variant, product: product) }.to change {
12
+ Spree::StockItem.where(stock_location: vendor.stock_locations.first).count
13
+ }
14
+ end
15
+
16
+ it "doesn't propagate to stock items for other vendors Stock Locations" do
17
+ expect { create(:variant, product: product) }.to_not change {
18
+ Spree::StockItem.where(stock_location: other_vendor.stock_locations.first).count
19
+ }
20
+ end
21
+ end
22
+
23
+ describe '#assign_vendor' do
24
+ context 'product vendorized, variant vendorized' do
25
+ it 'copies from product' do
26
+ expect(subject.attributes['vendor_id']).to be_nil
27
+ subject.save!
28
+ expect(subject.reload.attributes['vendor_id']).to eq(product.vendor_id)
29
+ end
30
+ end
31
+
32
+ context 'product not vendorized, variant vendorized' do
33
+ before do
34
+ SpreeMultiVendor::Config[:vendorized_models] = ['variant']
35
+ end
36
+
37
+ after do
38
+ SpreeMultiVendor::Config[:vendorized_models] = SpreeMultiVendor::Configuration::DEFAULT_VENDORIZED_MODELS
39
+ end
40
+
41
+ context 'no vendor' do
42
+ let(:product) { create(:product) }
43
+
44
+ it { expect { subject.save! }.not_to change { subject.vendor }.from(nil) }
45
+ end
46
+
47
+ context 'vendor assigned' do
48
+ subject { build(:variant, product: product, vendor: vendor) }
49
+
50
+ it { expect { subject.save! }.not_to change { subject.vendor }.from(vendor) }
51
+ end
52
+ end
53
+
54
+ context 'product vendorized, variant not vendorized' do
55
+ before do
56
+ SpreeMultiVendor::Config[:vendorized_models] = ['product']
57
+ end
58
+
59
+ after do
60
+ SpreeMultiVendor::Config[:vendorized_models] = SpreeMultiVendor::Configuration::DEFAULT_VENDORIZED_MODELS
61
+ end
62
+
63
+ it { expect { subject.save! }.not_to raise_error }
64
+ end
65
+ end
66
+
67
+ describe '#vendor' do
68
+ context 'product vendorized, variant vendorized' do
69
+ before { subject.save! }
70
+
71
+ it { expect(subject.vendor).to eq(product.vendor) }
72
+ end
73
+
74
+ context 'product not vendorized, variant vendorized' do
75
+ before do
76
+ SpreeMultiVendor::Config[:vendorized_models] = ['variant']
77
+ end
78
+
79
+ after do
80
+ SpreeMultiVendor::Config[:vendorized_models] = SpreeMultiVendor::Configuration::DEFAULT_VENDORIZED_MODELS
81
+ end
82
+
83
+ let(:product) { create(:product) }
84
+ subject { build(:variant, product: product, vendor: vendor) }
85
+
86
+ it { expect(subject.vendor).to eq(vendor) }
87
+ end
88
+
89
+ context 'product vendorized, variant not vendorized' do
90
+ before do
91
+ SpreeMultiVendor::Config[:vendorized_models] = ['product']
92
+ subject.save!
93
+ end
94
+
95
+ after do
96
+ SpreeMultiVendor::Config[:vendorized_models] = SpreeMultiVendor::Configuration::DEFAULT_VENDORIZED_MODELS
97
+ end
98
+
99
+ it { expect(subject.vendor).to eq(product.vendor) }
100
+ end
101
+ end
102
+
103
+ it { expect(described_class.whitelisted_ransackable_associations).to include('vendor') }
104
+ end
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::VendorOrderTotals do
4
+ let(:vendor) { create(:vendor) }
5
+ let(:order) { create(:completed_order_with_totals, line_items_count: 6, line_items_price: 100, shipment_cost: 50) }
6
+ let!(:shipment) { create(:shipment, order: order, cost: 20, stock_location: vendor.stock_locations.first) }
7
+ let!(:promotion) { create(:promotion_with_item_adjustment, adjustment_rate: 10, code: '10off') }
8
+
9
+ subject { described_class.new(order: order, vendor: vendor) }
10
+
11
+ before do
12
+ order.products.update_all(vendor_id: vendor.id)
13
+ order.vendor_line_items(vendor).update_all(additional_tax_total: 10)
14
+ order.vendor_line_items(vendor).update_all(included_tax_total: 5)
15
+ order.coupon_code = '10off'
16
+ Spree::PromotionHandler::Coupon.new(order).apply
17
+ order.reload
18
+ end
19
+
20
+ Spree::VendorOrderTotals::METHOD_NAMES.each do |method_name|
21
+ describe "##{method_name}" do
22
+ it { expect(subject.send(method_name)).to eq(order.send("vendor_#{method_name}", vendor)) }
23
+ end
24
+
25
+ describe "#display_#{method_name}" do
26
+ it { expect(subject.send("display_#{method_name}")).to eq(order.send("display_vendor_#{method_name}", vendor)) }
27
+ end
28
+ end
29
+
30
+ describe '#item_count' do
31
+ it { expect(subject.item_count).to eq(order.vendor_item_count(vendor)) }
32
+ end
33
+ end
@@ -0,0 +1,57 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::Vendor do
4
+ describe 'associations' do
5
+ it { is_expected.to have_many(:commissions) }
6
+ it { is_expected.to have_many(:products) }
7
+ it { is_expected.to have_many(:shipping_methods) }
8
+ it { is_expected.to have_many(:stock_locations) }
9
+ it { is_expected.to have_many(:variants) }
10
+ it { is_expected.to have_many(:vendor_users) }
11
+ it { is_expected.to have_many(:users).through(:vendor_users) }
12
+ it { is_expected.to have_one(:image) }
13
+ end
14
+
15
+ describe 'validations' do
16
+ it { is_expected.to validate_presence_of(:name) }
17
+ it { is_expected.to allow_value('flower').for(:name) }
18
+ it { is_expected.to allow_value('flower455').for(:name) }
19
+ it { is_expected.to allow_value('flower 455').for(:name) }
20
+ it { is_expected.to allow_value('flower abc').for(:name) }
21
+ it { is_expected.to allow_value('flower455 abc').for(:name) }
22
+ it { is_expected.to allow_value('flower 455 abc').for(:name) }
23
+ it { is_expected.to allow_value('flower & bucket').for(:name) }
24
+ end
25
+
26
+ describe 'initial state' do
27
+ it 'initial state should be pending' do
28
+ should be_pending
29
+ end
30
+ end
31
+
32
+ describe 'after_create' do
33
+ let!(:vendor) { build(:vendor) }
34
+
35
+ it 'creates a stock location with default country' do
36
+ expect { vendor.save! }.to change(Spree::StockLocation, :count).by(1)
37
+ stock_location = Spree::StockLocation.last
38
+ expect(vendor.stock_locations.first).to eq stock_location
39
+ expect(stock_location.country).to eq Spree::Country.default
40
+ end
41
+
42
+ it 'should act as list' do
43
+ expect(vendor).to respond_to(:set_list_position)
44
+ end
45
+ end
46
+
47
+ describe 'after_update' do
48
+ let!(:vendor) { create(:vendor) }
49
+
50
+ it 'updates stock_location names when vendor name changed' do
51
+ old_name = vendor.name
52
+ new_name = 'new vendor name'
53
+ vendor.name = new_name
54
+ expect { vendor.save! }.to change { vendor.stock_locations.pluck(:name).uniq }.from([old_name]).to([new_name])
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::VendorUser do
4
+ describe 'associations' do
5
+ it { is_expected.to belong_to(:vendor) }
6
+ it { is_expected.to belong_to(:user) }
7
+ end
8
+
9
+ describe 'validations' do
10
+ it { is_expected.to validate_uniqueness_of(:vendor_id).scoped_to(:user_id) }
11
+ end
12
+ end
@@ -0,0 +1,221 @@
1
+ require 'spec_helper'
2
+
3
+ module Spree
4
+ describe 'API V1 Products Spec', type: :request do
5
+ let(:vendor) { create(:vendor, state: :active) }
6
+ let(:other_vendor) { create(:vendor) }
7
+ let(:shipping_category) { create(:shipping_category) }
8
+ let(:selected_product) { product_without_vendor }
9
+ let(:params) { {} }
10
+
11
+ let!(:product_without_vendor) { create(:product) }
12
+ let!(:product_with_vendor_vendor) { create(:product, vendor: vendor) }
13
+ let!(:product_with_other_vendor) { create(:product, vendor: other_vendor) }
14
+
15
+ before { stub_authentication! }
16
+
17
+ describe 'products#index' do
18
+ before { get '/api/v1/products', params: params }
19
+
20
+ context 'as a regular user' do
21
+ it 'gets a list of all products' do
22
+ expect(response.status).to eq(200)
23
+ expect(json_response[:products].size).to eq(3)
24
+ end
25
+ end
26
+
27
+ context 'as an admin' do
28
+ sign_in_as_admin!
29
+
30
+ it 'gets a list of all products' do
31
+ expect(response.status).to eq(200)
32
+ expect(json_response[:products].size).to eq(3)
33
+ end
34
+ end
35
+
36
+ context 'as a vendor' do
37
+ let!(:current_api_user) do
38
+ user = create(:user, vendors: [vendor])
39
+ user
40
+ end
41
+
42
+ it 'gets a list of products assigned to that vendor' do
43
+ expect(response.status).to eq(200)
44
+ expect(json_response[:products].size).to eq(1)
45
+ expect(json_response[:products][0][:id]).to eq(vendor.products.first.id)
46
+ end
47
+ end
48
+ end
49
+
50
+ describe 'products#show' do
51
+ before { get "/api/v1/products/#{selected_product.id}" }
52
+
53
+ it 'gets a product' do
54
+ expect(response.status).to eq(200)
55
+ end
56
+
57
+ context 'as a vendor' do
58
+ let!(:current_api_user) do
59
+ user = create(:user, vendors: [vendor])
60
+ user
61
+ end
62
+
63
+ describe 'cannot view product of another vendor' do
64
+ let(:selected_product) { product_with_other_vendor }
65
+
66
+ it do
67
+ expect(response.status).to eq(404)
68
+ end
69
+ end
70
+
71
+ describe 'can view product assigned to that vendor' do
72
+ let(:selected_product) { product_with_vendor_vendor }
73
+
74
+ it do
75
+ expect(response.status).to eq(200)
76
+ end
77
+ end
78
+ end
79
+ end
80
+
81
+ describe 'products#create' do
82
+ let(:params) do
83
+ {
84
+ product: {
85
+ name: 'Product',
86
+ shipping_category_id: shipping_category.id,
87
+ price: 12.34
88
+ }
89
+ }
90
+ end
91
+
92
+ before { post '/api/v1/products', params: params }
93
+
94
+ context 'as regular user' do
95
+ it 'cannot create a new product' do
96
+ expect(response.status).to eq(401)
97
+ end
98
+ end
99
+
100
+ context 'as an admin' do
101
+ sign_in_as_admin!
102
+
103
+ it 'can create a new product' do
104
+ expect(response.status).to eq(201)
105
+ expect(json_response['name']).to eq(params[:product][:name])
106
+ expect(json_response['price']).to eq(params[:product][:price].to_s)
107
+ expect(json_response['shipping_category_id']).to eq(params[:product][:shipping_category_id])
108
+ end
109
+ end
110
+
111
+ context 'as a vendor' do
112
+ let!(:current_api_user) do
113
+ user = create(:user, vendors: [vendor])
114
+ user
115
+ end
116
+
117
+ it 'can create a new product assigned to vendor' do
118
+ expect(response.status).to eq(201)
119
+ product = Spree::Product.find(json_response[:id])
120
+ expect(product.vendor).to eq(vendor)
121
+ end
122
+ end
123
+ end
124
+
125
+ describe 'products#update' do
126
+ let(:params) do
127
+ {
128
+ product: {
129
+ name: 'Updated Prodouct',
130
+ shipping_category_id: shipping_category.id,
131
+ price: 12.34
132
+ }
133
+ }
134
+ end
135
+
136
+ before { put "/api/v1/products/#{selected_product.id}", params: params }
137
+
138
+ context 'as regular user' do
139
+ it 'cannot update product' do
140
+ expect(response.status).to eq(401)
141
+ end
142
+ end
143
+
144
+ context 'as an admin' do
145
+ sign_in_as_admin!
146
+
147
+ it 'can update product' do
148
+ expect(response.status).to eq(200)
149
+ expect(json_response['name']).to eq(params[:product][:name])
150
+ expect(json_response['price']).to eq(params[:product][:price].to_s)
151
+ expect(json_response['shipping_category_id']).to eq(params[:product][:shipping_category_id])
152
+ end
153
+ end
154
+
155
+ context 'as a vendor' do
156
+ let!(:current_api_user) do
157
+ user = create(:user, vendors: [vendor])
158
+ user
159
+ end
160
+
161
+ describe 'cannot update product of another vendor' do
162
+ let(:selected_product) { product_with_other_vendor }
163
+
164
+ it do
165
+ expect(response.status).to eq(404)
166
+ end
167
+ end
168
+
169
+ describe 'can update product assigned to that vendor' do
170
+ let(:selected_product) { product_with_vendor_vendor }
171
+
172
+ it do
173
+ expect(response.status).to eq(200)
174
+ end
175
+ end
176
+ end
177
+ end
178
+
179
+ describe 'products#destroy' do
180
+ before { delete "/api/v1/products/#{selected_product.id}" }
181
+
182
+ context 'as regular user' do
183
+ it 'cannot delete product' do
184
+ expect(response.status).to eq(401)
185
+ end
186
+ end
187
+
188
+ context 'as an admin' do
189
+ sign_in_as_admin!
190
+
191
+ it 'can delete product' do
192
+ expect(response.status).to eq(204)
193
+ expect(product_without_vendor.reload.deleted_at).to_not eq(nil)
194
+ end
195
+ end
196
+
197
+ context 'as a vendor' do
198
+ let!(:current_api_user) do
199
+ user = create(:user, vendors: [vendor])
200
+ user
201
+ end
202
+
203
+ describe 'cannot delete product of another vendor' do
204
+ let(:selected_product) { product_with_other_vendor }
205
+
206
+ it do
207
+ expect(response.status).to eq(404)
208
+ end
209
+ end
210
+
211
+ describe 'can delete product assigned to that vendor' do
212
+ let(:selected_product) { product_with_vendor_vendor }
213
+
214
+ it do
215
+ expect(response.status).to eq(204)
216
+ end
217
+ end
218
+ end
219
+ end
220
+ end
221
+ end