import: a card with two birthdays keeps both
The walk took the first BDAY it could read into the model and stored
the card without any of them, so a second date, or a spelling the
model does not read beside one it does, was gone on the Save. The
original column showed it unstruck and the row counted no loss. Only
a card's one BDAY moves now, EditedContact#split_birthday's rule, and
a card with several keeps them all, as a DAV PUT of it does.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kqo9sKAi4DnKCQkrtYoAwM
change
commit 029f65375ea11ffa3c76605d0e03ad3d60c77951
author Claude <noreply@anthropic.com>
date
parent vxszuyrv
diff --git a/lib/pro_tacts/web/import.rb b/lib/pro_tacts/web/import.rb
index 9d8941d..d95f346 100644
--- a/lib/pro_tacts/web/import.rb
+++ b/lib/pro_tacts/web/import.rb
@@ -545,10 +545,14 @@ module ProTacts
     # The birthday comes out of the card and into the model, Store#put's
     # split and its rule for a BDAY the model does not read, so the
     # editor renders the same row over an import as over a contact.
+    # Only a card's one BDAY moves (EditedContact#split_birthday): a
+    # card carrying several keeps every one of them in the card, where
+    # taking the first into the model would drop the rest from it.
     #: (VCard card, Integer index) -> Contact
     def import_contact(card, index)
       bdays, rest = card.extract("BDAY")
-      birthday = bdays.filter_map { it.property }.filter_map { Birthday.from_property(it) }.first
+      property = bdays.fetch(0).property if bdays.length == 1
+      birthday = property && Birthday.from_property(property)
       Contact.new(id: index.to_s, stored: birthday ? rest : card, birthday:, inherited: [])
     end
 
diff --git a/test/pro_tacts/admin/test_import_pages.rb b/test/pro_tacts/admin/test_import_pages.rb
index 4ebe1cd..6ea71b5 100644
--- a/test/pro_tacts/admin/test_import_pages.rb
+++ b/test/pro_tacts/admin/test_import_pages.rb
@@ -620,6 +620,24 @@ class AdminImportPagesTest < Minitest::Test
     end
   end
 
+  # Only a card's one BDAY moves into the model, Store#put's split: a
+  # card carrying two keeps both in the card, the second one a spelling
+  # the model does not read, where taking the first would drop the
+  # other on the Save with nothing to say it went.
+  def test_a_card_with_two_birthdays_keeps_both
+    card = "BEGIN:VCARD\r\nVERSION:3.0\r\nN:Booles;Jane;;;\r\nFN:Jane Booles\r\n" \
+           "BDAY:1980-01-02\r\nBDAY:sometime in May\r\nUID:ABC-123\r\nEND:VCARD\r\n"
+    with_contacts({}) do |store|
+      id = upload(card)
+
+      save(id, 0, first: "Jane", last: "Booles")
+
+      stored = store.contacts.fetch(0)
+      assert_nil stored.birthday
+      assert_equal ["1980-01-02", "sometime in May"], stored.stored.properties.select { it.name == "BDAY" }.map(&:value)
+    end
+  end
+
   # The group for the import is named when the file is uploaded;
   # which of this book's own groups a contact joins is asked beside
   # that contact's card, and written by the same Save as its fields.