class Accounts
Attributes
check_progress[R]
check_state[R]
Public Class Methods
archive(month_start = 1, this_year = nil, only_account = nil)
click to toggle source
# File lib/africompta/entities/account.rb, line 292 def self.archive(month_start = 1, this_year = nil, only_account = nil) if not this_year now = Time.now this_year = now.year now.month < month_start and this_year -= 1 end root = AccountRoot.actual if only_account root = only_account elsif not root dputs(0) { 'Error: Root-account not available!' } return false elsif (root.account_id > 0) dputs(0) { "Error: Can't archive with Root is not in root: #{root.account_id.inspect}!" } return false end archive = AccountRoot.archive if not archive archive = self.create('Archive') end years_archived = {} archive.accounts.each { |a| years_archived[a.name.to_i] = a } dputs(2) { 'Got root and archive' } # For every account we search the most-used year, so # that we can move the account to that archive. This way # we omit as many as possible updates for the clients, as # every displacement of a movement will have to be updated, # while the displacement of an account is much simpler root.get_tree_depth { |acc| dputs(2) { "Looking at account #{acc.path}" } years = search_account(acc, month_start) if years.size > 0 most_used = last_used = this_year if acc.accounts.count == 0 most_used = years.key(years.values.max) last_used = years.keys.max years.delete most_used end acc_path = acc.path dputs(3) { "most_used: #{most_used} - last_used: #{last_used}" + "- acc_path: #{acc_path}" } # First move all other movements around if years.keys.size > 0 create_accounts(acc, years, years_archived, this_year) move_movements(acc, years, month_start) end if most_used != this_year # Now move account to archive-year of most movements parent = archive_parent(acc, years_archived, most_used) if double = Accounts.get_by_path("#{parent.get_path}::#{acc.name}") dputs(3) { "Account #{acc.path_id} already exists in #{parent.path_id}" } # Move all movements acc.movements.each { |m| dputs(4) { "Moving movement #{m.to_json}" } value = m.value m.value = 0 if m.account_src == acc m.account_src_id = double else m.account_dst_id = double end m.value = value } # Delete acc acc.delete else dputs(3) { "Moving account #{acc.path_id} to #{parent.path_id}" } acc.parent = parent end end # Check whether we need to add the account to the current year if (last_used >= this_year - 1) and (most_used != this_year) dputs(3) { "Adding #{acc_path} to this year with mult #{acc.multiplier}" } Accounts.create_path(acc_path, 'Copied from archive', false, acc.multiplier, acc.keep_total) end if acc.keep_total dputs(2) { "Keeping total for #{acc_path}" } # And create a trail so that every year contains the previous # years worth of "total" sum_up_total(acc_path, years_archived, month_start) dputs(5) { "acc_path is now #{acc_path}" } else dputs(2) { "Not keeping for #{acc_path}" } end else dputs(3) { "Empty account #{acc.movements.count} - #{acc.accounts.count}" } end if acc.accounts.count == 0 movs = acc.movements case movs.count when 0 dputs(3) { "Deleting empty account #{acc.path}" } if acc.path != 'Root' acc.delete else dputs(2) { 'Not deleting root!' } end when 1 dputs(3) { "Found only one movement for #{acc.path}" } if movs.first.desc =~ /^-- Sum of/ dputs(3) { 'Deleting account which has only a sum' } movs.first.delete if acc.path != 'Root' acc.delete else dputs(2) { 'Not deleting root!' } end end end end } if DEBUG_LVL >= 3 self.dump end end
create(name, desc = 'Too lazy', parent = nil, global_id = '', mult = nil)
click to toggle source
Calls superclass method
# File lib/africompta/entities/account.rb, line 26 def self.create(name, desc = 'Too lazy', parent = nil, global_id = '', mult = nil) dputs(5) { "Parent is #{parent.inspect}" } if parent if parent.class != Account && parent != AccountRoot parent = Accounts.matches_by_id(parent).first end mult ||= parent.multiplier a = super(:name => name, :desc => desc, :account_id => parent.id, :global_id => global_id.to_s, :multiplier => mult, :deleted => false, :keep_total => parent.keep_total) else mult ||= 1 a = super(:name => name, :desc => desc, :account_id => 0, :global_id => global_id.to_s, :multiplier => mult, :deleted => false, :keep_total => false) end a.total = 0 if global_id == '' a.global_id = Users.match_by_name('local').full + '-' + a.id.to_s end a.new_index dputs(2) { "Created account #{a.path_id} - #{a.inspect}" } a end
create_path(path, desc = '', double_last = false, mult = 1, keep_total = false)
click to toggle source
# File lib/africompta/entities/account.rb, line 51 def self.create_path(path, desc = '', double_last = false, mult = 1, keep_total = false) dputs(3) { "Path: #{path.inspect}, mult: #{mult}" } elements = path.split('::') parent = AccountRoot while elements.size > 0 name = elements.shift dputs(4) { "Working on element #{name} with base of #{parent.path_id}" } child = parent.accounts.find { |a| dputs(5) { "Searching child #{a.name} - #{a.path_id}" } a.name == name } child and dputs(4) { "Found existing child #{child.path_id}" } if (not child) or (elements.size == 0 and double_last) dputs(4) { "Creating child #{name}" } child = Accounts.create(name, desc, parent) end parent = child end parent.set_nochildmult(name, desc, nil, mult, [], keep_total) end
dump(mov = false)
click to toggle source
# File lib/africompta/entities/account.rb, line 428 def self.dump(mov = false) dputs(1) { 'Root-tree is now' } AccountRoot.actual.dump_rec(mov) if archive = AccountRoot.archive dputs(1) { 'Archive-tree is now' } archive.dump_rec(mov) else dputs(1) { 'No archive-tree' } end AccountRoot.accounts.each { |a| dputs(1) { "Root-Account: #{a.inspect}" } } end
dump_raw(mov = false)
click to toggle source
# File lib/africompta/entities/account.rb, line 422 def self.dump_raw(mov = false) Accounts.search_all.each { |a| a.dump(mov) } end
find_by_path(parent)
click to toggle source
# File lib/africompta/entities/account.rb, line 145 def self.find_by_path(parent) return Accounts.get_by_path(parent) end
from_s(str)
click to toggle source
Gets an account from a string, if it doesn't exist yet, creates it. It will update it anyway.
# File lib/africompta/entities/account.rb, line 75 def self.from_s(str) str.force_encoding(Encoding::UTF_8) desc, str = str.split("\r") if not str dputs(0) { "Error: Invalid account found: #{desc}" } return [-1, nil] end global_id, total, name, multiplier, par, deleted_s, keep_total_s = str.split("\t") total, multiplier = total.to_f, multiplier.to_f deleted = deleted_s == 'true' keep_total = keep_total_s == 'true' dputs(3) { [global_id, total, name, multiplier].inspect } dputs(3) { [par, deleted_s, keep_total_s].inspect } dputs(5) { "deleted, keep_total is #{deleted.inspect}, #{keep_total.inspect}" } dputs(3) { 'Here comes the account: ' + global_id.to_s } dputs(3) { "global_id: #{global_id}" } if par.to_s.length > 0 parent = Accounts.match_by_global_id(par) parent_id = parent.id dputs(3) { "Parent is #{parent.inspect}" } else parent = nil parent_id = 0 end # Does the account already exist? our_a = nil if not (our_a = Accounts.match_by_global_id(global_id)) # Create it dputs(3) { "Creating account #{name} - #{desc} - #{parent} - #{global_id}" } our_a = Accounts.create(name, desc, parent, global_id) end # And update it our_a.deleted = deleted our_a.set_nochildmult(name, desc, parent_id, multiplier, [], keep_total) our_a.global_id = global_id dputs(2) { "Saved account #{name} with index #{our_a.rev_index} and global_id #{our_a.global_id}" } dputs(4) { "Account is now #{our_a.inspect}" } return our_a end
get_by_path(parent, elements = nil)
click to toggle source
# File lib/africompta/entities/account.rb, line 123 def self.get_by_path(parent, elements = nil) if not elements if parent return get_by_path(AccountRoot, parent.split('::')) else return nil end end child = elements.shift parent.accounts.each { |a| if a.name == child if elements.length > 0 return get_by_path(a, elements) else return a end end } return nil end
get_by_path_or_create(p, desc = '', last = false, mult = 1, keep = false)
click to toggle source
# File lib/africompta/entities/account.rb, line 118 def self.get_by_path_or_create(p, desc = '', last = false, mult = 1, keep = false) get_by_path(p) or create_path(p, desc, last, mult, keep) end
get_id_by_path(p)
click to toggle source
# File lib/africompta/entities/account.rb, line 149 def self.get_id_by_path(p) if a = get_by_path(p) return a.id.to_s else return nil end end
Public Instance Methods
archive_parent(acc, years_archived, year)
click to toggle source
# File lib/africompta/entities/account.rb, line 157 def archive_parent(acc, years_archived, year) dputs(3) { "years_archived is #{years_archived.inspect}" } if not years_archived.has_key? year dputs(2) { "Adding #{year}" } years_archived[year] = Accounts.create_path("Archive::#{year}", 'New archive') end # This means we're more than one level below root, so we can't # just copy easily if acc.path.split('::').count > 2 dputs(3) { "Creating archive #{acc.path} with mult #{acc.multiplier}" } return Accounts.create_path("Archive::#{year}::"+ "#{acc.parent.path.gsub(/^Root::/, '')}", 'New archive', false, acc.multiplier, acc.keep_total) else return years_archived[year] end end
bool_to_s(b)
click to toggle source
# File lib/africompta/entities/account.rb, line 496 def bool_to_s(b) (b && b != 'f') ? 'true' : 'false' end
check_against_db(file)
click to toggle source
# File lib/africompta/entities/account.rb, line 500 def check_against_db(file) # First build # in_db - content of 'file' in .to_s format # in_local - content available locally in .to_s format in_db, diff, in_local = [], [], [] dputs(3) { 'Searching all accounts' } @check_state = 'Collect local' @check_progress = 0.0 in_local = Accounts.search_all_ progress_step = 1.0 / (in_local.size + 1) dputs(3) { "Found #{in_local.size} accounts" } @check_state = 'Collect local' in_local = in_local.collect { |a| @check_progress += progress_step a.to_s } dputs(3) { 'Loading file-db' } @check_state = 'Collect file-DB' @check_progress = 0.0 SQLite3::Database.new(file) do |db| db.execute('select id, account_id, name, desc, global_id, total, '+ 'multiplier, "index", rev_index, deleted, keep_total '+ 'from compta_accounts').sort_by { |a| a[4] }.each do |row| #dputs(3) { "Looking at #{row}" } @check_progress += progress_step _, acc_id_, name_, desc_, gid_, tot_, mult_, _, _, del_, keep_, = row parent = if acc_id_ acc_id_ == 0 ? '' : db.execute("select * from compta_accounts where id=#{acc_id_}").first[4] else '' end in_db.push "#{desc_}\r#{gid_}\t" + "#{sprintf('%.3f', tot_.to_f.round(3))}\t#{name_.to_s}\t"+ "#{mult_.to_i.to_s}\t#{parent}" + "\t#{bool_to_s(del_)}" + "\t#{bool_to_s(keep_)}" end end # Now compare what is available only in db and what is available only locally dputs(3) { 'Comparing local accounts with file-db accounts' } @check_state = 'On one side' @check_progress = 0.0 in_db.delete_if { |a| @check_progress += progress_step in_local.delete(a) } # And search for accounts with same global-id but different content dputs(3) { 'Seaching mix-ups' } @check_state = 'Mixed-up' @check_progress = 0.0 progress_step = 1.0 / (in_db.size + 1) (in_db + in_local).sort_by { |a| a.match(/\r(.*?)\t/)[1] } in_db.delete_if { |a| @check_progress += progress_step gid = a.match(/\r(.*?)\t/)[1] if c = in_local.find { |b| b =~ /\r#{gid}\t/ } diff.push [a, c] in_local.delete c end } @check_state = 'Done' [in_db, diff, in_local] end
create_accounts(acc, years, years_archived, this_year)
click to toggle source
# File lib/africompta/entities/account.rb, line 190 def create_accounts(acc, years, years_archived, this_year) years.keys.each { |y| if y == this_year dputs(3) { "Creating path #{acc.path} with mult #{acc.multiplier}" } years[y] = Accounts.create_path(acc.path, acc.desc, true, acc.multiplier, acc.keep_total) else path = "#{archive_parent(acc, years_archived, y).path}::" + acc.name dputs(3) { "Creating other path #{path} with mult #{acc.multiplier}" } years[y] = Accounts.create_path(path, acc.desc, false, acc.multiplier, acc.keep_total) end dputs(3) { "years[y] is #{years[y].path_id}" } } end
init()
click to toggle source
# File lib/africompta/entities/account.rb, line 442 def init root = Accounts.create('Root', 'Initialisation') %w( Income Outcome Lending Cash ).each { |a| Accounts.create(a, 'Initialisation', root) } %w( Lending Cash ).each { |a| acc = Accounts.match_by_name(a) acc.multiplier = -1 acc.keep_total = true } Accounts.save end
listp_path()
click to toggle source
# File lib/africompta/entities/account.rb, line 488 def listp_path dputs(3) { 'Being called' } Accounts.search_all.select { |a| !a.deleted }.collect { |a| [a.id, a.path] }. sort { |a, b| a[1] <=> b[1] } end
load()
click to toggle source
Calls superclass method
# File lib/africompta/entities/account.rb, line 455 def load super if Accounts.search_by_name('Root').count == 0 dputs(1) { "Didn't find 'Root' in database - creating base" } Accounts.init end end
migration_1(a)
click to toggle source
# File lib/africompta/entities/account.rb, line 463 def migration_1(a) dputs(4) { Accounts.storage[:SQLiteAC].db_class.inspect } a.deleted = false # As most of the accounts in Cash have -1 and shall be kept, this # gives a good first initialisation a.keep_total = (a.multiplier == -1.0) || (a.multiplier == -1) dputs(4) { "#{a.name}: #{a.deleted.inspect} - #{a.keep_total.inspect}" } end
migration_2(a)
click to toggle source
# File lib/africompta/entities/account.rb, line 472 def migration_2(a) a.rev_index = a.id end
migration_3(m)
click to toggle source
# File lib/africompta/entities/account.rb, line 476 def migration_3(m) # dp "Migrating #{m.inspect}" if m.id == 1 && m.account_id != nil dp 'Root-account has parent...' m.account_id = 0 m.name = 'Root' m.desc = 'Root' m.global_id = Digest::MD5.hexdigest((rand 2**128).to_s).to_s + '-1' m.total = 0.0 end end
move_movements(acc, years, month_start)
click to toggle source
# File lib/africompta/entities/account.rb, line 207 def move_movements(acc, years, month_start) acc.movements.each { |mov| dputs(5) { 'Start of each' } y, m, _ = mov.date.to_s.split('-').collect { |d| d.to_i } dputs(5) { "Date of #{mov.desc} is #{mov.date}" } m < month_start and y -= 1 if years.has_key? y value = mov.value mov.value = 0 dputs(5) { "Moving to #{years[y].inspect}: " + "#{mov.account_src.id} - #{mov.account_dst.id} - #{acc.id}" } if mov.account_src.id == acc.id dputs(5) { 'Moving src' } mov.account_src_id = years[y] else dputs(5) { 'Moving dst' } mov.account_dst_id = years[y] end mov.value = value end } dputs(5) { "Movements left in account #{acc.path}:" } acc.movements.each { |m| dputs(5) { m.desc } } end
search_account(acc, month_start)
click to toggle source
# File lib/africompta/entities/account.rb, line 176 def search_account(acc, month_start) years = Hash.new(0) acc.movements.each { |mov| if not mov.desc =~ /^-- Sum of/ y, m, _ = mov.date.to_s.split('-').collect { |d| d.to_i } dputs(5) { "Date of #{mov.desc} is #{mov.date}" } m < month_start and y -= 1 years[y] += 1 end } dputs(3) { "years is #{years.inspect}" } years end
setup_data()
click to toggle source
# File lib/africompta/entities/account.rb, line 9 def setup_data @default_type = :SQLiteAC @data_field_id = :id value_int :index value_str :name value_str :desc value_str :global_id value_float :total value_int :multiplier value_int :rev_index value_bool :deleted value_bool :keep_total # This is the ID of the parent account value_int :account_id end
sum_up_total(acc_path, years_archived, month_start)
click to toggle source
# File lib/africompta/entities/account.rb, line 234 def sum_up_total(acc_path, years_archived, month_start) a_path = acc_path.sub(/[^:]*::/, '') dputs(2) { "Summing up account #{a_path}" } acc_sum = [] years_archived.each { |y, a| dputs(5) { "Found archived year #{y.inspect} which is #{y.class.name}" } aacc = Accounts.get_by_path(a.get_path + '::' + a_path) acc_sum.push [y, a, aacc] } dputs(5) { 'Trying to add current year' } if curr_acc = Accounts.get_by_path(acc_path) dputs(4) { 'Adding current year' } acc_sum.push [9999, nil, curr_acc] end last_total = 0 last_year_acc = nil last_year_acc_parent = nil last_year = 0 dputs(5) { "Sorting account_sums #{acc_sum.length}" } acc_sum.sort { |a, b| a[0] <=> b[0] }.each { |y, a, aacc| dputs(5) { "y, a, aacc: #{y}, #{a.to_json}, #{aacc.to_json}" } if aacc last_year_acc_parent and last_year_acc_parent.dump true dputs(4) { "Found archived account #{aacc.get_path} for year #{y}" + " with last_total #{last_total}" } dputs(5) { 'And has movements' } aacc.movements.each { |m| dputs(5) { m.to_json } } if last_total != 0 desc = "-- Sum of #{last_year} of #{last_year_acc.path}" date = "#{last_year + 1}-#{month_start.to_s.rjust(2, '0')}-01" dputs(3) { "Deleting old sums with date #{date.inspect}" } Movements.matches_by_desc("^#{desc}$").each { |m| dputs(3) { "Testing movement with date #{m.date.to_s.inspect}: #{m.to_json}" } if m.date.to_s == date.to_s dputs(3) { 'Deleting it' } m.delete end } dputs(3) { "Creating movement for the sum of last year: #{last_total}" } mov = Movements.create(desc, date, last_total, last_year_acc_parent, aacc) last_year_acc_parent.dump true dputs(3) { "Movement is: #{mov.to_json}" } end aacc.update_total dputs(5) { "#{aacc.total} - #{aacc.multiplier}" } last_total = aacc.total * aacc.multiplier else dputs(4) { "Didn't find archived account for #{y}" } last_total = 0 end last_year, last_year_acc, last_year_acc_parent = y, aacc, a } end