From a8646e0409cf24b26aabbd2d0640035da1bbf94e Mon Sep 17 00:00:00 2001 From: Vasia Ivanyshak Date: Wed, 9 Jun 2021 18:06:23 +0300 Subject: [PATCH 1/3] Shop Cart implementation - Order and OrderItem - Cart list page :rocket: --- Gemfile | 1 + Gemfile.lock | 3 ++ app/assets/stylesheets/app/application.scss | 1 + app/assets/stylesheets/app/base/_base.scss | 25 +++++++++++ app/helpers/admin/events_helper.rb | 2 - app/views/layouts/application.slim | 1 + .../app/controllers/shop/base_controller.rb | 4 ++ .../app/controllers/shop/items_controller.rb | 16 +++++++ .../shop/order_items_controller.rb | 44 +++++++++++++++++++ .../shop/app/helpers/shop/item_helper.rb | 6 +++ components/shop/app/models/shop/item.rb | 1 + components/shop/app/models/shop/order.rb | 25 +++++++++++ components/shop/app/models/shop/order_item.rb | 36 +++++++++++++++ .../shop/app/uploaders/item_image_uploader.rb | 4 +- .../shop/app/views/shop/items/index.slim | 15 ++++++- .../app/views/shop/order_items/index.slim | 32 ++++++++++++++ components/shop/config/routes.rb | 1 + .../migrate/20210611210804_create_orders.rb | 10 +++++ .../20210611211002_create_order_items.rb | 12 +++++ components/shop/shop.gemspec | 2 +- .../app/assets/stylesheets/application.css | 1 + components/shop/spec/dummy/db/schema.rb | 16 ++++++- .../shop/spec/factories/order_factory.rb | 7 +++ .../shop/spec/factories/order_item_factory.rb | 7 +++ .../spec/features/shop/add_to_cart_spec.rb | 16 ++++++- .../shop/spec/features/shop/home_page_spec.rb | 4 +- .../spec/features/shop/shopping_cart_spec.rb | 23 +++++++++- db/schema.rb | 16 ++++++- 28 files changed, 320 insertions(+), 11 deletions(-) create mode 100644 components/shop/app/controllers/shop/order_items_controller.rb create mode 100644 components/shop/app/helpers/shop/item_helper.rb create mode 100644 components/shop/app/models/shop/order.rb create mode 100644 components/shop/app/models/shop/order_item.rb create mode 100644 components/shop/app/views/shop/order_items/index.slim create mode 100644 components/shop/db/migrate/20210611210804_create_orders.rb create mode 100644 components/shop/db/migrate/20210611211002_create_order_items.rb create mode 100644 components/shop/spec/factories/order_factory.rb create mode 100644 components/shop/spec/factories/order_item_factory.rb diff --git a/Gemfile b/Gemfile index 8bd4dbb4f..ec41d2ff7 100644 --- a/Gemfile +++ b/Gemfile @@ -23,6 +23,7 @@ gem 'redis', '~>3.2' gem 'sidekiq' gem 'sidekiq-scheduler', '~> 2.1.4' gem 'simple_form' +gem 'font-awesome-rails' gem 'courses', path: 'components/courses' gem 'shop', path: 'components/shop' diff --git a/Gemfile.lock b/Gemfile.lock index 703e125f2..57febba2f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -226,6 +226,8 @@ GEM faraday (1.0.1) multipart-post (>= 1.2, < 3) ffi (1.12.2) + font-awesome-rails (4.7.0.7) + railties (>= 3.2, < 7) formatador (0.2.5) friendly_id (5.3.0) activerecord (>= 4.0.0) @@ -650,6 +652,7 @@ DEPENDENCIES ez-settings factory_bot_rails faker + font-awesome-rails friendly_id (~> 5.1) gibbon (~> 3.0) groupdate (~> 4.0) diff --git a/app/assets/stylesheets/app/application.scss b/app/assets/stylesheets/app/application.scss index 784c887b6..699553472 100644 --- a/app/assets/stylesheets/app/application.scss +++ b/app/assets/stylesheets/app/application.scss @@ -14,6 +14,7 @@ @import "https://fonts.googleapis.com/css2?family=Oswald&display=swap'"; @import "local_fonts"; +@import "font-awesome"; @import "normalize"; diff --git a/app/assets/stylesheets/app/base/_base.scss b/app/assets/stylesheets/app/base/_base.scss index e13171aa0..00a4046df 100644 --- a/app/assets/stylesheets/app/base/_base.scss +++ b/app/assets/stylesheets/app/base/_base.scss @@ -134,3 +134,28 @@ q { .pk-no-display { display: none !important; } + +.shopping_cart-icon { + display: inline-block; + position: fixed; + left: 1450px; + opacity: 80%; + + &:hover { + opacity: 100%; + } +} + +.cart_table { + margin: 40px; + + table { + border-collapse: collapse; + width: 100%; + } + + th, td { + text-align: left; + padding: 8px; + } +} \ No newline at end of file diff --git a/app/helpers/admin/events_helper.rb b/app/helpers/admin/events_helper.rb index cbe427952..08621ae74 100644 --- a/app/helpers/admin/events_helper.rb +++ b/app/helpers/admin/events_helper.rb @@ -42,7 +42,6 @@ def event_status_label(event) class: ['ui label', BG_STATUS_CLASS[event.status.to_sym]] end - # rubocop:disable Metrics/AbcSize def event_visitors(event) requested = event.pending_visit_requests.length approved = event.approved_visit_requests.length @@ -56,7 +55,6 @@ def event_visitors(event) t('events.index.visitors.visited') => visited } end - # rubocop:enable Metrics/AbcSize def event_verified_user_data(event) verified = event.verified_visitors.length diff --git a/app/views/layouts/application.slim b/app/views/layouts/application.slim index 1d80e12f7..27ed3352f 100644 --- a/app/views/layouts/application.slim +++ b/app/views/layouts/application.slim @@ -13,6 +13,7 @@ html link rel="icon" type="image/png" href="/favicon-16x16.png" sizes="16x16" link rel="manifest" href="/manifest.json" link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5" + link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.13.0/css/all.css" == render 'layouts/ga/head' if Rails.env.production? body.pk-main-layout class="#{yield(:main_body_class)}" diff --git a/components/shop/app/controllers/shop/base_controller.rb b/components/shop/app/controllers/shop/base_controller.rb index bc4a29bb8..96d7ec022 100644 --- a/components/shop/app/controllers/shop/base_controller.rb +++ b/components/shop/app/controllers/shop/base_controller.rb @@ -4,6 +4,10 @@ module Shop class BaseController < ApplicationController before_action :authenticate_user! + def current_order + session[:order_id] ? Shop::Order.find(session[:order_id]) : [] + end + private def render_form diff --git a/components/shop/app/controllers/shop/items_controller.rb b/components/shop/app/controllers/shop/items_controller.rb index a3dfaa367..6898d69f4 100644 --- a/components/shop/app/controllers/shop/items_controller.rb +++ b/components/shop/app/controllers/shop/items_controller.rb @@ -3,6 +3,22 @@ module Shop class ItemsController < BaseController helper_method :items + helper_method :current_order + + # @deprecated + # def add_to_cart + # cart = session[:cart] || [] + # cart << item.id unless cart.include?(item.id) + + # session[:cart] = cart + + # redirect_back(fallback_location: root_path, notice: "#{item.name} was successfully added to cart") + # end + + # def remove_from_cart + # session[:cart].delete(item.id) + # redirect_back(fallback_location: root_path, notice: "#{item.name} was successfully removed from cart") + # end private diff --git a/components/shop/app/controllers/shop/order_items_controller.rb b/components/shop/app/controllers/shop/order_items_controller.rb new file mode 100644 index 000000000..8845096a9 --- /dev/null +++ b/components/shop/app/controllers/shop/order_items_controller.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +module Shop + class OrderItemsController < BaseController + helper_method :item + helper_method :order_items + helper_method :current_order + + def create + order_item = ::Shop::OrderItem.new(item_id: item.id, order_id: order_id, price: item.price) + order_item.save + + redirect_to shop_items_path + end + + def update + order_item.increase_quantity! + redirect_to shop_order_items_path + end + + def destroy + order_item.reduce_quantity! + redirect_to shop_order_items_path + end + + private + + def order_items + order_items = Shop::OrderItem.includes(:item) + end + + def order_item + @order_items ||= Shop::OrderItem.find(params[:id]) + end + + def item + @items ||= Shop::Item.find(params[:item_id]) + end + + def order_id + session[:order_id] ||= Shop::Order.create.id + end + end +end diff --git a/components/shop/app/helpers/shop/item_helper.rb b/components/shop/app/helpers/shop/item_helper.rb new file mode 100644 index 000000000..4913c5ad9 --- /dev/null +++ b/components/shop/app/helpers/shop/item_helper.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +module Shop + module ItemHelper + end +end diff --git a/components/shop/app/models/shop/item.rb b/components/shop/app/models/shop/item.rb index daa6bdf13..d29ded302 100644 --- a/components/shop/app/models/shop/item.rb +++ b/components/shop/app/models/shop/item.rb @@ -5,6 +5,7 @@ class Item < ApplicationRecord self.table_name = 'shop_items' has_many :item_images + has_many :order_items accepts_nested_attributes_for :item_images scope :active, -> { where(published: true) } diff --git a/components/shop/app/models/shop/order.rb b/components/shop/app/models/shop/order.rb new file mode 100644 index 000000000..97a526c57 --- /dev/null +++ b/components/shop/app/models/shop/order.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +# status: shopping_cart, placed, processing, shipping, complete, canceled +# user: optional +# buyer: anonymous user +# shipment_address + +module Shop + class Order < ApplicationRecord + self.table_name = 'shop_orders' + + enum status: { shopping_cart: 0, placed: 1, processing: 2, shipping: 3, complete: 4, canceled: 5 } + + has_many :order_items + has_many :items, through: :order_items + + def total_sum + order_items.map(&:price).sum.to_f + end + + def order_items_count + order_items.count + end + end +end diff --git a/components/shop/app/models/shop/order_item.rb b/components/shop/app/models/shop/order_item.rb new file mode 100644 index 000000000..c21ae7cce --- /dev/null +++ b/components/shop/app/models/shop/order_item.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +module Shop + class OrderItem < ApplicationRecord + self.table_name = 'shop_order_items' + + validates_uniqueness_of :item_id + + belongs_to :order + belongs_to :item + + def increase_quantity! + self.quantity += 1 + update_price(quantity) + save! + end + + def reduce_quantity! + if self.quantity > 1 + self.quantity -= 1 + update_price(quantity) + save! + else + delete_order_item + end + end + + def delete_order_item + destroy! + end + + def update_price(qty) + self.price = item.price * qty + end + end +end diff --git a/components/shop/app/uploaders/item_image_uploader.rb b/components/shop/app/uploaders/item_image_uploader.rb index 9e3f8f8ae..99745b955 100644 --- a/components/shop/app/uploaders/item_image_uploader.rb +++ b/components/shop/app/uploaders/item_image_uploader.rb @@ -1,2 +1,4 @@ +# frozen_string_literal: true + class ItemImageUploader < CarrierWave::Uploader::Base -end \ No newline at end of file +end diff --git a/components/shop/app/views/shop/items/index.slim b/components/shop/app/views/shop/items/index.slim index d69d3fb48..1bb8c3031 100644 --- a/components/shop/app/views/shop/items/index.slim +++ b/components/shop/app/views/shop/items/index.slim @@ -1,5 +1,13 @@ h2.main-header Shop +- if current_order.present? + span.shopping_cart-icon + = link_to shop_order_items_path do + i.fas.fa-shopping-cart.fa-8x + div.item_counts + span + = current_order.order_items_count + .main ul.cards - items.each do |item| @@ -9,4 +17,9 @@ h2.main-header Shop .card_content h2.card_title = item.name p.card_text = item.description - button.btn.card_btn Add to cart + - if current_order.present? && current_order.items.include?(item) + = link_to shop_order_items_path do + button.btn.card_btn Added to the cart + - else + = link_to shop_order_items_path(item_id: item.id), method: :post do + button.btn.card_btn Add to cart \ No newline at end of file diff --git a/components/shop/app/views/shop/order_items/index.slim b/components/shop/app/views/shop/order_items/index.slim new file mode 100644 index 000000000..a1731de61 --- /dev/null +++ b/components/shop/app/views/shop/order_items/index.slim @@ -0,0 +1,32 @@ +h2.main-header Cart + +.cart_table + - if current_order.present? && current_order.order_items.any? + table + thead + tr + th Image + th.company Name + th.company Price + th.count Count + tbody + - order_items.each do |item| + tr + td= image_tag item.item.item_images.first.image_url(:small).to_s, class: 'card_image' if item.item.item_images.first.try(:image_url) + td= item.item.name + td= item.price + td + div + = link_to shop_order_item_path(item.id), method: :delete do + i.fas.fa-minus + + = item.quantity + + = link_to shop_order_item_path(item.id), method: :put do + i.fas.fa-plus + + .total_sum + h2 Total + h2= current_order.total_sum + - else + h1 Your cart is empty for now \ No newline at end of file diff --git a/components/shop/config/routes.rb b/components/shop/config/routes.rb index 1a826fb9d..b1c6ad8f6 100644 --- a/components/shop/config/routes.rb +++ b/components/shop/config/routes.rb @@ -3,6 +3,7 @@ Rails.application.routes.draw do namespace :shop do resources :items, only: %i[index] + resources :order_items, only: %i[index create update destroy] end namespace :admin do diff --git a/components/shop/db/migrate/20210611210804_create_orders.rb b/components/shop/db/migrate/20210611210804_create_orders.rb new file mode 100644 index 000000000..3ce78d97d --- /dev/null +++ b/components/shop/db/migrate/20210611210804_create_orders.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +class CreateOrders < ActiveRecord::Migration[5.2] + def change + create_table :shop_orders do |t| + t.integer :status, default: 0 + t.text :shipment_address + end + end +end diff --git a/components/shop/db/migrate/20210611211002_create_order_items.rb b/components/shop/db/migrate/20210611211002_create_order_items.rb new file mode 100644 index 000000000..c8a214828 --- /dev/null +++ b/components/shop/db/migrate/20210611211002_create_order_items.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +class CreateOrderItems < ActiveRecord::Migration[5.2] + def change + create_table :shop_order_items do |t| + t.integer :quantity, default: 1 + t.decimal :price, null: false + t.references :item + t.references :order + end + end +end diff --git a/components/shop/shop.gemspec b/components/shop/shop.gemspec index 8f747a731..51f55983a 100644 --- a/components/shop/shop.gemspec +++ b/components/shop/shop.gemspec @@ -29,11 +29,11 @@ Gem::Specification.new do |spec| spec.add_dependency 'rails', '~> 5.2.2', '>= 5.2.2.1' + spec.add_dependency 'carrierwave', '~> 1.2' spec.add_dependency 'friendly_id' spec.add_dependency 'kaminari' spec.add_dependency 'simple_form' spec.add_dependency 'slim-rails' - spec.add_dependency 'carrierwave', '~> 1.2' spec.add_development_dependency 'capybara' spec.add_development_dependency 'faker' diff --git a/components/shop/spec/dummy/app/assets/stylesheets/application.css b/components/shop/spec/dummy/app/assets/stylesheets/application.css index 0ebd7fe82..3e01e3d08 100644 --- a/components/shop/spec/dummy/app/assets/stylesheets/application.css +++ b/components/shop/spec/dummy/app/assets/stylesheets/application.css @@ -12,4 +12,5 @@ * *= require_tree . *= require_self + *= require font-awesome */ diff --git a/components/shop/spec/dummy/db/schema.rb b/components/shop/spec/dummy/db/schema.rb index 285f33b28..12bb42d0c 100644 --- a/components/shop/spec/dummy/db/schema.rb +++ b/components/shop/spec/dummy/db/schema.rb @@ -12,7 +12,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20_210_514_113_103) do +ActiveRecord::Schema.define(version: 20_210_611_211_002) do create_table 'shop_item_images', force: :cascade do |t| t.string 'image' t.integer 'item_id' @@ -30,4 +30,18 @@ t.datetime 'created_at', null: false t.datetime 'updated_at', null: false end + + create_table 'shop_order_items', force: :cascade do |t| + t.integer 'quantity' + t.decimal 'price', null: false + t.integer 'item_id' + t.integer 'order_id' + t.index ['item_id'], name: 'index_shop_order_items_on_item_id' + t.index ['order_id'], name: 'index_shop_order_items_on_order_id' + end + + create_table 'shop_orders', force: :cascade do |t| + t.integer 'status', default: 0 + t.text 'shipment_address' + end end diff --git a/components/shop/spec/factories/order_factory.rb b/components/shop/spec/factories/order_factory.rb new file mode 100644 index 000000000..1811b47e5 --- /dev/null +++ b/components/shop/spec/factories/order_factory.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :order, class: Shop::Order do + shipment_address { 'Lviv, Pivorak street' } + end +end diff --git a/components/shop/spec/factories/order_item_factory.rb b/components/shop/spec/factories/order_item_factory.rb new file mode 100644 index 000000000..ce7fdc606 --- /dev/null +++ b/components/shop/spec/factories/order_item_factory.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :order_item, class: Shop::OrderItem do + price { 12 } + end +end diff --git a/components/shop/spec/features/shop/add_to_cart_spec.rb b/components/shop/spec/features/shop/add_to_cart_spec.rb index 5179920ef..f047ab6d8 100644 --- a/components/shop/spec/features/shop/add_to_cart_spec.rb +++ b/components/shop/spec/features/shop/add_to_cart_spec.rb @@ -1,2 +1,16 @@ # frozen_string_literal: true -# TODO + +require 'rails_helper' + +RSpec.describe '/shop' do + let!(:active_item) { create(:item, published: true) } + + scenario 'display items' do + visit shop_items_path + + click_button 'Add to cart' + + expect(page).to have_current_path '/shop/items' + # expect(page).to have_button('Added to the cart') + end +end diff --git a/components/shop/spec/features/shop/home_page_spec.rb b/components/shop/spec/features/shop/home_page_spec.rb index c7ff126f1..8e266a2e4 100644 --- a/components/shop/spec/features/shop/home_page_spec.rb +++ b/components/shop/spec/features/shop/home_page_spec.rb @@ -8,10 +8,10 @@ let!(:item_b) { create(:item, published: false) } scenario 'display items' do - visit admin_shop_items_path + visit shop_items_path expect(page).to have_content item_a.name - expect(page).to have_content item_b.name + expect(page).to have_button('Add to cart') end end end diff --git a/components/shop/spec/features/shop/shopping_cart_spec.rb b/components/shop/spec/features/shop/shopping_cart_spec.rb index 464090415..203b33ba3 100644 --- a/components/shop/spec/features/shop/shopping_cart_spec.rb +++ b/components/shop/spec/features/shop/shopping_cart_spec.rb @@ -1 +1,22 @@ -# TODO +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'Shop Cart list' do + let!(:active_item) { create(:item, published: true) } + let!(:order) { create(:order) } + let!(:order_item) { create(:order_item, order: order, item: active_item) } + + # scenario 'display items' do + # session[:order_id] = order.id + # visit shop_order_items_path + + # expect(page).to have_content active_item.name + # end + + scenario 'display empty cart' do + visit shop_order_items_path + + expect(page).not_to have_content active_item.name + end +end diff --git a/db/schema.rb b/db/schema.rb index dd483650b..b6cfa8be6 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2021_05_14_113103) do +ActiveRecord::Schema.define(version: 2021_06_11_211002) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -252,6 +252,20 @@ t.datetime "updated_at", null: false end + create_table "shop_order_items", force: :cascade do |t| + t.integer "quantity", default: 1 + t.decimal "price", null: false + t.bigint "item_id" + t.bigint "order_id" + t.index ["item_id"], name: "index_shop_order_items_on_item_id" + t.index ["order_id"], name: "index_shop_order_items_on_order_id" + end + + create_table "shop_orders", force: :cascade do |t| + t.integer "status", default: 0 + t.text "shipment_address" + end + create_table "taggings", id: :serial, force: :cascade do |t| t.integer "tag_id" t.string "taggable_type" From a7cf6271b085be0110316987378146abfdfb808b Mon Sep 17 00:00:00 2001 From: Volodya Sveredyuk Date: Wed, 16 Jun 2021 18:58:14 +0300 Subject: [PATCH 2/3] OrderItemService --- .../app/controllers/shop/base_controller.rb | 6 +++- .../app/controllers/shop/items_controller.rb | 16 --------- .../shop/order_items_controller.rb | 12 +++---- .../app/decorators/shop/base_decorator.rb | 4 +++ .../decorators/shop/order_item_decorator.rb | 7 ++++ components/shop/app/models/shop/order.rb | 13 ++++---- components/shop/app/models/shop/order_item.rb | 24 -------------- .../app/services/shop/order_item_service.rb | 21 ++++++++++++ .../shop/app/views/shop/items/index.slim | 2 +- .../app/views/shop/order_items/index.slim | 4 +-- .../spec/features/shop/add_to_cart_spec.rb | 2 +- .../spec/features/shop/shopping_cart_spec.rb | 11 ++++--- .../services/shop/order_item_service_spec.rb | 33 +++++++++++++++++++ 13 files changed, 93 insertions(+), 62 deletions(-) create mode 100644 components/shop/app/decorators/shop/base_decorator.rb create mode 100644 components/shop/app/decorators/shop/order_item_decorator.rb create mode 100644 components/shop/app/services/shop/order_item_service.rb create mode 100644 components/shop/spec/services/shop/order_item_service_spec.rb diff --git a/components/shop/app/controllers/shop/base_controller.rb b/components/shop/app/controllers/shop/base_controller.rb index 96d7ec022..c245b1929 100644 --- a/components/shop/app/controllers/shop/base_controller.rb +++ b/components/shop/app/controllers/shop/base_controller.rb @@ -4,8 +4,12 @@ module Shop class BaseController < ApplicationController before_action :authenticate_user! + helper_method :current_order + def current_order - session[:order_id] ? Shop::Order.find(session[:order_id]) : [] + return unless session[:order_id] + + @current_order ||= Shop::Order.find(session[:order_id]) end private diff --git a/components/shop/app/controllers/shop/items_controller.rb b/components/shop/app/controllers/shop/items_controller.rb index 6898d69f4..a3dfaa367 100644 --- a/components/shop/app/controllers/shop/items_controller.rb +++ b/components/shop/app/controllers/shop/items_controller.rb @@ -3,22 +3,6 @@ module Shop class ItemsController < BaseController helper_method :items - helper_method :current_order - - # @deprecated - # def add_to_cart - # cart = session[:cart] || [] - # cart << item.id unless cart.include?(item.id) - - # session[:cart] = cart - - # redirect_back(fallback_location: root_path, notice: "#{item.name} was successfully added to cart") - # end - - # def remove_from_cart - # session[:cart].delete(item.id) - # redirect_back(fallback_location: root_path, notice: "#{item.name} was successfully removed from cart") - # end private diff --git a/components/shop/app/controllers/shop/order_items_controller.rb b/components/shop/app/controllers/shop/order_items_controller.rb index 8845096a9..406905211 100644 --- a/components/shop/app/controllers/shop/order_items_controller.rb +++ b/components/shop/app/controllers/shop/order_items_controller.rb @@ -2,9 +2,7 @@ module Shop class OrderItemsController < BaseController - helper_method :item - helper_method :order_items - helper_method :current_order + helper_method :item, :order_items def create order_item = ::Shop::OrderItem.new(item_id: item.id, order_id: order_id, price: item.price) @@ -14,19 +12,21 @@ def create end def update - order_item.increase_quantity! + OrderItemService.new(order_item).increase_quantity! redirect_to shop_order_items_path end def destroy - order_item.reduce_quantity! + OrderItemService.new(order_item).reduce_quantity! redirect_to shop_order_items_path end private def order_items - order_items = Shop::OrderItem.includes(:item) + @order_items ||= Shop::OrderItem.includes(:item).map do |order_item| + Shop::OrderItemDecorator.new(order_item) + end end def order_item diff --git a/components/shop/app/decorators/shop/base_decorator.rb b/components/shop/app/decorators/shop/base_decorator.rb new file mode 100644 index 000000000..190764d04 --- /dev/null +++ b/components/shop/app/decorators/shop/base_decorator.rb @@ -0,0 +1,4 @@ +module Shop + class BaseDecorator < SimpleDelegator + end +end diff --git a/components/shop/app/decorators/shop/order_item_decorator.rb b/components/shop/app/decorators/shop/order_item_decorator.rb new file mode 100644 index 000000000..aef29d376 --- /dev/null +++ b/components/shop/app/decorators/shop/order_item_decorator.rb @@ -0,0 +1,7 @@ +module Shop + class OrderItemDecorator < BaseDecorator + def sum + price * quantity + end + end +end diff --git a/components/shop/app/models/shop/order.rb b/components/shop/app/models/shop/order.rb index 97a526c57..ceb328584 100644 --- a/components/shop/app/models/shop/order.rb +++ b/components/shop/app/models/shop/order.rb @@ -14,12 +14,13 @@ class Order < ApplicationRecord has_many :order_items has_many :items, through: :order_items - def total_sum - order_items.map(&:price).sum.to_f - end + # TODO: OrderDecorator + # def total_sum + # order_items.map(&:price).sum.to_f + # end - def order_items_count - order_items.count - end + # def order_items_count + # order_items.count + # end end end diff --git a/components/shop/app/models/shop/order_item.rb b/components/shop/app/models/shop/order_item.rb index c21ae7cce..16af4dcf6 100644 --- a/components/shop/app/models/shop/order_item.rb +++ b/components/shop/app/models/shop/order_item.rb @@ -8,29 +8,5 @@ class OrderItem < ApplicationRecord belongs_to :order belongs_to :item - - def increase_quantity! - self.quantity += 1 - update_price(quantity) - save! - end - - def reduce_quantity! - if self.quantity > 1 - self.quantity -= 1 - update_price(quantity) - save! - else - delete_order_item - end - end - - def delete_order_item - destroy! - end - - def update_price(qty) - self.price = item.price * qty - end end end diff --git a/components/shop/app/services/shop/order_item_service.rb b/components/shop/app/services/shop/order_item_service.rb new file mode 100644 index 000000000..eeb891fd9 --- /dev/null +++ b/components/shop/app/services/shop/order_item_service.rb @@ -0,0 +1,21 @@ +module Shop + class OrderItemService + attr_reader :order_item + + def initialize(order_item) + @order_item = order_item + end + + def increase_quantity! + order_item.increment!(:quantity) + end + + def reduce_quantity! + if order_item.quantity > 1 + order_item.decrement!(:quantity) + else + order_item.destroy! + end + end + end +end diff --git a/components/shop/app/views/shop/items/index.slim b/components/shop/app/views/shop/items/index.slim index 1bb8c3031..e3224d4d9 100644 --- a/components/shop/app/views/shop/items/index.slim +++ b/components/shop/app/views/shop/items/index.slim @@ -22,4 +22,4 @@ h2.main-header Shop button.btn.card_btn Added to the cart - else = link_to shop_order_items_path(item_id: item.id), method: :post do - button.btn.card_btn Add to cart \ No newline at end of file + button.btn.card_btn Add to cart diff --git a/components/shop/app/views/shop/order_items/index.slim b/components/shop/app/views/shop/order_items/index.slim index a1731de61..95fee80f9 100644 --- a/components/shop/app/views/shop/order_items/index.slim +++ b/components/shop/app/views/shop/order_items/index.slim @@ -14,7 +14,7 @@ h2.main-header Cart tr td= image_tag item.item.item_images.first.image_url(:small).to_s, class: 'card_image' if item.item.item_images.first.try(:image_url) td= item.item.name - td= item.price + td= item.sum td div = link_to shop_order_item_path(item.id), method: :delete do @@ -29,4 +29,4 @@ h2.main-header Cart h2 Total h2= current_order.total_sum - else - h1 Your cart is empty for now \ No newline at end of file + h1 Your cart is empty for now diff --git a/components/shop/spec/features/shop/add_to_cart_spec.rb b/components/shop/spec/features/shop/add_to_cart_spec.rb index f047ab6d8..da51b5e6d 100644 --- a/components/shop/spec/features/shop/add_to_cart_spec.rb +++ b/components/shop/spec/features/shop/add_to_cart_spec.rb @@ -11,6 +11,6 @@ click_button 'Add to cart' expect(page).to have_current_path '/shop/items' - # expect(page).to have_button('Added to the cart') + expect(page).to have_button('Added to the cart') end end diff --git a/components/shop/spec/features/shop/shopping_cart_spec.rb b/components/shop/spec/features/shop/shopping_cart_spec.rb index 203b33ba3..944671411 100644 --- a/components/shop/spec/features/shop/shopping_cart_spec.rb +++ b/components/shop/spec/features/shop/shopping_cart_spec.rb @@ -7,12 +7,13 @@ let!(:order) { create(:order) } let!(:order_item) { create(:order_item, order: order, item: active_item) } - # scenario 'display items' do - # session[:order_id] = order.id - # visit shop_order_items_path + fscenario 'display items' do + # TODO: Mock controller session object + # session[:order_id] = order.id + visit shop_order_items_path - # expect(page).to have_content active_item.name - # end + expect(page).to have_content active_item.name + end scenario 'display empty cart' do visit shop_order_items_path diff --git a/components/shop/spec/services/shop/order_item_service_spec.rb b/components/shop/spec/services/shop/order_item_service_spec.rb new file mode 100644 index 000000000..fa7e8f077 --- /dev/null +++ b/components/shop/spec/services/shop/order_item_service_spec.rb @@ -0,0 +1,33 @@ +require 'rails_helper' + +RSpec.describe Shop::OrderItemService do + let(:order) { create(:order) } + let(:item) { create(:item) } + let!(:order_item) { create(:order_item, quantity: 1, order: order, item: item) } + + describe '#increase_quantity!' do + subject { described_class.new(order_item).increase_quantity! } + + it 'increase order item qty' do + expect { subject }.to change { order_item.quantity }.from(1).to(2) + end + end + + describe '#reduce_quantity!' do + subject { described_class.new(order_item).reduce_quantity! } + + context 'when > 1' do + let(:order_item) { create(:order_item, quantity: 3, order: order, item: item) } + + it 'increase order item qty' do + expect { subject }.to change { order_item.quantity }.from(3).to(2) + end + end + + context 'when eq 1' do + it 'increase order item qty' do + expect { subject }.to change { Shop::OrderItem.count }.from(1).to(0) + end + end + end +end From a0571f8efdd673a531152a3b0a0f44e49fb361df Mon Sep 17 00:00:00 2001 From: Volodya Sveredyuk Date: Wed, 16 Jun 2021 18:59:12 +0300 Subject: [PATCH 3/3] Find current order by id without 404 error --- components/shop/app/controllers/shop/base_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/shop/app/controllers/shop/base_controller.rb b/components/shop/app/controllers/shop/base_controller.rb index c245b1929..7fde64dba 100644 --- a/components/shop/app/controllers/shop/base_controller.rb +++ b/components/shop/app/controllers/shop/base_controller.rb @@ -9,7 +9,7 @@ class BaseController < ApplicationController def current_order return unless session[:order_id] - @current_order ||= Shop::Order.find(session[:order_id]) + @current_order ||= Shop::Order.find_by(id: session[:order_id]) end private