Skip to content

[No QA] Docs updates for E/A#99201 Unique ID mapping for company card CSV import - #99797

Draft
MelvinBot wants to merge 3 commits into
mainfrom
melvin/docs-99201-unique-id-company-card-csv
Draft

[No QA] Docs updates for E/A#99201 Unique ID mapping for company card CSV import#99797
MelvinBot wants to merge 3 commits into
mainfrom
melvin/docs-99201-unique-id-company-card-csv

Conversation

@MelvinBot

@MelvinBot MelvinBot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Explanation of Change

Help site updates for #99201, which added a mappable Unique ID column to the New Expensify company card spreadsheet import.

Updates docs/articles/new-expensify/connect-credit-cards/Import-Company-Card-Transactions-From-a-Spreadsheet.md:

  • Adds a ## How to use Unique ID to prevent duplicate transactions section explaining that Unique ID is the dedup key, what to map it to, and what happens when it isn't mapped.
  • Adds mapping Unique ID as a step in the main import flow and in the "upload additional transactions" flow.
  • Documents that a Unique ID must be more than 5 characters long. A value of 5 characters or fewer is too short to dedupe reliably, so Expensify ignores it and the row imports again on every re-upload (per tgolen in review).
  • Corrects the existing "Previously mapped fields will auto-fill" note. That was true for every mapping before this change, but the PR deliberately never restores a saved externalID index, so Unique ID must be re-mapped on every upload. Leaving the note as-is would tell admins the opposite of what the product does and lead straight to duplicate expenses.
  • Adds four FAQ entries: why re-uploading creates duplicates without Unique ID, why Unique ID comes back blank on a later upload, what happens to rows with a blank Unique ID cell, and whether there is a minimum Unique ID length.
  • Fixes UI labels that no longer match the product: Company CardsCompany cards, and Add card / Add CardAdd cards. Both verified against the live web UI and src/languages/en.ts.

The PR's test steps mention a Use advanced fields toggle, but that toggle has since been removed from CompanyCardsImportedPage.tsx (all mapping fields are now always offered), so it is intentionally not documented.

Fixed Issues

$ #99195
PROPOSAL:

Tests

// TODO: The human co-author must fill out the tests you ran before marking this PR as "ready for review"
// Please describe what tests you performed that validates your changed worked.

  • Verify that no errors appear in the JS console

Offline tests

QA Steps

// TODO: The human co-author must fill out the QA tests you ran before marking this PR as "ready for review".
// Please describe what QA needs to do to validate your changes and what areas do they need to test for regressions.

  • Verify that no errors appear in the JS console

PR Author Checklist

  • I linked the correct issue in the ### Fixed Issues section above
  • I wrote clear testing steps that cover the changes made in this PR
    • I added steps for local testing in the Tests section
    • I added steps for the expected offline behavior in the Offline steps section
    • I added steps for Staging and/or Production testing in the QA steps section
    • I added steps to cover failure scenarios (i.e. verify an input displays the correct error message if the entered data is not correct)
    • I turned off my network connection and tested it while offline to ensure it matches the expected behavior (i.e. verify the default avatar icon is displayed if app is offline)
    • I tested this PR with a High Traffic account against the staging or production API to ensure there are no regressions (e.g. long loading states that impact usability).
  • I included screenshots or videos for tests on all platforms
  • I ran the tests on all platforms & verified they passed on:
    • Android: Native
    • Android: mWeb Chrome
    • iOS: Native
    • iOS: mWeb Safari
    • MacOS: Chrome / Safari
  • I verified there are no console errors (if there's a console error not related to the PR, report it or open an issue for it to be fixed)
  • I followed proper code patterns (see Reviewing the code)
    • I verified that comments were added to code that is not self explanatory
    • I verified that any new or modified comments were clear, correct English, and explained "why" the code was doing something instead of only explaining "what" the code was doing.
    • I verified any copy / text that was added to the app is grammatically correct in English. It adheres to proper capitalization guidelines (note: only the first word of header/labels should be capitalized), and is either coming verbatim from figma or has been approved by marketing (in order to get marketing approval, ask the Bug Zero team member to add the Waiting for copy label to the issue)
  • If a new code pattern is added I verified it was agreed to be used by multiple Expensify engineers
  • I followed the guidelines as stated in the Review Guidelines
  • I tested other components that can be impacted by my changes (i.e. if the PR modifies a shared library or component like Avatar, I verified the components using Avatar are working as expected)
  • If a new CSS style is added I verified that:
    • A similar style doesn't already exist
    • The style can't be created with an existing StyleUtils function (i.e. StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))
  • If new assets were added or existing ones were modified, I verified that:
    • The assets are optimized and compressed (for SVG files, run npm run compress-svg)
    • The assets load correctly across all supported platforms.
  • If the PR modifies code that runs when editing or sending messages, I tested and verified there is no unexpected behavior for all supported markdown - URLs, single line code, code blocks, quotes, headings, bold, strikethrough, and italic.
  • If the PR modifies a generic component, I tested and verified that those changes do not break usages of that component in the rest of the App (i.e. if a shared library or component like Avatar is modified, I verified that Avatar is working as expected in all cases)
  • If the PR modifies a component related to any of the existing Storybook stories, I tested and verified all stories for that component are still working as expected.
  • If the PR modifies a component or page that can be accessed by a direct deeplink, I verified that the code functions as expected when the deeplink is used - from a logged in and logged out account.
  • If the PR modifies the UI (e.g. new buttons, new UI components, changing the padding/spacing/sizing, moving components, etc) or modifies the form input styles:
    • I verified that all the inputs inside a form are aligned with each other.
    • I added Design label and/or tagged @Expensify/design so the design team can review the changes.
  • I added unit tests for any new feature or bug fix in this PR to help automatically prevent regressions in this user flow.
  • If the main branch was merged into this PR after a review, I tested again and verified the outcome was still expected according to the Test steps.

Screenshots/Videos

Android: Native
Android: mWeb Chrome
iOS: Native
iOS: mWeb Safari
MacOS: Chrome / Safari

@tgolen, please review the files changed and confirm they reflect the current behavior. Then post a new comment saying @MelvinBot mark this PR as ready for review.

Co-authored-by: Tim Golen <tgolen@users.noreply.github.com>
@MelvinBot
MelvinBot requested a review from a team August 28, 2026 21:45
@MelvinBot MelvinBot added the HelpDot Apply this label if the issue relates to ExpensifyHelp label Aug 28, 2026
@github-actions github-actions Bot changed the title Docs updates for E/A#99201 Unique ID mapping for company card CSV import [No QA] Docs updates for E/A#99201 Unique ID mapping for company card CSV import Aug 28, 2026
@OSBotify

This comment has been minimized.


If you don't map **Unique ID**, Expensify treats every row in the file as a new transaction. Re-uploading the same file creates a duplicate expense for each row.

For **Unique ID** to work, the values in that column must be unique within the file and stay the same for the same transaction across uploads. If the column repeats the same value on different transactions, Expensify treats them as the same transaction and skips the later ones.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MelvinBot You should also mention here that the length of the unique ID has to be at least 5 characters long.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in a601ba9.

Added it in two places:

In this section (right after the uniqueness paragraph you commented on):

Each Unique ID value must also be at least 5 characters long. Expensify ignores anything shorter when it checks for transactions it has already imported, so rows with a very short Unique ID import again every time you re-upload the file. If your file numbers transactions with short values such as 1, 2, and 3, map Unique ID to a longer reference column instead.

As an FAQ entry, since every other Unique ID gotcha in this article has one:

Is there a minimum length for Unique ID?

Yes. A Unique ID value must be at least 5 characters long. Expensify ignores shorter values when it checks for transactions it has already imported, so those rows create duplicates on a re-upload even though Unique ID is mapped. Map Unique ID to a column with longer references, such as the bank's own transaction ID.


⚠️ One thing to confirm before merge: I couldn't verify the 5-character rule myself, so I took your word on the number and inferred the user-visible effect. Please sanity-check both.

What I checked and why the wording says "ignores" rather than "rejects"

There's no length check on Unique ID anywhere in the App codebase — the import screen's validate() only checks that required columns are mapped, that a card-identity column is mapped, that no field is mapped twice, and that required columns have no empty cells. Unique ID isn't in any of those, so a one-character value passes the client with no error and is sent through as-is. The only handling of the cell value is a blank check: an empty cell gets a generated ID.

So the 5-character rule has to be enforced on the backend, which I can't read from here. Since it lives in the dedup path rather than in import validation, I described the effect as "the value is ignored for duplicate matching, so the row imports again" rather than "the row is rejected" or "you'll see an error" — an error would have had to come from the App code, and there's no string for it.

Please correct me if either of these is wrong:

  1. Is the threshold "at least 5" (i.e. 4 characters fails), or is it "longer than 5"?
  2. Does a too-short value really just fall out of duplicate matching, or does something else happen to the row?

Docs-only markdown change, so no app tests apply. The change introduces no new vocabulary beyond what's already in the article, and spellcheck passed on the previous commit.


view run

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correction to my reply above: the quoted copy said "at least 5 characters," which was wrong. tgolen confirmed the threshold is more than 5 — a value of 5 characters or fewer is too short to dedupe reliably and gets ignored.

Fixed in d078d05, and verified on the deploy preview. Details in this comment.

Addresses review feedback from tgolen on #99797.

Co-authored-by: Tim Golen <tgolen@users.noreply.github.com>
@MelvinBot

Copy link
Copy Markdown
Contributor Author

Addressed the review feedback in a601ba9.

tgolen asked me to document that a Unique ID must be at least 5 characters long. I added it to the How to use Unique ID to prevent duplicate transactions section (where the comment was anchored) and as a matching FAQ entry, since every other Unique ID caveat in this article has one. Full wording is in the review thread.

⚠️ Please confirm two things before merge — I could not verify the 5-character rule myself, so I took tgolen's number on faith and inferred the user-visible effect:

  1. Is the threshold "at least 5" (so 4 characters fails), or "longer than 5"?
  2. Does a too-short value simply drop out of duplicate matching, or does something else happen to the row?
Why the wording says "ignored" rather than "rejected"

There is no length check on Unique ID anywhere in the App codebase. The import screen's validate() only checks that required columns are mapped, that a card-identity column is mapped, that no field is mapped twice, and that required columns contain no empty cells — see src/pages/workspace/companyCards/addNew/CompanyCardsImportedPage.tsx:116-163. Unique ID is registered as an optional column role with no isRequired flag, so it is in none of those checks. A one-character value passes the client with no error.

The only handling of the mapped cell's value is a blank check in src/libs/actions/CompanyCards.ts: an empty cell gets a generated ID, and anything non-blank passes through verbatim. There is also no "too short" error string in src/languages/en.ts — the only CSV errors are the required-columns, duplicate-columns, card-identity, and empty-value messages.

So the rule is enforced on the backend, which I can't read from here. Because it sits in the duplicate-detection path rather than in import validation, I described the effect as "the value is ignored for duplicate matching, so the row imports again on re-upload" rather than "the row is rejected" or "you'll see an error" — an error would have had to originate in App code, and there is none.

Checks

Docs-only markdown change, so no app tests apply and there was nothing to verify in the UI. On a601ba9, Spell check and Validate Docs Routes File both pass. The only failing check is Check independent approval, which is expected on a bot-authored PR awaiting review.

I also corrected the PR description, which said "Adds three FAQ entries" — it is four now.


view run

@tgolen

tgolen commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

@MelvinBot The unique ID must be more than 5 characters. If it is 5 or less, then it is too short to reliably dedupe anything, so it will be ignored.

A Unique ID of 5 characters or fewer is too short to dedupe reliably and is ignored. Corrects tgolen's feedback on #99797.

Co-authored-by: Tim Golen <tgolen@users.noreply.github.com>
@OSBotify

Copy link
Copy Markdown
Contributor

A preview of your ExpensifyHelp changes have been deployed to https://a5987b8b.helpdot.pages.dev ⚡️

Updated articles:

@MelvinBot

Copy link
Copy Markdown
Contributor Author

Corrected in d078d05. I had it off by one — "at least 5" would have told admins a 5-character ID works, when it's exactly the case that gets ignored.

In the Unique ID section:

Each Unique ID value must also be more than 5 characters long. A value of 5 characters or fewer is too short to reliably identify a transaction, so Expensify ignores it and imports the row again every time you re-upload the file. If your file numbers transactions with short values such as 1, 2, and 3, map Unique ID to a longer reference column instead.

In the FAQ (Is there a minimum length for Unique ID?):

Yes. A Unique ID value must be more than 5 characters long. Expensify ignores any value of 5 characters or fewer because it's too short to reliably identify a transaction, so those rows create duplicates on a re-upload even though Unique ID is mapped. Map Unique ID to a column with longer references, such as the bank's own transaction ID.

Your confirmation that the value is ignored (rather than the row being rejected or an error shown) matches what the App code does, so the surrounding wording stands — there's no length check or error string on the client side.

I also updated the PR description, which still described the old threshold.

Verified on the deploy preview: both sentences render as "more than 5 characters" at the updated article. Spell check, Validate Docs Routes File, and Deploy ExpensifyHelp all pass on d078d05; the only failing check is the expected independent-approval gate.


view run

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

HelpDot Apply this label if the issue relates to ExpensifyHelp

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants