africompta 1.9.10 → 1.9.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +6 -1
  3. data/Gemfile.lock +14 -8
  4. data/africompta.gemspec +3 -2
  5. data/bin/afri_compta.rb +119 -0
  6. data/bin/africompta.sh +23 -0
  7. data/config.yaml.default +19 -0
  8. data/lib/africompta.rb +7 -1
  9. data/lib/africompta/acaccess.rb +54 -68
  10. data/lib/africompta/acqooxview.rb +10 -10
  11. data/lib/africompta/entities/account.rb +120 -116
  12. data/lib/africompta/entities/config_base.rb +9 -0
  13. data/lib/africompta/entities/remote.rb +240 -6
  14. data/lib/africompta/entities/report.rb +136 -0
  15. data/lib/africompta/entities/users.rb +18 -6
  16. data/lib/africompta/views/compta/admin.rb +135 -0
  17. data/lib/africompta/views/compta/check.rb +245 -0
  18. data/lib/africompta/views/compta/edit_accounts.rb +138 -0
  19. data/lib/africompta/views/compta/edit_movements.rb +167 -0
  20. data/lib/africompta/views/compta/remotes.rb +23 -0
  21. data/lib/africompta/views/compta/tabs.rb +9 -0
  22. data/lib/africompta/views/compta/users.rb +23 -0
  23. data/lib/africompta/views/report/compta_executive.rb +221 -0
  24. data/lib/africompta/views/report/compta_flat.rb +79 -0
  25. data/lib/africompta/views/report/tabs.rb +7 -2
  26. data/po/afri_compta-ar.po +2356 -0
  27. data/po/afri_compta-en.po +2253 -0
  28. data/po/afri_compta-fr.po +4345 -0
  29. data/test/ac_account.rb +176 -0
  30. data/{Test → test}/ac_africompta.rb +0 -0
  31. data/{Test → test}/ac_big.rb +0 -0
  32. data/test/ac_merge.rb +177 -0
  33. data/{Test → test}/ac_movement.rb +14 -1
  34. data/{Test → test}/ac_sqlite.rb +0 -0
  35. data/{Test → test}/config_test.yaml +1 -6
  36. data/test/other.conf +1 -0
  37. data/test/other.rb +18 -0
  38. data/test/test.conf +1 -0
  39. data/{Test → test}/test.rb +7 -14
  40. metadata +63 -18
  41. data/Test/ac_account.rb +0 -128
  42. data/lib/africompta/africompta.rb +0 -83
  43. data/lib/africompta/views/edit/movement.rb +0 -8
  44. data/lib/africompta/views/edit/tabs.rb +0 -8
  45. data/lib/africompta/views/report/annual.rb +0 -3
@@ -5,15 +5,15 @@
5
5
  require 'digest/md5'
6
6
 
7
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
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
17
 
18
18
  class Date
19
19
  def month_s
@@ -67,7 +67,7 @@ class Float
67
67
  end
68
68
 
69
69
  module ACQooxView
70
- def self.load_entities(preload = true)
70
+ def self.load_entities_old(preload = true)
71
71
  require 'africompta/acaccess'
72
72
  Dir[File.dirname(__FILE__) + '/entities/*.rb'].each { |f|
73
73
  dputs(2) { "Adding #{f}" }
@@ -1,106 +1,6 @@
1
1
  require 'prawn'
2
2
  require 'prawn/measurement_extensions'
3
3
 
4
- class AccountRoot
5
-
6
- def self.actual
7
- self.accounts.find { |a| a.name == 'Root' }
8
- end
9
-
10
- def self.current
11
- AccountRoot.actual
12
- end
13
-
14
- def self.archive
15
- self.accounts.find { |a| a.name == 'Archive' }
16
- end
17
-
18
- def self.accounts
19
- Accounts.matches_by_account_id(0)
20
- end
21
-
22
- def self.clean
23
- count_mov, count_acc = 0, 0
24
- bad_mov, bad_acc = 0, 0
25
- log_msg 'Account.clean', 'starting to clean up'
26
- Movements.search_all_.each { |m|
27
- dputs(4) { "Testing movement #{m.inspect}" }
28
- if not m or not m.date or not m.desc or not m.value or
29
- not m.rev_index or not m.account_src or not m.account_dst
30
- if m and m.desc
31
- log_msg 'Account.clean', "Bad movement: #{m.inspect}"
32
- end
33
- m.delete
34
- bad_mov += 1
35
- end
36
- if m.rev_index
37
- count_mov = [count_mov, m.rev_index].max
38
- end
39
- }
40
- Accounts.search_all_.each { |a|
41
- if (a.account_id and (a.account_id > 0)) and (not a.account)
42
- log_msg 'Account.clean', "Account has unexistent parent: #{a.inspect}"
43
- a.delete
44
- bad_acc += 1
45
- end
46
- if !(a.account_id or a.deleted)
47
- log_msg 'Account.clean', "Account has undefined parent: #{a.inspect}"
48
- a.delete
49
- bad_acc += 1
50
- end
51
- if a.account_id == 0
52
- if !((a.name =~ /(Root|Archive)/) or a.deleted)
53
- log_msg 'Account.clean', 'Account is in root but neither ' +
54
- "'Root' nor 'Archive': #{a.inspect}"
55
- a.delete
56
- bad_acc += 1
57
- end
58
- end
59
- if !a.rev_index
60
- log_msg 'Account.clean', "Didn't find rev_index for #{a.inspect}"
61
- a.new_index
62
- bad_acc += 1
63
- end
64
- count_acc = [count_acc, a.rev_index].max
65
- }
66
-
67
- # Check also whether our counters are OK
68
- u_l = Users.match_by_name('local')
69
- dputs(1) { "Movements-index: #{count_mov} - #{u_l.movement_index}" }
70
- dputs(1) { "Accounts-index: #{count_acc} - #{u_l.account_index}" }
71
- @ul_mov, @ul_acc = u_l.movement_index, u_l.account_index
72
- if count_mov > u_l.movement_index
73
- log_msg 'Account.clean', 'Error, there is a bigger movement! Fixing'
74
- u_l.movement_index = count_mov + 1
75
- end
76
- if count_acc > u_l.account_index
77
- log_msg 'Account.clean', 'Error, there is a bigger account! Fixing'
78
- u_l.account_index = count_acc + 1
79
- end
80
- return [count_mov, bad_mov, count_acc, bad_acc]
81
- end
82
-
83
- def self.path_id
84
- return ''
85
- end
86
-
87
- def self.id
88
- 0
89
- end
90
-
91
- def self.mult
92
- 1
93
- end
94
-
95
- def self.keep_total
96
- false
97
- end
98
-
99
- def self.multiplier
100
- 1
101
- end
102
- end
103
-
104
4
  class Accounts < Entities
105
5
  self.needs %w(Users Movements)
106
6
 
@@ -126,7 +26,7 @@ class Accounts < Entities
126
26
  def self.create(name, desc = 'Too lazy', parent = nil, global_id = '', mult = nil)
127
27
  dputs(5) { "Parent is #{parent.inspect}" }
128
28
  if parent
129
- if parent.class != Account and parent != AccountRoot
29
+ if parent.class != Account && parent != AccountRoot
130
30
  parent = Accounts.matches_by_id(parent).first
131
31
  end
132
32
  mult ||= parent.multiplier
@@ -539,20 +439,24 @@ class Accounts < Entities
539
439
  }
540
440
  end
541
441
 
442
+ def init
443
+ root = Accounts.create('Root', 'Initialisation')
444
+ %w( Income Outcome Lending Cash ).each { |a|
445
+ Accounts.create(a, 'Initialisation', root)
446
+ }
447
+ %w( Lending Cash ).each { |a|
448
+ acc = Accounts.match_by_name(a)
449
+ acc.multiplier = -1
450
+ acc.keep_total = true
451
+ }
452
+ Accounts.save
453
+ end
454
+
542
455
  def load
543
456
  super
544
457
  if Accounts.search_by_name('Root').count == 0
545
- dputs(1) { "Didn't find 'Root' in database - creating base" }
546
- root = Accounts.create('Root', 'Initialisation')
547
- %w( Income Outcome Lending Cash ).each { |a|
548
- Accounts.create(a, 'Initialisation', root)
549
- }
550
- %w( Lending Cash ).each { |a|
551
- acc = Accounts.match_by_name(a)
552
- acc.multiplier = -1
553
- acc.keep_total = true
554
- }
555
- Accounts.save
458
+ dputs(0) { "Didn't find 'Root' in database - creating base" }
459
+ Accounts.init
556
460
  end
557
461
  end
558
462
 
@@ -652,12 +556,112 @@ class Accounts < Entities
652
556
  end
653
557
  end
654
558
 
559
+ class AccountRoot
560
+
561
+ def self.actual
562
+ self.accounts.find { |a| a.name == 'Root' }
563
+ end
564
+
565
+ def self.current
566
+ AccountRoot.actual
567
+ end
568
+
569
+ def self.archive
570
+ self.accounts.find { |a| a.name == 'Archive' }
571
+ end
572
+
573
+ def self.accounts
574
+ Accounts.matches_by_account_id(0)
575
+ end
576
+
577
+ def self.clean
578
+ count_mov, count_acc = 0, 0
579
+ bad_mov, bad_acc = 0, 0
580
+ log_msg 'Account.clean', 'starting to clean up'
581
+ Movements.search_all_.each { |m|
582
+ dputs(4) { "Testing movement #{m.inspect}" }
583
+ if not m or not m.date or not m.desc or not m.value or
584
+ not m.rev_index or not m.account_src or not m.account_dst
585
+ if m and m.desc
586
+ log_msg 'Account.clean', "Bad movement: #{m.inspect}"
587
+ end
588
+ m.delete
589
+ bad_mov += 1
590
+ end
591
+ if m.rev_index
592
+ count_mov = [count_mov, m.rev_index].max
593
+ end
594
+ }
595
+ Accounts.search_all_.each { |a|
596
+ if (a.account_id && (a.account_id > 0)) && (!a.account)
597
+ log_msg 'Account.clean', "Account has unexistent parent: #{a.inspect}"
598
+ a.delete
599
+ bad_acc += 1
600
+ end
601
+ if !(a.account_id || a.deleted)
602
+ log_msg 'Account.clean', "Account has undefined parent: #{a.inspect}"
603
+ a.delete
604
+ bad_acc += 1
605
+ end
606
+ if a.account_id == 0 || a.account_id == nil
607
+ if !((a.name =~ /(Root|Archive)/) || a.deleted)
608
+ log_msg 'Account.clean', 'Account is in root but neither ' +
609
+ "'Root' nor 'Archive': #{a.inspect}"
610
+ a.delete
611
+ bad_acc += 1
612
+ end
613
+ end
614
+ if !a.rev_index
615
+ log_msg 'Account.clean', "Didn't find rev_index for #{a.inspect}"
616
+ a.new_index
617
+ bad_acc += 1
618
+ end
619
+ count_acc = [count_acc, a.rev_index].max
620
+ }
621
+
622
+ # Check also whether our counters are OK
623
+ u_l = Users.match_by_name('local')
624
+ dputs(1) { "Movements-index: #{count_mov} - #{u_l.movement_index}" }
625
+ dputs(1) { "Accounts-index: #{count_acc} - #{u_l.account_index}" }
626
+ @ul_mov, @ul_acc = u_l.movement_index, u_l.account_index
627
+ if count_mov > u_l.movement_index
628
+ log_msg 'Account.clean', 'Error, there is a bigger movement! Fixing'
629
+ u_l.movement_index = count_mov + 1
630
+ end
631
+ if count_acc > u_l.account_index
632
+ log_msg 'Account.clean', 'Error, there is a bigger account! Fixing'
633
+ u_l.account_index = count_acc + 1
634
+ end
635
+ return [count_mov, bad_mov, count_acc, bad_acc]
636
+ end
637
+
638
+ def self.path_id
639
+ return ''
640
+ end
641
+
642
+ def self.id
643
+ 0
644
+ end
645
+
646
+ def self.mult
647
+ 1
648
+ end
649
+
650
+ def self.keep_total
651
+ false
652
+ end
653
+
654
+ def self.multiplier
655
+ 1
656
+ end
657
+ end
658
+
655
659
  class Account < Entity
656
660
 
657
661
  def data_set(f, v)
658
662
  if !@proxy.loading
659
- if !%w( _total _rev_index ).index(f.to_s)
660
- dputs(4) { "Updating index for field #{f.inspect} - #{@pre_init} - #{@proxy.loading} - #{caller}" }
663
+ if !%w( _total _rev_index _global_id ).index(f.to_s)
664
+ dputs(4) { "Updating index for field #{f.inspect} - #{@pre_init} - #{@proxy.loading}: #{v}" }
661
665
  new_index
662
666
  end
663
667
  end
@@ -714,7 +718,7 @@ class Account < Entity
714
718
  end
715
719
  self.rev_index = u_l.account_index
716
720
  u_l.account_index += 1
717
- dputs(3) { "Index for account #{name} is #{index}" }
721
+ dputs(3) { "Index for account #{name} is #{rev_index}" }
718
722
  end
719
723
 
720
724
  def update_total(precision = 3)
@@ -874,7 +878,7 @@ class Account < Entity
874
878
  end
875
879
 
876
880
  def delete(force = false)
877
- if not is_empty and force
881
+ if not is_empty && force
878
882
  movements_src.each { |m|
879
883
  dputs(3) { "Deleting movement #{m.to_json}" }
880
884
  m.delete
@@ -0,0 +1,9 @@
1
+ class ConfigBases < Entities
2
+ def add_config
3
+ value_block :vars_narrow
4
+
5
+ value_block :vars_wide
6
+
7
+ @@functions = %i( accounting accounting_standalone )
8
+ end
9
+ end
@@ -1,12 +1,12 @@
1
1
  class Remotes < Entities
2
-
3
- def create( d )
4
- r = super( d )
5
- d.has_key?( :account_index ) || r.account_index = 0
6
- d.has_key?( :movement_index ) || r.movement_index = 0
2
+
3
+ def create(d)
4
+ r = super(d)
5
+ d.has_key?(:account_index) || r.account_index = 0
6
+ d.has_key?(:movement_index) || r.movement_index = 0
7
7
  r
8
8
  end
9
-
9
+
10
10
  def setup_data
11
11
  value_str :url
12
12
  value_str :name
@@ -14,9 +14,59 @@ class Remotes < Entities
14
14
  value_int :account_index
15
15
  value_int :movement_index
16
16
  end
17
+
18
+ def self.get_db(url, name, pass)
19
+ vers = Net::HTTP.get(URI.parse("#{url}/merge/version/#{name},#{pass}"))
20
+ if vers != $VERSION.to_s
21
+ return (vers =~ /not known/) ? 'User and/or password wrong' : 'Version mismatch'
22
+ end
23
+
24
+ db = Net::HTTP.get(URI.parse("#{url}/merge/get_db/#{name},#{pass}"))
25
+
26
+ SQLite.dbs_close_all
27
+ # All accounting-stuff is stored in the same database
28
+ IO.write(Accounts.storage[:SQLiteAC].db_file, db)
29
+ IO.write('/tmp/compta.db', db)
30
+ SQLite.dbs_open_load_migrate
31
+
32
+ # Delete all users except for the 'local' who needs a new id
33
+ Users.search_all_.each { |u|
34
+ if u.name == 'local'
35
+ u.reset_id
36
+ else
37
+ u.delete
38
+ end
39
+ }
40
+
41
+ # Delete all other remotes and add ourselves
42
+ Remotes.search_all_.each { |r| r.delete }
43
+ remote = Remotes.create(url: url, name: name, pass: pass)
44
+
45
+ # Update all indexes
46
+ ret = remote.do_copied
47
+ if ret == true
48
+ remote
49
+ else
50
+ ret
51
+ end
52
+ end
17
53
  end
18
54
 
19
55
  class Remote < Entity
56
+ attr_reader :step, :got_accounts, :put_accounts, :got_movements,
57
+ :put_movements, :put_movements_changes
58
+
59
+ def setup_instance
60
+ u = Users.find_by_name('local')
61
+ @step = 'preparation'
62
+ @account_index_stop, @movement_index_stop,
63
+ @got_accounts, @put_accounts,
64
+ @got_movements, @put_movements, @put_movements_changes =
65
+ [0] * 7
66
+ @account_index_stop = u.account_index - 1
67
+ @movement_index_stop = u.movement_index - 1
68
+ end
69
+
20
70
  def update_movement_index
21
71
  self.movement_index = Users.match_by_name('local').movement_index - 1
22
72
  end
@@ -24,4 +74,188 @@ class Remote < Entity
24
74
  def update_account_index
25
75
  self.account_index = Users.match_by_name('local').account_index - 1
26
76
  end
77
+
78
+ def post_form(path, hash)
79
+ dputs(4) { "Starting postForm with path #{path}" }
80
+ Net::HTTP.post_form(URI.parse(url + "/merge/#{path}"),
81
+ {'user' => name, 'pass' => pass}.merge(hash))
82
+ dputs(4) { "Ending postForm with path #{path}" }
83
+ end
84
+
85
+ def get_form(path)
86
+ url_parsed = URI.parse(url)
87
+ dputs(4) { "Starting getForm with path #{path} - #{url_parsed.inspect}" }
88
+ dputs(4) { "Finished parsing #{url}" }
89
+ ret = Net::HTTP.get(url_parsed.host, "#{url_parsed.path}/merge/#{path}/#{name},#{pass}", url_parsed.port)
90
+ dputs(4) { "Ending getForm with path #{path}" }
91
+ ret
92
+ end
93
+
94
+ # Calls the remote end and searches for new accounts and movements
95
+ # on both sides. Steps:
96
+ # 0: check version
97
+ # 1: get remote accounts
98
+ # 2: send our accounts
99
+ # 3: get remote movements
100
+ # 4: send our movements
101
+ # 5: update the indexes
102
+ # The variable +step+ can be used to watch the progress if it is called
103
+ # in a thread
104
+ def do_merge
105
+ setup_instance
106
+
107
+ u = Users.find_by_name('local')
108
+ log_msg :Merging, "Starting to merge for #{url}"
109
+
110
+ @step = 'checking remote'
111
+ begin
112
+ check_version
113
+ rescue StandardError => e
114
+ @step = "done - error: #{e.to_s}"
115
+ return
116
+ end
117
+
118
+ @step = 'getting remote accounts'
119
+ get_remote_accounts
120
+
121
+ @step = 'sending our accounts'
122
+ send_accounts
123
+
124
+ @step = 'getting remote movements'
125
+ get_remote_movements
126
+
127
+ @step = 'sending movements'
128
+ send_movements
129
+
130
+ @step = 'updating all indexes'
131
+ # Update the pointer
132
+ update_movement_index
133
+ # Update the remote pointers
134
+ get_form('reset_user_indexes')
135
+
136
+ @step = 'done'
137
+ end
138
+
139
+ def check_version(show_error = true)
140
+ local_id = Users.find_by_name('local').full
141
+ dputs(2) { "Local id is: #{local_id}" }
142
+ if (local_id == get_form('local_id'))
143
+ show_error and dputs(0) { 'Both locals have same ID' }
144
+ raise 'Same ID for local'
145
+ end
146
+
147
+ # Check the versions
148
+ if (vers = get_form('version')) != $VERSION.to_s
149
+ if (vers =~ /not known/)
150
+ show_error and dputs(0) { 'Username / password not recognized' }
151
+ raise 'Wrong credentials'
152
+ else
153
+ show_error and dputs(0) { "Got version #{vers} instead of #{$VERSION.to_s}" }
154
+ raise 'Wrong version'
155
+ end
156
+ end
157
+ return true
158
+ end
159
+
160
+ def get_remote_accounts
161
+ dputs(2) { 'Getting remotes' }
162
+ accounts = get_form('accounts_get')
163
+ accounts.split("\n").each { |a|
164
+ acc = Accounts.from_s(a)
165
+ dputs(2) { "Got account #{acc.name}" }
166
+ @got_accounts += 1
167
+ }
168
+ end
169
+
170
+ def send_accounts
171
+ @account_index_start = account_index.to_i + 1
172
+ if @account_index_start <= @account_index_stop
173
+ dputs(1) { "Accounts to send: #{@account_index_start}..#{@account_index_stop}" }
174
+ # Just search all accounts for indexes and sort them
175
+ Accounts.data.select { |k, v|
176
+ v._rev_index.between?(@account_index_start, @account_index_stop)
177
+ }.collect { |k, v| Accounts.get_data_instance(k) }.
178
+ sort_by { |a| a.rev_index }.each { |acc|
179
+ dputs(2) { "Account with index #{acc.rev_index} is being transferred" }
180
+ post_form('account_put', {'account' => acc.to_s})
181
+ @put_accounts += 1
182
+
183
+ }
184
+ else
185
+ dputs(1) { 'No accounts to send' }
186
+ end
187
+ # Update the pointer
188
+ update_account_index
189
+ end
190
+
191
+ def get_remote_movements
192
+ dputs(1) { 'Getting movements' }
193
+ # Now merge the movements
194
+ movements = get_form('movements_get')
195
+ movements.split("\n").each { |m|
196
+ dputs(2) { "String is: \n#{m}" }
197
+ mov = Movements.from_s(m)
198
+ @got_movements += 1
199
+ }
200
+ end
201
+
202
+ def send_movements
203
+ #dputs_func
204
+ @movement_index_start = movement_index.to_i + 1
205
+ @movement_count = 0
206
+ if @movement_index_start <= @movement_index_stop
207
+ dputs(1) { "Movements to send: #{@movement_index_start}.." +
208
+ "#{@movement_index_stop}" }
209
+ movements = []
210
+ Movements.data.select { |k, v| v._rev_index.between?(
211
+ @movement_index_start, @movement_index_stop
212
+ ) }.each { |k, v|
213
+ m = Movements.get_data_instance(k)
214
+ movements.push(m.to_json)
215
+ @put_movements += 1
216
+ }
217
+ @movement_count = movements.size
218
+ dputs(1) { "Having #{movements.size} movements to send" }
219
+ while movements.size > 0
220
+ # We'll do it by bunches of 10
221
+ movements_put = movements.shift 10
222
+ dputs(1) { 'Putting one bunch of 10 movements' }
223
+ dputs(4) { movements_put.to_json }
224
+ post_form('movements_put', {'movements' => movements_put.to_json})
225
+ @put_movements_changes += 1
226
+ # TODO: remove
227
+ # movements = []
228
+ end
229
+ else
230
+ dputs(1) { 'No movements to send' }
231
+ end
232
+ end
233
+
234
+ # Is used to reset the pointers, supposes both databases are equal -
235
+ # Should probably be used with care
236
+ def do_copied
237
+ check_version
238
+
239
+ # Update local indexes
240
+ u = Users.find_by_name('local')
241
+ update_account_index
242
+ update_movement_index
243
+
244
+ # Update remote indexes
245
+ get_form('reset_user_indexes')
246
+
247
+ # Check if everything is OK
248
+ acc, mov = get_form('index').split(',')
249
+
250
+ if acc.to_i != u.account_index
251
+ dputs(0) { "Trying to do 'copied' with wrong account-indexes" }
252
+ dputs(0) { "#{acc.to_i} - #{u.account_index}" }
253
+ end
254
+ if mov.to_i != u.movement_index
255
+ dputs(0) { "Trying to do 'copied' with wrong movement-indexes" }
256
+ dputs(0) { "#{mov.to_i} - #{u.movement_index}" }
257
+ end
258
+
259
+ return true
260
+ end
27
261
  end