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
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.