[RUBY] Créer un site EC avec Rails 5 ⑩ ~ Créer une fonction de commande ~

introduction

Il s'agit d'une suite de la série "Créer un site EC avec Rails 5 ⑨](https://qiita.com/GreenFingers_tk/items/3fd347dee907328d20e0), qui crée un site EC sur lequel vous pouvez faire vos achats dans une boulangerie fictive. Nous implémenterons le modèle Order (fonction de commande), qui est la fonction principale et la partie la plus difficile de ce site.

Le mouvement de l'utilisateur est le suivant.

Mettez l'article dans le panier et appuyez sur le bouton "Passer à l'écran de commande"
↓
Entrez les informations de commande(orders/new)écran
·Mode de paiement
·Destinataire
Et appuyez sur le bouton "Passer à l'écran de confirmation"
↓
Confirmer les informations de commande(orders/confirm)écran
Vérifiez le contenu et appuyez sur le bouton "Confirmer la commande"
↓
orders/Afficher l'écran de remerciement (page statique)

Vous pouvez consulter l'historique des commandes sur l'écran de liste et de détails.

Le fait est que vous pouvez sélectionner l'adresse de livraison à saisir sur les commandes / nouvel écran parmi «votre adresse», «adresse enregistrée» et «nouvelle adresse». Lorsque vous sélectionnez une nouvelle adresse et remplissez le formulaire sur le nouvel écran, elle sera enregistrée en tant que données de commande ainsi que de nouvelles données dans le modèle d'adresse. L'imbrication des formulaires et le passage des écrans de confirmation avant d'enregistrer ces données sont des facteurs qui rendent difficile la mise en œuvre de cette fonctionnalité.

Code source

https://github.com/Sn16799/bakeryFUMIZUKI

Association de modèles

fumizuki_ER.jpg

Controller

app/controllers/orders_controller.rb


class OrdersController < ApplicationController
  before_action :authenticate_customer!
  before_action :set_customer

  def index
    @orders = @customer.orders
  end

  def create
    if current_customer.cart_items.exists?
      @order = Order.new(order_params)
      @order.customer_id = current_customer.id

      #Ajuster les arguments en fonction de la sélection du bouton radio d'adresse
      @add = params[:order][:add].to_i
      case @add
        when 1
          @order.post_code = @customer.post_code
          @order.send_to_address = @customer.address
          @order.addressee = full_name(@customer)
        when 2
          @order.post_code = params[:order][:post_code]
          @order.send_to_address = params[:order][:send_to_address]
          @order.addressee = params[:order][:addressee]
        when 3
          @order.post_code = params[:order][:post_code]
          @order.send_to_address = params[:order][:send_to_address]
          @order.addressee = params[:order][:addressee]
      end
      @order.save

      # send_to_Recherche de modèle d'adresse par adresse, créer un nouveau s'il n'y a pas de données applicables
      if Address.find_by(address: @order.send_to_address).nil?
        @address = Address.new
        @address.post_code = @order.post_code
        @address.address = @order.send_to_address
        @address.addressee = @order.addressee
        @address.customer_id = current_customer.id
        @address.save
      end

      # cart_Commander le contenu des articles_Nouvellement enregistré dans les articles
      current_customer.cart_items.each do |cart_item|
        order_item = @order.order_items.build
        order_item.order_id = @order.id
        order_item.product_id = cart_item.product_id
        order_item.quantity = cart_item.quantity
        order_item.order_price = cart_item.product.price
        order_item.save
        cart_item.destroy #order_Après avoir déplacé les informations vers l'article, le panier_l'élément est effacé
      end
      render :thanks
    else
      redirect_to customer_top_path
   flash[:danger] = 'Le chariot est vide.'
    end
  end

  def show
    @order = Order.find(params[:id])
    if @order.customer_id != current_customer.id
      redirect_back(fallback_location: root_path)
      flash[:alert] = "L'accès a échoué."
    end
  end

  def new
    @order = Order.new
  end

  def confirm
    @order = Order.new
    @cart_items = current_customer.cart_items
    @order.how_to_pay = params[:order][:how_to_pay]
    #Ajuster les arguments en fonction de la sélection du bouton radio d'adresse
    @add = params[:order][:add].to_i
    case @add
      when 1
        @order.post_code = @customer.post_code
        @order.send_to_address = @customer.address
        @order.addressee = @customer.family_name + @customer.first_name
      when 2
        @sta = params[:order][:send_to_address].to_i
        @send_to_address = Address.find(@sta)
        @order.post_code = @send_to_address.post_code
        @order.send_to_address = @send_to_address.address
        @order.addressee = @send_to_address.addressee
      when 3
        @order.post_code = params[:order][:new_add][:post_code]
        @order.send_to_address = params[:order][:new_add][:address]
        @order.addressee = params[:order][:new_add][:addressee]
    end
  end

  def thanks
  end

  private
  def set_customer
    @customer = current_customer
  end

  def order_params
    params.require(:order).permit(
      :created_at, :send_to_address, :addressee, :order_status, :how_to_pay, :post_code, :deliver_fee,
      order_items_attributes: [:order_id, :product_id, :quantity, :order_price, :make_status]
      )
  end

end

Ce serait bien si les informations envoyées par form_with pouvaient être sauvegardées telles quelles, mais form_with ne peut pas insérer l'écran de confirmation. Par conséquent, les actions de confirmation et de création récupèrent les paramètres reçus de la vue sous la forme de paramètres [: hoge].

De plus, lors de la création, en une seule action, "** Créer de nouvelles données de commande " " Déplacer les données de produit de CartItem vers OrderItem ** (Créer un nouvel article de commande et supprimer un article de panier enregistré)" ** Seulement lorsqu'il n'y a pas de données correspondantes dans le modèle d'adresse, vous devez effectuer les trois nouveaux enregistrements ** ". Chaque opération n'est pas compliquée, mais la quantité totale de code est assez importante.

View new

html:app/views/orders/.html.erb


<div class="col-lg-10 offset-1 space">

  <div class="row">
    <div class="col-lg-4">
      <h2>Entrez les informations de commande</h2>
    </div>
  </div>

  <%= form_with(model: @order, local: true, url: {action: 'confirm'}) do |f| %>

  <!--Mode de paiement-->
  <div class="row space">
    <h3><strong><%= f.label :Mode de paiement%></strong></h3>
  </div>

  <div class="row">
    <div class="col-lg-4 btn-group" data-toggle="buttons">
      <label class="btn btn-outline-secondary active" style="width:50%">
        <%= f.radio_button :how_to_pay, true, {checked: true} %>carte de crédit
      </label>
      <label class="btn btn-outline-secondary" style="width:50%">
        <%= f.radio_button :how_to_pay, false, {} %>virement
      </label>
    </div>
  </div>

  <!--Destinataire-->
  <div class="row space">
    <h3><strong><%= f.label :Destinataire%></strong></h3>
  </div>
  <!--Propre adresse-->
  <div class="row">
    <p>
      <label><%= f.radio_button :add, 1, checked: true, checked: "checked" %>Votre propre adresse</label><br>
      <%= @customer.post_code %>
      <%= @customer.address %>
      <%= @customer.full_name %>
    </p>
  </div>

  <!--Adresse enregistrée-->
  <div class="row space-sm">
    <p>
      <label><%= f.radio_button :add, 2, style: "display: inline-block" %>Sélectionnez à partir de l'adresse enregistrée</label><br>
      <%= f.collection_select :send_to_address, @customer.addresses, :id, :address %>
    </p>
  </div>

  <!--Nouvelle adresse-->
  <div class="row space-sm">
    <p><label><%= f.radio_button :add, 3 %>Nouvelle destination</label></p>
  </div>
  <div class="row">
    <div class="col-lg-12">
      <%= f.fields_for :new_add do |na| %>
      <div class="row">
        <div class="col-lg-3">
          <strong>Code postal(pas de trait d'union)</strong>
        </div>
        <div class="col-lg-6">
          <%= na.text_field :post_code, class: 'form-control' %>
        </div>
      </div>

      <div class="row">
        <div class="col-lg-3">
          <strong>adresse de rue</strong>
        </div>
        <div class="col-lg-6">
          <%= na.text_field :address, class: 'form-control' %>
        </div>
      </div>

      <div class="row">
        <div class="col-lg-3">
          <strong>adresse</strong>
        </div>
        <div class="col-lg-6">
          <%= na.text_field :addressee, class: 'form-control' %>
        </div>
      </div>
      <% end %>
    </div>
  </div>
  <!--Jusqu'ici-->

  <div class="row space">
    <div class="col-lg-2 offset-lg-7">
      <%= f.submit "Passer à l'écran de confirmation", class: "btn btn-danger"%>
    </div>
  </div>

  <% end %>
</div>

confirm

html:app/views/orders/.html.erb


<div class="col-lg-10 offset-1 space">
  <div class="row">
    <h2>Confirmer les informations de commande</h2>
  </div>

  <%= form_with(model: @order, local: true) do |f| %>

  <div class="d-none d-lg-block space">
    <div class="row">
      <div class="col-lg-5"><h4>Nom du produit</h4></div>
      <div class="col-lg-2"><h4>Prix unitaire (taxes incluses)</h4></div>
      <div class="col-lg-2"><h4>quantité</h4></div>
      <div class="col-lg-2"><h4>total</h4></div>
    </div>
  </div>

  <% sum_all = 0 %>
  <% @cart_items.each do |cart_item| %>
  <div class="row space-sm">
    <div class="col-lg-3">
      <%= link_to product_path(cart_item.product) do %>
      <%= attachment_image_tag(cart_item.product, :image, :fill, 100, 100, fallback: "no_img.jpg ") %>
      <% end %>
    </div>
    <div class="col-lg-2">
      <%= link_to product_path(cart_item.product) do %>
      <%= cart_item.product.name %>
      <% end %>
    </div>
    <div class="col-lg-2">
      <%= price_include_tax(cart_item.product.price) %>
    </div>
    <div class="col-lg-2">
      <%= cart_item.quantity %>
    </div>
    <div class="col-lg-2">
      <%= sum_product = price_include_tax(cart_item.product.price).to_i * cart_item.quantity %>Cercle
      <% sum_all += sum_product %>
    </div>
  </div>
  <% end %>

  <div class="row space">
    <div class="col-lg-12">
      <div class="row">
        <div class="col-lg-3">
          <strong>livraison</strong>
        </div>
        <div class="col-lg-3">
          <%= @order.deliver_fee %>Cercle
        </div>
      </div>
      <div class="row">
        <div class="col-lg-3">
          <strong>Total produit</strong>
        </div>
        <div class="col-lg-3">
          <%= sum_all.to_i %>Cercle
        </div>
      </div>
      <div class="row">
        <div class="col-lg-3">
          <strong>Montant facturé</strong>
        </div>
        <div class="col-lg-3">
          <% billling_amount = sum_all + @order.deliver_fee.to_i %>
          <%= billling_amount.to_i %>Cercle
        </div>
      </div>
    </div>
  </div>

  <div class="row space-sm">
    <div class="col-lg-2">
      <h3>Mode de paiement</h3>
    </div>
    <div class="col-lg-4">
      <%= how_to_pay(@order.how_to_pay) %>
    </div>
  </div>

  <div class="row space-sm">
    <div class="col-lg-2">
      <h3>Destinataire</h3>
    </div>
    <div class="col-lg-4">
      <%= @order.post_code %>
      <%= @order.send_to_address %>
      <%= @order.addressee %>
    </div>
  </div>

  <%= f.hidden_field :customer_id, :value => current_customer.id %>
  <%= f.hidden_field :post_code, :value => "#{@order.post_code}" %>
  <%= f.hidden_field :send_to_address, :value => "#{@order.send_to_address}" %>
  <%= f.hidden_field :addressee, :value => "#{@order.addressee}" %>
</div>

<div class="row space">
  <div class="col-lg-2 offset-lg-5">
    <%= f.submit "Confirmer l'achat", class: "btn btn-danger btn-lg" %>
  </div>
</div>


<% end %>
</div>

thanks

html:app/views/orders/.html.erb


<div class="col-lg-10 offset-1 space">
  <h2>Merci pour votre achat!</h2>
  <h3><%= link_to 'Retourner en haut', customer_top_path %></h3>
</div>

Cette page passe après avoir cliqué sur le bouton «Confirmer l'achat». Puisqu'il s'agit d'une page statique et n'a pas une telle fonction, il est bon de ne mettre que des mots simples comme décrit ci-dessus, ou d'afficher une photo avec un curseur, etc.

index

html:app/views/orders/.html.erb


<div class="col-lg-10 offset-1 space">

  <div class="row">
    <h2>Liste de l'historique des commandes</h2>
  </div>

  <div class="d-none d-lg-block space">
    <div class="row">
      <div class="col-lg-2">Date de commande</div>
      <div class="col-lg-3">Adresse de livraison</div>
      <div class="col-lg-2">Produit commandé</div>
      <div class="col-lg-2">Paiement</div>
      <div class="col-lg-1">Statut</div>
      <div class="col-lg-2">Détails de la commande</div>
    </div>
  </div>

  <% @orders.each do |order| %>
  <div class="row space-sm">
    <div class="col-lg-2">
      <%= simple_time(order.created_at) %>
    </div>
    <div class="col-lg-3">
      <div class="row">
        <%= order.post_code + " " + order.send_to_address %>
      </div>
      <div class="row">
        <%= order.addressee %>
      </div>
    </div>
    <div class="col-lg-2">
      <% sum_all = 0 %>
      <% order.order_items.each do |order_item| %>
      <%= order_item.product.name %><br>
      <% sub_total = price_include_tax(order_item.order_price).to_i * order_item.quantity %>
      <% sum_all += sub_total.to_i %>
      <% end %>
    </div>
    <div class="col-lg-2">
      <%= sum_all += order.deliver_fee.to_i %>Cercle
    </div>
    <div class="col-lg-1">
      <%= order_status(order) %>
    </div>
    <div class="col-lg-2">
      <%= link_to 'indiquer', order_path(order), class: "btn btn-sm btn-danger" %>
    </div>
  </div>
  <% end %>

</div>

show

html:app/views/orders/.html.erb


<div class="col-lg-10 offset-1 space">
  <div class="row">
    <h2>Détails de l'historique des commandes</h2>
  </div>

  <div class="row">
    <div class="col-lg-7">
      <div class="row space">
        <h3>Informations sur la commande</h3>
      </div>
      <div class="row">
        <div class="container">
          <div class="row space-sm">
            <div class="col-lg-3">
              <strong>Date de commande</strong>
            </div>
            <div class="col-lg-9">
              <%= simple_time(@order.created_at) %>
            </div>
          </div>
          <div class="row space-sm">
            <div class="col-lg-3">
              <strong>Adresse de livraison</strong>
            </div>
            <div class="col-lg-9">
              <%= @order.send_to_address %>
            </div>
          </div>
          <div class="row space-sm">
            <div class="col-lg-3">
              <strong>Mode de paiement</strong>
            </div>
            <div class="col-lg-9">
              <%= how_to_pay(@order.how_to_pay) %>
            </div>
          </div>
          <div class="row space-sm">
            <div class="col-lg-3">
              <strong>Statut</strong>
            </div>
            <div class="col-lg-9">
              <%= order_status(@order) %>
            </div>
          </div>
        </div>
      </div>

      <div class="row space">
        <h3>Détails de la commande</h3>
      </div>
      <div class="d-none d-lg-block">
        <div class="row">
          <div class="col-lg-4">
            <strong>Produit</strong>
          </div>
          <div class="col-lg-3">
            <strong>Prix unitaire (taxes incluses)</strong>
          </div>
          <div class="col-lg-2">
            <strong>Quantité</strong>
          </div>
          <div class="col-lg-2">
            <strong>total</strong>
          </div>
        </div>
      </div>
      <% sum_all = 0 %>
      <% @order.order_items.each do |order_item| %>
      <div class="row space-sm">
        <div class="col-lg-4">
          <%= order_item.product.name %>
        </div>
        <div class="col-lg-3">
          <%= price_include_tax(order_item.order_price) %>
        </div>
        <div class="col-lg-2">
          <%= order_item.quantity %>Pièces
        </div>
        <div class="col-lg-2">
          <%= sub_total = price_include_tax(order_item.order_price).to_i * order_item.quantity %>Cercle
          <% sum_all += sub_total %>
        </div>
      </div>
      <% end %>
    </div>

    <div class="col-lg-5">
      <div class="row space">
        <h3>Détails de facturation</h3>
      </div>
      <div class="row">
        <div class="container">
          <div class="row space-sm">
            <div class="col-lg-6">
              <strong>Total produit</strong>
            </div>
            <div class="col-6">
              <%= sum_all %>Cercle
            </div>
          </div>
          <div class="row space-sm">
            <div class="col-lg-6">
              <strong>Frais de livraison</strong>
            </div>
            <div class="col-lg-6">
              <%= @order.deliver_fee %>Cercle
            </div>
          </div>
          <div class="row space-sm">
            <div class="col-lg-6">
              <strong>Montant facturé</strong>
            </div>
            <div class="col-lg-6">
              <%= sum_all + @order.deliver_fee.to_i %>Cercle
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>

orders_show.jpg

Dans la figure ci-dessus, je voudrais afficher le montant total des produits dans la colonne "Informations de facturation", mais si je construis l'écran dans l'ordre du haut, il sera nécessaire de passer deux fois par une boucle pour "Informations de facturation" et "Détails de la commande". Ce sera un problème. Donc, en pensant que c'est une méthode bâclée, divisez l'écran verticalement en deux avec col, créez d'abord des colonnes pour «informations de commande» et «détails de commande», puis calculez le montant total dans la boucle «détails de la commande». Il est stocké dans une variable et appelé dans le champ "Informations de facturation". (Dans la colonne "Informations de facturation", j'ai oublié de définir la valeur par défaut des frais d'expédition dans la base de données. Le montant total du produit et le montant de facturation sont donc identiques. À l'origine, les frais d'expédition de 800 yens correspondent au montant de facturation pour le produit total. C'est une spécification à ajouter, et le code de vue y correspond également.)

Postscript

Il n'y avait que la fonction principale de ce site EC, et la quantité de code était importante et le contenu était compliqué. Dans cette implémentation, tout en suivant à peu près ce que j'ai fait dans l'implémentation de l'équipe de l'école auparavant, des modifications telles que la définition de nouvelles méthodes d'aide et de modèle pour rendre le code plus facile à voir et rendre l'écran réactif Était ajouté.

Il s'agit d'une note supplémentaire sur la spécification selon laquelle les données d'adresse sont enregistrées dans Address en même temps que Order lorsqu'une nouvelle adresse est sélectionnée dans l'action de création. Dans la description ci-dessus, la colonne d'adresse du modèle d'adresse est recherchée, et s'il n'y a pas de correspondance, elle est enregistrée. Pour une recherche plus précise

unless Address.find_by(addressee: @order.addressee, address: @order.send_to_address).exists?

En tant que tel, vous souhaiterez peut-être vous assurer que l'adresse et l'adresse correspondent. Bien que le code sera plus long.

Quoi qu'il en soit, cela complète la fonctionnalité du site client. Je suis heureux.

Recommended Posts

Créer un site EC avec Rails 5 ⑩ ~ Créer une fonction de commande ~
Créer un site EC avec Rails 5 ⑨ ~ Créer une fonction de panier ~
Créer un site EC avec Rails5 ⑦ ~ Adresse, modèle de genre ~
Créer un site EC avec Rails5 ④ ~ En-tête et pied de page ~
Créez un site EC avec Rails5 ⑥ ~ entrée de données de départ ~
Créer un site EC avec Rails5 ② ~ Paramètres Bootstrap4, définition du contrôleur / action ~
[Rails] Fonction de panier de site EC
Créer un site EC avec Rails5 ③-Définir des associations de modèles et d'autres choses-
Créer une fonction de pagination avec Rails Kaminari
[Retrait des rails] Créez une fonction de retrait simple avec des rails
Créez un site EC en utilisant des rayures! (Création de compte)
[Rails] Créer une application
Créer une classe immuable avec JAVA
Créer un portfolio avec rails + postgres sql
Créez une application avec Spring Boot 2
[Rails] Conception de bases de données pour le site EC
Créer un fichier Excel avec POI
Créez une application avec Spring Boot
Créer un site de catalogue d'applications à l'aide de l'interface de ligne de commande pour Microsoft 365 avec Docker
(Site CE) Validation lors de la saisie des informations de commande
Créons vous-même une instance avec .new. .. ..
[Java] Créer un module exécutable avec Gradle
[Rails6] Créer une nouvelle application avec Rails [Débutant]
Déploiement facile avec Capistrano + AWS (EC2) + Rails
Construire un environnement Rails 6 + MySQL avec Docker compose
Créez quand même une fonction de connexion avec Rails
[Rails 5] Créer une nouvelle application avec Rails [Débutant]
Faisons un écran d'erreur avec Rails
Nuxt.js × Créer une application en mode API Rails
Rétrograder une application existante créée avec les rails 5.2.4 vers 5.1.6
[Rails] rails nouveau pour créer une base de données avec PostgreSQL
Créez une API JSON prenant en charge le cryptage RSA avec wicket
Créez une discussion d'équipe avec Rails Action Cable
Faisons une fonction de recherche avec Rails (ransack)
Rails6.0 ~ Comment créer un environnement de développement respectueux de l'environnement
Créer un annotateur qui utilise kuromoji avec NLP4J [007]
[Rails] Comment créer un environnement avec Docker
J'ai essayé de créer une fonction / écran d'administrateur de site commercial avec Java et Spring
Les rails suivent la fonction
[Rails] Fonction de notification
[No.003] Créer un écran de liste de commandes pour le client
[Rails] Implémentation de la fonction glisser-déposer (avec effet)
Comment pousser une application développée avec Rails vers Github
Créez un serveur de fichiers HTTPS pour le développement avec ring-jetty-adapter
Créer un environnement de test E2E avec Docker x Cypress
Créer un service avec un modèle vide Liferay 7.0 / DXP
Créez un site de démonstration simple avec Spring Security avec Spring Boot 2.1