From ebb2b4d40713d535c06970b2a17aace4458ecf25 Mon Sep 17 00:00:00 2001 From: "Ira W. Snyder" Date: Thu, 22 Nov 2007 22:32:31 -0800 Subject: [PATCH] Add Purchase system Signed-off-by: Ira W. Snyder --- app/controllers/purchase_controller.rb | 109 ++++++++++++++++++ app/helpers/purchase_helper.rb | 2 + app/models/customer.rb | 2 + app/models/game.rb | 4 + app/models/merchandise.rb | 2 + app/models/merchandise_purchase.rb | 16 +++ app/models/purchase.rb | 13 +++ app/models/rentable.rb | 3 +- app/models/rentable_purchase.rb | 12 ++ app/models/video.rb | 5 + app/views/layouts/purchase.rhtml | 18 +++ app/views/purchase/begin.rhtml | 10 ++ app/views/purchase/buy.rhtml | 2 + app/views/purchase/buy_begin.rhtml | 11 ++ app/views/purchase/list.rhtml | 27 +++++ app/views/purchase/menu.rhtml | 14 +++ app/views/purchase/rent.rhtml | 2 + app/views/purchase/rent_begin.rhtml | 10 ++ db/development.sqlite3 | Bin 13312 -> 17408 bytes db/migrate/017_create_rentable_purchases.rb | 7 ++ .../018_create_merchandise_purchases.rb | 7 ++ db/migrate/019_remove_purchase_due_date.rb | 9 ++ db/schema.rb | 11 +- test/fixtures/merchandise_purchases.yml | 5 + test/fixtures/rentable_purchases.yml | 5 + test/functional/purchase_controller_test.rb | 18 +++ test/unit/merchandise_purchase_test.rb | 10 ++ test/unit/rentable_purchase_test.rb | 10 ++ 28 files changed, 340 insertions(+), 4 deletions(-) create mode 100644 app/controllers/purchase_controller.rb create mode 100644 app/helpers/purchase_helper.rb create mode 100644 app/models/merchandise_purchase.rb create mode 100644 app/models/rentable_purchase.rb create mode 100644 app/views/layouts/purchase.rhtml create mode 100644 app/views/purchase/begin.rhtml create mode 100644 app/views/purchase/buy.rhtml create mode 100644 app/views/purchase/buy_begin.rhtml create mode 100644 app/views/purchase/list.rhtml create mode 100644 app/views/purchase/menu.rhtml create mode 100644 app/views/purchase/rent.rhtml create mode 100644 app/views/purchase/rent_begin.rhtml create mode 100644 db/migrate/017_create_rentable_purchases.rb create mode 100644 db/migrate/018_create_merchandise_purchases.rb create mode 100644 db/migrate/019_remove_purchase_due_date.rb create mode 100644 test/fixtures/merchandise_purchases.yml create mode 100644 test/fixtures/rentable_purchases.yml create mode 100644 test/functional/purchase_controller_test.rb create mode 100644 test/unit/merchandise_purchase_test.rb create mode 100644 test/unit/rentable_purchase_test.rb diff --git a/app/controllers/purchase_controller.rb b/app/controllers/purchase_controller.rb new file mode 100644 index 0000000..1516498 --- /dev/null +++ b/app/controllers/purchase_controller.rb @@ -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 index 0000000..f8a3a64 --- /dev/null +++ b/app/helpers/purchase_helper.rb @@ -0,0 +1,2 @@ +module PurchaseHelper +end diff --git a/app/models/customer.rb b/app/models/customer.rb index 901b9a5..aba5044 100644 --- a/app/models/customer.rb +++ b/app/models/customer.rb @@ -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 diff --git a/app/models/game.rb b/app/models/game.rb index 0ca782b..efdb260 100644 --- a/app/models/game.rb +++ b/app/models/game.rb @@ -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 diff --git a/app/models/merchandise.rb b/app/models/merchandise.rb index 463cfd3..16fd1e8 100644 --- a/app/models/merchandise.rb +++ b/app/models/merchandise.rb @@ -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 index 0000000..94bf758 --- /dev/null +++ b/app/models/merchandise_purchase.rb @@ -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 diff --git a/app/models/purchase.rb b/app/models/purchase.rb index e688f99..ff07e41 100644 --- a/app/models/purchase.rb +++ b/app/models/purchase.rb @@ -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 diff --git a/app/models/rentable.rb b/app/models/rentable.rb index 69ca422..e52af25 100644 --- a/app/models/rentable.rb +++ b/app/models/rentable.rb @@ -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 index 0000000..77b8233 --- /dev/null +++ b/app/models/rentable_purchase.rb @@ -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 diff --git a/app/models/video.rb b/app/models/video.rb index 411c978..fdf90c3 100644 --- a/app/models/video.rb +++ b/app/models/video.rb @@ -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 index 0000000..26387b8 --- /dev/null +++ b/app/views/layouts/purchase.rhtml @@ -0,0 +1,18 @@ + + + + + + Bitem: <%= controller.action_name %> + <%= stylesheet_link_tag 'scaffold' %> + + + +

<%= flash[:notice] %>

+

<%= flash[:error] %>

+ +<%= yield %> + + + diff --git a/app/views/purchase/begin.rhtml b/app/views/purchase/begin.rhtml new file mode 100644 index 0000000..b108db7 --- /dev/null +++ b/app/views/purchase/begin.rhtml @@ -0,0 +1,10 @@ +

Start a Purchase

+ +

Please read the customer's ID number off of their card and enter +it in the box below.

+ +<%= start_form_tag :action => 'customer_ok'%> +<%= text_field 'customer_id', nil %> + <%= submit_tag 'Ok' %> +<%= end_form_tag %> + diff --git a/app/views/purchase/buy.rhtml b/app/views/purchase/buy.rhtml new file mode 100644 index 0000000..61c6d0f --- /dev/null +++ b/app/views/purchase/buy.rhtml @@ -0,0 +1,2 @@ +

Purchase#buy

+

Find me in app/views/purchase/buy.rhtml

diff --git a/app/views/purchase/buy_begin.rhtml b/app/views/purchase/buy_begin.rhtml new file mode 100644 index 0000000..7ea2912 --- /dev/null +++ b/app/views/purchase/buy_begin.rhtml @@ -0,0 +1,11 @@ +

Purchase some Merchandise

+ +

Please read the item's ID number off of the bar code, and type it into +the box below.

+ +<%= start_form_tag :action => 'buy_validate'%> +<%= text_field 'merchandise_id', nil %> + <%= submit_tag 'Ok' %> +<%= end_form_tag %> + + diff --git a/app/views/purchase/list.rhtml b/app/views/purchase/list.rhtml new file mode 100644 index 0000000..593b7a6 --- /dev/null +++ b/app/views/purchase/list.rhtml @@ -0,0 +1,27 @@ +

Listing purchases

+ + + + <% for column in Purchase.content_columns %> + + <% end %> + + +<% for purchase in @purchases %> + + <% for column in Purchase.content_columns %> + + <% end %> + + + + +<% end %> +
<%= column.human_name %>
<%=h purchase.send(column.name) %><%= link_to 'Show', :action => 'show', :id => purchase %><%= link_to 'Edit', :action => 'edit', :id => purchase %><%= link_to 'Destroy', { :action => 'destroy', :id => purchase }, :confirm => 'Are you sure?', :method => :post %>
+ +<%= 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 %> + +
+ +<%= link_to 'New purchase', :action => 'new' %> diff --git a/app/views/purchase/menu.rhtml b/app/views/purchase/menu.rhtml new file mode 100644 index 0000000..ce8a646 --- /dev/null +++ b/app/views/purchase/menu.rhtml @@ -0,0 +1,14 @@ +

Purchase Menu

+ + + + + +<%=link_to 'End Purchase', :action => 'index' %> diff --git a/app/views/purchase/rent.rhtml b/app/views/purchase/rent.rhtml new file mode 100644 index 0000000..076d2be --- /dev/null +++ b/app/views/purchase/rent.rhtml @@ -0,0 +1,2 @@ +

Purchase#rent

+

Find me in app/views/purchase/rent.rhtml

diff --git a/app/views/purchase/rent_begin.rhtml b/app/views/purchase/rent_begin.rhtml new file mode 100644 index 0000000..f69769a --- /dev/null +++ b/app/views/purchase/rent_begin.rhtml @@ -0,0 +1,10 @@ +

Check out a Rentable Item

+ +

Please read the item's ID number off of the bar code, and type it into +the box below.

+ +<%= start_form_tag :action => 'rent_validate'%> +<%= text_field 'rentable_id', nil %> + <%= submit_tag 'Ok' %> +<%= end_form_tag %> + diff --git a/db/development.sqlite3 b/db/development.sqlite3 index b02e912f1c434859016cd5497e185fcaec165e0e..3f10e53d0d1a2208886bfa8264c73991b11818cc 100644 GIT binary patch literal 17408 zcmeHOO>7%Q6rLZ)Zrr+QDZ!)(WRg%P4t24eA1ApGLz1@C4Qc*RB&4wQj+0gG-E?=| zVj)zefP|0`7Y>}b0XM`2i33~^;(~gjLWlzgBqRhvLP#K_O2FH-y}f&Ie(#RMOV(0N43Ekth7x2U#% z1}{gt?-^LaA=I<-$O(MbRM>)EQMoxhlws#IqiETR%BIXyk?>qm$#dP-l#}f7Y$QCM z9?K*(Q<;Q+%dK)MZ~AIOzP6qSUs0cb>W&6H_95h2>lp^$Y+RtOMZqYz$@f4I!QbK0|I1|l7T%l0F!q( z+S7)+Ar`fKq9qJFZ|#ewa$}x5o4B%O=moN*U($`MI!ohz0zwdD*`Wk!M7>zvJN`kFPtW_Yn^ zT(#D<4Zom3=eW&H_JoqZWEna>9tflM?l#~6%f=HMN(Won4$hJw4Cn=Fgtb&abd@(<^ND$wfB1l*^rDz4@|b8zpY8 zI@nrcY-)NYyOi@VCK$WUAa=qSWqVasY~IV9tEZvAv8%A;_(XDaVrZlvnpMkuJGj%S z_~x~iP_(-nzi_@f)LIX%dRHylJlwimY`{7!qh5AJG4tz+IglP1iF=)`QKG1-$t|mw z+5B01TZC(o@2q5F-OzVwN97l7po{0Vl2Y_Kkvx@*KOYP^Q)e^l5^%+NAgys7I_1kA zXd=NU_{%iZXGPpu|CD}%=x_8V_>f65AQ`x;7&t^J>dq+^H>Wf!A0!kVTRyW8#PB(_ zJXQ7oZzB2&{hoeJZ{AfymxGZE>_Y|)1SpDTb*;q1u=YD=6fo(mN-M1Vjx&a78fGgj z{*G+k2BXv6|M61@KSe&Jqx+Doe`d(niy_ypeW_}~*R5*H%d|9^bY8KTL1$MscCj~Y z%`WZ&(fL)A7ddPUz3if4Ku-4z)Bb{ExVwhts%wqj(O*zCljm*2+}T1daaB|9sACPx z^j(_SC@Qw|j>`5r;`)J5w4(!WW?UoMZOy7)Lrpwa%fv|D#(D6p=FQWsp(w=iuV1J& z=y({qs#zSGZREpS8gEGcb{F9GTrR`H*6hmdoFx5T--fB>;TG3?kAbn1>4q1SbnSma zZy?zHZ~QY$L28u@NCy5D20BR#>c^p+Vy2JR9Dj>5wLP85lED40$kg+~As;l@c|+IctEdH)a6QTYzw zU2^N?FeC#)3>;~9<9~Pp(M3wgHOR;$$iVf-+FTd^#Rb;~+$cNV9XNnq=1`OO=M{^& ze!IK)^&_&KPU^NfrK=r$HG74g;}0iN;91tg5j&~&`MEp6?(C#i&;QWjP&AXlo84O- z=~a&~e@kBB;U;pog|Gol^^Q4Vyd}k~uuHtcl(KDTdfwzEIP8qiMS{_XAI2+g2dv9Q zIBZ?D_~kO!^L)$Kwx&k62nU>i!AYnY-;9UdaK!b5tMj6Jd|SO9Y|>CI6Ppm*-tKxp z0`8fg0{{8058|5*^-|aWhadgOBpHwl>_Y~e{a>#CeR$*LkR=1afV2MF(E_43=$rHk zoux7IJNbgVOP(W7l0N(s{sg~)IZmTL(C6qKbPb@prNQ=AGz3uw2sNB<9EQx*>|KAoa5xqa#8EL^g1ETQ z&dbA_n<*^F=%XJF{POW@PscHK*8gGr5u#tx_u%(G&(JY?gnUQdC$AEPjFSld7KAeW zKN*PiiAdO&cVdn*ISx`b4dSf>yU$Bql)I)rdE PcE2DlDu}yJ2uJ<|0Vevt delta 124 zcmZqZU~I^kASJ|bmjM-+Zq&KV&M2^nXH4ts;!94C;)G!MTY!ISK*! z1 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 index 0000000..b49c4eb --- /dev/null +++ b/test/fixtures/merchandise_purchases.yml @@ -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 index 0000000..b49c4eb --- /dev/null +++ b/test/fixtures/rentable_purchases.yml @@ -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 index 0000000..bcbcfa7 --- /dev/null +++ b/test/functional/purchase_controller_test.rb @@ -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 index 0000000..85cf484 --- /dev/null +++ b/test/unit/merchandise_purchase_test.rb @@ -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 index 0000000..327daab --- /dev/null +++ b/test/unit/rentable_purchase_test.rb @@ -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 -- 2.25.1