moneyrail 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/.gitmodules +6 -0
- data/Changelog +21 -0
- data/README +1 -0
- data/Rakefile +32 -0
- data/VERSION +1 -0
- data/app/controllers/accounts_controller.rb +99 -0
- data/app/controllers/application_controller.rb +10 -0
- data/app/controllers/categories_controller.rb +99 -0
- data/app/controllers/home_controller.rb +31 -0
- data/app/controllers/items_controller.rb +105 -0
- data/app/controllers/logs_controller.rb +20 -0
- data/app/helpers/accounts_helper.rb +2 -0
- data/app/helpers/application_helper.rb +3 -0
- data/app/helpers/categories_helper.rb +2 -0
- data/app/helpers/home_helper.rb +2 -0
- data/app/helpers/items_helper.rb +2 -0
- data/app/helpers/logs_helper.rb +42 -0
- data/app/models/account.rb +12 -0
- data/app/models/category.rb +26 -0
- data/app/models/expense.rb +2 -0
- data/app/models/income.rb +2 -0
- data/app/models/item.rb +31 -0
- data/app/models/move.rb +37 -0
- data/app/models/simple_item.rb +27 -0
- data/app/views/accounts/edit.html.erb +16 -0
- data/app/views/accounts/index.html.erb +24 -0
- data/app/views/accounts/new.html.erb +15 -0
- data/app/views/accounts/show.html.erb +8 -0
- data/app/views/categories/edit.html.erb +20 -0
- data/app/views/categories/index.html.erb +26 -0
- data/app/views/categories/new.html.erb +19 -0
- data/app/views/categories/show.html.erb +13 -0
- data/app/views/home/index.html.erb +15 -0
- data/app/views/items/edit.html.erb +45 -0
- data/app/views/items/index.html.erb +38 -0
- data/app/views/items/new.html.erb +40 -0
- data/app/views/items/show.html.erb +3 -0
- data/app/views/layouts/application.html.erb +37 -0
- data/app/views/logs/view.html.erb +120 -0
- data/config/boot.rb +110 -0
- data/config/database.yml +25 -0
- data/config/environment.rb +41 -0
- data/config/environments/cucumber.rb +21 -0
- data/config/environments/development.rb +17 -0
- data/config/environments/production.rb +28 -0
- data/config/environments/test.rb +28 -0
- data/config/initializers/backtrace_silencers.rb +7 -0
- data/config/initializers/inflections.rb +10 -0
- data/config/initializers/mime_types.rb +5 -0
- data/config/initializers/new_rails_defaults.rb +19 -0
- data/config/initializers/session_store.rb +15 -0
- data/config/locales/en.yml +5 -0
- data/config/routes.rb +80 -0
- data/db/migrate/20090802070406_create_accounts.rb +14 -0
- data/db/migrate/20090802073601_create_categories.rb +15 -0
- data/db/migrate/20090804065900_create_items.rb +26 -0
- data/db/production.sqlite3 +0 -0
- data/doc/README_FOR_APP +2 -0
- data/features/step_definitions/webrat_steps.rb +129 -0
- data/features/support/env.rb +37 -0
- data/features/support/paths.rb +27 -0
- data/lib/tasks/cucumber.rake +20 -0
- data/lib/tasks/rspec.rake +182 -0
- data/main.rb +5 -0
- data/moneyrail.gemspec +170 -0
- data/public/404.html +30 -0
- data/public/422.html +30 -0
- data/public/500.html +30 -0
- data/public/favicon.ico +0 -0
- data/public/images/rails.png +0 -0
- data/public/javascripts/application.js +2 -0
- data/public/javascripts/controls.js +963 -0
- data/public/javascripts/dragdrop.js +973 -0
- data/public/javascripts/editor.js +188 -0
- data/public/javascripts/effects.js +1128 -0
- data/public/javascripts/jquery-ui.js +160 -0
- data/public/javascripts/jquery.js +32 -0
- data/public/javascripts/prototype.js +4320 -0
- data/public/robots.txt +5 -0
- data/public/stylesheets/editor.less +67 -0
- data/public/stylesheets/scaffold.css +54 -0
- data/script/about +4 -0
- data/script/autospec +6 -0
- data/script/console +3 -0
- data/script/cucumber +8 -0
- data/script/dbconsole +3 -0
- data/script/destroy +3 -0
- data/script/generate +3 -0
- data/script/performance/benchmarker +3 -0
- data/script/performance/profiler +3 -0
- data/script/plugin +3 -0
- data/script/runner +3 -0
- data/script/server +3 -0
- data/script/spec +10 -0
- data/script/spec_server +9 -0
- data/spec/_fixtures/accounts.yml +5 -0
- data/spec/_fixtures/categories.yml +19 -0
- data/spec/_fixtures/incomes.yml +7 -0
- data/spec/_fixtures/items.yml +7 -0
- data/spec/fixtures/accounts.yml +11 -0
- data/spec/fixtures/categories.yml +24 -0
- data/spec/fixtures/items.yml +82 -0
- data/spec/helpers/accounts_helper_spec.rb +11 -0
- data/spec/helpers/categories_helper_spec.rb +11 -0
- data/spec/helpers/items_helper_spec.rb +11 -0
- data/spec/models/account_spec.rb +13 -0
- data/spec/models/category_spec.rb +9 -0
- data/spec/models/income_spec.rb +9 -0
- data/spec/models/item_spec.rb +13 -0
- data/spec/rcov.opts +2 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +51 -0
- data/vendor/plugins/acts_as_list/README +23 -0
- data/vendor/plugins/acts_as_list/init.rb +3 -0
- data/vendor/plugins/acts_as_list/lib/active_record/acts/list.rb +256 -0
- data/vendor/plugins/acts_as_list/test/list_test.rb +332 -0
- data/vendor/plugins/less/LICENCE +20 -0
- data/vendor/plugins/less/README +52 -0
- data/vendor/plugins/less/init.rb +19 -0
- data/vendor/plugins/less/lib/less_for_rails.rb +37 -0
- data/vendor/plugins/less/test/less_for_rails_test.rb +15 -0
- metadata +202 -0
data/.gitignore
ADDED
data/.gitmodules
ADDED
data/Changelog
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
2009-08-19 Yutaka HARA <yhara(at)kmc.gr.jp>
|
2
|
+
|
3
|
+
* Released 0.0.1
|
4
|
+
|
5
|
+
* new: delete an item when title and amount are both empty
|
6
|
+
|
7
|
+
* fix: item deletion raises an error
|
8
|
+
|
9
|
+
* fix: fails to update cells whose position > 0
|
10
|
+
|
11
|
+
2009-08-17 Yutaka HARA <yhara(at)kmc.gr.jp>
|
12
|
+
|
13
|
+
* new: show logs
|
14
|
+
|
15
|
+
2009-08-16 Yutaka HARA <yhara(at)kmc.gr.jp>
|
16
|
+
|
17
|
+
* Released 0.0.0
|
18
|
+
|
19
|
+
|
20
|
+
|
21
|
+
|
data/README
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
see http://wiki.github.com/yhara/moneyrail
|
data/Rakefile
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# Add your own tasks in files placed in lib/tasks ending in .rake,
|
2
|
+
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
|
3
|
+
|
4
|
+
require(File.join(File.dirname(__FILE__), 'config', 'boot'))
|
5
|
+
|
6
|
+
require 'rake'
|
7
|
+
require 'rake/testtask'
|
8
|
+
require 'rake/rdoctask'
|
9
|
+
|
10
|
+
require 'tasks/rails'
|
11
|
+
|
12
|
+
# -- gem creation
|
13
|
+
|
14
|
+
PROJECT_NAME = "moneyrail"
|
15
|
+
|
16
|
+
begin
|
17
|
+
require 'jeweler'
|
18
|
+
rescue LoadError
|
19
|
+
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
20
|
+
end
|
21
|
+
|
22
|
+
Jeweler::Tasks.new do |gemspec|
|
23
|
+
gemspec.name = "#{PROJECT_NAME}"
|
24
|
+
gemspec.summary = "Household account book, written in Rails"
|
25
|
+
gemspec.email = "yutaka.hara/at/gmail.com"
|
26
|
+
gemspec.homepage = "http://github.com/yhara/#{PROJECT_NAME}"
|
27
|
+
gemspec.description = gemspec.summary
|
28
|
+
gemspec.authors = ["Yutaka HARA"]
|
29
|
+
gemspec.add_dependency('rails', '= 2.3.3')
|
30
|
+
gemspec.add_dependency('ruby-station-runtime', '>= 0.0.2')
|
31
|
+
gemspec.files.concat Dir["vendor/plugins/**/*"]
|
32
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.2
|
@@ -0,0 +1,99 @@
|
|
1
|
+
class AccountsController < ApplicationController
|
2
|
+
# GET /accounts
|
3
|
+
# GET /accounts.xml
|
4
|
+
def index
|
5
|
+
@accounts = Account.all(:order => "position")
|
6
|
+
|
7
|
+
respond_to do |format|
|
8
|
+
format.html # index.html.erb
|
9
|
+
format.xml { render :xml => @accounts }
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# GET /accounts/1
|
14
|
+
# GET /accounts/1.xml
|
15
|
+
def show
|
16
|
+
@account = Account.find(params[:id])
|
17
|
+
|
18
|
+
respond_to do |format|
|
19
|
+
format.html # show.html.erb
|
20
|
+
format.xml { render :xml => @account }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# GET /accounts/new
|
25
|
+
# GET /accounts/new.xml
|
26
|
+
def new
|
27
|
+
@account = Account.new
|
28
|
+
|
29
|
+
respond_to do |format|
|
30
|
+
format.html # new.html.erb
|
31
|
+
format.xml { render :xml => @account }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# GET /accounts/1/edit
|
36
|
+
def edit
|
37
|
+
@account = Account.find(params[:id])
|
38
|
+
end
|
39
|
+
|
40
|
+
# POST /accounts
|
41
|
+
# POST /accounts.xml
|
42
|
+
def create
|
43
|
+
@account = Account.new(params[:account])
|
44
|
+
|
45
|
+
respond_to do |format|
|
46
|
+
if @account.save
|
47
|
+
flash[:notice] = 'Account was successfully created.'
|
48
|
+
format.html { redirect_to(@account) }
|
49
|
+
format.xml { render :xml => @account, :status => :created, :location => @account }
|
50
|
+
else
|
51
|
+
format.html { render :action => "new" }
|
52
|
+
format.xml { render :xml => @account.errors, :status => :unprocessable_entity }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# PUT /accounts/1
|
58
|
+
# PUT /accounts/1.xml
|
59
|
+
def update
|
60
|
+
@account = Account.find(params[:id])
|
61
|
+
|
62
|
+
respond_to do |format|
|
63
|
+
if @account.update_attributes(params[:account])
|
64
|
+
flash[:notice] = 'Account was successfully updated.'
|
65
|
+
format.html { redirect_to(@account) }
|
66
|
+
format.xml { head :ok }
|
67
|
+
else
|
68
|
+
format.html { render :action => "edit" }
|
69
|
+
format.xml { render :xml => @account.errors, :status => :unprocessable_entity }
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# DELETE /accounts/1
|
75
|
+
# DELETE /accounts/1.xml
|
76
|
+
def destroy
|
77
|
+
@account = Account.find(params[:id])
|
78
|
+
@account.destroy
|
79
|
+
|
80
|
+
respond_to do |format|
|
81
|
+
format.html { redirect_to(accounts_url) }
|
82
|
+
format.xml { head :ok }
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def move_up
|
87
|
+
@account = Account.find(params[:id])
|
88
|
+
@account.move_higher
|
89
|
+
|
90
|
+
redirect_to :back
|
91
|
+
end
|
92
|
+
|
93
|
+
def move_down
|
94
|
+
@account = Account.find(params[:id])
|
95
|
+
@account.move_lower
|
96
|
+
|
97
|
+
redirect_to :back
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# Filters added to this controller apply to all controllers in the application.
|
2
|
+
# Likewise, all the methods added will be available for all controllers.
|
3
|
+
|
4
|
+
class ApplicationController < ActionController::Base
|
5
|
+
helper :all # include all helpers, all the time
|
6
|
+
protect_from_forgery # See ActionController::RequestForgeryProtection for details
|
7
|
+
|
8
|
+
# Scrub sensitive parameters from your log
|
9
|
+
# filter_parameter_logging :password
|
10
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
class CategoriesController < ApplicationController
|
2
|
+
# GET /categories
|
3
|
+
# GET /categories.xml
|
4
|
+
def index
|
5
|
+
@categories = Category.all(:order => "kind, position")
|
6
|
+
|
7
|
+
respond_to do |format|
|
8
|
+
format.html # index.html.erb
|
9
|
+
format.xml { render :xml => @categories }
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# GET /categories/1
|
14
|
+
# GET /categories/1.xml
|
15
|
+
def show
|
16
|
+
@category = Category.find(params[:id])
|
17
|
+
|
18
|
+
respond_to do |format|
|
19
|
+
format.html # show.html.erb
|
20
|
+
format.xml { render :xml => @category }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# GET /categories/new
|
25
|
+
# GET /categories/new.xml
|
26
|
+
def new
|
27
|
+
@category = Category.new
|
28
|
+
|
29
|
+
respond_to do |format|
|
30
|
+
format.html # new.html.erb
|
31
|
+
format.xml { render :xml => @category }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# GET /categories/1/edit
|
36
|
+
def edit
|
37
|
+
@category = Category.find(params[:id])
|
38
|
+
end
|
39
|
+
|
40
|
+
# POST /categories
|
41
|
+
# POST /categories.xml
|
42
|
+
def create
|
43
|
+
@category = Category.new(params[:category])
|
44
|
+
|
45
|
+
respond_to do |format|
|
46
|
+
if @category.save
|
47
|
+
flash[:notice] = 'Category was successfully created.'
|
48
|
+
format.html { redirect_to(@category) }
|
49
|
+
format.xml { render :xml => @category, :status => :created, :location => @category }
|
50
|
+
else
|
51
|
+
format.html { render :action => "new" }
|
52
|
+
format.xml { render :xml => @category.errors, :status => :unprocessable_entity }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# PUT /categories/1
|
58
|
+
# PUT /categories/1.xml
|
59
|
+
def update
|
60
|
+
@category = Category.find(params[:id])
|
61
|
+
|
62
|
+
respond_to do |format|
|
63
|
+
if @category.update_attributes(params[:category])
|
64
|
+
flash[:notice] = 'Category was successfully updated.'
|
65
|
+
format.html { redirect_to(@category) }
|
66
|
+
format.xml { head :ok }
|
67
|
+
else
|
68
|
+
format.html { render :action => "edit" }
|
69
|
+
format.xml { render :xml => @category.errors, :status => :unprocessable_entity }
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# DELETE /categories/1
|
75
|
+
# DELETE /categories/1.xml
|
76
|
+
def destroy
|
77
|
+
@category = Category.find(params[:id])
|
78
|
+
@category.destroy
|
79
|
+
|
80
|
+
respond_to do |format|
|
81
|
+
format.html { redirect_to(categories_url) }
|
82
|
+
format.xml { head :ok }
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def move_up
|
87
|
+
@account = Category.find(params[:id])
|
88
|
+
@account.move_higher
|
89
|
+
|
90
|
+
redirect_to :action => :index
|
91
|
+
end
|
92
|
+
|
93
|
+
def move_down
|
94
|
+
@account = Category.find(params[:id])
|
95
|
+
@account.move_lower
|
96
|
+
|
97
|
+
redirect_to :action => :index
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
class HomeController < ApplicationController
|
2
|
+
|
3
|
+
def index
|
4
|
+
@months = []
|
5
|
+
collect_months(@months)
|
6
|
+
append_current_month(@months)
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def collect_months(months)
|
12
|
+
oldest = Item.minimum("date")
|
13
|
+
newest = Item.maximum("date")
|
14
|
+
|
15
|
+
if oldest and newest
|
16
|
+
m = newest.beginning_of_month
|
17
|
+
while m >= oldest.beginning_of_month
|
18
|
+
months.push m
|
19
|
+
m <<= 1
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def append_current_month(months)
|
25
|
+
current = Time.now.to_date.beginning_of_month
|
26
|
+
unless months.include?(current)
|
27
|
+
months.push current
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
class ItemsController < ApplicationController
|
2
|
+
# GET /items
|
3
|
+
# GET /items.xml
|
4
|
+
def index
|
5
|
+
@items = Item.all(:order => "updated_at DESC")
|
6
|
+
|
7
|
+
respond_to do |format|
|
8
|
+
format.html # index.html.erb
|
9
|
+
format.xml { render :xml => @items }
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# GET /items/1
|
14
|
+
# GET /items/1.xml
|
15
|
+
def show
|
16
|
+
@item = Item.find(params[:id])
|
17
|
+
|
18
|
+
respond_to do |format|
|
19
|
+
format.html # show.html.erb
|
20
|
+
format.xml { render :xml => @item }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# GET /items/new
|
25
|
+
# GET /items/new.xml
|
26
|
+
def new
|
27
|
+
@item = Item.new
|
28
|
+
@accounts = Account.all
|
29
|
+
@categories = Category.all
|
30
|
+
|
31
|
+
respond_to do |format|
|
32
|
+
format.html # new.html.erb
|
33
|
+
format.xml { render :xml => @item }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# GET /items/1/edit
|
38
|
+
def edit
|
39
|
+
@item = Item.find(params[:id])
|
40
|
+
@accounts = Account.all
|
41
|
+
@categories = Category.all
|
42
|
+
end
|
43
|
+
|
44
|
+
# POST /items
|
45
|
+
# POST /items.xml
|
46
|
+
def create
|
47
|
+
@item = Item.new(params[:item])
|
48
|
+
@item.type = @item.category.kind
|
49
|
+
@accounts = Account.all
|
50
|
+
@categories = Category.all
|
51
|
+
|
52
|
+
respond_to do |format|
|
53
|
+
if @item.save
|
54
|
+
format.html {
|
55
|
+
flash[:notice] = 'Item was successfully created.'
|
56
|
+
redirect_to(@item)
|
57
|
+
}
|
58
|
+
format.xml { render :xml => @item, :status => :created, :location => @item }
|
59
|
+
format.json { render :text => "['ok', #{@item.id}]" }
|
60
|
+
else
|
61
|
+
logger.debug @item.errors.inspect
|
62
|
+
format.html { render :action => "new" }
|
63
|
+
format.xml { render :xml => @item.errors, :status => :unprocessable_entity }
|
64
|
+
format.json { render :text => "['error']" }
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# PUT /items/1
|
70
|
+
# PUT /items/1.xml
|
71
|
+
def update
|
72
|
+
@item = Item.find(params[:id])
|
73
|
+
@accounts = Account.all
|
74
|
+
@categories = Category.all
|
75
|
+
|
76
|
+
respond_to do |format|
|
77
|
+
if @item.update_attributes(params[:item])
|
78
|
+
format.html {
|
79
|
+
flash[:notice] = 'Item was successfully updated.'
|
80
|
+
redirect_to(@item)
|
81
|
+
}
|
82
|
+
format.xml { head :ok }
|
83
|
+
format.json { render :text => "['ok']" }
|
84
|
+
else
|
85
|
+
logger.debug @item.errors.inspect
|
86
|
+
format.html { render :action => "edit" }
|
87
|
+
format.xml { render :xml => @item.errors, :status => :unprocessable_entity }
|
88
|
+
format.json { render :text => "['error']" }
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# DELETE /items/1
|
94
|
+
# DELETE /items/1.xml
|
95
|
+
def destroy
|
96
|
+
@item = Item.find(params[:id])
|
97
|
+
@item.destroy
|
98
|
+
|
99
|
+
respond_to do |format|
|
100
|
+
format.html { redirect_to(items_url) }
|
101
|
+
format.xml { head :ok }
|
102
|
+
format.json { render :text => "['ok']" }
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class LogsController < ApplicationController
|
2
|
+
|
3
|
+
def view
|
4
|
+
@mode = params[:mode]
|
5
|
+
@year, @month = params[:year].to_i, params[:month].to_i
|
6
|
+
|
7
|
+
first_day = Date.new(@year, @month)
|
8
|
+
@month_range = first_day .. ((first_day >> 1) - 1)
|
9
|
+
|
10
|
+
@accounts = Account.all(:order => "position")
|
11
|
+
@categories = Category.hashed
|
12
|
+
@cat_all = [
|
13
|
+
@categories[:expense],
|
14
|
+
@categories[:income],
|
15
|
+
@categories[:move],
|
16
|
+
].flatten(1)
|
17
|
+
end
|
18
|
+
attr_reader :mode
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module LogsHelper
|
2
|
+
|
3
|
+
def cell(value)
|
4
|
+
if @mode == :edit
|
5
|
+
"<input type='text' value='#{h value}' />"
|
6
|
+
else
|
7
|
+
"<div>#{h value}</div>"
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
# returns [Date(2009/8/1), nil, nil, Income, ..]
|
12
|
+
def make_table_data(account)
|
13
|
+
condition = {
|
14
|
+
:conditions => {:date => @month_range},
|
15
|
+
}
|
16
|
+
items = [
|
17
|
+
account.expenses.all(condition),
|
18
|
+
account.incomes.all(condition),
|
19
|
+
account.moves_from.all(condition),
|
20
|
+
account.moves_to.all(condition),
|
21
|
+
].flatten(1)
|
22
|
+
|
23
|
+
@month_range.map{|today|
|
24
|
+
todays, items = items.partition{|m| m.date == today}
|
25
|
+
positions = todays.map(&:position)
|
26
|
+
max_position = (positions.empty?) ? 0 : positions.max
|
27
|
+
|
28
|
+
(0..max_position).map{|i|
|
29
|
+
day = (i==0) ? today : :no_date
|
30
|
+
[
|
31
|
+
today,
|
32
|
+
i,
|
33
|
+
i == max_position,
|
34
|
+
@cat_all.map{|cat|
|
35
|
+
todays.find{|m| m.category == cat && m.position == i}
|
36
|
+
}.unshift(day)
|
37
|
+
]
|
38
|
+
}
|
39
|
+
}.flatten(1)
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
class Account < ActiveRecord::Base
|
2
|
+
has_many :incomes
|
3
|
+
has_many :expenses
|
4
|
+
has_many :moves_from, :class_name => "Move", :foreign_key => :account_id_from
|
5
|
+
has_many :moves_to, :class_name => "Move", :foreign_key => :account_id_to
|
6
|
+
|
7
|
+
validates_presence_of :name
|
8
|
+
validates_uniqueness_of :name
|
9
|
+
|
10
|
+
# provides move_higher, move_lower, etc.
|
11
|
+
acts_as_list
|
12
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class Category < ActiveRecord::Base
|
2
|
+
validates_presence_of :name
|
3
|
+
validates_uniqueness_of :name
|
4
|
+
|
5
|
+
def validate
|
6
|
+
unless %w(Income Expense Move).include?(self.kind)
|
7
|
+
errors.add("kind", "#{self.kind.inspect} must be either of Income, Expense, Move")
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
# provides move_higher, move_lower, etc.
|
12
|
+
# categories are indexed in those of the same kind
|
13
|
+
acts_as_list :scope => 'kind == \'#{kind}\''
|
14
|
+
|
15
|
+
def self.hashed
|
16
|
+
{
|
17
|
+
:income =>
|
18
|
+
self.all(:conditions => {:kind => "Income"}, :order => "position"),
|
19
|
+
:expense =>
|
20
|
+
self.all(:conditions => {:kind => "Expense"}, :order => "position"),
|
21
|
+
:move =>
|
22
|
+
self.all(:conditions => {:kind => "Move"}, :order => "position")
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
data/app/models/item.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
class Item < ActiveRecord::Base
|
2
|
+
belongs_to :category
|
3
|
+
|
4
|
+
# validations
|
5
|
+
|
6
|
+
def before_validation
|
7
|
+
self.title = "" if self.title.nil?
|
8
|
+
self.amount = 0 if self.amount.blank?
|
9
|
+
end
|
10
|
+
|
11
|
+
validates_presence_of :date
|
12
|
+
|
13
|
+
validates_presence_of :amount, :position
|
14
|
+
validates_numericality_of :amount, :position
|
15
|
+
|
16
|
+
def self.find_conflict(item)
|
17
|
+
item.type.constantize.find_conflict(item)
|
18
|
+
end
|
19
|
+
|
20
|
+
def validate
|
21
|
+
# validate category
|
22
|
+
unless self.category && self.category.kind == self.type
|
23
|
+
errors.add("category", "the category is not for #{self.type}")
|
24
|
+
end
|
25
|
+
|
26
|
+
# validate index of day
|
27
|
+
if same = self.class.find_conflict(self)
|
28
|
+
errors.add("position", "position #{position} conflicts with #{same.title} (#{same.amount}, #{same.date})")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/app/models/move.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
class Move < Item
|
2
|
+
# associations
|
3
|
+
|
4
|
+
belongs_to :account_from, :class_name => "Account", :foreign_key => :account_id_from
|
5
|
+
belongs_to :account_to, :class_name => "Account", :foreign_key => :account_id_to
|
6
|
+
|
7
|
+
# validations
|
8
|
+
|
9
|
+
validates_presence_of :account_from
|
10
|
+
validates_presence_of :account_to
|
11
|
+
|
12
|
+
def self.find_conflict(item)
|
13
|
+
Move.all(:conditions => {
|
14
|
+
:date => item.date,
|
15
|
+
:account_id_from => item.account_id_from,
|
16
|
+
:account_id_to => item.account_id_to,
|
17
|
+
:category_id => item.category_id,
|
18
|
+
:position => item.position,
|
19
|
+
}).reject{|x| x.id == item.id}.first
|
20
|
+
end
|
21
|
+
|
22
|
+
def validate
|
23
|
+
if account_id_from == account_id_to
|
24
|
+
errors.add("account_id_from", "from and to must not be same")
|
25
|
+
end
|
26
|
+
super
|
27
|
+
end
|
28
|
+
|
29
|
+
# featues
|
30
|
+
|
31
|
+
acts_as_list :scope => [
|
32
|
+
'date = #{date}',
|
33
|
+
'account_id_from = #{account_id_from}',
|
34
|
+
'account_id_to = #{account_id_to}',
|
35
|
+
'category_id = #{category_id}',
|
36
|
+
].join(" AND ")
|
37
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class SimpleItem < Item
|
2
|
+
# associations
|
3
|
+
|
4
|
+
belongs_to :account
|
5
|
+
|
6
|
+
# validations
|
7
|
+
|
8
|
+
validates_presence_of :account
|
9
|
+
|
10
|
+
def self.find_conflict(item)
|
11
|
+
Item.all(:conditions => {
|
12
|
+
:type => item.type,
|
13
|
+
:date => item.date,
|
14
|
+
:account_id => item.account_id,
|
15
|
+
:category_id => item.category_id,
|
16
|
+
:position => item.position,
|
17
|
+
}).reject{|x| x.id == item.id}.first
|
18
|
+
end
|
19
|
+
|
20
|
+
# featues
|
21
|
+
|
22
|
+
acts_as_list :scope => [
|
23
|
+
'date = #{date}',
|
24
|
+
'account_id = #{account_id}',
|
25
|
+
'category_id = #{category_id}',
|
26
|
+
].join(" AND ")
|
27
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
<h2>Editing account</h2>
|
2
|
+
|
3
|
+
<% form_for(@account) do |f| %>
|
4
|
+
<%= f.error_messages %>
|
5
|
+
|
6
|
+
<p>
|
7
|
+
<%= f.label :name %><br />
|
8
|
+
<%= f.text_field :name %>
|
9
|
+
</p>
|
10
|
+
<p>
|
11
|
+
<%= f.submit 'Update' %>
|
12
|
+
</p>
|
13
|
+
<% end %>
|
14
|
+
|
15
|
+
<%= link_to 'Show', @account %> |
|
16
|
+
<%= link_to 'Back', accounts_path %>
|