고객이 Spree Commerce를 통해 품절 제품 구독 가능


때때로 고객이 구매하고자 하는 상품이 재고가 있을 때 그들에게 통지하는 것은 좋은 일이다.사실 그들은 다른 곳으로 가서 구매할 것이다. 그렇지?만약 다른 곳에 더 싼 것이 있다면, 그들은 거의 다른 곳으로 가서 구매할 것이다.그러나 만약에 당신이 판매하는 제품이 유일무이하고 품질이 더 좋거나 고객이 당신의 지지를 좋아하거나 전체적으로 당신을 좋아한다면 어떻게 해야 합니까?우리는 품절 전자 우편 통지 시스템을 실시할 것이다.나는 통지라고 부른다.그것의 묘미는 우리가 좋아하는 방식에 따라 변수를 명명하거나 정의할 수 있다는 데 있다😂
시작하기 전에 Spree가 제품 수를 어떻게 업데이트하는지 살펴보겠습니다.대부분의 기업이 '재고 추적' 을 사용하지 않음에도 불구하고 '재고량' 은 우리가 원하는 목표이다.우리는 이후의 해석에서 이 문제를 토론할 것이다.
만약 당신의 업무가 재고에 의존한다면, 이 강좌는 당신에게 적합합니다.
StockItemsController에서 우리는 세 가지 동작을 보고 싶다.
  • 업데이트
  • 생성
  • 폐기
  • 업데이트


    재고 품목을 업데이트할 때는 수량이 0을 초과하면 모든 고객에게 이메일을 보내야 합니다.🙄

    만들다


    마찬가지로 재고 이동을 추가할 때 모든 고객에게 이메일을 보냅니다.

    부수다


    우리는 더 이상 재고를 추적하지 않기로 결정했다.이렇게 하면 카트에 추가 버튼이 활성화되므로 왜 모든 고객에게 이메일을 보내지 않습니까?
    # see https://github.com/spree/spree/blob/master/backend/app/controllers/spree/admin/stock_items_controller.rb
    module Spree
        module Admin
            module StockItemsControllerDecorator
                def self.prepended(base)
                    base.before_action :process_notifiees_on_stock_item, only: :update
    
                    # We have not taken into account should stock_movement.save fails.
                    # see https://github.com/spree/spree/blob/master/backend/app/controllers/spree/admin/stock_items_controller.rb#L13
                    base.before_action :process_notifiees_on_stock_movement, only: :create
    
                    base.before_action :notify_notifiees, only: :destroy
                end
    
    
    
                private
                    # We've made the executive decision by not keeping stocks.
                    # Alert all customers that the product is available to purchase.
                    def notify_notifiees
                        variant_id = stock_item.variant.id
    
                        email_all_notifiees(variant_id)
                    end
    
                    def process_notifiees_on_stock_movement
                        quantity = params[:stock_movement][:quantity].to_i
                        variant_id = params[:variant_id]
    
                        if quantity > 0
                            email_all_notifiees(variant_id)
                        end
    
                    end
    
                    def email_all_notifiees(variant_id)
                        product_id = Spree::Variant.find(variant_id).product.id
    
                        notifiees = lookup_notifiees_by(product_id)
    
                        send_notification_email(notifiees)
    
                        # We said we'd delete their email address
                        notifiees.destroy_all
                    end
    
                    def process_notifiees_on_stock_item
                        # Backorderable: boolean
                        # stock_item.backorderable
    
                        # Number of items in stock: integer
                        # stock_item.count_on_hand
    
                        if stock_item.count_on_hand > 0
                            variant_id = stock_item.variant.id
                            email_all_notifiees(variant_id)
                        end
                    end
    
                    def lookup_notifiees_by(product_id)
                        ProductNotification.where(product_id: product_id)
                    end
    
                    def send_notification_email(notifiees)
                        if notifiees.present?
                            emails_to_send = notifiees.pluck(:email)
                            # send the email
                        end
                    end
            end
        end
    end
    ::Spree::Admin::StockItemsController.prepend Spree::Admin::StockItemsControllerDecorator if ::Spree::Admin::StockItemsController.included_modules.exclude?(Spree::Admin::StockItemsControllerDecorator)
    
    이런 논리는 고객이 어디에서 변형을 주문할지 고려하지 않고 제품 자체를 고려한다.이상적인 상황에서, 당신은 이 코드를 당신의 업무 논리에 맞게 바꾸기를 원합니다.My online store 서로 다른 유형의 변체를 사용하지 않지만, 내가 깊이 들어가면 변체를 사용할 수 있다.
    코드에서 너는 내가 있다는 것을 알아차릴 것이다. ProductNotificationProductNotification 모델은 내가'알림'을 저장하는 곳이다.전자 우편 주소, 제품 id (변형을 저장하고 싶을 수도 있고 변경할 수도 있습니다), 사용자 id (선택 가능) 를 저장했습니다.

    마이그레이션:


    class CreateProductNotifications < ActiveRecord::Migration[6.1]
      def change
        create_table :product_notifications do |t|
          t.references  :user, null: true
          t.references  :product
          t.string      :email
    
          t.timestamps
        end
      end
    end
    

    모델 번호:


    class ProductNotification < ApplicationRecord
        validates   :email, presence: true, format: { with: URI::MailTo::EMAIL_REGEXP } 
        belongs_to  :user, class_name: 'Spree::User', optional: true
        belongs_to  :product, class_name: 'Spree::Product'
    end
    
    
    user는 선택할 수 있습니다. 왜냐하면 저는 내빈도 구독하기를 원하기 때문입니다. 사용자가 로그인하면 사용자 id를 사용할 것입니다.

    Spree 제품 모델


    module Spree
        module ProductDecorator
            def self.prepended(base)
                base.has_many :product_notifications, class_name: 'ProductNotification', foreign_key: 'product_id', dependent: :destroy
            end
        end
    end
    
    ::Spree::Product.prepend Spree::ProductDecorator if ::Spree::Product.included_modules.exclude?(Spree::ProductDecorator)
    

    컨트롤러:


    class Spree::Products::NotifyController < ApplicationController
        include ActionView::Helpers::TextHelper
    
        layout false
    
        def notify_me
            email = strip_tags(notify_params[:email])
    
            @notif = ProductNotification.find_or_create_by(email: email) do |perm|
                perm.product_id = notify_params[:product_id]
                perm.user_id = notify_params[:user_id]
            end
    
            if @notif.save
                @notif_saved = true
            else
                @notif_saved = false
            end
        end
    
        private
            def notify_params
                params.require(:product_notification).permit(
                    :email,
                    :product_id
                ).tap do |p|
                    # Overkill to have this here, I know.
                    p[:user_id] = spree_current_user ? spree_current_user.id : nil
                end
            end
    end
    

    경로


    나는 모든 노선을 Spree::Core::Engine.add_routes do 블록 아래에 저장할 것이다.
    [..]
    
    Spree::Core::Engine.add_routes do
      [..]
      post '/products/notify', to: 'products/notify#notify_me', as: 'product_notify'
      [..]
    end
    

    프런트엔드


    프런트엔드에 대해 수정_cart_form.html했는데 제공할 수 없을 때 알림 양식이 표시됩니다.
    [..]
    <% if !@product.can_supply? %>
      <%= render 'notify_me_when_available' %>
    <% end %>
    
    및 내부_notify_me_when_available.html.erb:
    <form
        data-controller="product-notify"
        data-action="ajax:success->product-notify#result"
        class="product-notify-me mt-4" data-remote="true" action="<%= spree.product_notify_path %>" method="post">
        <input
            type="hidden"
            name="product_notification[product_id]"
            value="<%= @product.id %>"
        />
        <div class="form-group mb-2">
            <label>Notify me when in stock</label>
            <input 
                name="product_notification[email]"
                data-product-notify-target="email"
                class="spree-flat-input" 
                type="text" placeholder="Enter your email address" 
            />    
        </div>
        <span class="product-description">Your email address will only be used for this notification, after which it gets deleted.</span>
        <div>
            <button class="btn btn-primary w-100 text-uppercase font-weight-bold mt-2">Notify me</button>
        </div>
        <div data-product-notify-target="display">
        </div>
    </form>
    
    지금까지, 나는 rails 폼 요소를 사용하는 방식을 바꾸지 않았지만, 당신은 반드시 이렇게 해야 합니다.나는 이곳에서 자극을 사용하는데, 너는 정말 필요 없다.나는 단지 위의 컨트롤러에 aax 요청을 해서 product_idemail 를 전송하고, 오류가 발생했을 때 UI를 업데이트할 뿐이다.
    내부views/spree/products/notify/notify_me.html.erb:
    <%# This is server-rendered %>
    
    <% if @notif_saved %>
        <div class="alert alert-success">
            Great! We'll send you a one-time email when item becomes available.
        </div>
    <% else %>
        <div class="alert alert-danger">
            Oops! You may have provided an invalid email address.
        </div>
    <% end %>
    

    자극 제어기:


    import { Controller } from "stimulus"
    
    export default class extends Controller {
      static targets = [ "display", "email" ]
    
      result(event) {
        const data = event.detail[0].body.innerHTML
        if (data.length > 0) {
          this.emailTarget.value = ""
          return this.displayTarget.innerHTML = data
        }
      }
    }
    
    간단한 자바스크립트로 원하는 것을 실현할 수 있기 때문에 자극을 사용하는 것은 지나치다.자극을 사용하려면 views/spree/shared/_head.html.erb에서 js 파일을 가져와야 합니다
    이 단계에서는 전자 메일 주소를 저장할 때 다음을 볼 수 있습니다.

    notifees 계수 보이기


    제품의 총 잠재 고객 수를 확인하려면 views/spree/admin/products/index.html.erb, 루프 내, 표에 다음을 추가합니다.
     <td class="notify"><%= product.product_notifications.count %></td>
    

    실수로 메일을 보냈어요.


    "backordable"에 확인 작업을 추가하길 원할 수도 있습니다. 언제든지 선택/취소를 하면 이메일을 보낼 수 있습니다😁

    큰 문제


    이 실현에는 결함이 하나 있다."인벤토리 추적"을 선택/선택 취소하면 작업이 Spree::Admin::VariantsIncludingMasterController#update, 가리키기here에 있기 때문에 트리거되지 않습니다.

    결론


    나는 여전히 Spree Commerce의 기능을 탐색하고 있다.나는 이 실현이 결코 완벽하지 않다고 생각하지만, 나에게는 이것이 출발점이다.더 좋은 방법을 찾겠지만, Rails 이벤트 경로에 가서 데이터베이스 업데이트를 보고 싶지 않습니다.만약 더 좋은 해결 방안이 있다면 저에게 알려 주세요.현재, 나는 이러한 방법을 사용하여 코드를 변경하여 고객이 제품 id가 아닌 변형 id를 구독할 수 있도록 할 것이다.

    편집자


    한 번 써서 여기저기 쓰다.우리는 VariantsIncludingMasterController의 or 논리가 필요하기 때문에 코드를 StockItemsControllerDecorator에서 조수 방법으로 옮깁니다.
    module NotifyCustomersHelper
        # We've made the executive decision by not keeping stocks.
        # Alert all customers that the product is available to purchase.
        def notify_notifiees
            variant_id = stock_item.variant.id
    
            email_all_notifiees(variant_id)
        end
    
        def process_notifiees_on_stock_movement
            quantity = params[:stock_movement][:quantity].to_i
            variant_id = params[:variant_id]
    
            if quantity > 0
                email_all_notifiees(variant_id)
            end
    
        end
    
        def email_all_notifiees(variant_id)
            product_id = Spree::Variant.find(variant_id).product.id
    
            notifiees = lookup_notifiees_by(product_id)
    
            send_notification_email(notifiees)
    
            # We said we'd delete their email address
            notifiees.destroy_all
        end
    
        def process_notifiees_on_stock_item
            # Backorderable: boolean
            # stock_item.backorderable
    
            # Number of items in stock: integer
            # stock_item.count_on_hand
    
            if stock_item.count_on_hand > 0
                variant_id = stock_item.variant.id
                email_all_notifiees(variant_id)
            end
        end
    
        def lookup_notifiees_by(product_id)
            ProductNotification.where(product_id: product_id)
        end
    
        def send_notification_email(notifiees)
            if notifiees.present?
                emails_to_send = notifiees.pluck(:email)
                # send the email
            end
        end
    end
    
    이제 우리StockItemsControllerDecorator는:
    # see https://github.com/spree/spree/blob/master/backend/app/controllers/spree/admin/stock_items_controller.rb
    module Spree
        module Admin
            module StockItemsControllerDecorator
                include NotifyCustomersHelper
    
                def self.prepended(base)
                    base.before_action :process_notifiees_on_stock_item, only: :update
    
                    # We have not taken into account should stock_movement.save fails.
                    # see https://github.com/spree/spree/blob/master/backend/app/controllers/spree/admin/stock_items_controller.rb#L13
                    base.before_action :process_notifiees_on_stock_movement, only: :create
    
                    base.before_action :notify_notifiees, only: :destroy
                end
            end
        end
    end
    ::Spree::Admin::StockItemsController.prepend Spree::Admin::StockItemsControllerDecorator if ::Spree::Admin::StockItemsController.included_modules.exclude?(Spree::Admin::StockItemsControllerDecorator)
    
    다음에 VariantsIncludingMasterControllerDecorator 내부에 spree/admin/variants_including_master_controller_decorator.rb:
    # See: https://github.com/spree/spree/blob/master/backend/app/controllers/spree/admin/variants_including_master_controller.rb
    module Spree
        module Admin
            module VariantsIncludingMasterControllerDecorator
                include NotifyCustomersHelper
    
                def self.prepended(base)
                    base.before_action :send_notification_email_on_inventory_change, only: :update
                end
    
                def send_notification_email_on_inventory_change
                    variant_id = params[:id].to_i
                    track_inventory = params[:variant][:track_inventory]
    
                    # If we're no longer tracking, send email
                    # track_inventory comes in the form of string "true" or "false"
                    if track_inventory == 'false'
                        email_all_notifiees(variant_id)
                    end
                end
            end
        end
    end
    ::Spree::Admin::VariantsIncludingMasterController.prepend Spree::Admin::VariantsIncludingMasterControllerDecorator if ::Spree::Admin::VariantsIncludingMasterController.included_modules.exclude?(Spree::Admin::VariantsIncludingMasterControllerDecorator)
    
    이제 재고를 추적하지 않으면 모든 고객이 이메일 통지를 받게 됩니다.이런 방법에 주의해라."이 확인란의 선택을 실수로 취소하여 모든""관찰가능""고객에게 이메일을 보낼 수 있습니다."이 조작을 위한 전용 단추를 만들어야 할 수도 있습니다.

    숙제


    고객이 보고 있는 제품을 send_notification_email 기능에 전달해야 합니다.이메일에서 고객은 링크를 클릭하여 제품 페이지로 바로 이동합니다.

    좋은 웹페이지 즐겨찾기