moneyrail 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 %>
|