Skip to content

Comments

Fix monetary custom field UX: clarify currency format and catch trailing-symbol mistakes early#57

Merged
baruchiro merged 7 commits intomainfrom
copilot/improve-paperless-ux
Feb 24, 2026
Merged

Fix monetary custom field UX: clarify currency format and catch trailing-symbol mistakes early#57
baruchiro merged 7 commits intomainfrom
copilot/improve-paperless-ux

Conversation

Copy link

Copilot AI commented Feb 22, 2026

  • Explore repo structure and understand the codebase
  • Create src/tools/utils/monetary.ts – validation utility for monetary custom field values
  • Update update_document and bulk_edit_documents tools with improved descriptions and validation
  • Update create_custom_field tool description
  • Refactor per review feedback (round 1): filter+forEach, shared const, validateCustomFields function, remove verbose comments, SYMBOL_TO_CODE-driven regex
  • Refactor per review feedback (round 2): add ILS, move validateCustomFields to monetary.ts, create shared descriptions.ts, add unit tests with node:test
  • Add .github/workflows/ci.yml to run npm test on every PR and push to main
  • Trim per-symbol unit tests to a single representative case
  • Build verified (no errors)
  • All 3 tests pass
  • Changeset created
Original prompt

This section details on the original issue you should resolve

<issue_title>Improve Paperless UX for custom amount field – clarify currency format, prevent input errors</issue_title>
<issue_description>- Field addition flow for 'Amount' in Paperless receipt encountered validation issues.

  • User tried to add the value '10.00$' for the field 'Amount', but the backend requires currency code as a prefix (e.g. USD10.00).
  • Backend error: Only accepts two-decimal numbers; currency code must be a prefix (e.g. GBP123.45, USD10.00).
  • Action taken: Corrected to 'USD10.00' per requirements; addition succeeded.
  • Verification checklist confirms document status is valid for inbox removal.

Expected fix:

  • UX should clarify currency formatting rules early and prevent common mistakes (e.g. trailing $).
  • Suggest currency prefix before input submission.
  • Field validation should return user-friendly, actionable error messages.
  • Provide in-place documentation/examples for acceptable value formats.</issue_description>

Comments on the Issue (you are @copilot in this section)


🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.

@changeset-bot
Copy link

changeset-bot bot commented Feb 22, 2026

🦋 Changeset detected

Latest commit: 72eec16

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@baruchiro/paperless-mcp Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Co-authored-by: baruchiro <17686879+baruchiro@users.noreply.github.com>
Copilot AI changed the title [WIP] Improve Paperless UX for custom amount field Fix monetary custom field UX: clarify currency format and catch trailing-symbol mistakes early Feb 22, 2026
Copilot AI requested a review from baruchiro February 22, 2026 07:45
@baruchiro baruchiro marked this pull request as ready for review February 22, 2026 08:20
Copilot AI review requested due to automatic review settings February 22, 2026 08:20
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR improves the user experience for Paperless-NGX monetary custom fields by adding early validation and documentation. When users attempt to set a monetary field with a trailing currency symbol (e.g., 10.00$), the system now catches the mistake before making the API call and provides an actionable error message explaining the correct format (USD10.00).

Changes:

  • Adds client-side validation that detects and rejects common monetary format mistakes (trailing currency symbols)
  • Updates tool descriptions to document the required currency code prefix format for monetary fields
  • Provides clear, actionable error messages with format examples when validation fails

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
src/tools/utils/monetary.ts New validation utility that detects trailing currency symbol patterns and generates helpful error messages with the correct format
src/tools/documents.ts Integrates monetary validation in bulk_edit_documents and update_document tools; updates parameter descriptions to document the monetary format requirement
src/tools/customFields.ts Updates create_custom_field tool description to document the monetary format requirement upfront
.changeset/improve-monetary-field-ux.md Changeset file documenting this UX improvement as a patch release

Comment on lines 98 to 105
if (add_custom_fields) {
for (const cf of add_custom_fields) {
if (typeof cf.value === "string") {
const monetaryError = getMonetaryValidationError(cf.value);
if (monetaryError) throw new Error(monetaryError);
}
}
}
Copy link
Owner

Choose a reason for hiding this comment

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

You can do something like add_custom_fields?.filter(value === string).forEach

Copy link
Author

Choose a reason for hiding this comment

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

Refactored to filter+forEach in 5224c05 (inside the extracted validateCustomFields function).

])
.describe(
"The value for the custom field. For documentlink fields, use a single document ID (e.g., 123) or an array of document IDs (e.g., [123, 456])."
"The value for the custom field. For monetary fields, use currency code prefix format (e.g., USD10.00, GBP123.45, EUR9.99) — NOT trailing symbol format (e.g., 10.00$). For documentlink fields, use a single document ID (e.g., 123) or an array of document IDs (e.g., [123, 456])."
Copy link
Owner

Choose a reason for hiding this comment

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

You're updating this description in many places, let's save the description in one const place and reuse it.

Copy link
Author

Choose a reason for hiding this comment

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

Extracted to CUSTOM_FIELD_VALUE_DESCRIPTION const at the top of documents.ts and reused in both tools in 5224c05.

Comment on lines 387 to 394
if (updateData.custom_fields) {
for (const cf of updateData.custom_fields) {
if (typeof cf.value === "string") {
const monetaryError = getMonetaryValidationError(cf.value);
if (monetaryError) throw new Error(monetaryError);
}
}
}
Copy link
Owner

Choose a reason for hiding this comment

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

This pattern already added in this PR, you should consider a function for "validateCustomFields"

Copy link
Author

Choose a reason for hiding this comment

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

Extracted to a validateCustomFields function in 5224c05, called in both bulk_edit_documents and update_document.

Copy link
Owner

Choose a reason for hiding this comment

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

Too many comments. And too long. You should consider omitting them at all.

Copy link
Author

Choose a reason for hiding this comment

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

Removed all comments in 5224c05.

Comment on lines 11 to 21
/** Regex detecting values with a trailing currency symbol (common mistake, e.g., "10.00$") */
const TRAILING_SYMBOL_REGEX = /^(\d+(?:\.\d+)?)[$€£¥₹]$/;

/** Map of common currency symbols to ISO 4217 currency codes */
const SYMBOL_TO_CODE: Record<string, string> = {
$: "USD",
"€": "EUR",
"£": "GBP",
"¥": "JPY",
"₹": "INR",
};
Copy link
Owner

Choose a reason for hiding this comment

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

If you will declare the mapping first, you will be able to use its Object.keys in the regex

Copy link
Author

Choose a reason for hiding this comment

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

Done in 5224c05SYMBOL_TO_CODE is now declared first and Object.keys(SYMBOL_TO_CODE).join("") is used to build the character class in the regex dynamically.

Co-authored-by: baruchiro <17686879+baruchiro@users.noreply.github.com>
auto-merge was automatically disabled February 22, 2026 08:51

Head branch was pushed to by a user without write access

Copilot AI requested a review from baruchiro February 22, 2026 08:51
Comment on lines 12 to 21
function validateCustomFields(
custom_fields: { field: number; value: unknown }[] | undefined
) {
custom_fields
?.filter((cf) => typeof cf.value === "string")
.forEach((cf) => {
const monetaryError = getMonetaryValidationError(cf.value as string);
if (monetaryError) throw new Error(monetaryError);
});
}
Copy link
Owner

Choose a reason for hiding this comment

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

I think it should be in a shared place too.

Copy link
Author

Choose a reason for hiding this comment

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

Moved validateCustomFields to src/tools/utils/monetary.ts and exported it from there in 7bb0c4f.

Comment on lines 9 to 10
const CUSTOM_FIELD_VALUE_DESCRIPTION =
"The value for the custom field. For monetary fields, use currency code prefix format (e.g., USD10.00, GBP123.45, EUR9.99) — NOT trailing symbol format (e.g., 10.00$). For documentlink fields, use a single document ID (e.g., 123) or an array of document IDs (e.g., [123, 456]).";
Copy link
Owner

Choose a reason for hiding this comment

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

Consider a shared file for string constants/prompts/descriptions.

Do not apply it for all strings, it will be ongoing process.

Copy link
Author

Choose a reason for hiding this comment

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

Created src/tools/utils/descriptions.ts as the shared file for string constants. CUSTOM_FIELD_VALUE_DESCRIPTION is now exported from there and imported in documents.ts in 7bb0c4f.

Comment on lines 1 to 7
const SYMBOL_TO_CODE: Record<string, string> = {
$: "USD",
"€": "EUR",
"£": "GBP",
"¥": "JPY",
"₹": "INR",
};
Copy link
Owner

Choose a reason for hiding this comment

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

Add ₪ ILS

Copy link
Author

Choose a reason for hiding this comment

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

Added "₪": "ILS" to SYMBOL_TO_CODE in 7bb0c4f.

Comment on lines +13 to +29
export function getMonetaryValidationError(value: string): string | null {
const trailingMatch = TRAILING_SYMBOL_REGEX.exec(value);
if (trailingMatch) {
const amount = trailingMatch[1];
const symbol = value.slice(-1);
const code = SYMBOL_TO_CODE[symbol] || "USD";
const numericAmount = parseFloat(amount);
const formattedAmount = isNaN(numericAmount) ? amount : numericAmount.toFixed(2);
return (
`Invalid monetary format "${value}". ` +
`Paperless-NGX requires the currency code as a prefix, e.g. "${code}${formattedAmount}". ` +
`Use the format: {CURRENCY_CODE}{amount} (e.g., USD10.00, GBP123.45, EUR9.99).`
);
}

return null;
}
Copy link
Owner

Choose a reason for hiding this comment

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

This must be covered with unit test.

Copy link
Author

Choose a reason for hiding this comment

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

Added src/tools/utils/monetary.test.ts with 8 unit tests (covering valid formats, non-monetary strings, and all 6 currency symbols including ILS) using Node's built-in node:test runner in 7bb0c4f. Updated package.json test script — all tests pass.

…t tests

Co-authored-by: baruchiro <17686879+baruchiro@users.noreply.github.com>
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"test": "node --require ts-node/register --test src/**/*.test.ts",
Copy link
Owner

Choose a reason for hiding this comment

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

@copilot open a new PR to run GitHub Actions Workflow with this tests o
for every PR.

Copy link
Author

Choose a reason for hiding this comment

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

I can't open a new PR, but I've added .github/workflows/ci.yml to this PR in eff830c. It runs npm test on every PR and on pushes to main.

Co-authored-by: baruchiro <17686879+baruchiro@users.noreply.github.com>
auto-merge was automatically disabled February 22, 2026 17:01

Head branch was pushed to by a user without write access

Copilot AI requested a review from baruchiro February 22, 2026 17:02
Comment on lines 18 to 53
test("returns error for trailing dollar sign", () => {
const err = getMonetaryValidationError("10.00$");
assert.ok(err);
assert.match(err, /USD10\.00/);
assert.match(err, /currency code as a prefix/);
});

test("returns error for trailing euro sign", () => {
const err = getMonetaryValidationError("123€");
assert.ok(err);
assert.match(err, /EUR123\.00/);
});

test("returns error for trailing pound sign", () => {
const err = getMonetaryValidationError("50£");
assert.ok(err);
assert.match(err, /GBP50\.00/);
});

test("returns error for trailing shekel sign", () => {
const err = getMonetaryValidationError("100₪");
assert.ok(err);
assert.match(err, /ILS100\.00/);
});

test("returns error for trailing rupee sign", () => {
const err = getMonetaryValidationError("200₹");
assert.ok(err);
assert.match(err, /INR200\.00/);
});

test("returns error for trailing yen sign", () => {
const err = getMonetaryValidationError("500¥");
assert.ok(err);
assert.match(err, /JPY500\.00/);
});
Copy link
Owner

Choose a reason for hiding this comment

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

No need to test every symbol. The idea is clear after the first symbol.

Copy link
Author

Choose a reason for hiding this comment

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

Trimmed to a single representative case in 72eec16.

Co-authored-by: baruchiro <17686879+baruchiro@users.noreply.github.com>
@baruchiro baruchiro enabled auto-merge (squash) February 24, 2026 07:15
@baruchiro baruchiro disabled auto-merge February 24, 2026 07:19
@baruchiro baruchiro merged commit 77d77e9 into main Feb 24, 2026
2 checks passed
@baruchiro baruchiro deleted the copilot/improve-paperless-ux branch February 24, 2026 07:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Improve Paperless UX for custom amount field – clarify currency format, prevent input errors

2 participants