import: bring in the ORG and flag a company card is read by
Import dropped a company's ORG and the flag that marks it so;
it opened as a person, blank name boxes in a row.

Assisted-by: Claude Opus 5.5 via Claude Code
change rzqtnwvxsstqnozouwutvmrynnktkwnp
commit 38eeadd8ccb7f5792490020fc53c45bdd4097fe5
author Alpha Chen <alpha@kejadlen.dev>
date
parent stvrrxpv
diff --git a/lib/pro_tacts/import/vcf.rb b/lib/pro_tacts/import/vcf.rb
index 68a7b59..ec5248f 100644
--- a/lib/pro_tacts/import/vcf.rb
+++ b/lib/pro_tacts/import/vcf.rb
@@ -38,8 +38,11 @@ module ProTacts
       # an import brings in. The envelope (BEGIN, END, VERSION, UID),
       # the fields a screen shows (Contact's own readers: N, FN,
       # NICKNAME, TEL, EMAIL, ADR, NOTE, PHOTO, and the X-ABLabel that
-      # names a row), and BDAY, which the store takes into the model on
-      # the way in
+      # names a row), the two a company card is read by (ORG, whose
+      # name the editor's Organization field falls back to and which
+      # the dashboard's search reads, and X-ABShowAs, which puts that
+      # field there — docs/plans/2026-09-24-company-cards.md), and
+      # BDAY, which the store takes into the model on the way in
       # (docs/plans/2026-09-11-every-birthday-in-the-model.md).
       #
       # Everything else is unknown, which here means "no screen will
@@ -48,7 +51,7 @@ module ProTacts
       # importing one would only pad the cards with what nobody can
       # read.
       KNOWN = [
-        *%w[BEGIN END VERSION UID N FN NICKNAME BDAY TEL EMAIL ADR NOTE PHOTO],
+        *%w[BEGIN END VERSION UID N FN NICKNAME BDAY TEL EMAIL ADR NOTE PHOTO ORG X-ABSHOWAS],
         LABEL,
       ].freeze #: Array[String]
 
diff --git a/test/pro_tacts/admin/test_import_pages.rb b/test/pro_tacts/admin/test_import_pages.rb
index e9f2b93..3d19452 100644
--- a/test/pro_tacts/admin/test_import_pages.rb
+++ b/test/pro_tacts/admin/test_import_pages.rb
@@ -278,6 +278,39 @@ class AdminImportPagesTest < Minitest::Test
     end
   end
 
+  # The shape macOS writes a company in (test/fixtures/cards/
+  # company.vcf): N empty, the name in FN and ORG, and the flag. It
+  # opens in the company editor and comes in as a company.
+  def test_a_company_card_comes_in_as_a_company
+    company = <<~CARD.gsub("\n", "\r\n")
+      BEGIN:VCARD
+      VERSION:3.0
+      N:;;;;
+      FN:Bletchley Park
+      ORG:Bletchley Park Trust;
+      X-ABShowAs:COMPANY
+      UID:GHI-789
+      END:VCARD
+    CARD
+    with_contacts({}) do |store|
+      id = upload(company)
+
+      get "/import/#{id}/0"
+
+      refute_includes last_response.body, "not imported"
+      assert_includes last_response.body, "<span>Organization</span>"
+      assert_includes last_response.body, %(name="last" value="Bletchley Park")
+
+      save(id, 0, first: "", last: "Bletchley Park")
+
+      assert_equal 303, last_response.status
+      park = store.contacts.fetch(0)
+
+      assert_predicate park, :company?
+      assert_equal "Bletchley Park Trust;", park.organization
+    end
+  end
+
   def test_an_index_past_the_end_of_the_file_is_not_found
     with_contacts({}) do |_store|
       id = upload(JANE)
diff --git a/test/pro_tacts/import/test_vcf.rb b/test/pro_tacts/import/test_vcf.rb
index 7c7d9b4..1175783 100644
--- a/test/pro_tacts/import/test_vcf.rb
+++ b/test/pro_tacts/import/test_vcf.rb
@@ -162,6 +162,29 @@ class ImportVcfTest < Minitest::Test
     assert Vcf.cards(revised).fetch(0).card?
   end
 
+  # A company card is read by its flag and its ORG
+  # (docs/plans/2026-09-24-company-cards.md), so both come in and
+  # neither is a decision; without them it would arrive as a person.
+  def test_reading_a_company_card_keeps_what_makes_it_one
+    company = <<~CARD.gsub("\n", "\r\n")
+      BEGIN:VCARD
+      VERSION:3.0
+      N:;;;;
+      FN:Bletchley Park
+      ORG:Bletchley Park Trust;
+      X-ABShowAs:COMPANY
+      UID:GHI-789
+      END:VCARD
+    CARD
+    cards = Vcf.cards(company)
+
+    reading = Vcf.read(cards.fetch(0))
+
+    assert_equal company, reading.card.to_s
+    assert_empty reading.dropped
+    assert_empty Vcf.unknown(cards)
+  end
+
   def test_reading_a_card_leaves_behind_what_no_screen_shows
     reading = Vcf.read(Vcf.cards(JANE).fetch(0))