mirror of
https://github.com/we-promise/sure.git
synced 2026-05-12 23:25:00 +00:00
feat(exports): preserve recurring transactions (#1638)
* feat(exports): preserve recurring transactions * fix(exports): harden recurring import records
This commit is contained in:
@@ -178,6 +178,14 @@ class Family::DataExporter
|
||||
}.to_json
|
||||
end
|
||||
|
||||
# Export recurring transactions after accounts and merchants so import can remap dependencies.
|
||||
@family.recurring_transactions.includes(:account, :merchant).find_each do |recurring_transaction|
|
||||
lines << {
|
||||
type: "RecurringTransaction",
|
||||
data: serialize_recurring_transaction_for_export(recurring_transaction)
|
||||
}.to_json
|
||||
end
|
||||
|
||||
# Export transactions with full data (exclude split parents, export children instead)
|
||||
@family.transactions.includes(:category, :merchant, :tags, entry: :account).merge(Entry.excluding_split_parents).find_each do |transaction|
|
||||
lines << {
|
||||
@@ -270,6 +278,28 @@ class Family::DataExporter
|
||||
lines.join("\n")
|
||||
end
|
||||
|
||||
def serialize_recurring_transaction_for_export(recurring_transaction)
|
||||
{
|
||||
id: recurring_transaction.id,
|
||||
account_id: recurring_transaction.account_id,
|
||||
merchant_id: recurring_transaction.merchant_id,
|
||||
amount: recurring_transaction.amount,
|
||||
currency: recurring_transaction.currency,
|
||||
expected_day_of_month: recurring_transaction.expected_day_of_month,
|
||||
last_occurrence_date: recurring_transaction.last_occurrence_date,
|
||||
next_expected_date: recurring_transaction.next_expected_date,
|
||||
status: recurring_transaction.status,
|
||||
occurrence_count: recurring_transaction.occurrence_count,
|
||||
name: recurring_transaction.name,
|
||||
manual: recurring_transaction.manual,
|
||||
expected_amount_min: recurring_transaction.expected_amount_min,
|
||||
expected_amount_max: recurring_transaction.expected_amount_max,
|
||||
expected_amount_avg: recurring_transaction.expected_amount_avg,
|
||||
created_at: recurring_transaction.created_at,
|
||||
updated_at: recurring_transaction.updated_at
|
||||
}
|
||||
end
|
||||
|
||||
def serialize_rule_for_export(rule)
|
||||
{
|
||||
name: rule.name,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
require "set"
|
||||
|
||||
class Family::DataImporter
|
||||
SUPPORTED_TYPES = %w[Account Category Tag Merchant Transaction Trade Valuation Budget BudgetCategory Rule].freeze
|
||||
SUPPORTED_TYPES = %w[Account Category Tag Merchant RecurringTransaction Transaction Trade Valuation Budget BudgetCategory Rule].freeze
|
||||
ACCOUNTABLE_TYPES = Accountable::TYPES.freeze
|
||||
|
||||
def initialize(family, ndjson_content)
|
||||
@@ -12,6 +12,7 @@ class Family::DataImporter
|
||||
categories: {},
|
||||
tags: {},
|
||||
merchants: {},
|
||||
recurring_transactions: {},
|
||||
budgets: {},
|
||||
securities: {}
|
||||
}
|
||||
@@ -30,6 +31,7 @@ class Family::DataImporter
|
||||
import_categories(records["Category"] || [])
|
||||
import_tags(records["Tag"] || [])
|
||||
import_merchants(records["Merchant"] || [])
|
||||
import_recurring_transactions(records["RecurringTransaction"] || [])
|
||||
import_transactions(records["Transaction"] || [])
|
||||
import_trades(records["Trade"] || [])
|
||||
import_valuations(records["Valuation"] || [])
|
||||
@@ -187,6 +189,68 @@ class Family::DataImporter
|
||||
end
|
||||
end
|
||||
|
||||
def import_recurring_transactions(records)
|
||||
records.each do |record|
|
||||
data = record["data"]
|
||||
old_id = data["id"]
|
||||
|
||||
new_account_id = remap_optional_id(:accounts, data["account_id"])
|
||||
next if data["account_id"].present? && new_account_id.blank?
|
||||
|
||||
new_merchant_id = remap_optional_id(:merchants, data["merchant_id"])
|
||||
next if data["merchant_id"].present? && new_merchant_id.blank?
|
||||
|
||||
expected_day_of_month = recurring_expected_day_for(data["expected_day_of_month"])
|
||||
next unless expected_day_of_month
|
||||
last_occurrence_date = parse_import_date(data["last_occurrence_date"])
|
||||
next_expected_date = parse_import_date(data["next_expected_date"])
|
||||
next unless last_occurrence_date && next_expected_date
|
||||
|
||||
recurring_transaction = @family.recurring_transactions.build(
|
||||
account_id: new_account_id,
|
||||
merchant_id: new_merchant_id,
|
||||
amount: data["amount"].to_d,
|
||||
currency: data["currency"] || @family.currency,
|
||||
expected_day_of_month: expected_day_of_month,
|
||||
last_occurrence_date: last_occurrence_date,
|
||||
next_expected_date: next_expected_date,
|
||||
status: recurring_transaction_status_for(data["status"]),
|
||||
occurrence_count: data["occurrence_count"].to_i,
|
||||
name: data["name"],
|
||||
manual: boolean_import_value(data, "manual", default: false),
|
||||
expected_amount_min: data["expected_amount_min"]&.to_d,
|
||||
expected_amount_max: data["expected_amount_max"]&.to_d,
|
||||
expected_amount_avg: data["expected_amount_avg"]&.to_d
|
||||
)
|
||||
|
||||
recurring_transaction.save!
|
||||
@id_mappings[:recurring_transactions][old_id] = recurring_transaction.id
|
||||
end
|
||||
end
|
||||
|
||||
def remap_optional_id(mapping_key, old_id)
|
||||
return if old_id.blank?
|
||||
|
||||
@id_mappings[mapping_key][old_id]
|
||||
end
|
||||
|
||||
def recurring_transaction_status_for(status)
|
||||
status.to_s.in?(RecurringTransaction.statuses.keys) ? status.to_s : "active"
|
||||
end
|
||||
|
||||
def recurring_expected_day_for(value)
|
||||
return if value.blank?
|
||||
|
||||
expected_day = value.to_i
|
||||
expected_day if expected_day.between?(1, 31)
|
||||
end
|
||||
|
||||
def boolean_import_value(data, key, default:)
|
||||
return default unless data.key?(key)
|
||||
|
||||
ActiveModel::Type::Boolean.new.cast(data[key])
|
||||
end
|
||||
|
||||
def import_transactions(records)
|
||||
records.each do |record|
|
||||
data = record["data"]
|
||||
|
||||
Reference in New Issue
Block a user