diff --git a/app/models/account_import.rb b/app/models/account_import.rb
index f5b790a09..02f19c05b 100644
--- a/app/models/account_import.rb
+++ b/app/models/account_import.rb
@@ -18,7 +18,19 @@ class AccountImport < Import
account.save!
manager = Account::OpeningBalanceManager.new(account)
- result = manager.set_opening_balance(balance: row.amount.to_d)
+
+ # Parse date if provided, otherwise use default
+ balance_date = if row.date.present?
+ begin
+ Date.strptime(row.date, date_format)
+ rescue ArgumentError => e
+ raise OpeningBalanceError, "Invalid date format for '#{row.date}': #{e.message}"
+ end
+ else
+ nil
+ end
+
+ result = manager.set_opening_balance(balance: row.amount.to_d, date: balance_date)
# Re-raise since we should never have an error here
if result.error
@@ -37,7 +49,7 @@ class AccountImport < Import
end
def column_keys
- %i[entity_type name amount currency]
+ %i[entity_type name amount currency date]
end
def dry_run
@@ -48,10 +60,10 @@ class AccountImport < Import
def csv_template
template = <<-CSV
- Account type*,Name*,Balance*,Currency
- Checking,Main Checking Account,1000.00,USD
- Savings,Emergency Fund,5000.00,USD
- Credit Card,Rewards Card,-500.00,USD
+ Account type*,Name*,Balance*,Currency,Balance Date
+ Checking,Main Checking Account,1000.00,USD,01/01/2024
+ Savings,Emergency Fund,5000.00,USD,01/15/2024
+ Credit Card,Rewards Card,-500.00,USD,02/01/2024
CSV
CSV.parse(template, headers: true)
diff --git a/app/views/import/configurations/_account_import.html.erb b/app/views/import/configurations/_account_import.html.erb
index 28096ff9c..a8c4ec010 100644
--- a/app/views/import/configurations/_account_import.html.erb
+++ b/app/views/import/configurations/_account_import.html.erb
@@ -6,5 +6,13 @@
<%= form.select :amount_col_label, import.csv_headers, { include_blank: "Leave empty", label: "Balance" }, required: true %>
<%= form.select :currency_col_label, import.csv_headers, { include_blank: "Default", label: "Currency" } %>
+
+ <%= form.select :date_col_label, import.csv_headers, { include_blank: "Leave empty", label: "Balance Date" } %>
+ <%= form.select :date_format,
+ Family::DATE_FORMATS,
+ { label: "Date Format", prompt: "Select format" },
+ required: @import.date_col_label.present? %>
+
+
<%= form.submit "Apply configuration", disabled: import.complete? %>
<% end %>
diff --git a/test/models/account_import_test.rb b/test/models/account_import_test.rb
index 29204c0fd..d433cab8f 100644
--- a/test/models/account_import_test.rb
+++ b/test/models/account_import_test.rb
@@ -63,8 +63,95 @@ class AccountImportTest < ActiveSupport::TestCase
end
end
- test "column_keys returns expected keys" do
- assert_equal %i[entity_type name amount currency], @import.column_keys
+ test "import creates accounts with explicit balance dates" do
+ import_csv = <<~CSV
+ type,name,amount,currency,date
+ depository,Main Checking,1000.00,USD,01/15/2024
+ depository,Savings Account,5000.00,USD,02/01/2024
+ CSV
+
+ @import.update!(
+ raw_file_str: import_csv,
+ entity_type_col_label: "type",
+ name_col_label: "name",
+ amount_col_label: "amount",
+ currency_col_label: "currency",
+ date_col_label: "date",
+ date_format: "%m/%d/%Y"
+ )
+
+ @import.generate_rows_from_csv
+
+ # Create mappings for account types
+ @import.mappings.create! key: "depository", value: "Depository", type: "Import::AccountTypeMapping"
+
+ @import.reload
+
+ # Perform the import
+ @import.publish
+
+ # Check if import succeeded
+ if @import.failed?
+ fail "Import failed with error: #{@import.error}"
+ end
+
+ assert_equal "complete", @import.status
+
+ # Verify accounts were created with correct dates
+ accounts = @import.accounts.order(:name)
+
+ checking_account = accounts.find { |a| a.name == "Main Checking" }
+ savings_account = accounts.find { |a| a.name == "Savings Account" }
+
+ checking_valuation = checking_account.valuations.opening_anchor.first
+ savings_valuation = savings_account.valuations.opening_anchor.first
+
+ assert_equal Date.parse("2024-01-15"), checking_valuation.entry.date
+ assert_equal Date.parse("2024-02-01"), savings_valuation.entry.date
+ end
+
+ test "import creates accounts with default dates when date column not provided" do
+ import_csv = <<~CSV
+ type,name,amount,currency
+ depository,Main Checking,1000.00,USD
+ CSV
+
+ @import.update!(
+ raw_file_str: import_csv,
+ entity_type_col_label: "type",
+ name_col_label: "name",
+ amount_col_label: "amount",
+ currency_col_label: "currency"
+ )
+
+ @import.generate_rows_from_csv
+
+ # Create mappings for account types
+ @import.mappings.create! key: "depository", value: "Depository", type: "Import::AccountTypeMapping"
+
+ @import.reload
+
+ # Perform the import
+ @import.publish
+
+ # Check if import succeeded
+ if @import.failed?
+ fail "Import failed with error: #{@import.error}"
+ end
+
+ assert_equal "complete", @import.status
+
+ # Verify account was created with default date (2 years ago or 1 day before oldest entry)
+ account = @import.accounts.first
+ valuation = account.valuations.opening_anchor.first
+
+ # Default date should be 2 years ago when there are no other entries
+ expected_default_date = 2.years.ago.to_date
+ assert_equal expected_default_date, valuation.entry.date
+ end
+
+ test "column_keys returns expected keys including date" do
+ assert_equal %i[entity_type name amount currency date], @import.column_keys
end
test "required_column_keys returns expected keys" do