f2ynab 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: f8609bfb036c5b362835c7a41a2f3b0e24fcc70be71eaa1058dc70d38f64707b
4
+ data.tar.gz: 235c3f993a5246cfddf859983b95deba338654e836c4c3ff92044664a446bdc5
5
+ SHA512:
6
+ metadata.gz: b9b2175e94d7a87234b9ee81c0fa7d0b2fd9bfa14e9897a0e075608d5859cf7131007f011705041cd8ccf5aba0f82027e130cdb6baf5b02ec44ee0e00fb8768b
7
+ data.tar.gz: 417b7eaad8d5f1e596620f499068a695ebde721019f07451e6468f7ec9e68b26803dea0fa364bcb4528d7b6b5f327044f3bde9d45b3d8392a86843dae6a5ad1e
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 Scott Robertson
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,5 @@
1
+ # F2ynab
2
+
3
+ ![Tests](https://github.com/syncforynab/f2ynab/workflows/Tests/badge.svg)
4
+
5
+ Shared library for [Fintech to YNAB](https://github.com/fintech-to-ynab/fintech-to-ynab)
@@ -0,0 +1,21 @@
1
+ require 'money'
2
+ require 'ynab'
3
+ require 'starling'
4
+ require 'rest-client'
5
+
6
+ require "f2ynab/version"
7
+
8
+ require 'f2ynab/import/csv/amex'
9
+ require 'f2ynab/import/csv/mbna'
10
+ require 'f2ynab/import/csv/starling_bank'
11
+ require 'f2ynab/import/csv/default'
12
+ require 'f2ynab/import/monzo'
13
+ require 'f2ynab/import/starling'
14
+
15
+ require 'f2ynab/webhooks/monzo'
16
+ require 'f2ynab/webhooks/starling'
17
+
18
+ require 'f2ynab/ynab/bulk_transaction_creator'
19
+ require 'f2ynab/ynab/client'
20
+ require 'f2ynab/ynab/import_id_creator'
21
+ require 'f2ynab/ynab/transaction_creator'
@@ -0,0 +1,37 @@
1
+ require 'csv'
2
+
3
+ # @note This is used to import Amex CSV Statements
4
+ # Export the statement CSV from within the web app
5
+
6
+ module F2ynab
7
+ module Import
8
+ module Csv
9
+ class Amex
10
+ def initialize(ynab_client, path)
11
+ @path = path
12
+ @ynab_client = ynab_client
13
+ @import_id_creator = ::F2ynab::YNAB::ImportIdCreator.new
14
+ end
15
+
16
+ def import
17
+ transactions_to_create = []
18
+
19
+ ::CSV.foreach(@path) do |transaction|
20
+ amount = (transaction[1].to_f * 1000).to_i
21
+ date = Date.parse(transaction[0])
22
+
23
+ transactions_to_create << {
24
+ id: @import_id_creator.import_id(amount, date),
25
+ amount: amount,
26
+ payee_name: transaction[2],
27
+ date: date,
28
+ description: transaction[2],
29
+ }
30
+ end
31
+
32
+ ::F2ynab::YNAB::BulkTransactionCreator.new(@ynab_client, transactions_to_create).create
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,44 @@
1
+ require 'csv'
2
+
3
+ # @note The CSV must contain: amount, description and date (YYYY-MM-DD)
4
+ # @note the amount must be the full value (so $100 would be 100)
5
+
6
+ module F2ynab
7
+ module Import
8
+ module Csv
9
+ class Default
10
+ FORMATS = {
11
+ 'default' => '::F2ynab::Import::Csv::Default',
12
+ 'starling' => '::F2ynab::Import::Csv::StarlingBank',
13
+ 'mbna' => '::F2ynab::Import::Csv::MBNA',
14
+ 'amex' => '::F2ynab::Import::Csv::Amex',
15
+ }
16
+
17
+ def initialize(ynab_client, path)
18
+ @path = path
19
+ @ynab_client = ynab_client
20
+ @import_id_creator = ::F2ynab::YNAB::ImportIdCreator.new
21
+ end
22
+
23
+ def import
24
+ transactions_to_create = []
25
+
26
+ ::CSV.foreach(@path, headers: true) do |transaction|
27
+ transaction = transaction.to_h.symbolize_keys
28
+ amount = (transaction[:amount].to_f * 1000).to_i
29
+ date = Date.parse(transaction[:date])
30
+
31
+ transactions_to_create << {
32
+ id: @import_id_creator.import_id(amount, date),
33
+ amount: amount,
34
+ payee_name: transaction[:description],
35
+ date: date,
36
+ }
37
+ end
38
+
39
+ ::F2ynab::YNAB::BulkTransactionCreator.new(@ynab_client, transactions_to_create).create
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,37 @@
1
+ require 'csv'
2
+
3
+ # @note This is used to import MBNA CSV Statements
4
+ # Export the statement CSV from within the web app
5
+
6
+ module F2ynab
7
+ module Import
8
+ module Csv
9
+ class MBNA
10
+ def initialize(ynab_client, path)
11
+ @path = path
12
+ @ynab_client = ynab_client
13
+ @import_id_creator = ::F2ynab::YNAB::ImportIdCreator.new
14
+ end
15
+
16
+ def import
17
+ transactions_to_create = []
18
+
19
+ ::CSV.foreach(@path, headers: true) do |transaction|
20
+ amount = (transaction['Amount'].to_f * 1000).to_i
21
+ date = Date.parse(transaction['Transaction Date'])
22
+
23
+ transactions_to_create << {
24
+ id: @import_id_creator.import_id(amount, date),
25
+ amount: amount,
26
+ payee_name: transaction['Description'],
27
+ date: date,
28
+ description: transaction['Description'],
29
+ }
30
+ end
31
+
32
+ ::F2ynab::YNAB::BulkTransactionCreator.new(@ynab_client, transactions_to_create).create
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,40 @@
1
+ require 'csv'
2
+
3
+ # @note This is used to import Starling Bank CSV Statements
4
+ # Export the statement CSV from within the app
5
+
6
+ module F2ynab
7
+ module Import
8
+ module Csv
9
+ class StarlingBank
10
+ def initialize(ynab_client, path)
11
+ @path = path
12
+ @ynab_client = ynab_client
13
+ @import_id_creator = ::F2ynab::YNAB::ImportIdCreator.new
14
+ end
15
+
16
+ def import
17
+ transactions_to_create = []
18
+
19
+ ::CSV.foreach(@path, headers: true) do |transaction|
20
+ # First row can be blank
21
+ next unless transaction['Date'].present?
22
+
23
+ amount = (transaction['Amount (GBP)'].to_f * 1000).to_i
24
+ date = Date.parse(transaction['Date'])
25
+
26
+ transactions_to_create << {
27
+ id: @import_id_creator.import_id(amount, date),
28
+ amount: amount,
29
+ payee_name: transaction['Counter Party'],
30
+ date: date,
31
+ description: transaction['Reference'],
32
+ }
33
+ end
34
+
35
+ ::F2ynab::YNAB::BulkTransactionCreator.new(@ynab_client, transactions_to_create).create
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,91 @@
1
+ module F2ynab
2
+ module Import
3
+ class Monzo
4
+ BASE_URL = 'https://api.monzo.com'
5
+
6
+ def initialize(ynab_client, access_token, monzo_account_id, from: 1.year.ago, skip_tags: false, skip_foreign_currency_flag: false, skip_emoji: false)
7
+ @access_token = access_token
8
+ @monzo_account_id = monzo_account_id
9
+ @ynab_client = ynab_client
10
+ @from = from
11
+
12
+ @skip_tags = skip_tags
13
+ @skip_foreign_currency_flag = skip_foreign_currency_flag
14
+ @skip_emoji = skip_emoji
15
+ end
16
+
17
+ def import
18
+ transactions_to_create = []
19
+ transactions.reject { |t| t[:decline_reason].present? || t[:amount].zero? }.each do |transaction|
20
+ transactions_to_create << transaction_hash(transaction)
21
+ end
22
+
23
+ ::F2ynab::YNAB::BulkTransactionCreator.new(@ynab_client, transactions_to_create).create
24
+ end
25
+
26
+ private
27
+
28
+ def payee_name(transaction)
29
+ payee_name = transaction[:merchant].try(:[], :name)
30
+ payee_name ||= transaction[:counterparty][:name] if transaction[:counterparty].present?
31
+ payee_name ||= 'Topup' if transaction[:is_load]
32
+ payee_name ||= transaction[:description]
33
+ payee_name
34
+ end
35
+
36
+ def description_and_flag(transaction)
37
+ description = ''
38
+ flag = nil
39
+
40
+ foreign_transaction = transaction[:local_currency] != transaction[:currency]
41
+ if foreign_transaction
42
+ money = ::Money.new(transaction[:local_amount].abs, transaction[:local_currency])
43
+ description.prepend("(#{money.format}) ")
44
+ flag = 'orange' unless @skip_foreign_currency_flag.present?
45
+ end
46
+
47
+ unless @skip_emoji
48
+ description.prepend("#{transaction[:merchant][:emoji]} ") if transaction[:merchant].try(:[], :emoji)
49
+ end
50
+
51
+ unless @skip_tags
52
+ description << transaction[:merchant][:metadata][:suggested_tags] if transaction[:merchant].try(:[], :metadata).try(:[], :suggested_tags)
53
+ end
54
+
55
+ [description.strip, flag]
56
+ end
57
+
58
+ def transaction_hash(transaction)
59
+ description, flag = description_and_flag(transaction)
60
+ timestamp = Time.parse(transaction[:created])
61
+ id = transaction[:id]
62
+
63
+ # We used to prepend "m" to monzo transactions. Keep doing that for now to stop any duplicates.
64
+ id.prepend('M') if timestamp < Time.at(1580652776)
65
+
66
+ {
67
+ id: id,
68
+ amount: transaction[:amount] * 10,
69
+ payee_name: payee_name(transaction),
70
+ date: timestamp.to_date,
71
+ description: description,
72
+ cleared: transaction[:settled].present?,
73
+ flag: flag,
74
+ }
75
+ end
76
+
77
+ def transactions
78
+ since = @from.present? ? "&since=#{@from.strftime('%FT%TZ')}" : nil
79
+ get("/transactions?account_id=#{@monzo_account_id}#{since}&expand[]=merchant")[:transactions]
80
+ end
81
+
82
+ def get(url)
83
+ parse_response(RestClient.get(BASE_URL + url, 'Authorization' => "Bearer #{@access_token}"))
84
+ end
85
+
86
+ def parse_response(response)
87
+ JSON.parse(response.body, symbolize_names: true)
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,26 @@
1
+ module F2ynab
2
+ module Import
3
+ class Starling
4
+ def initialize(ynab_client, access_token, from: nil)
5
+ @starling = ::Starling::Client.new(access_token: access_token)
6
+ @ynab_client = ynab_client
7
+ @from = from
8
+ end
9
+
10
+ def import
11
+ from = (@from || @starling.account.get.created_at).to_date
12
+ transactions_to_create = []
13
+ @starling.transactions.list(params: { from: from, to: Date.today }).each do |transaction|
14
+ transactions_to_create << {
15
+ id: "S:#{transaction.id}",
16
+ amount: (transaction.amount * 1000).to_i,
17
+ payee_name: transaction.narrative.strip,
18
+ date: transaction.created,
19
+ }
20
+ end
21
+
22
+ ::F2ynab::YNAB::BulkTransactionCreator.new(@ynab_client, transactions_to_create).create
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,3 @@
1
+ module F2ynab
2
+ VERSION = "0.1.3"
3
+ end
@@ -0,0 +1,58 @@
1
+ module F2ynab
2
+ module Webhooks
3
+ class Monzo
4
+ def initialize(ynab_client, webhook, skip_tags: false, skip_foreign_currency_flag: false, skip_emoji: false)
5
+ @ynab_client = ynab_client
6
+ @webhook = webhook
7
+ @skip_tags = skip_tags
8
+ @skip_foreign_currency_flag = skip_foreign_currency_flag
9
+ @skip_emoji = skip_emoji
10
+ end
11
+
12
+ def import
13
+ return { warning: :unsupported_type } unless @webhook[:type] == 'transaction.created'
14
+ return { warning: :zero_value } if @webhook[:data][:amount].to_i.zero?
15
+ return { warning: :declined } if @webhook[:data][:decline_reason].present?
16
+
17
+ payee_name = @webhook[:data][:merchant].try(:[], :name)
18
+ payee_name ||= @webhook[:data][:counterparty][:name] if @webhook[:data][:counterparty].present?
19
+ payee_name ||= 'Topup' if @webhook[:data][:metadata][:is_topup]
20
+ payee_name ||= @webhook[:data][:description]
21
+
22
+ description = ''
23
+ flag = nil
24
+
25
+ foreign_transaction = @webhook[:data][:local_currency] != @webhook[:data][:currency]
26
+ if foreign_transaction
27
+ money = ::Money.new(@webhook[:data][:local_amount].abs, @webhook[:data][:local_currency])
28
+ description.prepend("(#{money.format}) ")
29
+ flag = 'orange' unless @skip_foreign_currency_flag
30
+ end
31
+
32
+ unless @skip_emoji
33
+ description.prepend("#{@webhook[:data][:merchant][:emoji]} ") if @webhook[:data][:merchant].try(:[], :emoji)
34
+ end
35
+
36
+ unless @skip_tags
37
+ description << @webhook[:data][:merchant][:metadata][:suggested_tags] if @webhook[:data][:merchant].try(:[], :metadata).try(:[], :suggested_tags)
38
+ end
39
+
40
+ # If this is a split repayment, then add that to the description
41
+ if @webhook[:data][:metadata].try(:[], :p2p_initiator) == 'payment-request' && @webhook[:data][:merchant].present? && @webhook[:data][:counterparty].present?
42
+ description << " (Repayment to #{@webhook[:data][:counterparty][:name]})"
43
+ end
44
+
45
+ ::F2ynab::YNAB::TransactionCreator.new(
46
+ @ynab_client,
47
+ id: @webhook[:data][:id],
48
+ date: Time.parse(@webhook[:data][:created]).to_date,
49
+ amount: @webhook[:data][:amount] * 10,
50
+ payee_name: payee_name,
51
+ description: description.strip,
52
+ cleared: !foreign_transaction,
53
+ flag: flag,
54
+ ).create
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,68 @@
1
+ module F2ynab
2
+ module Webhooks
3
+ class Starling
4
+ WEBHOOKS_TYPES = %w[
5
+ TRANSACTION_AUTH_DECLINED
6
+ TRANSACTION_AUTH_PARTIAL_REVERSAL
7
+ TRANSACTION_AUTH_FULL_REVERSAL
8
+ TRANSACTION_CARD
9
+ TRANSACTION_CASH_WITHDRAWAL
10
+ TRANSACTION_DECLINED_INSUFFICIENT_FUNDS
11
+ TRANSACTION_DIRECT_CREDIT
12
+ TRANSACTION_DIRECT_DEBIT
13
+ TRANSACTION_DIRECT_DEBIT_INSUFFICIENT_FUNDS
14
+ TRANSACTION_DIRECT_DEBIT_DISPUTE
15
+ TRANSACTION_DECLINED_INSUFFICIENT_FUNDS
16
+ TRANSACTION_FASTER_PAYMENT_IN
17
+ TRANSACTION_FASTER_PAYMENT_OUT
18
+ TRANSACTION_FASTER_PAYMENT_REVERSAL
19
+ TRANSACTION_INTEREST_PAYMENT
20
+ TRANSACTION_INTERNAL_TRANSFER
21
+ TRANSACTION_INTEREST_WAIVED_DEPOSIT
22
+ TRANSACTION_NOSTRO_DEPOSIT
23
+ TRANSACTION_MOBILE_WALLET
24
+ TRANSACTION_ON_US_PAYMENT_OUT
25
+ TRANSACTION_ON_US_PAYMENT_IN
26
+ SCHEDULED_PAYMENT_CANCELLED
27
+ SCHEDULED_PAYMENT_INSUFFICIENT_FUNDS
28
+ TRANSACTION_STRIPE_FUNDING
29
+ INTEREST_CHARGE
30
+ ]
31
+
32
+ def initialize(ynab_client, webhook, skip_foreign_currency_flag: false)
33
+ @webhook = webhook
34
+ @ynab_client = ynab_client
35
+ @skip_foreign_currency_flag = skip_foreign_currency_flag
36
+ end
37
+
38
+ def import
39
+ return { warning: :unsupported_type } unless @webhook[:webhookType].in?(WEBHOOKS_TYPES)
40
+
41
+ payee_name = @webhook[:content][:counterParty]
42
+ amount = (@webhook[:content][:amount].to_f * 1000).to_i
43
+
44
+ # Direct Debits need to be swapped to negative
45
+ amount *= -1 if amount.positive? && @webhook[:webhookType] == 'TRANSACTION_DIRECT_DEBIT'
46
+
47
+ description = @webhook[:content][:forCustomer].to_s
48
+ flag = nil
49
+
50
+ foreign_transaction = @webhook[:content][:sourceCurrency] != 'GBP'
51
+ if foreign_transaction && !@skip_foreign_currency_flag
52
+ flag = 'orange'
53
+ end
54
+
55
+ ::F2ynab::YNAB::TransactionCreator.new(
56
+ @ynab_client,
57
+ id: "S:#{@webhook[:content][:transactionUid]}",
58
+ date: Time.parse(@webhook[:timestamp]).to_date,
59
+ amount: amount,
60
+ payee_name: payee_name,
61
+ description: description.strip,
62
+ cleared: !foreign_transaction,
63
+ flag: flag,
64
+ ).create
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,52 @@
1
+ module F2ynab
2
+ module YNAB
3
+ class BulkTransactionCreator
4
+ BATCH_SIZE = 100
5
+
6
+ def initialize(client, transactions)
7
+ @transactions = transactions
8
+ @client = client
9
+ end
10
+
11
+ def create
12
+ if @transactions.size.zero?
13
+ Rails.logger.info(:no_transactions_to_create)
14
+ return false
15
+ end
16
+
17
+ created_transactions_ids = []
18
+ batches = (@transactions.size.to_f / BATCH_SIZE).ceil
19
+
20
+ Rails.logger.info("Splitting #{@transactions.size} transactions into #{batches} batches")
21
+
22
+ @transactions.each_slice(BATCH_SIZE).with_index do |transactions, index|
23
+ Rails.logger.info("Processing batch #{index + 1} of #{batches}")
24
+
25
+ transactions_to_create = []
26
+ transactions.each do |transaction|
27
+ transactions_to_create << {
28
+ import_id: transaction[:id].to_s.truncate(36),
29
+ account_id: @client.selected_account_id,
30
+ payee_name: transaction[:payee_name].to_s.truncate(50),
31
+ amount: transaction[:amount],
32
+ memo: transaction[:description],
33
+ date: transaction[:date].to_date,
34
+ cleared: transaction[:cleared] ? 'Cleared' : 'Uncleared',
35
+ flag: transaction[:flag],
36
+ }
37
+ end
38
+
39
+ if transactions_to_create.any?
40
+ create_transactions = @client.create_transactions(transactions_to_create)
41
+ Rails.logger.info(create_transactions)
42
+ created_transactions_ids += create_transactions.transaction_ids if create_transactions
43
+ else
44
+ Rails.logger.info(:no_transactions_to_create)
45
+ end
46
+ end
47
+
48
+ created_transactions_ids
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,79 @@
1
+ module F2ynab
2
+ module YNAB
3
+ class Client
4
+ def initialize(access_token, budget_id, account_id)
5
+ @access_token = access_token
6
+ @budget_id = budget_id
7
+ @account_id = account_id
8
+ end
9
+
10
+ def budgets
11
+ @_budgets ||= client.budgets.get_budgets.data.budgets
12
+ end
13
+
14
+ def accounts
15
+ @_accounts ||= client.accounts.get_accounts(selected_budget_id).data.accounts
16
+ end
17
+
18
+ def category(category_id)
19
+ client.categories.get_category_by_id(selected_budget_id, category_id).data.category
20
+ end
21
+
22
+ def transactions(since_date: nil, account_id: nil)
23
+ if account_id.present?
24
+ client.transactions.get_transactions_by_account(selected_budget_id, account_id, since_date: since_date).data.transactions
25
+ else
26
+ client.transactions.get_transactions(selected_budget_id, since_date: since_date).data.transactions
27
+ end
28
+ end
29
+
30
+ def create_transaction(id: nil, payee_id: nil, payee_name: nil, amount: nil, cleared: nil, date: nil, memo: nil, flag: nil)
31
+ client.transactions.create_transaction(
32
+ selected_budget_id,
33
+ transaction: {
34
+ account_id: selected_account_id,
35
+ date: date.to_s,
36
+ amount: amount,
37
+ payee_id: payee_id,
38
+ payee_name: payee_name,
39
+ cleared: cleared ? "Cleared" : 'Uncleared',
40
+ memo: memo,
41
+ flag_color: flag,
42
+ import_id: id,
43
+ },
44
+ ).data.transaction
45
+ rescue StandardError => e
46
+ Rails.logger.error('YNAB::Client.create_transaction failure')
47
+ Rails.logger.error("YNAB::Client.create_transaction Response: #{e.response_body}")
48
+ Rails.logger.error(e)
49
+ Raven.capture_exception(e) if defined?(Raven) && e.message != 'Conflict'
50
+ { error: e.message }
51
+ end
52
+
53
+ def create_transactions(transactions)
54
+ client.transactions.bulk_create_transactions(selected_budget_id, transactions: transactions).data.bulk
55
+ rescue StandardError => e
56
+ Rails.logger.error('YNAB::Client.create_transactions failure')
57
+ Rails.logger.error("YNAB::Client.create_transactions Request Body: #{transactions}")
58
+ Rails.logger.error("YNAB::Client.create_transactions Response: #{e.response_body}")
59
+ Rails.logger.error(e)
60
+ Raven.capture_exception(e) if defined?(Raven) && e.message != 'Conflict'
61
+ raise
62
+ end
63
+
64
+ def selected_budget_id
65
+ @budget_id || budgets.first.id
66
+ end
67
+
68
+ def selected_account_id
69
+ @account_id || accounts.reject(&:closed).select { |a| a.type == 'checking' }.first.id
70
+ end
71
+
72
+ protected
73
+
74
+ def client
75
+ @_client ||= ::YnabApi::Client.new(@access_token)
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,16 @@
1
+ module F2ynab
2
+ module YNAB
3
+ class ImportIdCreator
4
+ def initialize
5
+ @occurence = {}
6
+ end
7
+
8
+ def import_id(amount, date)
9
+ key = ['YNAB', amount, date].join(':')
10
+ @occurence[key] ||= 0
11
+ @occurence[key] += 1
12
+ key + ":#{@occurence[key]}"
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,30 @@
1
+ module F2ynab
2
+ module YNAB
3
+ class TransactionCreator
4
+ def initialize(client, id: nil, date: nil, amount: nil, payee_name: nil, description: true, flag: nil, cleared: true)
5
+ @id = id
6
+ @date = date
7
+ @amount = amount
8
+ @payee_name = payee_name
9
+ @description = description
10
+ @cleared = cleared
11
+ @flag = flag
12
+ @client = client
13
+ end
14
+
15
+ def create
16
+ create = @client.create_transaction(
17
+ id: @id.to_s.truncate(36),
18
+ payee_name: @payee_name.to_s.truncate(50),
19
+ amount: @amount,
20
+ cleared: @cleared,
21
+ date: @date.to_date,
22
+ memo: @description,
23
+ flag: @flag,
24
+ )
25
+
26
+ create.try(:id).present? ? create : { error: :failed, data: create }
27
+ end
28
+ end
29
+ end
30
+ end
metadata ADDED
@@ -0,0 +1,185 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: f2ynab
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.3
5
+ platform: ruby
6
+ authors:
7
+ - Scott Robertson
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-01-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: money
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '6.13'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '6.13'
27
+ - !ruby/object:Gem::Dependency
28
+ name: ynab
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.5'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.5'
41
+ - !ruby/object:Gem::Dependency
42
+ name: starling-ruby
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.2'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.2'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rest-client
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '2.0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '2.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: bundler
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '2'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '2'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rake
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '13.0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '13.0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: simplecov
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '0.16'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '0.16'
111
+ - !ruby/object:Gem::Dependency
112
+ name: minitest
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '5.11'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '5.11'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rubocop
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '1.8'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '1.8'
139
+ description: Fintech to YNAB Library
140
+ email:
141
+ - scottymeuk@gmail.com
142
+ executables: []
143
+ extensions: []
144
+ extra_rdoc_files: []
145
+ files:
146
+ - LICENSE.txt
147
+ - README.md
148
+ - lib/f2ynab.rb
149
+ - lib/f2ynab/import/csv/amex.rb
150
+ - lib/f2ynab/import/csv/default.rb
151
+ - lib/f2ynab/import/csv/mbna.rb
152
+ - lib/f2ynab/import/csv/starling_bank.rb
153
+ - lib/f2ynab/import/monzo.rb
154
+ - lib/f2ynab/import/starling.rb
155
+ - lib/f2ynab/version.rb
156
+ - lib/f2ynab/webhooks/monzo.rb
157
+ - lib/f2ynab/webhooks/starling.rb
158
+ - lib/f2ynab/ynab/bulk_transaction_creator.rb
159
+ - lib/f2ynab/ynab/client.rb
160
+ - lib/f2ynab/ynab/import_id_creator.rb
161
+ - lib/f2ynab/ynab/transaction_creator.rb
162
+ homepage: https://github.com/syncforynab/f2ynab
163
+ licenses:
164
+ - MIT
165
+ metadata: {}
166
+ post_install_message:
167
+ rdoc_options: []
168
+ require_paths:
169
+ - lib
170
+ required_ruby_version: !ruby/object:Gem::Requirement
171
+ requirements:
172
+ - - ">="
173
+ - !ruby/object:Gem::Version
174
+ version: '0'
175
+ required_rubygems_version: !ruby/object:Gem::Requirement
176
+ requirements:
177
+ - - ">="
178
+ - !ruby/object:Gem::Version
179
+ version: '0'
180
+ requirements: []
181
+ rubygems_version: 3.1.4
182
+ signing_key:
183
+ specification_version: 4
184
+ summary: Fintech to YNAB Library
185
+ test_files: []