diff --git a/Gemfile.lock b/Gemfile.lock index e228e29..2afb47b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,8 +1,8 @@ GIT - remote: https://github.com/spacemunkay/acts_as_loggable.git - revision: fd2c276abad00567a294a1a841dce9861e240c82 + remote: git@github.com:spacemunkay/acts_as_loggable.git + revision: 6033fe5a44878bb374f73fe473fed9a3396ddc74 specs: - acts_as_loggable (0.0.6) + acts_as_loggable (0.0.7) activerecord (>= 3.0) activesupport (~> 3.0) diff --git a/app/components/app_tab_panel.rb b/app/components/app_tab_panel.rb index 7442c27..6b346a6 100644 --- a/app/components/app_tab_panel.rb +++ b/app/components/app_tab_panel.rb @@ -10,17 +10,33 @@ class AppTabPanel < Netzke::Basepack::TabPanel #all users # (had to use hash for borders to get the title to display properly) - @@app_tab_panel_items = [ :bikes_border, {layout: :fit, wrappedComponent: :brands_and_models_border, title: "Brands/Models"}] + @@app_tab_panel_items = [ :bikes_border, + { layout: :fit, + wrappedComponent: :brands_and_models_border, + title: "Brands/Models"} + ] #for users if controller.current_user.user? # (had to use hash for borders to get the title to display properly) - @@app_tab_panel_items.concat [{ layout: :fit, wrappedComponent: :user_profile_border, title: "Profile"}] + @@app_tab_panel_items.concat [{ layout: :fit, + wrappedComponent: :user_profile_border, + title: "Profile"}, + { layout: :fit, + wrappedComponent: :user_transactions_border, + title: "Transactions"} + ] end #for admins if controller.current_user.admin? # (had to use hash for borders to get the title to display properly) - @@app_tab_panel_items.concat [{ layout: :fit, wrappedComponent: :users_and_profiles_border, title: "Users/Profiles"}, :logs] + @@app_tab_panel_items.concat [{ layout: :fit, + wrappedComponent: :users_and_profiles_border, + title: "Users/Profiles"}, + { layout: :fit, + wrappedComponent: :transactions_border, + title: "Transactions"}, + :logs] end @@app_tab_panel_items.each do |item| @@ -31,7 +47,6 @@ class AppTabPanel < Netzke::Basepack::TabPanel end end - c.active_tab = 0 c.prevent_header = true c.tbar = [:sign_out] c.items = @@app_tab_panel_items diff --git a/app/components/bike_logs.rb b/app/components/bike_logs.rb index 9564af4..9c0b1fe 100644 --- a/app/components/bike_logs.rb +++ b/app/components/bike_logs.rb @@ -44,6 +44,7 @@ class BikeLogs < Netzke::Basepack::Grid { :name => :start_date}, { :name => :end_date}, { :name => :description}, + #had to hack acts_as_loggable/log.rb to get this to work { :name => :bike_action__action, :field_label => 'Action'} ] end diff --git a/app/components/bikes_border.rb b/app/components/bikes_border.rb index a3480dc..ab626da 100644 --- a/app/components/bikes_border.rb +++ b/app/components/bikes_border.rb @@ -6,6 +6,7 @@ class BikesBorder < Netzke::Base def configure(c) super + c.header = false c.title = "Bikes" c.items = [ { netzke_component: :bikes, region: :center, split: true }, diff --git a/app/components/customers.rb b/app/components/customers.rb new file mode 100644 index 0000000..89a75c7 --- /dev/null +++ b/app/components/customers.rb @@ -0,0 +1,21 @@ +class Customers < Netzke::Basepack::Grid + def configure(c) + c.model = "Customer" + end + + #override with nil to remove actions + def default_bbar + [ :apply, :add_in_form, :search ] + end + + #needed for transactions customer selection + js_configure do |c| + c.mixin :init_component + end + + #needed for transactions customer selection + endpoint :select_customer do |params, this| + session[:selected_customer_id] = params[:customer_id] + session[:selected_customer_type] = params[:customer_type] + end +end diff --git a/app/components/customers/javascripts/init_component.js b/app/components/customers/javascripts/init_component.js new file mode 100644 index 0000000..29aa00c --- /dev/null +++ b/app/components/customers/javascripts/init_component.js @@ -0,0 +1,11 @@ +{ + initComponent: function(){ + // calling superclass's initComponent + this.callParent(); + this.getView().on('itemclick', function(view, record){ + // The beauty of using Ext.Direct: calling 3 endpoints in a row, which results in a single call to the server! + console.log("user: " + record.get('id') ); + this.selectCustomer({customer_id: record.get('id'), customer_type: 'Customer'}); + }, this); + } +} diff --git a/app/components/transaction_logs.rb b/app/components/transaction_logs.rb new file mode 100644 index 0000000..7cbdc4e --- /dev/null +++ b/app/components/transaction_logs.rb @@ -0,0 +1,67 @@ +class TransactionLogs < Netzke::Basepack::Grid + + def configure(c) + super + + c.model = "ActsAsLoggable::Log" + c.title = "Transaction Payments" + c.data_store = {auto_load: false} + c.scope = lambda { |rel| rel.where(:loggable_type => 'Transaction',:loggable_id => session[:selected_transaction_id]);} + c.strong_default_attrs = { + :loggable_type => 'Transaction', + :loggable_id => session[:selected_transaction_id], + :log_action_type => 'ActsAsLoggable::TransactionAction', + :logger_type => 'User', + :logger_id => controller.current_user.id, + :start_date => Time.now.to_formatted_s(:db), + :end_date => Time.now.to_formatted_s(:db) + } + + c.columns = [ + { :name => :start_date, :format => "g:ia - D, M j - Y", :width => 165, :default_value => Time.now.to_formatted_s(:db), :text => 'Date' }, + { :name => :description, :text => "Amount"} , + { :name => :transaction_action__action, :text => 'Method'}, + { :name => :logged_by, :getter => lambda{ |rec| + user = User.find_by_id(rec.logger_id) + user.nil? ? "" : "#{user.first_name} #{user.last_name}" + }, + :text => "Processed by" + } + ] + + if controller.current_user.user? + c.prohibit_update = true + c.prohibit_create = true + c.prohibit_delete = true + end + + end + + def default_fields_for_forms + customer = nil + item = nil + if session[:selected_transaction_id] + trans = Transaction.find_by_id(session[:selected_transaction_id]) + customer = trans.customer + item = trans.item + end + customer = "No Customer Selected" if customer.nil? + item = "No Item Selected" if item.nil? + [ + { :no_binding => true, :xtype => 'displayfield', :fieldLabel => "Payment from:", :value => "#{customer.to_s}"}, + { :no_binding => true, :xtype => 'displayfield', :fieldLabel => "Payment for:", :value => "#{item.to_s}"}, + { :name => :description, :xtype => 'numberfield', :field_label => 'Amount'}, + #had to hack acts_as_loggable/log.rb to get this to work + { :name => :transaction_action__action, :field_label => 'Payment Method'} + ] + end + + + #override with nil to remove actions + def default_bbar + bbar = [ :search ] + bbar.concat [ :apply, :add_in_form ] if not controller.current_user.user? + bbar + end + +end diff --git a/app/components/transactions.rb b/app/components/transactions.rb new file mode 100644 index 0000000..4fe60bd --- /dev/null +++ b/app/components/transactions.rb @@ -0,0 +1,62 @@ +class Transactions < Netzke::Basepack::Grid + def configure(c) + super + c.model = "Transaction" + c.strong_default_attrs = { + :vendor_id => controller.current_user.id, + :customer_id => session[:selected_customer_id], + :customer_type => session[:selected_customer_type] + } + c.columns = [ + :amount, + :item, + { :name => :bike__serial_number}, + { :name => :vendor, :getter => lambda { |rec| + user = rec.vendor + user.nil? ? "" : "#{user.first_name} #{user.last_name}" + } + }, + { :name => :customer, :getter => lambda { |rec| + user = rec.customer + user.nil? ? "" : "#{user.first_name} #{user.last_name}" + } + }, + :created_at + ] + + end + + def default_fields_for_forms + bike_store = Bike.all.map { |b| [b.id, b.serial_number] } + user_store = User.all.map { |u| [u.id, u.to_s] } + customer = nil + if session[:selected_customer_type] == "User" + customer = User.find_by_id(session[:selected_customer_id]) + elsif session[:selected_customer_type] == "Customer" + customer = Customer.find_by_id(session[:selected_customer_id]) + end + + customer = "No User Selected" if customer.nil? + [ + { :no_binding => true, :xtype => 'displayfield', :fieldLabel => "Creating Transaction for:", :value => "#{customer.to_s}"}, + :amount, + :item, + { :name => :for_bike, :checkboxName => :bike_item, :inputValue => true, :title => "Selling a bike?", + :xtype => 'fieldset', :checkboxToggle => true, :collapsed => true, :items => [ + {:xtype => 'combo', :no_binding => true, :name => :bike_id, :title => 'Bike', :fieldLabel => 'Bike', :store => bike_store} + ] + } + ] + end + + #override with nil to remove actions + def default_bbar + [ :apply, :add_in_form, :search ] + end +=begin + #needed for transaction selection + js_configure do |c| + c.mixin :init_component + end +=end +end diff --git a/app/components/transactions_border.rb b/app/components/transactions_border.rb new file mode 100644 index 0000000..b66296f --- /dev/null +++ b/app/components/transactions_border.rb @@ -0,0 +1,30 @@ +class TransactionsBorder < Netzke::Base + # Remember regions collapse state and size + include Netzke::Basepack::ItemPersistence + component :transactions + component :transaction_logs + #users and customers components are required for the transactions form + component :users_and_customers_accordian + + def configure(c) + super + c.header = false + c.title = "Transactions" + c.items = [ + { netzke_component: :transactions, region: :center, height: 300, split: true }, + { netzke_component: :transaction_logs, region: :east, width: 300, split: true }, + { netzke_component: :users_and_customers_accordian, region: :south, height: 300, split: true } + ] + end + + js_configure do |c| + c.layout = :border + c.border = false + c.mixin :init_component + end + + endpoint :select_transaction do |params, this| + session[:selected_transaction_id] = params[:transaction_id] + end + +end diff --git a/app/components/transactions_border/javascripts/init_component.js b/app/components/transactions_border/javascripts/init_component.js new file mode 100644 index 0000000..5d41919 --- /dev/null +++ b/app/components/transactions_border/javascripts/init_component.js @@ -0,0 +1,14 @@ +{ + initComponent: function(){ + // calling superclass's initComponent + this.callParent(); + + // setting the 'rowclick' event + var view = this.getComponent('transactions').getView(); + view.on('itemclick', function(view, record){ + // The beauty of using Ext.Direct: calling 3 endpoints in a row, which results in a single call to the server! + this.selectTransaction({transaction_id: record.get('id')}); + this.getComponent('transaction_logs').getStore().load(); + }, this); + } +} diff --git a/app/components/user_logs.rb b/app/components/user_logs.rb index c5dfbbf..ee0aebd 100644 --- a/app/components/user_logs.rb +++ b/app/components/user_logs.rb @@ -58,6 +58,7 @@ class UserLogs < Netzke::Basepack::Grid { :name => :start_date}, { :name => :end_date}, { :name => :description}, + #had to hack acts_as_loggable/log.rb to get this to work { :name => :user_action__action, :field_label => 'Action', :value => action_id}, { :name => :for_bike, :checkboxName => :copy_log, :inputValue => true, :title => "Copy description to a Bike's History?", :xtype => 'fieldset', :checkboxToggle => true, :collapsed => true, :items => [ {:xtype => 'combo', :no_binding => true, :name => :copy_id, :title => 'Bike', :fieldLabel => 'Bike', :store => bike_store, :value => bike_id} diff --git a/app/components/user_transactions.rb b/app/components/user_transactions.rb new file mode 100644 index 0000000..78688a3 --- /dev/null +++ b/app/components/user_transactions.rb @@ -0,0 +1,40 @@ +class UserTransactions < Netzke::Basepack::Grid + + def configure(c) + super + + c.model = "Transaction" + c.title = "Transactions" + c.scope = lambda { |rel| rel.where(:customer_id => controller.current_user.id, :customer_type => 'User');} + c.data_store = { auto_load: true } + c.columns = [ + :amount, + :item, + { :name => :bike__serial_number}, + { :name => :vendor, :getter => lambda { |rec| + user = rec.vendor + user.nil? ? "" : "#{user.first_name} #{user.last_name}" + } + }, + { :name => :customer, :getter => lambda { |rec| + user = rec.customer + user.nil? ? "" : "#{user.first_name} #{user.last_name}" + } + }, + :created_at + ] + + if controller.current_user.user? + c.prohibit_update = true + c.prohibit_create = true + c.prohibit_delete = true + end + end + + #override with nil to remove actions + def default_bbar + bbar = [ :search ] + bbar.concat [ :apply, :add_in_form ] if not controller.current_user.user? + bbar + end +end diff --git a/app/components/user_transactions_border.rb b/app/components/user_transactions_border.rb new file mode 100644 index 0000000..0b50dd1 --- /dev/null +++ b/app/components/user_transactions_border.rb @@ -0,0 +1,27 @@ +class UserTransactionsBorder < Netzke::Base + # Remember regions collapse state and size + include Netzke::Basepack::ItemPersistence + component :user_transactions + component :transaction_logs + + def configure(c) + super + c.header = false + c.title = "Transactions" + c.items = [ + { netzke_component: :user_transactions, region: :center, height: 300, split: true }, + { netzke_component: :transaction_logs, region: :south, height: 300, split: true } + ] + end + + js_configure do |c| + c.layout = :border + c.border = false + c.mixin :init_component + end + + endpoint :select_transaction do |params, this| + session[:selected_transaction_id] = params[:transaction_id] + end + +end diff --git a/app/components/user_transactions_border/javascripts/init_component.js b/app/components/user_transactions_border/javascripts/init_component.js new file mode 100644 index 0000000..8266a14 --- /dev/null +++ b/app/components/user_transactions_border/javascripts/init_component.js @@ -0,0 +1,14 @@ +{ + initComponent: function(){ + // calling superclass's initComponent + this.callParent(); + + // setting the 'rowclick' event + var view = this.getComponent('user_transactions').getView(); + view.on('itemclick', function(view, record){ + // The beauty of using Ext.Direct: calling 3 endpoints in a row, which results in a single call to the server! + this.selectTransaction({transaction_id: record.get('id')}); + this.getComponent('transaction_logs').getStore().load(); + }, this); + } +} diff --git a/app/components/users.rb b/app/components/users.rb index e545993..5934bd2 100644 --- a/app/components/users.rb +++ b/app/components/users.rb @@ -1,6 +1,7 @@ class Users < Netzke::Basepack::Grid def configure(c) super + c.header = false c.model = "User" c.columns = [ @@ -15,6 +16,17 @@ class Users < Netzke::Basepack::Grid #override with nil to remove actions def default_bbar - [ :apply, :add_in_form ] + [ :apply, :add_in_form, :search ] + end + + #needed for transactions customer selection + js_configure do |c| + c.mixin :init_component + end + + #needed for transactions customer selection + endpoint :select_customer do |params, this| + session[:selected_customer_id] = params[:customer_id] + session[:selected_customer_type] = params[:customer_type] end end diff --git a/app/components/users/javascripts/init_component.js b/app/components/users/javascripts/init_component.js new file mode 100644 index 0000000..d330667 --- /dev/null +++ b/app/components/users/javascripts/init_component.js @@ -0,0 +1,11 @@ +{ + initComponent: function(){ + // calling superclass's initComponent + this.callParent(); + this.getView().on('itemclick', function(view, record){ + // The beauty of using Ext.Direct: calling 3 endpoints in a row, which results in a single call to the server! + console.log("user: " + record.get('id') ); + this.selectCustomer({customer_id: record.get('id'), customer_type: 'User'}); + }, this); + } +} diff --git a/app/components/users_and_customers_accordian.rb b/app/components/users_and_customers_accordian.rb new file mode 100644 index 0000000..841aef7 --- /dev/null +++ b/app/components/users_and_customers_accordian.rb @@ -0,0 +1,10 @@ +class UsersAndCustomersAccordian < Netzke::Basepack::Accordion + component :customers + component :users + + def configure(c) + c.prevent_header = true + c.items = [ :customers, :users ] + super + end +end diff --git a/app/components/users_and_profiles_border.rb b/app/components/users_and_profiles_border.rb index 11b6209..e91cb5a 100644 --- a/app/components/users_and_profiles_border.rb +++ b/app/components/users_and_profiles_border.rb @@ -9,7 +9,7 @@ class UsersAndProfilesBorder < Netzke::Base super c.header = false c.items = [ - { netzke_component: :users, region: :center, width: 300, split: true }, + { netzke_component: :users, header: "Users", region: :center, width: 300, split: true }, { netzke_component: :user_profiles, region: :south, height: 150, split: true}, { netzke_component: :user_logs, region: :east, split: true} ] diff --git a/app/models/acts_as_loggable/transaction_action.rb b/app/models/acts_as_loggable/transaction_action.rb index f4a2307..fbf95b7 100644 --- a/app/models/acts_as_loggable/transaction_action.rb +++ b/app/models/acts_as_loggable/transaction_action.rb @@ -1,7 +1,8 @@ class ActsAsLoggable::TransactionAction < ActiveRecord::Base attr_accessible :action - - belongs_to :bike + + has_many :logs + #belongs_to :bike def to_s self.action diff --git a/app/models/bike.rb b/app/models/bike.rb index b57b59f..4389ffb 100644 --- a/app/models/bike.rb +++ b/app/models/bike.rb @@ -2,6 +2,8 @@ class Bike < ActiveRecord::Base acts_as_loggable attr_accessible :serial_number, :bike_brand_id, :bike_model_id, :color, :bike_style_id, :seat_tube_height, :top_tube_length, :wheel_size, :value, :bike_condition_id, :bike_status_id + + has_many :transactions has_one :owner, :class_name => 'User' belongs_to :bike_brand diff --git a/app/models/customer.rb b/app/models/customer.rb new file mode 100644 index 0000000..90d8673 --- /dev/null +++ b/app/models/customer.rb @@ -0,0 +1,22 @@ +class Customer < ActiveRecord::Base + attr_accessible :first_name, :last_name, :addrStreet1, + :addrStreet2, :addrCity, :addrState, :addrZip, :phone, :email + + has_many :transactions, :as => :customer + + validates :first_name, :presence => true + validates :last_name, :presence => true + #validates :addrStreet1, :presence => true + #validates :addrStreet2, :presence => true + #validates :addrCity, :presence => true + #validates :addrState, :presence => true + #validates :addrZip, :presence => true + #validates :phone, :presence => true + #validates :email, :presence => true + + self.per_page = 15 + + def to_s + "#{first_name} #{last_name}" + end +end diff --git a/app/models/transaction.rb b/app/models/transaction.rb new file mode 100644 index 0000000..66e93f6 --- /dev/null +++ b/app/models/transaction.rb @@ -0,0 +1,19 @@ +class Transaction < ActiveRecord::Base + acts_as_loggable + + attr_accessible :vendor_id, :customer_id, :customer_type, :bike_id, :amount, :item + + belongs_to :vendor, :class_name => 'User', :foreign_key => 'vendor_id' + belongs_to :bike + belongs_to :customer, :polymorphic => true + + validates :vendor_id, :presence => true + validates :customer_id, :presence => { :message => "Choose a User or Customer"} + validates :customer_type, :presence => { :message => "Choose a User or Customer"} + validates :amount, :presence => true + validates :item, :presence => true + + def to_s + "#{amount} #{item} #{bike_id}" + end +end diff --git a/app/models/user.rb b/app/models/user.rb index a656337..c51f98a 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -10,6 +10,7 @@ class User < ActiveRecord::Base :first_name, :last_name, :nickname, :user_role_id, :bike_id, :user_profiles_attributes + has_many :transactions has_many :user_profiles accepts_nested_attributes_for :user_profiles, allow_destroy: false diff --git a/db/migrate/20121205043759_create_transactions.rb b/db/migrate/20121205043759_create_transactions.rb index 86f5222..bba51db 100644 --- a/db/migrate/20121205043759_create_transactions.rb +++ b/db/migrate/20121205043759_create_transactions.rb @@ -1,9 +1,12 @@ class CreateTransactions < ActiveRecord::Migration def change create_table :transactions do |t| - t.integer "user_id", :null => false + t.integer "vendor_id", :null => false + t.integer "customer_id", :null => false + t.string "customer_type", :null => false t.integer "bike_id" t.integer "amount", :null => false + t.string "item", :null => false #Adding whether or not a user sold or purchased the bike #could be used to help keep track of external sales. #aka, a collective member (user) sold a bike to @@ -11,6 +14,7 @@ class CreateTransactions < ActiveRecord::Migration #Currently this model automatically assumes that the user is #purchasing a bike, or a part for a bike from the collective #t.boolean "user_sold_flag", :default => false + t.timestamps end end end diff --git a/db/migrate/20130120142249_create_customers.rb b/db/migrate/20130120142249_create_customers.rb new file mode 100644 index 0000000..6c23c86 --- /dev/null +++ b/db/migrate/20130120142249_create_customers.rb @@ -0,0 +1,19 @@ +class CreateCustomers < ActiveRecord::Migration + def change + create_table :customers do |t| + t.string "first_name", :null => false + t.string "last_name", :null => false + t.string "addrStreet1" + t.string "addrStreet2" + t.string "addrCity" + t.string "addrState" + t.string "addrZip" + t.string "phone" + t.string "email" + end + + add_index :customers, :phone, :unique => true + add_index :customers, :email, :unique => true + end + +end diff --git a/db/schema.rb b/db/schema.rb index 0b8e94a..f76382c 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20121229160809) do +ActiveRecord::Schema.define(:version => 20130120142249) do create_table "bike_actions", :force => true do |t| t.string "action", :limit => 128, :null => false @@ -64,6 +64,21 @@ ActiveRecord::Schema.define(:version => 20121229160809) do add_index "bikes", ["serial_number"], :name => "index_bikes_on_serial_number", :unique => true + create_table "customers", :force => true do |t| + t.string "first_name", :null => false + t.string "last_name", :null => false + t.string "addrStreet1" + t.string "addrStreet2" + t.string "addrCity" + t.string "addrState" + t.string "addrZip" + t.string "phone" + t.string "email" + end + + add_index "customers", ["email"], :name => "index_customers_on_email", :unique => true + add_index "customers", ["phone"], :name => "index_customers_on_phone", :unique => true + create_table "logs", :force => true do |t| t.integer "loggable_id" t.string "loggable_type" @@ -88,9 +103,14 @@ ActiveRecord::Schema.define(:version => 20121229160809) do end create_table "transactions", :force => true do |t| - t.integer "user_id", :null => false - t.integer "bike_id" - t.integer "amount", :null => false + t.integer "vendor_id", :null => false + t.integer "customer_id", :null => false + t.string "customer_type", :null => false + t.integer "bike_id" + t.integer "amount", :null => false + t.string "item", :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false end create_table "user_actions", :force => true do |t|