appydave-tools 0.10.3 → 0.10.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +1 -0
- data/CHANGELOG.md +7 -0
- data/bin/bank_reconciliation.rb +23 -0
- data/lib/appydave/tools/{prompt_tools → llm}/models/llm_info.rb +1 -1
- data/lib/appydave/tools/llm/openai_completion.rb +11 -0
- data/lib/appydave/tools/prompt_tools/prompt_completion.rb +2 -2
- data/lib/appydave/tools/version.rb +1 -1
- data/lib/appydave/tools.rb +8 -6
- data/package-lock.json +2 -2
- data/package.json +1 -1
- metadata +4 -9
- data/lib/appydave/tools/bank_reconciliation/_doc.md +0 -36
- data/lib/appydave/tools/bank_reconciliation/clean/clean_transactions.rb +0 -145
- data/lib/appydave/tools/bank_reconciliation/clean/mapper.rb +0 -136
- data/lib/appydave/tools/bank_reconciliation/clean/read_transactions.rb +0 -90
- data/lib/appydave/tools/bank_reconciliation/models/transaction.rb +0 -101
- data/lib/appydave/tools/configuration/models/bank_reconciliation_config.rb +0 -97
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 40337ac9a46d2a5d2b7a645b14e572f011c84b52e958ba880be666d6dfe3413b
|
4
|
+
data.tar.gz: 4e4e4bd076cae97a6cdec63dd70313f49efcbb19fb6295ffd1ef297dbbf08e42
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 86448501dd1959eb0173ed98af2dead6965b56bf9cfcdc5007d335b73ecd94f8bb7e5cda9f099997285509e93f7ecff1368740fbe38c16c5e4240b1578462116
|
7
|
+
data.tar.gz: 9e32acedc6fbc3b7e21123a56f068e7c91a6b6b4cc58a7315102a08b9455950f531755c5d1405b66dd52a9df60132aab08395877f771d8b096bb364aa6e83e7e
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
## [0.10.3](https://github.com/klueless-io/appydave-tools/compare/v0.10.2...v0.10.3) (2024-06-17)
|
2
|
+
|
3
|
+
|
4
|
+
### Bug Fixes
|
5
|
+
|
6
|
+
* extending bank reconciliation with platform and banking mapping ([848b044](https://github.com/klueless-io/appydave-tools/commit/848b044bf4bb7c27bae6cf33aba400ab68eb105c))
|
7
|
+
|
1
8
|
## [0.10.2](https://github.com/klueless-io/appydave-tools/compare/v0.10.1...v0.10.2) (2024-06-17)
|
2
9
|
|
3
10
|
|
data/bin/bank_reconciliation.rb
CHANGED
@@ -14,6 +14,7 @@ class BankReconciliationCLI
|
|
14
14
|
def initialize
|
15
15
|
@commands = {
|
16
16
|
'clean' => method(:clean_transactions),
|
17
|
+
'transform' => method(:transform),
|
17
18
|
'process' => method(:process_transactions),
|
18
19
|
'filter' => method(:filter_transactions)
|
19
20
|
}
|
@@ -73,6 +74,28 @@ class BankReconciliationCLI
|
|
73
74
|
cleaner.clean_transactions(include_patterns, output_file)
|
74
75
|
end
|
75
76
|
|
77
|
+
def transform(args)
|
78
|
+
options = {}
|
79
|
+
OptionParser.new do |opts|
|
80
|
+
opts.banner = 'Usage: bank_reconciliation.rb clean [options]'
|
81
|
+
|
82
|
+
opts.on('-c', '--to-csv', 'Write chart of accounts JSON to CSV') { options[:to_csv] = true }
|
83
|
+
opts.on('-j', '--to-json', 'Write chart of accounts CSV to JSON') { options[:to_json] = true }
|
84
|
+
|
85
|
+
opts.on('-d', '--debug', 'Enable debug mode') do
|
86
|
+
options[:debug] = true
|
87
|
+
end
|
88
|
+
|
89
|
+
opts.on_tail('-h', '--help', 'Show this message') do
|
90
|
+
puts opts
|
91
|
+
exit
|
92
|
+
end
|
93
|
+
end.parse!(args)
|
94
|
+
|
95
|
+
Appydave::Tools::Configuration::Models::BankReconciliationConfig.new.coa_to_csv if options[:to_csv]
|
96
|
+
Appydave::Tools::Configuration::Models::BankReconciliationConfig.new.coa_csv_to_json if options[:to_json]
|
97
|
+
end
|
98
|
+
|
76
99
|
def process_transactions(args)
|
77
100
|
options = {}
|
78
101
|
OptionParser.new do |opts|
|
@@ -16,7 +16,7 @@ module Appydave
|
|
16
16
|
attr_reader :clipboard
|
17
17
|
|
18
18
|
def initialize(options = {})
|
19
|
-
|
19
|
+
setup_options(options)
|
20
20
|
|
21
21
|
validate_options
|
22
22
|
end
|
@@ -44,7 +44,7 @@ module Appydave
|
|
44
44
|
|
45
45
|
private
|
46
46
|
|
47
|
-
def
|
47
|
+
def setup_options(options)
|
48
48
|
@prompt = options.delete(:prompt)
|
49
49
|
@prompt_file = options.delete(:prompt_file)
|
50
50
|
@llm = Appydave::Tools::PromptTools::Models::LlmInfo.new(
|
data/lib/appydave/tools.rb
CHANGED
@@ -24,6 +24,9 @@ require 'appydave/tools/types/hash_type'
|
|
24
24
|
require 'appydave/tools/types/array_type'
|
25
25
|
require 'appydave/tools/types/base_model'
|
26
26
|
|
27
|
+
require 'appydave/tools/llm/models/llm_info'
|
28
|
+
require 'appydave/tools/llm/openai_completion'
|
29
|
+
|
27
30
|
require 'appydave/tools/cli_actions/base_action'
|
28
31
|
|
29
32
|
# May want to move this into the tools location
|
@@ -38,16 +41,15 @@ require 'appydave/tools/configuration/configurable'
|
|
38
41
|
require 'appydave/tools/configuration/config'
|
39
42
|
require 'appydave/tools/configuration/models/config_base'
|
40
43
|
require 'appydave/tools/configuration/models/settings_config'
|
41
|
-
require 'appydave/tools/configuration/models/bank_reconciliation_config'
|
44
|
+
# require 'appydave/tools/configuration/models/bank_reconciliation_config'
|
42
45
|
require 'appydave/tools/configuration/models/channels_config'
|
43
46
|
require 'appydave/tools/configuration/models/youtube_automation_config'
|
44
47
|
require 'appydave/tools/name_manager/project_name'
|
45
|
-
require 'appydave/tools/bank_reconciliation/clean/clean_transactions'
|
46
|
-
require 'appydave/tools/bank_reconciliation/clean/read_transactions'
|
47
|
-
require 'appydave/tools/bank_reconciliation/clean/mapper'
|
48
|
-
require 'appydave/tools/bank_reconciliation/models/transaction'
|
48
|
+
# require 'appydave/tools/bank_reconciliation/clean/clean_transactions'
|
49
|
+
# require 'appydave/tools/bank_reconciliation/clean/read_transactions'
|
50
|
+
# require 'appydave/tools/bank_reconciliation/clean/mapper'
|
51
|
+
# require 'appydave/tools/bank_reconciliation/models/transaction'
|
49
52
|
|
50
|
-
require 'appydave/tools/prompt_tools/models/llm_info'
|
51
53
|
require 'appydave/tools/prompt_tools/prompt_completion'
|
52
54
|
|
53
55
|
require 'appydave/tools/subtitle_master/clean'
|
data/package-lock.json
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
{
|
2
2
|
"name": "appydave-tools",
|
3
|
-
"version": "0.10.
|
3
|
+
"version": "0.10.4",
|
4
4
|
"lockfileVersion": 3,
|
5
5
|
"requires": true,
|
6
6
|
"packages": {
|
7
7
|
"": {
|
8
8
|
"name": "appydave-tools",
|
9
|
-
"version": "0.10.
|
9
|
+
"version": "0.10.4",
|
10
10
|
"devDependencies": {
|
11
11
|
"@klueless-js/semantic-release-rubygem": "github:klueless-js/semantic-release-rubygem",
|
12
12
|
"@semantic-release/changelog": "^6.0.3",
|
data/package.json
CHANGED
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.10.
|
4
|
+
version: 0.10.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Cruwys
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-10-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activemodel
|
@@ -167,11 +167,6 @@ files:
|
|
167
167
|
- bin/youtube_manager.rb
|
168
168
|
- images.log
|
169
169
|
- lib/appydave/tools.rb
|
170
|
-
- lib/appydave/tools/bank_reconciliation/_doc.md
|
171
|
-
- lib/appydave/tools/bank_reconciliation/clean/clean_transactions.rb
|
172
|
-
- lib/appydave/tools/bank_reconciliation/clean/mapper.rb
|
173
|
-
- lib/appydave/tools/bank_reconciliation/clean/read_transactions.rb
|
174
|
-
- lib/appydave/tools/bank_reconciliation/models/transaction.rb
|
175
170
|
- lib/appydave/tools/cli_actions/_doc.md
|
176
171
|
- lib/appydave/tools/cli_actions/base_action.rb
|
177
172
|
- lib/appydave/tools/cli_actions/get_video_action.rb
|
@@ -180,7 +175,6 @@ files:
|
|
180
175
|
- lib/appydave/tools/configuration/_doc.md
|
181
176
|
- lib/appydave/tools/configuration/config.rb
|
182
177
|
- lib/appydave/tools/configuration/configurable.rb
|
183
|
-
- lib/appydave/tools/configuration/models/bank_reconciliation_config.rb
|
184
178
|
- lib/appydave/tools/configuration/models/channels_config.rb
|
185
179
|
- lib/appydave/tools/configuration/models/config_base.rb
|
186
180
|
- lib/appydave/tools/configuration/models/settings_config copy.xrb
|
@@ -190,10 +184,11 @@ files:
|
|
190
184
|
- lib/appydave/tools/debuggable.rb
|
191
185
|
- lib/appydave/tools/gpt_context/_doc.md
|
192
186
|
- lib/appydave/tools/gpt_context/file_collector.rb
|
187
|
+
- lib/appydave/tools/llm/models/llm_info.rb
|
188
|
+
- lib/appydave/tools/llm/openai_completion.rb
|
193
189
|
- lib/appydave/tools/name_manager/_doc.md
|
194
190
|
- lib/appydave/tools/name_manager/project_name.rb
|
195
191
|
- lib/appydave/tools/prompt_tools/_doc.md
|
196
|
-
- lib/appydave/tools/prompt_tools/models/llm_info.rb
|
197
192
|
- lib/appydave/tools/prompt_tools/prompt_completion.rb
|
198
193
|
- lib/appydave/tools/subtitle_master/_doc.md
|
199
194
|
- lib/appydave/tools/subtitle_master/clean.rb
|
@@ -1,36 +0,0 @@
|
|
1
|
-
# Bank reconciliation
|
2
|
-
|
3
|
-
[ChatGPT conversation](https://chatgpt.com/c/5d382562-95e5-4243-9b74-c3807d363486)
|
4
|
-
|
5
|
-
|
6
|
-
## Code structure
|
7
|
-
|
8
|
-
```bash
|
9
|
-
├─ lib
|
10
|
-
│ ├─ appydave
|
11
|
-
│ │ └─ tools
|
12
|
-
│ │ ├─ bank_reconciliation
|
13
|
-
│ │ │ ├─ clean
|
14
|
-
│ │ │ │ ├─ read_transactions.rb
|
15
|
-
│ │ │ │ ├─ transaction_cleaner.rb
|
16
|
-
│ │ │ ├─ models
|
17
|
-
│ │ │ │ ├─ raw_transaction.rb
|
18
|
-
│ │ │ │ └─ reconciled_transaction.rb
|
19
|
-
│ │ └─ configuration
|
20
|
-
│ │ └─ models
|
21
|
-
│ │ └─ bank_reconciliation_config.rb
|
22
|
-
└─ spec
|
23
|
-
├─ appydave
|
24
|
-
│ ├─ tools
|
25
|
-
│ │ ├─ bank_reconciliation
|
26
|
-
│ │ │ ├─ clean
|
27
|
-
│ │ │ │ ├─ read_transactions_spec.rb
|
28
|
-
│ │ │ ├─ models
|
29
|
-
│ │ │ │ └─ raw_transaction_spec.rb
|
30
|
-
│ │ └─ configuration
|
31
|
-
│ │ └─ models
|
32
|
-
│ │ └─ bank_reconciliation_config_spec.rb
|
33
|
-
└─ fixtures
|
34
|
-
└─ bank-reconciliation
|
35
|
-
└─ bank-west.csv
|
36
|
-
```
|
@@ -1,145 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Appydave
|
4
|
-
module Tools
|
5
|
-
module BankReconciliation
|
6
|
-
module Clean
|
7
|
-
# Clean transactions
|
8
|
-
class CleanTransactions
|
9
|
-
include KLog::Logging
|
10
|
-
include Appydave::Tools::Configuration::Configurable
|
11
|
-
include Appydave::Tools::Debuggable
|
12
|
-
|
13
|
-
attr_reader :transaction_folder
|
14
|
-
attr_reader :output_folder
|
15
|
-
attr_reader :transactions
|
16
|
-
|
17
|
-
# (config_file)
|
18
|
-
def initialize(transaction_folder: nil, output_folder: nil, debug: false)
|
19
|
-
@debug = debug
|
20
|
-
# needs to use config.bank_reconciliation.transaction_folder
|
21
|
-
transaction_folder ||= '/Volumes/Expansion/Sync/bank-reconciliation/original-transactions'
|
22
|
-
output_folder ||= File.join(transaction_folder, 'clean')
|
23
|
-
|
24
|
-
@transaction_folder = transaction_folder
|
25
|
-
@output_folder = output_folder
|
26
|
-
end
|
27
|
-
|
28
|
-
def clean_transactions(input_globs, output_file)
|
29
|
-
log_info("Starting transaction cleaning with input patterns: #{input_globs}")
|
30
|
-
|
31
|
-
raw_transactions = grab_raw_transactions(input_globs)
|
32
|
-
log_info("Total raw transactions collected: #{raw_transactions.size}")
|
33
|
-
|
34
|
-
transactions, duplicates_count = deduplicate_across_files(raw_transactions)
|
35
|
-
log_info("Duplicates found and removed: #{duplicates_count}")
|
36
|
-
|
37
|
-
transactions = Mapper.new.map(transactions)
|
38
|
-
log_info('Transactions mapped to chart of accounts and bank accounts')
|
39
|
-
|
40
|
-
log_kv 'Deduped consolidated transactions', duplicates_count if duplicates_count.positive?
|
41
|
-
|
42
|
-
save_to_csv(transactions, output_file)
|
43
|
-
log_kv('Transaction Output File', full_output_file(output_file))
|
44
|
-
|
45
|
-
@transactions = transactions
|
46
|
-
end
|
47
|
-
|
48
|
-
private
|
49
|
-
|
50
|
-
def grab_raw_transactions(input_globs)
|
51
|
-
original_dir = Dir.pwd
|
52
|
-
transactions = []
|
53
|
-
|
54
|
-
begin
|
55
|
-
Dir.chdir(transaction_folder)
|
56
|
-
|
57
|
-
input_globs.each do |glob|
|
58
|
-
Dir.glob(glob).each do |file|
|
59
|
-
log_kv 'Reading transactions from', file
|
60
|
-
raw_transactions = ReadTransactions.new(file).read
|
61
|
-
deduped_transactions, duplicates_count = deduplicate_within_file(raw_transactions)
|
62
|
-
|
63
|
-
if duplicates_count.positive?
|
64
|
-
log_kv 'Duplicates count within file', duplicates_count
|
65
|
-
log_kv 'File', file
|
66
|
-
end
|
67
|
-
|
68
|
-
transactions += deduped_transactions
|
69
|
-
end
|
70
|
-
end
|
71
|
-
ensure
|
72
|
-
Dir.chdir(original_dir)
|
73
|
-
end
|
74
|
-
|
75
|
-
transactions
|
76
|
-
end
|
77
|
-
|
78
|
-
def deduplicate_within_file(transactions)
|
79
|
-
unique_transactions = transactions.uniq do |transaction|
|
80
|
-
[
|
81
|
-
transaction.bsb_number,
|
82
|
-
transaction.account_number,
|
83
|
-
transaction.transaction_date,
|
84
|
-
transaction.narration,
|
85
|
-
transaction.cheque_number,
|
86
|
-
transaction.debit,
|
87
|
-
transaction.credit,
|
88
|
-
transaction.balance,
|
89
|
-
transaction.transaction_type
|
90
|
-
]
|
91
|
-
end
|
92
|
-
|
93
|
-
duplicates = transactions.size - unique_transactions.size
|
94
|
-
|
95
|
-
[unique_transactions, duplicates]
|
96
|
-
end
|
97
|
-
|
98
|
-
def deduplicate_across_files(transactions)
|
99
|
-
grouped_transactions = transactions.group_by do |transaction|
|
100
|
-
[
|
101
|
-
transaction.bsb_number,
|
102
|
-
transaction.account_number,
|
103
|
-
transaction.transaction_date,
|
104
|
-
transaction.narration,
|
105
|
-
transaction.cheque_number,
|
106
|
-
transaction.debit,
|
107
|
-
transaction.credit,
|
108
|
-
transaction.balance,
|
109
|
-
transaction.transaction_type
|
110
|
-
]
|
111
|
-
end
|
112
|
-
|
113
|
-
unique_transactions = []
|
114
|
-
duplicates_count = 0
|
115
|
-
|
116
|
-
grouped_transactions.each_value do |dupes|
|
117
|
-
unique_transaction = dupes.first
|
118
|
-
unique_transaction.source_files = dupes.flat_map(&:source_files).uniq
|
119
|
-
duplicates_count += dupes.size - 1
|
120
|
-
unique_transactions << unique_transaction
|
121
|
-
end
|
122
|
-
|
123
|
-
[unique_transactions, duplicates_count]
|
124
|
-
end
|
125
|
-
|
126
|
-
def full_output_file(output_file)
|
127
|
-
File.join(output_folder, output_file)
|
128
|
-
end
|
129
|
-
|
130
|
-
def save_to_csv(transactions, output_file)
|
131
|
-
FileUtils.mkdir_p(output_folder)
|
132
|
-
output_file = full_output_file(output_file)
|
133
|
-
|
134
|
-
CSV.open(output_file, 'w') do |csv|
|
135
|
-
csv << Appydave::Tools::BankReconciliation::Models::Transaction.csv_headers
|
136
|
-
transactions.each do |transaction|
|
137
|
-
csv << transaction.to_csv_row
|
138
|
-
end
|
139
|
-
end
|
140
|
-
end
|
141
|
-
end
|
142
|
-
end
|
143
|
-
end
|
144
|
-
end
|
145
|
-
end
|
@@ -1,136 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Appydave
|
4
|
-
module Tools
|
5
|
-
module BankReconciliation
|
6
|
-
module Clean
|
7
|
-
# Map transactions to chart of accounts and bank accounts
|
8
|
-
class Mapper
|
9
|
-
include Appydave::Tools::Configuration::Configurable
|
10
|
-
|
11
|
-
# "bank_accounts": [
|
12
|
-
# {
|
13
|
-
# "account_number": "5435 6859 0116 7736",
|
14
|
-
# "bsb": "",
|
15
|
-
# "name": "Mastercard",
|
16
|
-
# "platform": "Bankwest"
|
17
|
-
# },
|
18
|
-
# {
|
19
|
-
# "account_number": "303-092",
|
20
|
-
# "bsb": "1361644",
|
21
|
-
# "name": "atcall",
|
22
|
-
# "platform": "Bankwest"
|
23
|
-
# },
|
24
|
-
|
25
|
-
def map(transactions)
|
26
|
-
transactions.map do |original_transaction|
|
27
|
-
transaction = original_transaction.dup
|
28
|
-
|
29
|
-
transaction = map_chart_of_account(transaction)
|
30
|
-
map_bank_account(transaction)
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
private
|
35
|
-
|
36
|
-
def map_chart_of_account(transaction)
|
37
|
-
equality_match(transaction) ||
|
38
|
-
trigram_match(transaction, 0.9, '90%') ||
|
39
|
-
trigram_match(transaction, 0.8, '80%') ||
|
40
|
-
trigram_match(transaction, 0.7, '70%') ||
|
41
|
-
trigram_match(transaction, 0.6, '60%') ||
|
42
|
-
trigram_match(transaction, 0.5, '50%') ||
|
43
|
-
start_with_match(transaction) ||
|
44
|
-
includes(transaction)
|
45
|
-
transaction
|
46
|
-
end
|
47
|
-
|
48
|
-
def map_bank_account(transaction)
|
49
|
-
bank_account = config.bank_reconciliation.get_bank_account(transaction.account_number, transaction.bsb_number)
|
50
|
-
|
51
|
-
if bank_account
|
52
|
-
transaction.account_name = bank_account.name
|
53
|
-
transaction.platform = bank_account.platform
|
54
|
-
end
|
55
|
-
|
56
|
-
transaction
|
57
|
-
end
|
58
|
-
|
59
|
-
def equality_match(transaction)
|
60
|
-
coa = config.bank_reconciliation.chart_of_accounts.find do |chart_of_account|
|
61
|
-
chart_of_account.narration.to_s.delete(' ').downcase == transaction.narration.delete(' ').downcase
|
62
|
-
end
|
63
|
-
|
64
|
-
return nil unless coa
|
65
|
-
|
66
|
-
transaction.coa_match_type = 'equality'
|
67
|
-
transaction.coa_code = coa.code
|
68
|
-
transaction
|
69
|
-
end
|
70
|
-
|
71
|
-
def start_with_match(transaction)
|
72
|
-
coa = config.bank_reconciliation.chart_of_accounts.find do |chart_of_account|
|
73
|
-
transaction.narration.to_s.delete(' ').downcase.start_with?(chart_of_account.narration.to_s.downcase)
|
74
|
-
end
|
75
|
-
|
76
|
-
return nil unless coa
|
77
|
-
|
78
|
-
transaction.coa_match_type = 'starts_with'
|
79
|
-
transaction.coa_code = coa.code
|
80
|
-
transaction
|
81
|
-
end
|
82
|
-
|
83
|
-
def includes(transaction)
|
84
|
-
coa = config.bank_reconciliation.chart_of_accounts.find do |chart_of_account|
|
85
|
-
transaction.narration.to_s.delete(' ').downcase.include?(chart_of_account.narration.delete(' ').to_s.downcase)
|
86
|
-
end
|
87
|
-
|
88
|
-
return nil unless coa
|
89
|
-
|
90
|
-
transaction.coa_match_type = 'includes'
|
91
|
-
transaction.coa_code = coa.code
|
92
|
-
transaction
|
93
|
-
end
|
94
|
-
|
95
|
-
def trigram_match(transaction, score_threshold, match_type)
|
96
|
-
scored_transactions = config.bank_reconciliation.chart_of_accounts.map do |coa|
|
97
|
-
{
|
98
|
-
coa: coa,
|
99
|
-
score: compare(coa.narration, transaction.narration)
|
100
|
-
}
|
101
|
-
end
|
102
|
-
|
103
|
-
scored_transactions.sort_by! { |t| t[:score] }.reverse!
|
104
|
-
|
105
|
-
best = scored_transactions.first
|
106
|
-
|
107
|
-
return nil unless best
|
108
|
-
return nil if best[:score] < score_threshold
|
109
|
-
|
110
|
-
coa = best[:coa]
|
111
|
-
|
112
|
-
transaction.coa_match_type = match_type
|
113
|
-
transaction.coa_code = coa.code
|
114
|
-
transaction
|
115
|
-
end
|
116
|
-
|
117
|
-
def compare(text1, text2)
|
118
|
-
text1_trigs = trigramify(text1)
|
119
|
-
text2_trigs = trigramify(text2)
|
120
|
-
|
121
|
-
all_cnt = (text1_trigs | text2_trigs).size
|
122
|
-
same_cnt = (text1_trigs & text2_trigs).size
|
123
|
-
|
124
|
-
same_cnt.to_f / all_cnt
|
125
|
-
end
|
126
|
-
|
127
|
-
def trigramify(text)
|
128
|
-
trigs = []
|
129
|
-
text.chars.each_cons(3) { |v| trigs << v.join }
|
130
|
-
trigs
|
131
|
-
end
|
132
|
-
end
|
133
|
-
end
|
134
|
-
end
|
135
|
-
end
|
136
|
-
end
|
@@ -1,90 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Appydave
|
4
|
-
module Tools
|
5
|
-
module BankReconciliation
|
6
|
-
module Clean
|
7
|
-
# Read transactions from a CSV file
|
8
|
-
class ReadTransactions
|
9
|
-
attr_reader :platform
|
10
|
-
attr_reader :transactions
|
11
|
-
|
12
|
-
def initialize(file)
|
13
|
-
@file = file
|
14
|
-
end
|
15
|
-
|
16
|
-
def read
|
17
|
-
csv_lines = File.read(@file).lines
|
18
|
-
|
19
|
-
@platform = detect_platform(csv_lines)
|
20
|
-
|
21
|
-
case platform
|
22
|
-
when :bankwest
|
23
|
-
read_bankwest(csv_lines)
|
24
|
-
when :bankwest2
|
25
|
-
read_bankwest2(csv_lines)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
private
|
30
|
-
|
31
|
-
def read_bankwest(csv_lines)
|
32
|
-
@transactions = []
|
33
|
-
|
34
|
-
# Skip the header line and parse each subsequent line
|
35
|
-
CSV.parse(csv_lines.join, headers: true).each do |row|
|
36
|
-
transaction = Models::Transaction.new(
|
37
|
-
bsb_number: row['BSB Number'],
|
38
|
-
account_number: row['Account Number'],
|
39
|
-
transaction_date: row['Transaction Date'],
|
40
|
-
narration: row['Narration'],
|
41
|
-
cheque_number: row['Cheque Number'],
|
42
|
-
debit: row['Debit'],
|
43
|
-
credit: row['Credit'],
|
44
|
-
balance: row['Balance'],
|
45
|
-
transaction_type: row['Transaction Type']
|
46
|
-
)
|
47
|
-
transaction.add_source_file(@file)
|
48
|
-
@transactions << transaction
|
49
|
-
end
|
50
|
-
|
51
|
-
@transactions
|
52
|
-
end
|
53
|
-
|
54
|
-
def read_bankwest2(csv_lines)
|
55
|
-
@transactions = []
|
56
|
-
|
57
|
-
# Skip the header line and parse each subsequent line
|
58
|
-
CSV.parse(csv_lines.join, headers: true).each do |row|
|
59
|
-
transaction = Models::Transaction.new(
|
60
|
-
bsb_number: row['BSB / Account Number'].split(' - ').first,
|
61
|
-
account_number: row['BSB / Account Number'].split(' - ').last,
|
62
|
-
transaction_date: row['Transaction Date'],
|
63
|
-
narration: row['Narration'],
|
64
|
-
cheque_number: row['Cheque Number'],
|
65
|
-
debit: row['Debit'],
|
66
|
-
credit: row['Credit'],
|
67
|
-
balance: row['Balance'],
|
68
|
-
transaction_type: row['Transaction Type']
|
69
|
-
)
|
70
|
-
transaction.add_source_file(@file)
|
71
|
-
@transactions << transaction
|
72
|
-
end
|
73
|
-
|
74
|
-
@transactions
|
75
|
-
end
|
76
|
-
|
77
|
-
# For bankwest the first row is the CSV will look like:
|
78
|
-
# BSB Number,Account Number,Transaction Date,Narration,Cheque Number,Debit,Credit,Balance,Transaction Type
|
79
|
-
def detect_platform(csv_lines)
|
80
|
-
return :bankwest if csv_lines.first.start_with?('BSB Number,Account Number,Transaction Date,Narration,Cheque Number,Debit,Credit,Balance,Transaction Type')
|
81
|
-
return :bankwest2 if csv_lines.first.start_with?('Account Name,BSB / Account Number,Transaction Date,Narration,Cheque Number,Debit,Credit,Balance,Transaction Type')
|
82
|
-
|
83
|
-
puts "Unknown platform detected. CSV columns are: #{csv_lines.first.strip}"
|
84
|
-
raise Appydave::Tools::Error, 'Unknown platform'
|
85
|
-
end
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
@@ -1,101 +0,0 @@
|
|
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
|
-
|
24
|
-
def initialize(bsb_number: nil,
|
25
|
-
account_number: nil,
|
26
|
-
transaction_date: nil,
|
27
|
-
narration: nil,
|
28
|
-
cheque_number: nil,
|
29
|
-
debit: nil,
|
30
|
-
credit: nil,
|
31
|
-
balance: nil,
|
32
|
-
transaction_type: nil,
|
33
|
-
platform: nil,
|
34
|
-
coa_code: nil,
|
35
|
-
coa_match_type: nil,
|
36
|
-
account_name: nil)
|
37
|
-
@bsb_number = bsb_number&.strip
|
38
|
-
@account_number = account_number&.strip
|
39
|
-
@transaction_date = transaction_date&.strip
|
40
|
-
@transaction_date = Date.strptime(@transaction_date, '%d/%m/%Y')
|
41
|
-
@narration = narration&.gsub(/\s{2,}/, ' ')&.strip
|
42
|
-
@cheque_number = cheque_number&.strip
|
43
|
-
@debit = debit&.strip
|
44
|
-
@credit = credit&.strip
|
45
|
-
@balance = balance&.strip
|
46
|
-
@transaction_type = transaction_type&.strip
|
47
|
-
@platform = platform
|
48
|
-
@coa_code = coa_code
|
49
|
-
@coa_match_type = coa_match_type
|
50
|
-
@account_name = account_name
|
51
|
-
@source_files = []
|
52
|
-
end
|
53
|
-
|
54
|
-
def add_source_file(source_file)
|
55
|
-
@source_files << source_file.strip unless @source_files.include?(source_file.strip)
|
56
|
-
end
|
57
|
-
|
58
|
-
# cheque_number
|
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
|
-
]
|
76
|
-
end
|
77
|
-
|
78
|
-
# @cheque_number,
|
79
|
-
|
80
|
-
def to_csv_row
|
81
|
-
[
|
82
|
-
@platform,
|
83
|
-
@account_name,
|
84
|
-
@bsb_number,
|
85
|
-
@account_number,
|
86
|
-
@transaction_date,
|
87
|
-
@narration,
|
88
|
-
@debit,
|
89
|
-
@credit,
|
90
|
-
@balance,
|
91
|
-
@transaction_type,
|
92
|
-
@coa_code,
|
93
|
-
@coa_match_type,
|
94
|
-
@source_files
|
95
|
-
]
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
@@ -1,97 +0,0 @@
|
|
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
|
-
def get_bank_account(account_number, bsb = nil)
|
24
|
-
account_data = data['bank_accounts'].find do |account|
|
25
|
-
account['account_number'] == account_number && (account['bsb'].nil? || account['bsb'] == bsb)
|
26
|
-
end
|
27
|
-
|
28
|
-
BankAccount.new(account_data) if account_data
|
29
|
-
end
|
30
|
-
|
31
|
-
# Retrieve a chart of account entry by code
|
32
|
-
def get_chart_of_account(code)
|
33
|
-
entry_data = data['chart_of_accounts'].find { |entry| entry['code'] == code }
|
34
|
-
ChartOfAccount.new(entry_data) if entry_data
|
35
|
-
end
|
36
|
-
|
37
|
-
def print
|
38
|
-
log.subheading 'Bank Reconciliation - Accounts'
|
39
|
-
|
40
|
-
tp bank_accounts, :account_number, :bsb, :name, :bank
|
41
|
-
|
42
|
-
log.subheading 'Bank Reconciliation - Chart of Accounts'
|
43
|
-
|
44
|
-
tp chart_of_accounts, :code, :narration
|
45
|
-
end
|
46
|
-
|
47
|
-
private
|
48
|
-
|
49
|
-
def default_data
|
50
|
-
{
|
51
|
-
'bank_accounts' => [],
|
52
|
-
'chart_of_accounts' => []
|
53
|
-
}
|
54
|
-
end
|
55
|
-
|
56
|
-
# Inner class to represent a bank account
|
57
|
-
class BankAccount
|
58
|
-
attr_accessor :account_number, :bsb, :name, :platform
|
59
|
-
|
60
|
-
def initialize(data)
|
61
|
-
@account_number = data['account_number']
|
62
|
-
@bsb = data['bsb']
|
63
|
-
@name = data['name']
|
64
|
-
@platform = data['platform']
|
65
|
-
end
|
66
|
-
|
67
|
-
def to_h
|
68
|
-
{
|
69
|
-
'account_number' => @account_number,
|
70
|
-
'bsb' => @bsb,
|
71
|
-
'name' => @name,
|
72
|
-
'platform' => @platform
|
73
|
-
}
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
# Inner class to represent a chart of account entry
|
78
|
-
class ChartOfAccount
|
79
|
-
attr_accessor :code, :narration
|
80
|
-
|
81
|
-
def initialize(data)
|
82
|
-
@code = data['code']
|
83
|
-
@narration = data['narration']
|
84
|
-
end
|
85
|
-
|
86
|
-
def to_h
|
87
|
-
{
|
88
|
-
'code' => @code,
|
89
|
-
'narration' => @narration
|
90
|
-
}
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|