africompta 1.9.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +74 -0
- data/TODO +31 -0
- data/Test/ac_account.rb +128 -0
- data/Test/ac_africompta.rb +1001 -0
- data/Test/ac_big.rb +62 -0
- data/Test/ac_movement.rb +59 -0
- data/Test/ac_sqlite.rb +139 -0
- data/Test/config_test.yaml +31 -0
- data/Test/db.testGestion +0 -0
- data/Test/test.rb +39 -0
- data/VERSION +140 -0
- data/africompta.gemspec +20 -0
- data/lib/africompta/acaccess.rb +257 -0
- data/lib/africompta/acqooxview.rb +77 -0
- data/lib/africompta/africompta.rb +83 -0
- data/lib/africompta/entities/account.rb +995 -0
- data/lib/africompta/entities/acschemas.rb +16 -0
- data/lib/africompta/entities/movement.rb +292 -0
- data/lib/africompta/entities/remote.rb +27 -0
- data/lib/africompta/entities/users.rb +55 -0
- data/lib/africompta/views/edit/movement.rb +8 -0
- data/lib/africompta/views/edit/tabs.rb +8 -0
- data/lib/africompta/views/report/annual.rb +3 -0
- data/lib/africompta/views/report/tabs.rb +3 -0
- data/lib/africompta.rb +2 -0
- metadata +84 -0
@@ -0,0 +1,257 @@
|
|
1
|
+
# This is the interface to AfriCompta. It is called through different
|
2
|
+
# Post/Get-handlers over HTTP
|
3
|
+
|
4
|
+
|
5
|
+
$VERSION = 0x1120
|
6
|
+
|
7
|
+
class ACaccess < RPCQooxdooPath
|
8
|
+
def self.parse(r, p, q)
|
9
|
+
dputs(2) { "in ACaccess: #{p} - #{q.inspect}" }
|
10
|
+
method = p.gsub(/^\/acaccess\/merge\//, '')
|
11
|
+
dputs(3) { "Calling method #{method} of #{r}" }
|
12
|
+
case r
|
13
|
+
when /GET/
|
14
|
+
ret = self.get(method)
|
15
|
+
when /POST/
|
16
|
+
ret = self.post(method, q)
|
17
|
+
end
|
18
|
+
dputs(3) { "Result is #{ret.inspect}" }
|
19
|
+
ret
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.print_movements(start, stop)
|
23
|
+
start, stop = start.to_i, stop.to_i
|
24
|
+
dputs(2) { "Doing print_movements from #{start.class}:#{start}"+
|
25
|
+
" to #{stop.class}:#{stop}" }
|
26
|
+
ret = ''
|
27
|
+
# Movements.search_all.select{|m|
|
28
|
+
# mi = m.rev_index
|
29
|
+
# m and mi and mi >= start and mi <= stop
|
30
|
+
Movements.search_index_range(start, stop).each { |m|
|
31
|
+
if start > 0
|
32
|
+
dputs(4) { "Mer: Movement #{m.desc}, #{m.value}" }
|
33
|
+
end
|
34
|
+
ret += m.to_s + "\n"
|
35
|
+
}
|
36
|
+
dputs(3) { "Found movements: #{ret.inspect}" }
|
37
|
+
ret
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.print_movements_actual(start, stop)
|
41
|
+
start, stop = start.to_i, stop.to_i
|
42
|
+
dputs(2) { "Doing print_movements_actual from #{start.class}:#{start}"+
|
43
|
+
" to #{stop.class}:#{stop}" }
|
44
|
+
ret = ''
|
45
|
+
actual_ids = []
|
46
|
+
AccountRoot.actual.get_tree { |a|
|
47
|
+
actual_ids.push a.id
|
48
|
+
}
|
49
|
+
movs = Movements.search_all.select { |m|
|
50
|
+
mi = m.rev_index
|
51
|
+
m and mi and mi >= start and mi <= stop and actual_ids.find(m.account_src_id)
|
52
|
+
}
|
53
|
+
dputs(2) { "Found #{movs.length} movements between #{start}..#{stop}" }
|
54
|
+
movs.each { |m|
|
55
|
+
if start > 0
|
56
|
+
dputs(4) { "Mer: Movement #{m.desc}, #{m.value}" }
|
57
|
+
ai = actual_ids.find(m.account_src_id.id)
|
58
|
+
dputs(4) { "movement_src is #{m.account_src_id.inspect} from #{ai.inspect}" }
|
59
|
+
dputs(4) { "actual_ids is #{actual_ids.inspect}" }
|
60
|
+
end
|
61
|
+
ret += m.to_s + "\n"
|
62
|
+
}
|
63
|
+
dputs(3) { "Found movements: #{ret.inspect}" }
|
64
|
+
ret
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.accounts_fetch_old(user)
|
68
|
+
ret = ''
|
69
|
+
|
70
|
+
Accounts.matches_by_account_id(0).to_a.concat(
|
71
|
+
Accounts.matches_by_account_id(nil)).sort { |a, b|
|
72
|
+
a.global_id <=> b.global_id }.each { |a|
|
73
|
+
dputs(2) { "Found one root-account #{a.rev_index} - #{a.path_id}" }
|
74
|
+
if a.global_id
|
75
|
+
dputs(3) { "It's global" }
|
76
|
+
a.get_tree { |acc|
|
77
|
+
dputs(4) { "In get_tree #{acc.path_id}: #{acc.deleted == true} - #{acc.rev_index}" }
|
78
|
+
if acc.rev_index > user.account_index
|
79
|
+
dputs(4) { "Found account #{acc.name} with index #{acc.rev_index}" }
|
80
|
+
ret += "#{acc.to_s}\n"
|
81
|
+
end
|
82
|
+
}
|
83
|
+
else
|
84
|
+
dputs(3) { "It's not global" }
|
85
|
+
end
|
86
|
+
dputs(3) { 'Will search for next' }
|
87
|
+
}
|
88
|
+
ret
|
89
|
+
end
|
90
|
+
|
91
|
+
def self.accounts_fetch(user)
|
92
|
+
Accounts.data.select { |_k, v| v._rev_index > user.account_index }.
|
93
|
+
collect { |k, _v| Accounts.get_data_instance(k) }.
|
94
|
+
sort_by { |a| a.path }.
|
95
|
+
collect { |a| a.to_s }.
|
96
|
+
join("\n")
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.get(p)
|
100
|
+
# Two cases:
|
101
|
+
# path/arg/user,pass - arg is used
|
102
|
+
# path/user,pass - arg is nil
|
103
|
+
path, arg, id = p.split("/")
|
104
|
+
arg, id = id, arg if not id
|
105
|
+
user, pass = id.split(",")
|
106
|
+
|
107
|
+
log_msg 'ACaccess.get', "get-merge-path #{path} - #{arg} with " +
|
108
|
+
"user #{user} and pass #{pass}"
|
109
|
+
u = Users.match_by_name(user)
|
110
|
+
u_local = Users.match_by_name('local')
|
111
|
+
if not (u and u.pass == pass)
|
112
|
+
return "User #{user} not known with pass #{pass}"
|
113
|
+
end
|
114
|
+
|
115
|
+
case path
|
116
|
+
when /accounts_get(.*)/
|
117
|
+
# Gets all accounts available (to that user) that have been changed
|
118
|
+
# since the last update, again, do it from the root(s), else we have
|
119
|
+
# a problem for children without parents
|
120
|
+
ret = ''
|
121
|
+
dputs(2) { "user index is: #{u.account_index}" }
|
122
|
+
# Returns only one account
|
123
|
+
if $1 == '_one'
|
124
|
+
return Accounts.match_by_global_id(arg).to_s
|
125
|
+
end
|
126
|
+
if $1 == '_all'
|
127
|
+
dputs(2) { 'Putting all accounts' }
|
128
|
+
ret = Accounts.search_all.collect { |acc|
|
129
|
+
dputs(4) { "Found account #{acc.name} with index #{acc.rev_index}" }
|
130
|
+
acc.to_s(true)
|
131
|
+
}.join("\n")
|
132
|
+
elsif $1 == '_count'
|
133
|
+
ret += Accounts.search_all.size.to_s
|
134
|
+
elsif $1 == '_part'
|
135
|
+
acc_start, acc_end = arg.split(",")
|
136
|
+
dputs(2) { "Putting accounts #{acc_start}..#{acc_end}" }
|
137
|
+
Accounts.search_all.select { |acc|
|
138
|
+
acc.rev_index >= acc_start.to_i and acc.rev_index <= acc_end.to_i
|
139
|
+
}.each { |acc|
|
140
|
+
dputs(4) { "Found account #{acc.name} with index #{acc.rev_index}" }
|
141
|
+
ret += "#{acc.to_s(true)}\n"
|
142
|
+
}
|
143
|
+
else
|
144
|
+
dputs(2) { 'Starting to search accounts' }
|
145
|
+
t = Time.now
|
146
|
+
ret += ACaccess.accounts_fetch(u)
|
147
|
+
dputs(2) { "Found #{ret.count("\n")} after #{Time.now - t} seconds" }
|
148
|
+
end
|
149
|
+
dputs(3) { 'Finished search' }
|
150
|
+
return ret
|
151
|
+
|
152
|
+
# Gets all movements (for the accounts of that user)
|
153
|
+
when /movements_get(.*)/
|
154
|
+
dputs(2) { "movements_get#{$1} with #{arg.inspect}" }
|
155
|
+
start, stop = u.movement_index + 1, u_local.movement_index - 1
|
156
|
+
# Returns only one account
|
157
|
+
if $1 == '_one'
|
158
|
+
return Movements.match_by_global_id(arg).to_s
|
159
|
+
end
|
160
|
+
if $1 == '_all_actual'
|
161
|
+
start, stop = arg.split(/,/)
|
162
|
+
ret = print_movements_actual(start, stop)
|
163
|
+
else
|
164
|
+
if $1 == '_all'
|
165
|
+
start, stop = arg.split(/,/)
|
166
|
+
end
|
167
|
+
ret = print_movements(start, stop)
|
168
|
+
end
|
169
|
+
dputs(2) { "Sending a total of #{ret.length}" }
|
170
|
+
dputs(3) { "Sending:\n #{ret.inspect}" }
|
171
|
+
return ret
|
172
|
+
|
173
|
+
when 'version'
|
174
|
+
return $VERSION.to_s
|
175
|
+
|
176
|
+
when 'index'
|
177
|
+
return [u_local.account_index, u_local.movement_index].join(",")
|
178
|
+
|
179
|
+
when 'local_id'
|
180
|
+
return u_local.full
|
181
|
+
|
182
|
+
when 'reset_user_indexes'
|
183
|
+
u.update_account_index
|
184
|
+
u.update_movement_index
|
185
|
+
|
186
|
+
when 'reset_user_account_indexes'
|
187
|
+
u.update_account_index
|
188
|
+
|
189
|
+
when 'reset_user_movement_indexes'
|
190
|
+
u.update_movement_index
|
191
|
+
|
192
|
+
when 'movement_delete'
|
193
|
+
dputs(3) { "Going to delete movement #{arg}" }
|
194
|
+
while mov = Movements.match_by_global_id(arg)
|
195
|
+
dputs(3) { "Found movement #{mov.inspect}" }
|
196
|
+
mov.delete
|
197
|
+
end
|
198
|
+
dputs(3) { 'Finished deleting' }
|
199
|
+
end
|
200
|
+
return ''
|
201
|
+
end
|
202
|
+
|
203
|
+
def self.post(path, input)
|
204
|
+
dputs(5) { "self.post with #{path} and #{input.inspect}" }
|
205
|
+
log_msg 'ACaccess.post', "post-merge-path #{path} with " +
|
206
|
+
"user #{input['user']} and pass #{input['pass']}"
|
207
|
+
user, pass = input['user'], input['pass']
|
208
|
+
u = Users.match_by_name(user)
|
209
|
+
if not (u and u.pass == pass)
|
210
|
+
dputs(1) { "Didn't find user #{user}" }
|
211
|
+
return "User #{user} not known with pass #{pass}"
|
212
|
+
end
|
213
|
+
case path
|
214
|
+
# Retrieves id of the path of the account
|
215
|
+
when /account_get_id/
|
216
|
+
account = input['account']
|
217
|
+
dputs(2) { "account_get_id with path #{account}" }
|
218
|
+
a = Accounts.get_id_by_path(account)
|
219
|
+
a and return a
|
220
|
+
dputs(2) { "didn't find anything" }
|
221
|
+
return nil
|
222
|
+
|
223
|
+
when 'movements_put'
|
224
|
+
dputs(3) { "Going to put some movements: #{input['movements'].inspect}" }
|
225
|
+
movs = ActiveSupport::JSON.decode(input['movements'])
|
226
|
+
dputs(3) { "movs is now #{movs.inspect}" }
|
227
|
+
if movs.size > 0
|
228
|
+
movs.each { |m|
|
229
|
+
if mov = Movements.from_json(m)
|
230
|
+
dputs(2) { "Saved movement #{mov.global_id}" }
|
231
|
+
else
|
232
|
+
dputs(0) { "Error: couldn't create movement from #{m.inspect}" }
|
233
|
+
end
|
234
|
+
u.update_movement_index
|
235
|
+
}
|
236
|
+
end
|
237
|
+
when 'movement_delete'
|
238
|
+
dputs(3) { 'Going to delete movement' }
|
239
|
+
while mov = Movements.match_by_global_id(input['global_id'])
|
240
|
+
dputs(3) { "Found movement #{mov.inspect}" }
|
241
|
+
mov.delete
|
242
|
+
end
|
243
|
+
dputs(3) { 'Finished deleting' }
|
244
|
+
when 'account_put'
|
245
|
+
dputs(3) { "Going to put account #{input['account'].inspect}" }
|
246
|
+
acc = Accounts.from_s(input['account'])
|
247
|
+
u.update_account_index
|
248
|
+
dputs(2) { "Saved account #{acc.global_id}" }
|
249
|
+
when 'account_delete'
|
250
|
+
dputs(3) { 'Going to delete account' }
|
251
|
+
acc = Accounts.match_by_global_id(input['global_id'])
|
252
|
+
acc.delete(true)
|
253
|
+
end
|
254
|
+
return 'ok'
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# AfriCompta - handler of a simple accounting-system for "Gestion"
|
2
|
+
#
|
3
|
+
# What follows are some definitions used by other modules
|
4
|
+
|
5
|
+
require 'digest/md5'
|
6
|
+
|
7
|
+
# We want a simple time-print
|
8
|
+
class Time
|
9
|
+
def to_s
|
10
|
+
day.to_s + '/' + month.to_s
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_ss
|
14
|
+
to_s + '/' + year.to_s
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class Date
|
19
|
+
def month_s
|
20
|
+
%w(janvier février mars avril mai juin
|
21
|
+
juillet août septembre octobre novembre décembre)[month-1]
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_s_eu
|
25
|
+
strftime('%d/%m/%y')
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_s_euy
|
29
|
+
strftime('%d/%m/%Y')
|
30
|
+
end
|
31
|
+
|
32
|
+
def Date.from_s(s)
|
33
|
+
# Do some date-magic, so that we can give either the day, day and month or
|
34
|
+
# a complete date. The rest is filled up with todays date.
|
35
|
+
date = []
|
36
|
+
if s.index('/')
|
37
|
+
date = s.split('/')
|
38
|
+
else
|
39
|
+
date = s.split('-').reverse
|
40
|
+
end
|
41
|
+
da = Date.today
|
42
|
+
d = [da.day, da.month, da.year]
|
43
|
+
date += d.last(3 - date.size)
|
44
|
+
if date[2].to_s.size > 2
|
45
|
+
date = Date.strptime(date.join('/'), '%d/%m/%Y')
|
46
|
+
else
|
47
|
+
date = Date.strptime(date.join('/'), '%d/%m/%y')
|
48
|
+
end
|
49
|
+
return date
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class SQLiteAC < SQLite
|
54
|
+
def configure(config)
|
55
|
+
super(config, 'compta', 'compta.db')
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class Float
|
60
|
+
def round(precision = 0)
|
61
|
+
if precision > 0
|
62
|
+
return (self * 10**precision).round / 10.0**precision
|
63
|
+
else
|
64
|
+
return super()
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
module ACQooxView
|
70
|
+
def self.load_entities(preload = true)
|
71
|
+
require 'africompta/acaccess'
|
72
|
+
Dir[File.dirname(__FILE__) + '/entities/*.rb'].each { |f|
|
73
|
+
dputs(2) { "Adding #{f}" }
|
74
|
+
require(f)
|
75
|
+
}
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
#!/usr/bin/ruby -I../QooxView -wKU
|
2
|
+
|
3
|
+
DEBUG_LVL=5
|
4
|
+
VERSION_AFRICOMPTA='1.9.8'
|
5
|
+
require 'fileutils'
|
6
|
+
|
7
|
+
AFRICOMPTA_DIR=File.dirname(__FILE__)
|
8
|
+
CONFIG_FILE='config.yaml'
|
9
|
+
if not FileTest.exists? CONFIG_FILE
|
10
|
+
puts "Config-file doesn't exist"
|
11
|
+
print 'Do you want me to copy a standard one? [Y/n] '
|
12
|
+
if gets.chomp.downcase != 'n'
|
13
|
+
FileUtils.cp 'config.yaml.default', 'config.yaml'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
begin
|
18
|
+
require 'qooxview'
|
19
|
+
require 'africompta/acqooxview'
|
20
|
+
rescue Exception => e
|
21
|
+
puts e.inspect
|
22
|
+
puts e.to_s
|
23
|
+
puts e.backtrace
|
24
|
+
|
25
|
+
dputs( 0 ){ "#{e.inspect}" }
|
26
|
+
dputs( 0 ){ "#{e.to_s}" }
|
27
|
+
puts e.backtrace
|
28
|
+
|
29
|
+
puts "Couldn't start AfriCompta - perhaps missing libraries?"
|
30
|
+
exit
|
31
|
+
end
|
32
|
+
|
33
|
+
Welcome.nologin
|
34
|
+
QooxView::init( 'entities', 'views')
|
35
|
+
|
36
|
+
# Autosave every 2 minutes
|
37
|
+
if ConfigBase.autosave == %w(true)
|
38
|
+
$autosave = Thread.new{
|
39
|
+
loop {
|
40
|
+
sleep 2 * 60
|
41
|
+
Entities.save_all
|
42
|
+
}
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
trap('SIGINT') {
|
47
|
+
throw :ctrl_c
|
48
|
+
}
|
49
|
+
|
50
|
+
$profiling = get_config( nil, :profiling )
|
51
|
+
catch :ctrl_c do
|
52
|
+
begin
|
53
|
+
webrick_port = get_config( 3302, :Webrick, :port )
|
54
|
+
dputs(2){"Starting at port #{webrick_port}" }
|
55
|
+
if $profiling
|
56
|
+
require 'rubygems'
|
57
|
+
require 'perftools'
|
58
|
+
PerfTools::CpuProfiler.start("/tmp/#{$profiling}") do
|
59
|
+
QooxView::startWeb webrick_port
|
60
|
+
end
|
61
|
+
else
|
62
|
+
QooxView::startWeb webrick_port
|
63
|
+
end
|
64
|
+
rescue Exception => e
|
65
|
+
dputs( 0 ){ "#{e.inspect}" }
|
66
|
+
dputs( 0 ){ "#{e.to_s}" }
|
67
|
+
puts e.backtrace
|
68
|
+
dputs( 0 ){ 'Saving all' }
|
69
|
+
Entities.save_all
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
if get_config( true, :autosave )
|
74
|
+
$autosave.kill
|
75
|
+
end
|
76
|
+
|
77
|
+
if $profiling
|
78
|
+
puts "Now run the following:
|
79
|
+
pprof.rb --pdf /tmp/#{$profiling} > /tmp/#{$profiling}.pdf
|
80
|
+
open /tmp/#{$profiling}.pdf
|
81
|
+
CPUPROFILE_FREQUENCY=500
|
82
|
+
"
|
83
|
+
end
|