Kamis, 18 September 2014

Membuat Aplikasi POS dengan Ruby on Rails 4 part 2

Pada post sebelumnya  kita sudah mengerjakan desain dan model aplikasi, sekarang kita akan lanjutkan ke fungsi dan  bagian yang belum lengkapnya.

Fungsi Generate Code

Product mempunyai attribut code dan codenya nanti akan di generate secara otomatis.
Attribut ini nantinya bisa digunakan untuk barcode.
  1. Pada folder view/productIndex dan formnya dirubah
  2. File _form.html.erb bagian user dan codenya di hilangkan 
  3.  <%= form_for(@product) do |f| %>  
      <% if @product.errors.any? %>  
       <div id="error_explanation">  
        <h2><%= pluralize(@product.errors.count, "error") %> prohibited this product from being saved:</h2>  
        <ul>  
        <% @product.errors.full_messages.each do |message| %>  
         <li><%= message %></li>  
        <% end %>  
        </ul>  
       </div>  
      <% end %>  
      <div class="field">  
       <%= f.label :name %><br>  
       <%= f.text_field :name %>  
      </div>  
      <div class="field">  
       <%= f.label :price %><br>  
       <%= f.text_field :price %>  
      </div>  
      <div class="actions">  
       <%= f.submit %>  
      </div>  
     <% end %>  
    
  4. Untuk index.html.erb codenya seperti berikut
  5.  <h1>Listing Product</h1>  
     <table class="table table-striped">  
      <thead>  
       <tr>  
        <th>Name</th>  
        <th>Code</th>  
        <th>Price</th>  
        <th></th>  
        <th colspan="3"></th>  
       </tr>  
      </thead>  
      <tbody>  
       <% @products.each do |product| %>  
        <tr>  
         <td><%= product.name %></td>  
         <td><%= product.code %></td>  
         <td><%= product.price %></td>  
         <td><%= link_to 'Show', product %></td>  
         <td><%= link_to 'Edit', edit_product_path(product) %></td>  
         <td>  
           <%= link_to 'Destroy', product, method: :delete, data: { confirm: 'Are you sure?' } %>  
         </td>  
        </tr>  
       <% end %>  
      </tbody>  
     </table>  
     <br>  
     <%= link_to 'New Product', new_product_path %>  
    
  6. Tambahkan code pada model product
  7.   before_create :set_code  
      def generate_code(size = 6)  
       charset = %w{ 2 3 4 6 7 9 A C D E F G H J K M N P Q R T V W X Y Z}  
       (0...size).map{ charset.to_a[rand(charset.size)] }.join  
      end  
      def set_code  
       self.code = generate_code(6)  
      end  
    
  8. Selanjutnya di controller productnya  tambahkan baris berikut pada method create
  9.  @product = Product.new(product_params)   
     @product.user_id = current_user.id #tambahkan code ini setelah code diatas  
    
  10. Lakukan input data di url http://localhost:3000/products/new

Membuat Nested Form 

  1. Edit model sale.rb tambahkan code berikut
  2.  has_many :items  
     accepts_nested_attributes_for :items, allow_destroy: true  
    
  3. Edit partial _form.html.erb di dalam folder view/sales
  4.  <%= form_for(@sale) do |f| %>  
      <% if @sale.errors.any? %>  
       <div id="error_explanation">  
        <h2><%= pluralize(@sale.errors.count, "error") %> prohibited this sale from being saved:</h2>  
        <ul>  
        <% @sale.errors.full_messages.each do |message| %>  
         <li><%= message %></li>  
        <% end %>  
        </ul>  
       </div>  
      <% end %>  
      <div class="field">  
       <%= f.label :name %><br>  
       <%= f.text_field :name %>  
      </div>  
      <%= f.fields_for :items do |builder| %>   
        <%= render 'item_fields', f: builder %>   
      <% end %>   
      <%= link_to_add_fields "Add Item", f, :items %>   
      <div class="actions">  
       <%= f.submit %>  
      </div>  
     <% end %>  
     <script type="text/javascript">   
      jQuery(function() {   
       $('form').on('click', '.remove_fields', function(event) {   
       $(this).prev('input[type=hidden]').val('1');   
       $(this).closest('fieldset').hide();   
       return event.preventDefault();   
       });   
       return $('form').on('click', '.add_fields', function(event) {   
       var regexp, time;   
       time = new Date().getTime();   
       regexp = new RegExp($(this).data('id'), 'g');   
       $(this).before($(this).data('fields').replace(regexp, time));   
       return event.preventDefault();   
       });   
      });   
      </script>   
    
  5. Edit aplication_helper.rb tambahkan code berikut
  6.   def link_to_add_fields(name, f, association)  
       new_object = f.object.send(association).klass.new  
       id = new_object.object_id  
       fields = f.fields_for(association, new_object, child_index: id) do |builder|  
        render(association.to_s.singularize + "_fields", f: builder)  
       end  
       link_to(name, '#', class: "add_fields", data: {id: id, fields: fields.gsub("\n", "")})  
      end 
    
  7. buat file partial _item_fields.html.erb di folder sales/app/views/sales/ isinya seperti berikut
  8.  <fieldset>  
      <%= f.label :product, "Product" %>  
      <%= f.collection_select :product_id, Product.all, :id, :name %>  
      <%= f.label :quantity, "Quantity" %>  
      <%= f.text_field :quantity, class: 'padli' %>  
      <%= f.hidden_field :_destroy %>  
      <%= link_to "remove", '#', class: "remove_fields" %>  
     </fieldset>  
    
  9. Periksa di http://localhost:3000/sales/new hasilnya akan seperti gambar berikut

Fungsi Hitung Penjualan 

Tadikan di form penjualan kita sudah bisa input item dan jumlahnya,  tapi total penjualanya belum terhitung. Nah sekarang kita akan buat menghitung total nya:
  1. Edit model item ketikan code berikut:
  2.   before_save :set_total  
      def set_total  
       if self.quantity.blank?  
        0  
       else  
            self.total = self.quantity * self.product.price  
       end  
      end  
      def subtotal  
       if self.quantity.blank?  
        0  
       else  
            self.quantity * self.product.price  
       end  
      end  
    
  3. Edit model sale untuk menghitung total harga penjualan berikut codenya
  4.   def subtotals  
       self.items.map { |i| i.subtotal }  
      end  
      def total_all  
       subtotals.sum  
      end  
    
  5. Edit controllers sales_controller.rb
  6. Tambahkan pada method create code berikut
  7.  @sale = Sale.new(sale_params)  
     @sale.user_id = current_user.id #tambahkan ini setelah code di atas  
     @sale.total = @sale.total_all #tambahkan ini setelah code di atas  
    
  8. Tambahkan parameter yang akan di permit
  9.  params.require(:sale).permit(:name, items_attributes: [:id,:product_id, :price,:quantity,:total,:_destroy])  
    
  10. Lakukan input data penjualan  http://localhost:3000/sales/new tambahkan beberapa item kemudian simpan
  11. Periksa apakah total penjualanya terhitung
  12. Edit file show di folder view sale
  13.  <p id="notice"><%= notice %></p>  
     <p>  
      <strong>Name:</strong>  
      <%= @sale.name %>  
     </p>  
     <%= link_to 'Edit', edit_sale_path(@sale) %> |  
     <%= link_to 'Back', sales_path %>  
     <h1>Listing Item</h1>  
     <table class="table table-striped">  
      <thead>  
       <tr>  
        <th>Product</th>  
        <th>price</th>  
        <th>quantity</th>  
        <th>total</th>  
        <th></th>  
       </tr>  
      </thead>  
      <tbody>  
       <% @sale.items.each do |item| %>  
        <tr>  
         <td><%= item.product.name %></td>  
         <td><%= item.product.price %></td>  
         <td><%= item.quantity %></td>  
         <td><%= item.total %></td>  
        </tr>  
       <% end %>  
      </tbody>  
     </table>  
     <hr>  
      <p> Total </p>  
      <%= @sale.total%>  
     <hr>  
    

Mengatur User-Roles

Pada bagian ini kita akan mengatur hak akses user pada aplikasi POS.
  1. Generate cancan
  2. $ rails g cancan:ability
  3. Tambahkan code di class ability.rb seperti berikut: 
  4.  class Ability  
      include CanCan::Ability  
      def initialize(user)  
       if user.present?  
        if user.role.name == 'admin'  
         can :manage, :all  
        else  
         can :create , Product  
         can :create , Sale  
         can :read, :all  
        end  
       else  
        can :read, :all  
       end  
      end  
     end  
    
  5.  Edit products_controller tambahkan  load and authorize seperti berikut 
  6.  class ProductsController < ApplicationController  
      before_action :set_product, only: [:show, :edit, :update, :destroy]  
      load_and_authorize_resource  
      # GET /products  
      # GET /products.json  
      def index  
       @products = Product.all  
      end  
      # GET /products/1  
      # GET /products/1.json  
      def show  
      end  
      # GET /products/new  
      def new  
       @product = Product.new  
      end  
      # GET /products/1/edit  
      def edit  
      end  
      # POST /products  
      # POST /products.json  
      def create  
       @product = Product.new(product_params)  
       @product.user_id = current_user.id #tambahkan code ini setelah code diatas   
       respond_to do |format|  
        if @product.save  
         format.html { redirect_to @product, notice: 'Product was successfully created.' }  
         format.json { render :show, status: :created, location: @product }  
        else  
         format.html { render :new }  
         format.json { render json: @product.errors, status: :unprocessable_entity }  
        end  
       end  
      end  
      # PATCH/PUT /products/1  
      # PATCH/PUT /products/1.json  
      def update  
       respond_to do |format|  
        if @product.update(product_params)  
         format.html { redirect_to @product, notice: 'Product was successfully updated.' }  
         format.json { render :show, status: :ok, location: @product }  
        else  
         format.html { render :edit }  
         format.json { render json: @product.errors, status: :unprocessable_entity }  
        end  
       end  
      end  
      # DELETE /products/1  
      # DELETE /products/1.json  
      def destroy  
       @product.destroy  
       respond_to do |format|  
        format.html { redirect_to products_url, notice: 'Product was successfully destroyed.' }  
        format.json { head :no_content }  
       end  
      end  
      private  
       # Use callbacks to share common setup or constraints between actions.  
       def set_product  
        @product = Product.find(params[:id])  
       end  
       # Never trust parameters from the scary internet, only allow the white list through.  
       def product_params  
        params.require(:product).permit(:code, :name, :price, :user_id)  
       end  
     end  
    
  7. Edit sales_controller tambahkan  load and authorize seperti berikut
  8.  class SalesController < ApplicationController  
      before_action :set_sale, only: [:show, :edit, :update, :destroy]  
      load_and_authorize_resource  
      # GET /sales  
      # GET /sales.json  
      def index  
       @sales = Sale.all  
      end  
      # GET /sales/1  
      # GET /sales/1.json  
      def show  
      end  
      # GET /sales/new  
      def new  
       @sale = Sale.new  
      end  
      # GET /sales/1/edit  
      def edit  
      end  
      # POST /sales  
      # POST /sales.json  
      def create  
       @sale = Sale.new(sale_params)  
       @sale.user_id = current_user.id #tambahkan ini setelah code di atas   
       @sale.total = @sale.total_all #tambahkan ini setelah code di atas  
       respond_to do |format|  
        if @sale.save  
         format.html { redirect_to @sale, notice: 'Sale was successfully created.' }  
         format.json { render :show, status: :created, location: @sale }  
        else  
         format.html { render :new }  
         format.json { render json: @sale.errors, status: :unprocessable_entity }  
        end  
       end  
      end  
      # PATCH/PUT /sales/1  
      # PATCH/PUT /sales/1.json  
      def update  
       respond_to do |format|  
        if @sale.update(sale_params)  
         format.html { redirect_to @sale, notice: 'Sale was successfully updated.' }  
         format.json { render :show, status: :ok, location: @sale }  
        else  
         format.html { render :edit }  
         format.json { render json: @sale.errors, status: :unprocessable_entity }  
        end  
       end  
      end  
      # DELETE /sales/1  
      # DELETE /sales/1.json  
      def destroy  
       @sale.destroy  
       respond_to do |format|  
        format.html { redirect_to sales_url, notice: 'Sale was successfully destroyed.' }  
        format.json { head :no_content }  
       end  
      end  
      private  
       # Use callbacks to share common setup or constraints between actions.  
       def set_sale  
        @sale = Sale.find(params[:id])  
       end  
       # Never trust parameters from the scary internet, only allow the white list through.  
       def sale_params  
        params.require(:sale).permit(:name, items_attributes: [:id,:product_id, :price,:quantity,:total,:_destroy])  
       end  
     end  
    
  9. Edit aplication_controller tambahkan baris code berikut
  10.   rescue_from CanCan::AccessDenied do |exception|  
       flash[:error] = "Access denied."  
       redirect_to root_url  
      end  
    
  11. Untuk link tinggal di kasih kondisi seperti berikut
  12.      <td><%= link_to 'Show', sale %></td>  
         <% if can? :update, Sale %>  
          <td><%= link_to 'Edit', edit_sale_path(sale) %></td>  
         <%end%>  
         <% if can? :destroy, Sale %>  
          <td><%= link_to 'Destroy', sale, method: :delete, data: { confirm: 'Are you sure?' } %></td>  
         <% end %>  
    

Public Activities

Fitur ini bertujuan untuk mencatat aktifitas user pada aplikasi. Sehingga apabila ada karyawan yang melakukan aktifitas pelanggaran / kecurangan, sistem admin bisa melihat bukti aktifitasnya.
  1. Generate public_activity
  2. $ rails g public_activity:migration
    $ rake db:migrate
  3. Di aplication_controller.rb tambahkan code
  4.  include PublicActivity::StoreController  
    
  5. Di model product.rb dan sale.rb  tambahkan code berikut
  6.  include PublicActivity::Model  
      tracked owner: ->(controller, model) { controller && controller.current_user}  
    
  7. Buat controller untuk  activities
  8. $ rails g controller activities 
  9. Di routesnya tambahkan code
  10.   root to: 'home#index'  
      resources :activities  
    
  11. Di controllernya tambahkan code berikut 
  12.  class ActivitiesController < ApplicationController  
      def index  
       @activities = PublicActivity::Activity.order("created_at desc")  
      end  
     end  
    
  13. Di viewnya buat file index.html.erb di folder activities isi dengan code berikut
  14.  <h1>Listing Activities</h1>  
     <table class="table table-striped table-bordered table-condensed sortable">  
       <thead>  
         <tr>  
           <th><%= "Activities"%></th>  
         </tr>  
       </thead>  
       <tbody>  
            <% @activities.each do |activity| %>  
         <tr>  
           <td valign="top">   
             <%= activity.owner.email %>  
             <%= render_activity activity %>  
           </td>  
         </tr>  
         <% end %>  
       </tbody>  
       <tfoot>  
         <tr>  
           <td colspan="3"></td>  
         </tr>  
       </tfoot>  
     </table>  
    
  15. Buat folder public_activity di view
  16. Di dalam folder public_activity buat folder product dan sale
  17. Di dalam folder product dan sale buat file html partial _create.html.erb ,_update.html.erb, _destroy.html.erb
  18. Create.html.erb  codenya seperti berikut
  19.  added product  
     <% if activity.trackable %>  
      <%= link_to activity.trackable.name, product_path(activity.trackable.id) %>  
     <% else %>  
      which has since been removed  
     <% end %>  
    
  20. _update.html.erb
  21.  Updated product  
     <% if activity.trackable %>  
      <%= link_to activity.trackable.name, product_path(activity.trackable.id) %>  
     <% else %>  
      which has since been removed  
     <% end %>  
    
  22. _destroy.html.erb
  23.  removed a produc  
    
  24. Masuk folder layout dan edit file application.html.erb tambahkan code dibawah class nav navbar-nav
  25.  ·<li><%= link_to "All Activities", activities_path %></li>  
    
  26. Terakhir lakukan proses insert, update dan delete di http://localhost:3000/sales dan http://localhost:3000/products
  27. Hasilnya  bisa di akses di http://localhost:3000/activities

25 komentar:

  1. makasih pak tutorial nya
    saya ada masalah di baris

    <%= f.fields_for :items do |builder| %>

    keterangan

    uninitialized constant Sale::Item

    tolong dibantu ya pak ? makasih :)

    BalasHapus

  2. Itu karena Model itemnya belum ada.
    Ada yang terlewat, yaitu: baris perintah

    rails g model item product:belongs_to sale:belongs_to quantity:decimal total:decimal

    coba periksa kembali part 1

    Thanks,

    BalasHapus
  3. ini postingan paling menakjubkan pak, thank's a lot brother . . :)
    ditunggu postingan selanjutnya pak . .

    BalasHapus
  4. Very nice Post, sebagai tambahan, edit activities_controller.rb:

    class ActivitiesController < ApplicationController

    def index
    @activities = PublicActivity::Activity.all
    end
    end

    BalasHapus
    Balasan
    1. Makasih Mas,

      Iya ada yang kurang, makasih sudah diingatkan :D

      Kita tambahkan sekarang

      Hapus
  5. Salam,

    Trimkasih sdh berbagi postingan yg sgt bermanfaat bagi kami pembaca.

    Saya punya saran, bagaimana dibuat postingan review aplikasi yg pernah ada atau yg opensource basia ruby on rails (misalnya aplikasi pos, crm, cms, edms, dll)

    Tks

    BalasHapus
    Balasan
    1. Salam,

      Thanks masukannya Mas Yudha.
      Ide bagus, akan kami coba buat untuk post mendatang.

      Hapus
  6. sedikit saran di tutorial nya saat membuat public_activity :

    tutorial no 8 membuat folder public_activity terdapat tutorial no 9
    jadi :
    public_activity
    ----->activity
    ---->product
    ---->sale

    terima kasih

    BalasHapus
  7. om saya ada masalah di error ini knp ya??

    ActionView::MissingTemplate in Sales#new

    BalasHapus
    Balasan
    1. Error itu artinya kamu load view yang tidak ada.

      Hapus
  8. saya ada masalah penambahan file patial item, nah file itu di taruh dmna ya???

    BalasHapus
    Balasan
    1. di folder sales/app/views/sales/

      Hapus
  9. Mas mau tanya di Fungsi Hitung Penjualan bagian menambah bagian user id di sale "@sale.user_id = current_user.id" kok saya error begini ya? NoMethodError in SalesController#create
    undefined method `id' for nil:NilClass .. Mohon infonya, trims

    BalasHapus
    Balasan
    1. User login nya udah di implementasi belum Mas ?

      Hapus
  10. ada yg kurang gan untuk destroy nya, saya error

    Error: Cannot delete or update a parent row: a foreign key constraint fails (`sales_development`.`items`, CONSTRAINT `fk_rails_f6e50c029b` FOREIGN KEY (`sale_id`) REFERENCES `sales` (`id`)): DELETE FROM `sales` WHERE `sales`.`id` = 5

    BalasHapus
    Balasan
    1. Mas Keci, error ini karena kamu mau delete data sales yang sudah ada items nya ( child data )

      Hapus
  11. Komentar ini telah dihapus oleh administrator blog.

    BalasHapus
  12. mantap pak, lumayan buat refrensi rails di indonesia. mantap pak, ditunggu blog selanjutnya

    BalasHapus