Add Purchase system
authorIra W. Snyder <devel@irasnyder.com>
Fri, 23 Nov 2007 06:32:31 +0000 (22:32 -0800)
committerIra W. Snyder <devel@irasnyder.com>
Fri, 23 Nov 2007 06:32:31 +0000 (22:32 -0800)
Signed-off-by: Ira W. Snyder <devel@irasnyder.com>
28 files changed:
app/controllers/purchase_controller.rb [new file with mode: 0644]
app/helpers/purchase_helper.rb [new file with mode: 0644]
app/models/customer.rb
app/models/game.rb
app/models/merchandise.rb
app/models/merchandise_purchase.rb [new file with mode: 0644]
app/models/purchase.rb
app/models/rentable.rb
app/models/rentable_purchase.rb [new file with mode: 0644]
app/models/video.rb
app/views/layouts/purchase.rhtml [new file with mode: 0644]
app/views/purchase/begin.rhtml [new file with mode: 0644]
app/views/purchase/buy.rhtml [new file with mode: 0644]
app/views/purchase/buy_begin.rhtml [new file with mode: 0644]
app/views/purchase/list.rhtml [new file with mode: 0644]
app/views/purchase/menu.rhtml [new file with mode: 0644]
app/views/purchase/rent.rhtml [new file with mode: 0644]
app/views/purchase/rent_begin.rhtml [new file with mode: 0644]
db/development.sqlite3
db/migrate/017_create_rentable_purchases.rb [new file with mode: 0644]
db/migrate/018_create_merchandise_purchases.rb [new file with mode: 0644]
db/migrate/019_remove_purchase_due_date.rb [new file with mode: 0644]
db/schema.rb
test/fixtures/merchandise_purchases.yml [new file with mode: 0644]
test/fixtures/rentable_purchases.yml [new file with mode: 0644]
test/functional/purchase_controller_test.rb [new file with mode: 0644]
test/unit/merchandise_purchase_test.rb [new file with mode: 0644]
test/unit/rentable_purchase_test.rb [new file with mode: 0644]

diff --git a/app/controllers/purchase_controller.rb b/app/controllers/purchase_controller.rb
new file mode 100644 (file)
index 0000000..1516498
--- /dev/null
@@ -0,0 +1,109 @@
+class PurchaseController < ApplicationController
+
+  def index
+    redirect_to :action => :begin
+  end
+
+  def list
+    @purchase_pages, @purchase = paginate :purchases, :per_page => 100
+  end
+
+  def begin
+    # enter a customer id here
+    render :action => 'begin'
+    session[:total] = 0.00
+  end
+
+  def customer_ok
+    if Customer.find_by_id(params[:customer_id])
+      session[:customer_id] = params[:customer_id]
+      redirect_to :action => :menu
+    else
+      flash[:error] = "Customer ID is invalid"
+      redirect_to :action => :begin
+    end
+  end
+
+  def menu
+    @customer = Customer.find_by_id(session[:customer_id])
+    @total_price = session[:total]
+    render :action => 'menu'
+  end
+
+  def rent_begin
+    render :action => 'rent_begin'
+  end
+
+  def rent_validate
+    @customer = Customer.find_by_id(session[:customer_id])
+    @rentable = Rentable.find_by_id(params[:rentable_id])
+
+    if @customer.nil?
+      flash[:error] = "Customer ID is invalid"
+      redirect_to :action => :begin
+      return
+    end
+
+    if @rentable.nil?
+      flash[:error] = "Rentable ID is invalid"
+      redirect_to :action => :rent_begin
+      return
+    end
+
+    # Actually record the purchase
+    purchase = RentablePurchase.new
+    purchase.customer_id = session[:customer_id]
+    purchase.date = Time.now.to_date
+    purchase.price = @rentable.calculated_price
+    session[:total] += @rentable.calculated_price
+    purchase.rentable = @rentable
+    purchase.save!
+
+    flash[:notice] = "Successfully made purchase"
+    redirect_to :action => :menu
+  end
+
+  def buy_begin
+    render :action => 'buy_begin'
+  end
+
+  def buy_validate
+    @customer = Customer.find_by_id(session[:customer_id])
+    @merchandise = Merchandise.find_by_id(params[:merchandise_id])
+  
+    if @customer.nil?
+      flash[:error] = "Customer ID is invalid"
+      redirect_to :action => :begin
+      return
+    end
+
+    if @merchandise.nil?
+      flash[:error] = "Merchandise ID is invalid"
+      redirect_to :action => :buy_begin
+      return
+    end
+
+    if @merchandise.quantity < 1
+      flash[:error] = "The system thinks we are out of this merchandise item!"
+      redirect_to :action => :buy_begin
+      return
+    end
+
+    # Actually record the purchase
+    purchase = MerchandisePurchase.new
+    purchase.customer_id = session[:customer_id]
+    purchase.date = Time.now.to_date
+    purchase.price = @merchandise.price
+    session[:total] += @merchandise.price
+    purchase.merchandise = @merchandise
+    purchase.quantity = 1
+    @merchandise.quantity -= 1
+
+    # Save both the merchandise (we changed the quantity) and the purchase to the log
+    @merchandise.save!
+    purchase.save!
+
+    flash[:notice] = "Successfully made purchase"
+    redirect_to :action => :menu
+  end
+end
diff --git a/app/helpers/purchase_helper.rb b/app/helpers/purchase_helper.rb
new file mode 100644 (file)
index 0000000..f8a3a64
--- /dev/null
@@ -0,0 +1,2 @@
+module PurchaseHelper
+end
index 901b9a5..aba5044 100644 (file)
@@ -1,6 +1,8 @@
 class Customer < ActiveRecord::Base
   has_many :coitems
   has_many :bitems
+  has_many :merchandise_purchases
+  has_many :rentable_purchases
 
   validates_presence_of :name, :email, :phone, :address
   validates_numericality_of :debt
index 0ca782b..efdb260 100644 (file)
@@ -2,4 +2,8 @@ class Game < Rentable
   validates_presence_of :game_genre
   validates_presence_of :platform
 
+  def calculated_price
+    # FIXME: generate this based on day of week, newrelase
+    return 11
+  end
 end
index 463cfd3..16fd1e8 100644 (file)
@@ -1,4 +1,6 @@
 class Merchandise < ActiveRecord::Base
+  has_many :merchandise_purchases
+
   validates_presence_of :name
   validates_numericality_of :quantity
   validates_numericality_of :price
diff --git a/app/models/merchandise_purchase.rb b/app/models/merchandise_purchase.rb
new file mode 100644 (file)
index 0000000..94bf758
--- /dev/null
@@ -0,0 +1,16 @@
+class MerchandisePurchase < Purchase
+  belongs_to :merchandise
+  belongs_to :customer
+
+  validates_presence_of :merchandise_id
+  validates_presence_of :quantity
+
+  validates_numericality_of :merchandise_id
+  validates_numericality_of :quantity
+
+  protected
+  def validate
+    errors.add(:merchandise_id, "does not exist in the database") if merchandise.nil?
+    #errors.add(:quantity, "must be 1 or greater") if quantity < 1
+  end
+end
index e688f99..ff07e41 100644 (file)
@@ -1,2 +1,15 @@
 class Purchase < ActiveRecord::Base
+  belongs_to :customer
+
+  validates_presence_of :customer_id
+  validates_presence_of :date
+  validates_presence_of :price
+  validates_numericality_of :price
+
+  protected
+  def validate
+    errors.add(:price, "cannot be negative") if price < 0
+    errors.add(:price, "cannot be less than $0.01") if price < 0.01
+    errors.add(:customer_id, "does not exist in the database") if customer.nil?
+  end
 end
index 69ca422..e52af25 100644 (file)
@@ -1,5 +1,6 @@
 class Rentable < ActiveRecord::Base
-  has_many :coitem
+  has_many :coitems
+  has_many :rentable_purchases
 
   validates_presence_of :title
   # don't validate newrelease, false is ok
diff --git a/app/models/rentable_purchase.rb b/app/models/rentable_purchase.rb
new file mode 100644 (file)
index 0000000..77b8233
--- /dev/null
@@ -0,0 +1,12 @@
+class RentablePurchase < Purchase
+  belongs_to :rentable
+  belongs_to :customer
+
+  validates_presence_of :rentable_id
+  validates_numericality_of :rentable_id
+
+  protected
+  def validate
+    errors.add(:rentable_id, "is not in the database") if rentable.nil?
+  end
+end
index 411c978..fdf90c3 100644 (file)
@@ -6,6 +6,11 @@ class Video < Rentable
   validates_presence_of :video_genre
   validates_presence_of :media
 
+  def calculated_price
+    # FIXME: generate this based on day of week, newrelase
+    return 11
+  end
+
   protected
   def validate
     errors.add(:video_genre, "does not exist in the database") if video_genre.nil?
diff --git a/app/views/layouts/purchase.rhtml b/app/views/layouts/purchase.rhtml
new file mode 100644 (file)
index 0000000..26387b8
--- /dev/null
@@ -0,0 +1,18 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+       "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+  <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
+  <title>Bitem: <%= controller.action_name %></title>
+  <%= stylesheet_link_tag 'scaffold' %>
+</head>
+<body>
+
+<p style="color: green"><%= flash[:notice] %></p>
+<p style="color: red"><%= flash[:error] %></p>
+
+<%= yield  %>
+
+</body>
+</html>
diff --git a/app/views/purchase/begin.rhtml b/app/views/purchase/begin.rhtml
new file mode 100644 (file)
index 0000000..b108db7
--- /dev/null
@@ -0,0 +1,10 @@
+<h1>Start a Purchase</h1>
+
+<p>Please read the customer's ID number off of their card and enter
+it in the box below.</p>
+
+<%= start_form_tag :action => 'customer_ok'%>
+<%= text_field 'customer_id', nil  %>
+  <%= submit_tag 'Ok' %></form>
+<%= end_form_tag %>
+
diff --git a/app/views/purchase/buy.rhtml b/app/views/purchase/buy.rhtml
new file mode 100644 (file)
index 0000000..61c6d0f
--- /dev/null
@@ -0,0 +1,2 @@
+<h1>Purchase#buy</h1>
+<p>Find me in app/views/purchase/buy.rhtml</p>
diff --git a/app/views/purchase/buy_begin.rhtml b/app/views/purchase/buy_begin.rhtml
new file mode 100644 (file)
index 0000000..7ea2912
--- /dev/null
@@ -0,0 +1,11 @@
+<h1>Purchase some Merchandise</h1>
+
+<p>Please read the item's ID number off of the bar code, and type it into
+the box below.</p>
+
+<%= start_form_tag :action => 'buy_validate'%>
+<%= text_field 'merchandise_id', nil %>
+  <%= submit_tag 'Ok' %></form>
+<%= end_form_tag %>
+
+
diff --git a/app/views/purchase/list.rhtml b/app/views/purchase/list.rhtml
new file mode 100644 (file)
index 0000000..593b7a6
--- /dev/null
@@ -0,0 +1,27 @@
+<h1>Listing purchases</h1>
+
+<table>
+  <tr>
+  <% for column in Purchase.content_columns %>
+    <th><%= column.human_name %></th>
+  <% end %>
+  </tr>
+  
+<% for purchase in @purchases %>
+  <tr>
+  <% for column in Purchase.content_columns %>
+    <td><%=h purchase.send(column.name) %></td>
+  <% end %>
+    <td><%= link_to 'Show', :action => 'show', :id => purchase %></td>
+    <td><%= link_to 'Edit', :action => 'edit', :id => purchase %></td>
+    <td><%= link_to 'Destroy', { :action => 'destroy', :id => purchase }, :confirm => 'Are you sure?', :method => :post %></td>
+  </tr>
+<% end %>
+</table>
+
+<%= link_to 'Previous page', { :page => @purchase_pages.current.previous } if @purchase_pages.current.previous %>
+<%= link_to 'Next page', { :page => @purchase_pages.current.next } if @purchase_pages.current.next %> 
+
+<br />
+
+<%= link_to 'New purchase', :action => 'new' %>
diff --git a/app/views/purchase/menu.rhtml b/app/views/purchase/menu.rhtml
new file mode 100644 (file)
index 0000000..ce8a646
--- /dev/null
@@ -0,0 +1,14 @@
+<h1>Purchase Menu</h1>
+
+<ul>
+  <li>Customer ID: <%= @customer.id %></li>
+  <li>Customer Name: <%= @customer.name %></li>
+  <li>Total Price: <%= @total_price %></li>
+</ul>
+
+<ul>
+  <li><%=link_to 'Rent an Item', :action => 'rent_begin' %></li>
+  <li><%=link_to 'Buy Merchandise', :action => 'buy_begin' %></li>
+</ul>
+
+<%=link_to 'End Purchase', :action => 'index' %>
diff --git a/app/views/purchase/rent.rhtml b/app/views/purchase/rent.rhtml
new file mode 100644 (file)
index 0000000..076d2be
--- /dev/null
@@ -0,0 +1,2 @@
+<h1>Purchase#rent</h1>
+<p>Find me in app/views/purchase/rent.rhtml</p>
diff --git a/app/views/purchase/rent_begin.rhtml b/app/views/purchase/rent_begin.rhtml
new file mode 100644 (file)
index 0000000..f69769a
--- /dev/null
@@ -0,0 +1,10 @@
+<h1>Check out a Rentable Item</h1>
+
+<p>Please read the item's ID number off of the bar code, and type it into
+the box below.</p>
+
+<%= start_form_tag :action => 'rent_validate'%>
+<%= text_field 'rentable_id', nil %>
+  <%= submit_tag 'Ok' %></form>
+<%= end_form_tag %>
+
index b02e912..3f10e53 100644 (file)
Binary files a/db/development.sqlite3 and b/db/development.sqlite3 differ
diff --git a/db/migrate/017_create_rentable_purchases.rb b/db/migrate/017_create_rentable_purchases.rb
new file mode 100644 (file)
index 0000000..9d6f312
--- /dev/null
@@ -0,0 +1,7 @@
+class CreateRentablePurchases < ActiveRecord::Migration
+  def self.up
+  end
+
+  def self.down
+  end
+end
diff --git a/db/migrate/018_create_merchandise_purchases.rb b/db/migrate/018_create_merchandise_purchases.rb
new file mode 100644 (file)
index 0000000..4acf566
--- /dev/null
@@ -0,0 +1,7 @@
+class CreateMerchandisePurchases < ActiveRecord::Migration
+  def self.up
+  end
+
+  def self.down
+  end
+end
diff --git a/db/migrate/019_remove_purchase_due_date.rb b/db/migrate/019_remove_purchase_due_date.rb
new file mode 100644 (file)
index 0000000..a917da9
--- /dev/null
@@ -0,0 +1,9 @@
+class RemovePurchaseDueDate < ActiveRecord::Migration
+  def self.up
+    remove_column :purchases, :due_date
+  end
+
+  def self.down
+    add_column :purchases, :due_date, :date
+  end
+end
index 212ac71..2e0f138 100644 (file)
@@ -2,7 +2,7 @@
 # migrations feature of ActiveRecord to incrementally modify your database, and
 # then regenerate this schema definition.
 
-ActiveRecord::Schema.define(:version => 16) do
+ActiveRecord::Schema.define(:version => 19) do
 
   create_table "bitems", :force => true do |t|
     t.column "customer_id",    :integer, :null => false
@@ -29,6 +29,9 @@ ActiveRecord::Schema.define(:version => 16) do
     t.column "name", :string, :null => false
   end
 
+  create_table "merchandise_purchases", :force => true do |t|
+  end
+
   create_table "merchandises", :force => true do |t|
     t.column "name",     :string,                                                 :null => false
     t.column "quantity", :integer,                               :default => 0,   :null => false
@@ -39,9 +42,8 @@ ActiveRecord::Schema.define(:version => 16) do
     t.column "type",           :string
     t.column "customer_id",    :integer
     t.column "date",           :date
-    t.column "price",          :decimal, :precision => 8, :scale => 2, :default => 0.0
+    t.column "price",          :decimal, :default => 0.0
     t.column "rentable_id",    :integer
-    t.column "due_date",       :date
     t.column "merchandise_id", :integer
     t.column "quantity",       :integer
   end
@@ -57,6 +59,9 @@ ActiveRecord::Schema.define(:version => 16) do
     t.column "platform",    :integer
   end
 
+# Could not dump table "sqlite_sequence" because of following StandardError
+#   Unknown type '' for column 'name'
+
   create_table "videogenres", :force => true do |t|
     t.column "name", :string, :null => false
   end
diff --git a/test/fixtures/merchandise_purchases.yml b/test/fixtures/merchandise_purchases.yml
new file mode 100644 (file)
index 0000000..b49c4eb
--- /dev/null
@@ -0,0 +1,5 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+one:
+  id: 1
+two:
+  id: 2
diff --git a/test/fixtures/rentable_purchases.yml b/test/fixtures/rentable_purchases.yml
new file mode 100644 (file)
index 0000000..b49c4eb
--- /dev/null
@@ -0,0 +1,5 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+one:
+  id: 1
+two:
+  id: 2
diff --git a/test/functional/purchase_controller_test.rb b/test/functional/purchase_controller_test.rb
new file mode 100644 (file)
index 0000000..bcbcfa7
--- /dev/null
@@ -0,0 +1,18 @@
+require File.dirname(__FILE__) + '/../test_helper'
+require 'purchase_controller'
+
+# Re-raise errors caught by the controller.
+class PurchaseController; def rescue_action(e) raise e end; end
+
+class PurchaseControllerTest < Test::Unit::TestCase
+  def setup
+    @controller = PurchaseController.new
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+  end
+
+  # Replace this with your real tests.
+  def test_truth
+    assert true
+  end
+end
diff --git a/test/unit/merchandise_purchase_test.rb b/test/unit/merchandise_purchase_test.rb
new file mode 100644 (file)
index 0000000..85cf484
--- /dev/null
@@ -0,0 +1,10 @@
+require File.dirname(__FILE__) + '/../test_helper'
+
+class MerchandisePurchaseTest < Test::Unit::TestCase
+  fixtures :merchandise_purchases
+
+  # Replace this with your real tests.
+  def test_truth
+    assert true
+  end
+end
diff --git a/test/unit/rentable_purchase_test.rb b/test/unit/rentable_purchase_test.rb
new file mode 100644 (file)
index 0000000..327daab
--- /dev/null
@@ -0,0 +1,10 @@
+require File.dirname(__FILE__) + '/../test_helper'
+
+class RentablePurchaseTest < Test::Unit::TestCase
+  fixtures :rentable_purchases
+
+  # Replace this with your real tests.
+  def test_truth
+    assert true
+  end
+end