Files
sure/app/models/actual_import.rb
Sure Admin (bot) 4fd460d551 Add Actual Budget CSV import flow (#1830)
* Add Actual Budget CSV import flow

* Address Actual import review feedback
2026-05-18 18:38:53 +02:00

105 lines
2.6 KiB
Ruby

class ActualImport < Import
after_create :set_mappings
DEFAULT_COLUMN_MAPPINGS = {
signage_convention: "inflows_positive",
date_col_label: "Date",
date_format: "%Y-%m-%d",
name_col_label: "Payee",
amount_col_label: "Amount",
account_col_label: "Account",
category_col_label: "Category",
notes_col_label: "Notes"
}.freeze
CATEGORY_GROUP_COLUMN = "Category_Group".freeze
def self.default_column_mappings
DEFAULT_COLUMN_MAPPINGS
end
def generate_rows_from_csv
rows.destroy_all
mapped_rows = csv_rows.map.with_index(1) do |row, index|
{
source_row_number: index,
account: row[account_col_label].to_s,
date: row[date_col_label].to_s,
amount: signed_csv_amount(row).to_s,
currency: default_currency.to_s,
name: row[name_col_label].to_s,
category: combined_category(row),
notes: row[notes_col_label].to_s
}
end
rows.insert_all!(mapped_rows)
update_column(:rows_count, rows.count)
end
def import!
transaction do
mappings.each(&:create_mappable!)
rows.each do |row|
account = mappings.accounts.mappable_for(row.account)
category = mappings.categories.mappable_for(row.category)
entry = account.entries.build \
date: row.date_iso,
amount: row.signed_amount,
name: row.name,
currency: account.currency.presence || family.currency,
notes: row.notes,
entryable: Transaction.new(category: category),
import: self
entry.save!
end
end
end
def mapping_steps
[ Import::CategoryMapping, Import::AccountMapping ]
end
def required_column_keys
%i[date amount]
end
def column_keys
%i[date amount name category account notes]
end
def csv_template
template = <<~CSV
Account,Date,Payee,Notes,Category_Group,Category,Amount,Split_Amount,Cleared
Checking Account,2024-01-01,Employer,Monthly salary,Income,Paycheck,2500.00,0,Reconciled
Credit Card,2024-01-03,Coffee Shop,Morning coffee,Food,Coffee,-4.25,0,Cleared
CSV
CSV.parse(template, headers: true)
end
def signed_csv_amount(csv_row)
csv_row[amount_col_label].to_d
end
private
def set_mappings
assign_attributes(self.class.default_column_mappings)
save!
end
def combined_category(row)
category = row[category_col_label].to_s.strip
category_group = row[CATEGORY_GROUP_COLUMN].to_s.strip
return category if category_group.blank?
return category_group if category.blank?
"#{category_group}: #{category}"
end
end