appydave-tools 0.85.0 → 0.86.0

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.
@@ -0,0 +1,158 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Appydave
4
+ module Tools
5
+ module BankReconciliation
6
+ module Models
7
+ # Unified transaction model for raw and reconciled data
8
+ class Transaction
9
+ attr_accessor :bsb_number,
10
+ :account_number,
11
+ :transaction_date,
12
+ :narration,
13
+ :cheque_number,
14
+ :debit,
15
+ :credit,
16
+ :balance,
17
+ :transaction_type,
18
+ :platform,
19
+ :coa_code,
20
+ :coa_match_type,
21
+ :account_name,
22
+ :source_files,
23
+ :fin_year
24
+
25
+ def initialize(bsb_number: nil,
26
+ account_number: nil,
27
+ transaction_date: nil,
28
+ narration: nil,
29
+ cheque_number: nil,
30
+ debit: nil,
31
+ credit: nil,
32
+ balance: nil,
33
+ transaction_type: nil,
34
+ platform: nil,
35
+ coa_code: nil,
36
+ coa_match_type: nil,
37
+ account_name: nil)
38
+ account_number = account_number&.strip
39
+ @bsb_number = bsb_number&.strip
40
+ @account_number = account_number
41
+ @transaction_date = parse_date(transaction_date)
42
+ @narration = narration&.gsub(/\s{2,}/, ' ')&.strip
43
+ @cheque_number = cheque_number&.strip
44
+ @debit = debit # clean_amount(debit, account_number, coa_code)
45
+ @credit = credit # clean_amount(credit, account_number, coa_code)
46
+ @balance = balance&.strip
47
+ @transaction_type = transaction_type&.strip
48
+ @platform = platform
49
+ @coa_code = coa_code
50
+ @coa_match_type = coa_match_type
51
+ @account_name = account_name
52
+ @source_files = []
53
+ @fin_year = determine_fin_year(@transaction_date)
54
+ end
55
+
56
+ def add_source_file(source_file)
57
+ @source_files << source_file.strip unless @source_files.include?(source_file.strip)
58
+ end
59
+
60
+ def self.csv_headers
61
+ %i[
62
+ platform
63
+ account_name
64
+ bsb_number
65
+ account_number
66
+ transaction_date
67
+ narration
68
+ debit
69
+ credit
70
+ balance
71
+ transaction_type
72
+ coa_code
73
+ coa_match_type
74
+ source_files
75
+ fin_year
76
+ ]
77
+ end
78
+
79
+ def to_csv_row
80
+ [
81
+ @platform,
82
+ @account_name,
83
+ @bsb_number,
84
+ @account_number,
85
+ @transaction_date,
86
+ @narration,
87
+ @debit ? format('%.2f', @debit) : '',
88
+ @credit ? format('%.2f', @credit) : '',
89
+ @balance,
90
+ @transaction_type,
91
+ @coa_code,
92
+ @coa_match_type,
93
+ @source_files.join('; '),
94
+ @fin_year
95
+ ]
96
+ end
97
+
98
+ def clean_amount(amount)
99
+ return nil if amount.nil? || amount.strip.empty?
100
+
101
+ cleaned_amount = amount.to_f.round(2)
102
+
103
+ if swap_plus_minus_for_transactions
104
+ if cleaned_amount.negative?
105
+ cleaned_amount = cleaned_amount.abs
106
+ # else
107
+ # cleaned_amount *= -1
108
+ end
109
+ puts "Fixed negative amount for account: #{account_number} and COA: #{coa_code}, original amount: #{amount}, fixed amount: #{cleaned_amount}" if coa_code == 'DANCE'
110
+ end
111
+ cleaned_amount
112
+ end
113
+
114
+ # Returns true if this transaction's amount should have its sign flipped
115
+ # based on the per-account, per-FY, per-COA rules stored in config.
116
+ #
117
+ # Rules live in ~/.config/appydave/bank-reconciliation.json under
118
+ # `sign_flip_rules` — see BankReconciliationConfig::SignFlipRule.
119
+ # Personal account numbers MUST NOT be hardcoded here; the config file
120
+ # is local-only and never committed to the repo.
121
+ def swap_plus_minus_for_transactions
122
+ sign_flip_rules.any? do |rule|
123
+ (rule.fin_year.nil? || rule.fin_year == fin_year) &&
124
+ rule.account_number == account_number &&
125
+ rule.coa_codes.include?(coa_code)
126
+ end
127
+ end
128
+
129
+ private
130
+
131
+ def sign_flip_rules
132
+ Appydave::Tools::Configuration::Config.bank_reconciliation.sign_flip_rules
133
+ rescue StandardError
134
+ []
135
+ end
136
+
137
+ def parse_date(date_string)
138
+ formats = ['%d/%m/%Y', '%Y-%m-%d %H:%M:%S']
139
+ formats.each do |format|
140
+ return Date.strptime(date_string, format)
141
+ rescue Date::Error
142
+ next
143
+ end
144
+ raise Date::Error, "Invalid date format: #{date_string}"
145
+ end
146
+
147
+ def determine_fin_year(date)
148
+ if date.month >= 7
149
+ "#{date.year}-#{date.year + 1}"
150
+ else
151
+ "#{date.year - 1}-#{date.year}"
152
+ end
153
+ end
154
+ end
155
+ end
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,152 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Appydave
4
+ module Tools
5
+ module Configuration
6
+ module Models
7
+ # Bank reconciliation configuration
8
+ class BankReconciliationConfig < ConfigBase
9
+ # def
10
+ # Retrieve all bank accounts
11
+ def bank_accounts
12
+ data['bank_accounts'].map do |account|
13
+ BankAccount.new(account)
14
+ end
15
+ end
16
+
17
+ def chart_of_accounts
18
+ data['chart_of_accounts'].map do |entry|
19
+ ChartOfAccount.new(entry)
20
+ end
21
+ end
22
+
23
+ # Sign-flip rules — per-account, per-fin-year, per-COA exceptions where the
24
+ # raw bank-export sign is wrong and needs flipping during clean_amount.
25
+ # Personal account numbers belong here (in config), not in source.
26
+ def sign_flip_rules
27
+ (data['sign_flip_rules'] || []).map do |rule|
28
+ SignFlipRule.new(rule)
29
+ end
30
+ end
31
+
32
+ def get_bank_account(account_number, bsb = nil)
33
+ normalized_bsb = bsb.to_s.empty? ? nil : bsb
34
+ account_data = data['bank_accounts'].find do |account|
35
+ acct_bsb = account['bsb'].to_s.empty? ? nil : account['bsb']
36
+ account['account_number'] == account_number && (normalized_bsb.nil? || acct_bsb == normalized_bsb)
37
+ end
38
+
39
+ BankAccount.new(account_data) if account_data
40
+ end
41
+
42
+ # Retrieve a chart of account entry by code
43
+ def get_chart_of_account(code)
44
+ entry_data = data['chart_of_accounts'].find { |entry| entry['code'] == code }
45
+ ChartOfAccount.new(entry_data) if entry_data
46
+ end
47
+
48
+ def print
49
+ log.subheading 'Bank Reconciliation - Accounts'
50
+
51
+ tp bank_accounts, :account_number, :bsb, :name, :bank
52
+
53
+ log.subheading 'Bank Reconciliation - Chart of Accounts'
54
+
55
+ tp chart_of_accounts, :code, :narration
56
+ end
57
+
58
+ def coa_to_csv
59
+ csv_file_path = File.join(Config.config_path, 'bank_reconciliation.chart_of_accounts.csv')
60
+
61
+ CSV.open(csv_file_path, 'w') do |csv|
62
+ csv << %w[code narration]
63
+
64
+ chart_of_accounts.sort_by(&:code).each do |entry|
65
+ csv << [entry.code, entry.narration]
66
+ end
67
+ end
68
+ end
69
+
70
+ def coa_csv_to_json
71
+ csv_file_path = File.join(Config.config_path, 'bank_reconciliation.chart_of_accounts.csv')
72
+
73
+ coa_data = CSV.read(csv_file_path, headers: true).map(&:to_h)
74
+
75
+ data['chart_of_accounts'] = coa_data
76
+
77
+ save
78
+ end
79
+
80
+ private
81
+
82
+ def default_data
83
+ {
84
+ 'bank_accounts' => [],
85
+ 'chart_of_accounts' => [],
86
+ 'sign_flip_rules' => []
87
+ }
88
+ end
89
+
90
+ # Inner class to represent a bank account
91
+ class BankAccount
92
+ attr_accessor :account_number, :bsb, :name, :platform
93
+
94
+ def initialize(data)
95
+ @account_number = data['account_number']
96
+ @bsb = data['bsb']
97
+ @name = data['name']
98
+ @platform = data['platform']
99
+ end
100
+
101
+ def to_h
102
+ {
103
+ 'account_number' => @account_number,
104
+ 'bsb' => @bsb,
105
+ 'name' => @name,
106
+ 'platform' => @platform
107
+ }
108
+ end
109
+ end
110
+
111
+ # Inner class to represent a chart of account entry
112
+ class ChartOfAccount
113
+ attr_accessor :code, :narration
114
+
115
+ def initialize(data)
116
+ @code = data['code']
117
+ @narration = data['narration']
118
+ end
119
+
120
+ def to_h
121
+ {
122
+ 'code' => @code,
123
+ 'narration' => @narration
124
+ }
125
+ end
126
+ end
127
+
128
+ # Inner class to represent a sign-flip rule.
129
+ # Matches a transaction by (optional fin_year, account_number, coa_code).
130
+ # When nil, fin_year matches any year.
131
+ class SignFlipRule
132
+ attr_accessor :fin_year, :account_number, :coa_codes
133
+
134
+ def initialize(data)
135
+ @fin_year = data['fin_year']
136
+ @account_number = data['account_number']
137
+ @coa_codes = data['coa_codes'] || []
138
+ end
139
+
140
+ def to_h
141
+ {
142
+ 'fin_year' => @fin_year,
143
+ 'account_number' => @account_number,
144
+ 'coa_codes' => @coa_codes
145
+ }
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end
152
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Appydave
4
4
  module Tools
5
- VERSION = '0.85.0'
5
+ VERSION = '0.86.0'
6
6
  end
7
7
  end
@@ -56,7 +56,13 @@ require 'appydave/tools/configuration/models/settings_config'
56
56
  require 'appydave/tools/configuration/models/brands_config'
57
57
  require 'appydave/tools/configuration/models/channels_config'
58
58
  require 'appydave/tools/configuration/models/youtube_automation_config'
59
+ require 'appydave/tools/configuration/models/bank_reconciliation_config'
59
60
  require 'appydave/tools/configuration/example_installer'
61
+
62
+ require 'appydave/tools/bank_reconciliation/models/transaction'
63
+ require 'appydave/tools/bank_reconciliation/clean/read_transactions'
64
+ require 'appydave/tools/bank_reconciliation/clean/mapper'
65
+ require 'appydave/tools/bank_reconciliation/clean/clean_transactions'
60
66
  require 'appydave/tools/name_manager/project_name'
61
67
 
62
68
  require 'appydave/tools/prompt_tools/prompt_completion'
@@ -132,6 +138,7 @@ Appydave::Tools::Configuration::Config.set_default do |config|
132
138
  config.register(:brands, Appydave::Tools::Configuration::Models::BrandsConfig)
133
139
  config.register(:channels, Appydave::Tools::Configuration::Models::ChannelsConfig)
134
140
  config.register(:youtube_automation, Appydave::Tools::Configuration::Models::YoutubeAutomationConfig)
141
+ config.register(:bank_reconciliation, Appydave::Tools::Configuration::Models::BankReconciliationConfig)
135
142
  end
136
143
 
137
144
  module Appydave
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "appydave-tools",
3
- "version": "0.85.0",
3
+ "version": "0.86.0",
4
4
  "description": "AppyDave YouTube Automation Tools",
5
5
  "scripts": {
6
6
  "release": "semantic-release"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: appydave-tools
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.85.0
4
+ version: 0.86.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Cruwys
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-04-06 00:00:00.000000000 Z
11
+ date: 2026-06-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel
@@ -136,6 +136,20 @@ dependencies:
136
136
  - - "~>"
137
137
  - !ruby/object:Gem::Version
138
138
  version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: multi_json
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: '1.15'
146
+ type: :runtime
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: '1.15'
139
153
  - !ruby/object:Gem::Dependency
140
154
  name: pstore
141
155
  requirement: !ruby/object:Gem::Requirement
@@ -369,6 +383,11 @@ files:
369
383
  - lib/appydave/tools/app_context/app_finder.rb
370
384
  - lib/appydave/tools/app_context/globs_loader.rb
371
385
  - lib/appydave/tools/app_context/options.rb
386
+ - lib/appydave/tools/bank_reconciliation/_doc.md
387
+ - lib/appydave/tools/bank_reconciliation/clean/clean_transactions.rb
388
+ - lib/appydave/tools/bank_reconciliation/clean/mapper.rb
389
+ - lib/appydave/tools/bank_reconciliation/clean/read_transactions.rb
390
+ - lib/appydave/tools/bank_reconciliation/models/transaction.rb
372
391
  - lib/appydave/tools/brain_context/brain_finder.rb
373
392
  - lib/appydave/tools/brain_context/omi_finder.rb
374
393
  - lib/appydave/tools/brain_context/options.rb
@@ -381,6 +400,7 @@ files:
381
400
  - lib/appydave/tools/configuration/config.rb
382
401
  - lib/appydave/tools/configuration/configurable.rb
383
402
  - lib/appydave/tools/configuration/example_installer.rb
403
+ - lib/appydave/tools/configuration/models/bank_reconciliation_config.rb
384
404
  - lib/appydave/tools/configuration/models/brands_config.rb
385
405
  - lib/appydave/tools/configuration/models/channels_config.rb
386
406
  - lib/appydave/tools/configuration/models/config_base.rb