diff --git a/app/models/account_statement/coverage.rb b/app/models/account_statement/coverage.rb index 225df183c..33233d72d 100644 --- a/app/models/account_statement/coverage.rb +++ b/app/models/account_statement/coverage.rb @@ -130,11 +130,11 @@ class AccountStatement::Coverage linked_statements = statements_covering(linked_statement_scope, month) ambiguous_statements = statements_covering(ambiguous_statement_scope, month) - status = if linked_statements.size > 1 + status = if linked_statements.many? && overlapping_statements?(linked_statements) "duplicate" elsif linked_statements.any? { |statement| statement.reconciliation_mismatched?(balance_lookup: balance_lookup) } "mismatched" - elsif linked_statements.one? + elsif linked_statements.any? "covered" elsif ambiguous_statements.any? "ambiguous" @@ -177,6 +177,13 @@ class AccountStatement::Coverage end end + def overlapping_statements?(statements) + statements.combination(2).any? do |a, b| + a.period_start_on <= b.period_end_on && + b.period_start_on <= a.period_end_on + end + end + def balance_lookup @balance_lookup ||= begin currencies = linked_statement_scope.map(&:statement_currency).compact.uniq diff --git a/test/models/account_statement_test.rb b/test/models/account_statement_test.rb index 38f956ecc..08438332e 100644 --- a/test/models/account_statement_test.rb +++ b/test/models/account_statement_test.rb @@ -756,6 +756,41 @@ class AccountStatementTest < ActiveSupport::TestCase assert_equal "mismatched", statuses[mismatched_month] end + test "coverage does not mark adjacent statements as duplicate when they only share a calendar month" do + account = Account.create!( + family: @family, + owner: users(:family_admin), + name: "Adjacent Statement Checking", + balance: 0, + currency: "USD", + accountable: Depository.new + ) + + march = Date.new(2026, 3, 1) + + create_statement_with_period( + account: account, + period_start_on: Date.new(2026, 1, 8), + period_end_on: Date.new(2026, 3, 6), + content: "adjacent-a" + ) + + create_statement_with_period( + account: account, + period_start_on: Date.new(2026, 3, 7), + period_end_on: Date.new(2026, 4, 7), + content: "adjacent-b" + ) + + coverage = AccountStatement::Coverage.new( + account, + start_month: march, + end_month: march + ) + + assert_equal "covered", coverage.months.first.status + end + private def create_statement(account:, month:, content:, suggested_account: nil, closing_balance: nil) @@ -776,4 +811,24 @@ class AccountStatementTest < ActiveSupport::TestCase ) statement end + + def create_statement_with_period(account:, period_start_on:, period_end_on:, content:, suggested_account: nil) + statement = AccountStatement.create_from_upload!( + family: @family, + account: account, + file: uploaded_file( + filename: "statement_#{content}_#{period_start_on}_#{period_end_on}.csv", + content_type: "text/csv", + content: "date,amount\n#{period_start_on},1\n#{period_end_on},2\n#{content}\n" + ) + ) + + statement.update!( + suggested_account: suggested_account, + period_start_on: period_start_on, + period_end_on: period_end_on + ) + + statement + end end