appydave-tools 0.10.3 → 0.10.4
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.
- 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
|