From e1790951c6d2a6ecd425be4281d118ebfc6a58b0 Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Tue, 28 Jul 2026 04:44:42 +0530 Subject: [PATCH 01/27] Add the amount debited and amount reimbursed columns for withdrawal groups --- src/CONST/index.ts | 8 + src/languages/en.ts | 2 + src/libs/SearchParser/autocompleteParser.js | 424 ++++++++++++-------- src/libs/SearchParser/baseRules.peggy | 4 + src/libs/SearchParser/searchParser.js | 424 ++++++++++++-------- src/types/onyx/SearchResults.ts | 12 + tests/unit/Search/ColumnAvailabilityTest.ts | 2 + 7 files changed, 538 insertions(+), 338 deletions(-) diff --git a/src/CONST/index.ts b/src/CONST/index.ts index 44ec0632b64b..f8873cc6ce8a 100644 --- a/src/CONST/index.ts +++ b/src/CONST/index.ts @@ -7030,6 +7030,8 @@ const CONST = { BANK_ACCOUNT: this.TABLE_COLUMNS.GROUP_BANK_ACCOUNT, WITHDRAWAL_ID: this.TABLE_COLUMNS.GROUP_WITHDRAWAL_ID, EXPENSES: this.TABLE_COLUMNS.GROUP_EXPENSES, + AMOUNT_DEBITED: this.TABLE_COLUMNS.GROUP_AMOUNT_DEBITED, + AMOUNT_REIMBURSED: this.TABLE_COLUMNS.GROUP_AMOUNT_REIMBURSED, TOTAL: this.TABLE_COLUMNS.GROUP_TOTAL, }, CATEGORY: { @@ -7108,6 +7110,8 @@ const CONST = { this.TABLE_COLUMNS.GROUP_BANK_ACCOUNT, this.TABLE_COLUMNS.GROUP_WITHDRAWAL_ID, this.TABLE_COLUMNS.GROUP_EXPENSES, + this.TABLE_COLUMNS.GROUP_AMOUNT_DEBITED, + this.TABLE_COLUMNS.GROUP_AMOUNT_REIMBURSED, this.TABLE_COLUMNS.GROUP_TOTAL, ], CATEGORY: [this.TABLE_COLUMNS.GROUP_CATEGORY, this.TABLE_COLUMNS.GROUP_EXPENSES, this.TABLE_COLUMNS.GROUP_TOTAL], @@ -7228,6 +7232,8 @@ const CONST = { GROUP_YEAR: 'groupyear', GROUP_QUARTER: 'groupquarter', GROUP_WITHDRAWAL_STATUS: 'groupWithdrawalStatus', + GROUP_AMOUNT_DEBITED: 'groupAmountDebited', + GROUP_AMOUNT_REIMBURSED: 'groupAmountReimbursed', }, SYNTAX_OPERATORS: { AND: 'and', @@ -7448,6 +7454,8 @@ const CONST = { [this.TABLE_COLUMNS.GROUP_YEAR]: 'group-year', [this.TABLE_COLUMNS.GROUP_QUARTER]: 'group-quarter', [this.TABLE_COLUMNS.GROUP_WITHDRAWAL_STATUS]: 'group-withdrawal-status', + [this.TABLE_COLUMNS.GROUP_AMOUNT_DEBITED]: 'group-amount-debited', + [this.TABLE_COLUMNS.GROUP_AMOUNT_REIMBURSED]: 'group-amount-reimbursed', }; }, NOT_MODIFIER: 'Not', diff --git a/src/languages/en.ts b/src/languages/en.ts index c395ce79f068..f2d175a57b89 100644 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -430,6 +430,8 @@ const translations = { longReportID: 'Long Report ID', withdrawalID: 'Withdrawal ID', internationalReimbursementIDs: 'International reimbursement IDs', + amountDebited: 'Amount debited', + amountReimbursed: 'Amount reimbursed', withdrawalStatus: 'Withdrawal status', paidStatus: 'Paid status', bankAccounts: 'Bank accounts', diff --git a/src/libs/SearchParser/autocompleteParser.js b/src/libs/SearchParser/autocompleteParser.js index 3f3d7a740d60..dc445c90f584 100644 --- a/src/libs/SearchParser/autocompleteParser.js +++ b/src/libs/SearchParser/autocompleteParser.js @@ -276,21 +276,23 @@ function peg$parse(input, options) { var peg$c90 = "group-bank-account"; var peg$c91 = "group-withdrawn"; var peg$c92 = "group-withdrawal-id"; - var peg$c93 = "group-category"; - var peg$c94 = "group-tag"; - var peg$c95 = "group-merchant"; - var peg$c96 = "group-month"; - var peg$c97 = "group-week"; - var peg$c98 = "group-year"; - var peg$c99 = "group-quarter"; - var peg$c100 = "!="; - var peg$c101 = ">="; - var peg$c102 = ">"; - var peg$c103 = "<="; - var peg$c104 = "<"; - var peg$c105 = "\u201C"; - var peg$c106 = "\u201D"; - var peg$c107 = "\""; + var peg$c93 = "group-amount-debited"; + var peg$c94 = "group-amount-reimbursed"; + var peg$c95 = "group-category"; + var peg$c96 = "group-tag"; + var peg$c97 = "group-merchant"; + var peg$c98 = "group-month"; + var peg$c99 = "group-week"; + var peg$c100 = "group-year"; + var peg$c101 = "group-quarter"; + var peg$c102 = "!="; + var peg$c103 = ">="; + var peg$c104 = ">"; + var peg$c105 = "<="; + var peg$c106 = "<"; + var peg$c107 = "\u201C"; + var peg$c108 = "\u201D"; + var peg$c109 = "\""; var peg$r0 = /^[ \t\r\n\xA0,:=<>!]/; var peg$r1 = /^[:=]/; @@ -403,36 +405,38 @@ function peg$parse(input, options) { var peg$e94 = peg$literalExpectation("group-bank-account", true); var peg$e95 = peg$literalExpectation("group-withdrawn", true); var peg$e96 = peg$literalExpectation("group-withdrawal-id", true); - var peg$e97 = peg$literalExpectation("group-category", true); - var peg$e98 = peg$literalExpectation("group-tag", true); - var peg$e99 = peg$literalExpectation("group-merchant", true); - var peg$e100 = peg$literalExpectation("group-month", true); - var peg$e101 = peg$literalExpectation("group-week", true); - var peg$e102 = peg$literalExpectation("group-year", true); - var peg$e103 = peg$literalExpectation("group-quarter", true); - var peg$e104 = peg$otherExpectation("operator"); - var peg$e105 = peg$literalExpectation("!=", false); - var peg$e106 = peg$literalExpectation(">=", false); - var peg$e107 = peg$literalExpectation(">", false); - var peg$e108 = peg$literalExpectation("<=", false); - var peg$e109 = peg$literalExpectation("<", false); - var peg$e110 = peg$otherExpectation("word"); - var peg$e111 = peg$classExpectation([" ", ",", "\t", "\n", "\r", "\xA0"], true, false); - var peg$e112 = peg$otherExpectation("whitespace"); - var peg$e113 = peg$classExpectation([" ", "\t", "\r", "\n", "\xA0"], false, false); - var peg$e114 = peg$otherExpectation("quote"); - var peg$e115 = peg$classExpectation([" ", ",", "\"", "\u201D", "\u201C", "\t", "\n", "\r", "\xA0"], true, false); - var peg$e116 = peg$classExpectation(["\"", ["\u201C", "\u201D"]], false, false); - var peg$e117 = peg$classExpectation(["\"", "\u201D", "\u201C", "\r", "\n"], true, false); - var peg$e118 = peg$literalExpectation("\u201C", false); - var peg$e119 = peg$literalExpectation("\u201D", false); - var peg$e120 = peg$literalExpectation("\"", false); - var peg$e121 = peg$classExpectation([" ", "\t", "\n", "\r", "\xA0", ["a", "z"], ["A", "Z"], ["0", "9"]], false, false); - var peg$e122 = peg$classExpectation([["a", "z"], ["A", "Z"], ["0", "9"]], false, false); - var peg$e123 = peg$classExpectation([" ", "\t", "\n", "\r", "\xA0"], false, false); - var peg$e124 = peg$classExpectation([" ", "\t", "\n", "\r", "\xA0", ["a", "z"], ["A", "Z"]], false, false); - var peg$e125 = peg$classExpectation([","], false, false); - var peg$e126 = peg$classExpectation([" ", "\t", "\n", "\r", "\xA0", ","], false, false); + var peg$e97 = peg$literalExpectation("group-amount-debited", true); + var peg$e98 = peg$literalExpectation("group-amount-reimbursed", true); + var peg$e99 = peg$literalExpectation("group-category", true); + var peg$e100 = peg$literalExpectation("group-tag", true); + var peg$e101 = peg$literalExpectation("group-merchant", true); + var peg$e102 = peg$literalExpectation("group-month", true); + var peg$e103 = peg$literalExpectation("group-week", true); + var peg$e104 = peg$literalExpectation("group-year", true); + var peg$e105 = peg$literalExpectation("group-quarter", true); + var peg$e106 = peg$otherExpectation("operator"); + var peg$e107 = peg$literalExpectation("!=", false); + var peg$e108 = peg$literalExpectation(">=", false); + var peg$e109 = peg$literalExpectation(">", false); + var peg$e110 = peg$literalExpectation("<=", false); + var peg$e111 = peg$literalExpectation("<", false); + var peg$e112 = peg$otherExpectation("word"); + var peg$e113 = peg$classExpectation([" ", ",", "\t", "\n", "\r", "\xA0"], true, false); + var peg$e114 = peg$otherExpectation("whitespace"); + var peg$e115 = peg$classExpectation([" ", "\t", "\r", "\n", "\xA0"], false, false); + var peg$e116 = peg$otherExpectation("quote"); + var peg$e117 = peg$classExpectation([" ", ",", "\"", "\u201D", "\u201C", "\t", "\n", "\r", "\xA0"], true, false); + var peg$e118 = peg$classExpectation(["\"", ["\u201C", "\u201D"]], false, false); + var peg$e119 = peg$classExpectation(["\"", "\u201D", "\u201C", "\r", "\n"], true, false); + var peg$e120 = peg$literalExpectation("\u201C", false); + var peg$e121 = peg$literalExpectation("\u201D", false); + var peg$e122 = peg$literalExpectation("\"", false); + var peg$e123 = peg$classExpectation([" ", "\t", "\n", "\r", "\xA0", ["a", "z"], ["A", "Z"], ["0", "9"]], false, false); + var peg$e124 = peg$classExpectation([["a", "z"], ["A", "Z"], ["0", "9"]], false, false); + var peg$e125 = peg$classExpectation([" ", "\t", "\n", "\r", "\xA0"], false, false); + var peg$e126 = peg$classExpectation([" ", "\t", "\n", "\r", "\xA0", ["a", "z"], ["A", "Z"]], false, false); + var peg$e127 = peg$classExpectation([","], false, false); + var peg$e128 = peg$classExpectation([" ", "\t", "\n", "\r", "\xA0", ","], false, false); var peg$f0 = function(ranges) { return { autocomplete, ranges }; }; var peg$f1 = function(filters) { return filters.filter(Boolean).flat(); }; @@ -576,39 +580,41 @@ function peg$parse(input, options) { var peg$f81 = function() { return "groupBankAccount"; }; var peg$f82 = function() { return "groupWithdrawn"; }; var peg$f83 = function() { return "groupWithdrawalID"; }; - var peg$f84 = function() { return "groupCategory"; }; - var peg$f85 = function() { return "groupTag"; }; - var peg$f86 = function() { return "groupMerchant"; }; - var peg$f87 = function() { return "groupmonth"; }; - var peg$f88 = function() { return "groupweek"; }; - var peg$f89 = function() { return "groupyear"; }; - var peg$f90 = function() { return "groupquarter"; }; - var peg$f91 = function() { return "eq"; }; - var peg$f92 = function() { return "neq"; }; - var peg$f93 = function() { return "gte"; }; - var peg$f94 = function() { return "gt"; }; - var peg$f95 = function() { return "lte"; }; - var peg$f96 = function() { return "lt"; }; - var peg$f97 = function(o) { + var peg$f84 = function() { return "groupAmountDebited"; }; + var peg$f85 = function() { return "groupAmountReimbursed"; }; + var peg$f86 = function() { return "groupCategory"; }; + var peg$f87 = function() { return "groupTag"; }; + var peg$f88 = function() { return "groupMerchant"; }; + var peg$f89 = function() { return "groupmonth"; }; + var peg$f90 = function() { return "groupweek"; }; + var peg$f91 = function() { return "groupyear"; }; + var peg$f92 = function() { return "groupquarter"; }; + var peg$f93 = function() { return "eq"; }; + var peg$f94 = function() { return "neq"; }; + var peg$f95 = function() { return "gte"; }; + var peg$f96 = function() { return "gt"; }; + var peg$f97 = function() { return "lte"; }; + var peg$f98 = function() { return "lt"; }; + var peg$f99 = function(o) { if (nameOperator) { expectingNestedQuote = (o === "eq"); // Use simple parser if no valid operator is found } isColumnsContext = false; return o; }; - var peg$f98 = function(chars) { return chars.join("").trim(); }; - var peg$f99 = function() { + var peg$f100 = function(chars) { return chars.join("").trim(); }; + var peg$f101 = function() { isColumnsContext = false; return "and"; }; - var peg$f100 = function() { return expectingNestedQuote; }; - var peg$f101 = function(start, inner, end) { //handle no-breaking space + var peg$f102 = function() { return expectingNestedQuote; }; + var peg$f103 = function(start, inner, end) { //handle no-breaking space return [...start, '"', ...inner, '"', ...end].join(""); }; - var peg$f102 = function(start) {return "“"}; - var peg$f103 = function(start) {return "”"}; - var peg$f104 = function(start) {return "\""}; - var peg$f105 = function(start, inner, end) { + var peg$f104 = function(start) {return "“"}; + var peg$f105 = function(start) {return "”"}; + var peg$f106 = function(start) {return "\""}; + var peg$f107 = function(start, inner, end) { return [...start, '"', ...inner, '"'].join(""); }; var peg$currPos = options.peg$currPos | 0; @@ -2542,19 +2548,25 @@ function peg$parse(input, options) { if (s0 === peg$FAILED) { s0 = peg$parsegroupWithdrawalId(); if (s0 === peg$FAILED) { - s0 = peg$parsegroupCategory(); + s0 = peg$parsegroupAmountDebited(); if (s0 === peg$FAILED) { - s0 = peg$parsegroupTag(); + s0 = peg$parsegroupAmountReimbursed(); if (s0 === peg$FAILED) { - s0 = peg$parsegroupMerchant(); + s0 = peg$parsegroupCategory(); if (s0 === peg$FAILED) { - s0 = peg$parsegroupMonth(); + s0 = peg$parsegroupTag(); if (s0 === peg$FAILED) { - s0 = peg$parsegroupWeek(); + s0 = peg$parsegroupMerchant(); if (s0 === peg$FAILED) { - s0 = peg$parsegroupYear(); + s0 = peg$parsegroupMonth(); if (s0 === peg$FAILED) { - s0 = peg$parsegroupQuarter(); + s0 = peg$parsegroupWeek(); + if (s0 === peg$FAILED) { + s0 = peg$parsegroupYear(); + if (s0 === peg$FAILED) { + s0 = peg$parsegroupQuarter(); + } + } } } } @@ -3503,13 +3515,13 @@ function peg$parse(input, options) { return s0; } - function peg$parsegroupCategory() { + function peg$parsegroupAmountDebited() { var s0, s1, s2, s3; s0 = peg$currPos; - s1 = input.substr(peg$currPos, 14); + s1 = input.substr(peg$currPos, 20); if (s1.toLowerCase() === peg$c93) { - peg$currPos += 14; + peg$currPos += 20; } else { s1 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$e97); } @@ -3540,13 +3552,13 @@ function peg$parse(input, options) { return s0; } - function peg$parsegroupTag() { + function peg$parsegroupAmountReimbursed() { var s0, s1, s2, s3; s0 = peg$currPos; - s1 = input.substr(peg$currPos, 9); + s1 = input.substr(peg$currPos, 23); if (s1.toLowerCase() === peg$c94) { - peg$currPos += 9; + peg$currPos += 23; } else { s1 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$e98); } @@ -3577,7 +3589,7 @@ function peg$parse(input, options) { return s0; } - function peg$parsegroupMerchant() { + function peg$parsegroupCategory() { var s0, s1, s2, s3; s0 = peg$currPos; @@ -3614,13 +3626,13 @@ function peg$parse(input, options) { return s0; } - function peg$parsegroupMonth() { + function peg$parsegroupTag() { var s0, s1, s2, s3; s0 = peg$currPos; - s1 = input.substr(peg$currPos, 11); + s1 = input.substr(peg$currPos, 9); if (s1.toLowerCase() === peg$c96) { - peg$currPos += 11; + peg$currPos += 9; } else { s1 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$e100); } @@ -3651,13 +3663,13 @@ function peg$parse(input, options) { return s0; } - function peg$parsegroupWeek() { + function peg$parsegroupMerchant() { var s0, s1, s2, s3; s0 = peg$currPos; - s1 = input.substr(peg$currPos, 10); + s1 = input.substr(peg$currPos, 14); if (s1.toLowerCase() === peg$c97) { - peg$currPos += 10; + peg$currPos += 14; } else { s1 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$e101); } @@ -3688,13 +3700,13 @@ function peg$parse(input, options) { return s0; } - function peg$parsegroupYear() { + function peg$parsegroupMonth() { var s0, s1, s2, s3; s0 = peg$currPos; - s1 = input.substr(peg$currPos, 10); + s1 = input.substr(peg$currPos, 11); if (s1.toLowerCase() === peg$c98) { - peg$currPos += 10; + peg$currPos += 11; } else { s1 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$e102); } @@ -3725,13 +3737,13 @@ function peg$parse(input, options) { return s0; } - function peg$parsegroupQuarter() { + function peg$parsegroupWeek() { var s0, s1, s2, s3; s0 = peg$currPos; - s1 = input.substr(peg$currPos, 13); + s1 = input.substr(peg$currPos, 10); if (s1.toLowerCase() === peg$c99) { - peg$currPos += 13; + peg$currPos += 10; } else { s1 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$e103); } @@ -3762,6 +3774,80 @@ function peg$parse(input, options) { return s0; } + function peg$parsegroupYear() { + var s0, s1, s2, s3; + + s0 = peg$currPos; + s1 = input.substr(peg$currPos, 10); + if (s1.toLowerCase() === peg$c100) { + peg$currPos += 10; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e104); } + } + if (s1 !== peg$FAILED) { + s2 = peg$currPos; + peg$silentFails++; + s3 = peg$parsewordBoundary(); + peg$silentFails--; + if (s3 !== peg$FAILED) { + peg$currPos = s2; + s2 = undefined; + } else { + s2 = peg$FAILED; + } + if (s2 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f91(); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parsegroupQuarter() { + var s0, s1, s2, s3; + + s0 = peg$currPos; + s1 = input.substr(peg$currPos, 13); + if (s1.toLowerCase() === peg$c101) { + peg$currPos += 13; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e105); } + } + if (s1 !== peg$FAILED) { + s2 = peg$currPos; + peg$silentFails++; + s3 = peg$parsewordBoundary(); + peg$silentFails--; + if (s3 !== peg$FAILED) { + peg$currPos = s2; + s2 = undefined; + } else { + s2 = peg$FAILED; + } + if (s2 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f92(); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + function peg$parseoperator() { var s0, s1; @@ -3776,77 +3862,77 @@ function peg$parse(input, options) { } if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$f91(); + s1 = peg$f93(); } s0 = s1; if (s0 === peg$FAILED) { s0 = peg$currPos; - if (input.substr(peg$currPos, 2) === peg$c100) { - s1 = peg$c100; + if (input.substr(peg$currPos, 2) === peg$c102) { + s1 = peg$c102; peg$currPos += 2; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e105); } + if (peg$silentFails === 0) { peg$fail(peg$e107); } } if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$f92(); + s1 = peg$f94(); } s0 = s1; if (s0 === peg$FAILED) { s0 = peg$currPos; - if (input.substr(peg$currPos, 2) === peg$c101) { - s1 = peg$c101; + if (input.substr(peg$currPos, 2) === peg$c103) { + s1 = peg$c103; peg$currPos += 2; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e106); } + if (peg$silentFails === 0) { peg$fail(peg$e108); } } if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$f93(); + s1 = peg$f95(); } s0 = s1; if (s0 === peg$FAILED) { s0 = peg$currPos; if (input.charCodeAt(peg$currPos) === 62) { - s1 = peg$c102; + s1 = peg$c104; peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e107); } + if (peg$silentFails === 0) { peg$fail(peg$e109); } } if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$f94(); + s1 = peg$f96(); } s0 = s1; if (s0 === peg$FAILED) { s0 = peg$currPos; - if (input.substr(peg$currPos, 2) === peg$c103) { - s1 = peg$c103; + if (input.substr(peg$currPos, 2) === peg$c105) { + s1 = peg$c105; peg$currPos += 2; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e108); } + if (peg$silentFails === 0) { peg$fail(peg$e110); } } if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$f95(); + s1 = peg$f97(); } s0 = s1; if (s0 === peg$FAILED) { s0 = peg$currPos; if (input.charCodeAt(peg$currPos) === 60) { - s1 = peg$c104; + s1 = peg$c106; peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e109); } + if (peg$silentFails === 0) { peg$fail(peg$e111); } } if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$f96(); + s1 = peg$f98(); } s0 = s1; } @@ -3857,7 +3943,7 @@ function peg$parse(input, options) { peg$silentFails--; if (s0 === peg$FAILED) { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e104); } + if (peg$silentFails === 0) { peg$fail(peg$e106); } } return s0; @@ -3870,7 +3956,7 @@ function peg$parse(input, options) { s1 = peg$parseoperator(); if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$f97(s1); + s1 = peg$f99(s1); } s0 = s1; @@ -3888,7 +3974,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e111); } + if (peg$silentFails === 0) { peg$fail(peg$e113); } } if (s2 !== peg$FAILED) { while (s2 !== peg$FAILED) { @@ -3898,7 +3984,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e111); } + if (peg$silentFails === 0) { peg$fail(peg$e113); } } } } else { @@ -3906,13 +3992,13 @@ function peg$parse(input, options) { } if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$f98(s1); + s1 = peg$f100(s1); } s0 = s1; peg$silentFails--; if (s0 === peg$FAILED) { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e110); } + if (peg$silentFails === 0) { peg$fail(peg$e112); } } return s0; @@ -3924,7 +4010,7 @@ function peg$parse(input, options) { s0 = peg$currPos; s1 = peg$parse_(); peg$savedPos = s0; - s1 = peg$f99(); + s1 = peg$f101(); s0 = s1; return s0; @@ -3940,7 +4026,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e113); } + if (peg$silentFails === 0) { peg$fail(peg$e115); } } while (s1 !== peg$FAILED) { s0.push(s1); @@ -3949,12 +4035,12 @@ function peg$parse(input, options) { peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e113); } + if (peg$silentFails === 0) { peg$fail(peg$e115); } } } peg$silentFails--; s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e112); } + if (peg$silentFails === 0) { peg$fail(peg$e114); } return s0; } @@ -3964,7 +4050,7 @@ function peg$parse(input, options) { s0 = peg$currPos; peg$savedPos = peg$currPos; - s1 = peg$f100(); + s1 = peg$f102(); if (s1) { s1 = undefined; } else { @@ -4001,7 +4087,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e115); } + if (peg$silentFails === 0) { peg$fail(peg$e117); } } while (s2 !== peg$FAILED) { s1.push(s2); @@ -4010,7 +4096,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e115); } + if (peg$silentFails === 0) { peg$fail(peg$e117); } } } s2 = input.charAt(peg$currPos); @@ -4018,7 +4104,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e116); } + if (peg$silentFails === 0) { peg$fail(peg$e118); } } if (s2 !== peg$FAILED) { s3 = []; @@ -4027,7 +4113,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s4 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e117); } + if (peg$silentFails === 0) { peg$fail(peg$e119); } } while (s4 !== peg$FAILED) { s3.push(s4); @@ -4036,7 +4122,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s4 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e117); } + if (peg$silentFails === 0) { peg$fail(peg$e119); } } } s4 = input.charAt(peg$currPos); @@ -4044,7 +4130,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s4 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e116); } + if (peg$silentFails === 0) { peg$fail(peg$e118); } } if (s4 !== peg$FAILED) { s5 = []; @@ -4053,7 +4139,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s6 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e111); } + if (peg$silentFails === 0) { peg$fail(peg$e113); } } while (s6 !== peg$FAILED) { s5.push(s6); @@ -4062,11 +4148,11 @@ function peg$parse(input, options) { peg$currPos++; } else { s6 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e111); } + if (peg$silentFails === 0) { peg$fail(peg$e113); } } } peg$savedPos = s0; - s0 = peg$f101(s1, s3, s5); + s0 = peg$f103(s1, s3, s5); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -4078,7 +4164,7 @@ function peg$parse(input, options) { peg$silentFails--; if (s0 === peg$FAILED) { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e114); } + if (peg$silentFails === 0) { peg$fail(peg$e116); } } return s0; @@ -4095,7 +4181,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e115); } + if (peg$silentFails === 0) { peg$fail(peg$e117); } } while (s2 !== peg$FAILED) { s1.push(s2); @@ -4104,7 +4190,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e115); } + if (peg$silentFails === 0) { peg$fail(peg$e117); } } } s2 = input.charAt(peg$currPos); @@ -4112,7 +4198,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e116); } + if (peg$silentFails === 0) { peg$fail(peg$e118); } } if (s2 !== peg$FAILED) { s3 = []; @@ -4121,7 +4207,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s4 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e117); } + if (peg$silentFails === 0) { peg$fail(peg$e119); } } if (s4 === peg$FAILED) { s4 = peg$currPos; @@ -4137,15 +4223,15 @@ function peg$parse(input, options) { } if (s5 !== peg$FAILED) { if (input.charCodeAt(peg$currPos) === 8220) { - s6 = peg$c105; + s6 = peg$c107; peg$currPos++; } else { s6 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e118); } + if (peg$silentFails === 0) { peg$fail(peg$e120); } } if (s6 !== peg$FAILED) { peg$savedPos = s4; - s4 = peg$f102(s1); + s4 = peg$f104(s1); } else { peg$currPos = s4; s4 = peg$FAILED; @@ -4168,15 +4254,15 @@ function peg$parse(input, options) { } if (s5 !== peg$FAILED) { if (input.charCodeAt(peg$currPos) === 8221) { - s6 = peg$c106; + s6 = peg$c108; peg$currPos++; } else { s6 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e119); } + if (peg$silentFails === 0) { peg$fail(peg$e121); } } if (s6 !== peg$FAILED) { peg$savedPos = s4; - s4 = peg$f103(s1); + s4 = peg$f105(s1); } else { peg$currPos = s4; s4 = peg$FAILED; @@ -4199,15 +4285,15 @@ function peg$parse(input, options) { } if (s5 !== peg$FAILED) { if (input.charCodeAt(peg$currPos) === 34) { - s6 = peg$c107; + s6 = peg$c109; peg$currPos++; } else { s6 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e120); } + if (peg$silentFails === 0) { peg$fail(peg$e122); } } if (s6 !== peg$FAILED) { peg$savedPos = s4; - s4 = peg$f104(s1); + s4 = peg$f106(s1); } else { peg$currPos = s4; s4 = peg$FAILED; @@ -4226,7 +4312,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s4 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e117); } + if (peg$silentFails === 0) { peg$fail(peg$e119); } } if (s4 === peg$FAILED) { s4 = peg$currPos; @@ -4242,15 +4328,15 @@ function peg$parse(input, options) { } if (s5 !== peg$FAILED) { if (input.charCodeAt(peg$currPos) === 8220) { - s6 = peg$c105; + s6 = peg$c107; peg$currPos++; } else { s6 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e118); } + if (peg$silentFails === 0) { peg$fail(peg$e120); } } if (s6 !== peg$FAILED) { peg$savedPos = s4; - s4 = peg$f102(s1); + s4 = peg$f104(s1); } else { peg$currPos = s4; s4 = peg$FAILED; @@ -4273,15 +4359,15 @@ function peg$parse(input, options) { } if (s5 !== peg$FAILED) { if (input.charCodeAt(peg$currPos) === 8221) { - s6 = peg$c106; + s6 = peg$c108; peg$currPos++; } else { s6 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e119); } + if (peg$silentFails === 0) { peg$fail(peg$e121); } } if (s6 !== peg$FAILED) { peg$savedPos = s4; - s4 = peg$f103(s1); + s4 = peg$f105(s1); } else { peg$currPos = s4; s4 = peg$FAILED; @@ -4304,15 +4390,15 @@ function peg$parse(input, options) { } if (s5 !== peg$FAILED) { if (input.charCodeAt(peg$currPos) === 34) { - s6 = peg$c107; + s6 = peg$c109; peg$currPos++; } else { s6 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e120); } + if (peg$silentFails === 0) { peg$fail(peg$e122); } } if (s6 !== peg$FAILED) { peg$savedPos = s4; - s4 = peg$f104(s1); + s4 = peg$f106(s1); } else { peg$currPos = s4; s4 = peg$FAILED; @@ -4328,7 +4414,7 @@ function peg$parse(input, options) { s4 = peg$parseclosingQuote(); if (s4 !== peg$FAILED) { peg$savedPos = s0; - s0 = peg$f105(s1, s3, s4); + s0 = peg$f107(s1, s3, s4); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -4340,7 +4426,7 @@ function peg$parse(input, options) { peg$silentFails--; if (s0 === peg$FAILED) { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e114); } + if (peg$silentFails === 0) { peg$fail(peg$e116); } } return s0; @@ -4355,7 +4441,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e116); } + if (peg$silentFails === 0) { peg$fail(peg$e118); } } if (s1 !== peg$FAILED) { s2 = peg$currPos; @@ -4393,7 +4479,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e121); } + if (peg$silentFails === 0) { peg$fail(peg$e123); } } while (s2 !== peg$FAILED) { s1.push(s2); @@ -4402,7 +4488,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e121); } + if (peg$silentFails === 0) { peg$fail(peg$e123); } } } s2 = []; @@ -4411,7 +4497,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s3 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e122); } + if (peg$silentFails === 0) { peg$fail(peg$e124); } } while (s3 !== peg$FAILED) { s2.push(s3); @@ -4420,7 +4506,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s3 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e122); } + if (peg$silentFails === 0) { peg$fail(peg$e124); } } } s3 = []; @@ -4429,7 +4515,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s4 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e123); } + if (peg$silentFails === 0) { peg$fail(peg$e125); } } while (s4 !== peg$FAILED) { s3.push(s4); @@ -4438,7 +4524,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s4 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e123); } + if (peg$silentFails === 0) { peg$fail(peg$e125); } } } s4 = peg$parseoperator(); @@ -4457,7 +4543,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e124); } + if (peg$silentFails === 0) { peg$fail(peg$e126); } } while (s2 !== peg$FAILED) { s1.push(s2); @@ -4466,7 +4552,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e124); } + if (peg$silentFails === 0) { peg$fail(peg$e126); } } } s2 = peg$currPos; @@ -4513,7 +4599,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s0 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e125); } + if (peg$silentFails === 0) { peg$fail(peg$e127); } } } } @@ -4529,7 +4615,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s0 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e126); } + if (peg$silentFails === 0) { peg$fail(peg$e128); } } if (s0 === peg$FAILED) { s0 = peg$currPos; diff --git a/src/libs/SearchParser/baseRules.peggy b/src/libs/SearchParser/baseRules.peggy index a6afcc350bfa..501e62d9352c 100644 --- a/src/libs/SearchParser/baseRules.peggy +++ b/src/libs/SearchParser/baseRules.peggy @@ -125,6 +125,8 @@ columnsValues / groupBankAccount / groupWithdrawn / groupWithdrawalId + / groupAmountDebited + / groupAmountReimbursed / groupCategory / groupTag / groupMerchant @@ -159,6 +161,8 @@ groupFeed = "group-feed"i &wordBoundary { return "gr groupBankAccount = "group-bank-account"i &wordBoundary { return "groupBankAccount"; } groupWithdrawn = "group-withdrawn"i &wordBoundary { return "groupWithdrawn"; } groupWithdrawalId = "group-withdrawal-id"i &wordBoundary { return "groupWithdrawalID"; } +groupAmountDebited = "group-amount-debited"i &wordBoundary { return "groupAmountDebited"; } +groupAmountReimbursed = "group-amount-reimbursed"i &wordBoundary { return "groupAmountReimbursed"; } groupCategory = "group-category"i &wordBoundary { return "groupCategory"; } groupTag = "group-tag"i &wordBoundary { return "groupTag"; } groupMerchant = "group-merchant"i &wordBoundary { return "groupMerchant"; } diff --git a/src/libs/SearchParser/searchParser.js b/src/libs/SearchParser/searchParser.js index 23ce77c8a6ee..9be7e4142a22 100644 --- a/src/libs/SearchParser/searchParser.js +++ b/src/libs/SearchParser/searchParser.js @@ -277,21 +277,23 @@ function peg$parse(input, options) { var peg$c90 = "group-bank-account"; var peg$c91 = "group-withdrawn"; var peg$c92 = "group-withdrawal-id"; - var peg$c93 = "group-category"; - var peg$c94 = "group-tag"; - var peg$c95 = "group-merchant"; - var peg$c96 = "group-month"; - var peg$c97 = "group-week"; - var peg$c98 = "group-year"; - var peg$c99 = "group-quarter"; - var peg$c100 = "!="; - var peg$c101 = ">="; - var peg$c102 = ">"; - var peg$c103 = "<="; - var peg$c104 = "<"; - var peg$c105 = "\u201C"; - var peg$c106 = "\u201D"; - var peg$c107 = "\""; + var peg$c93 = "group-amount-debited"; + var peg$c94 = "group-amount-reimbursed"; + var peg$c95 = "group-category"; + var peg$c96 = "group-tag"; + var peg$c97 = "group-merchant"; + var peg$c98 = "group-month"; + var peg$c99 = "group-week"; + var peg$c100 = "group-year"; + var peg$c101 = "group-quarter"; + var peg$c102 = "!="; + var peg$c103 = ">="; + var peg$c104 = ">"; + var peg$c105 = "<="; + var peg$c106 = "<"; + var peg$c107 = "\u201C"; + var peg$c108 = "\u201D"; + var peg$c109 = "\""; var peg$r0 = /^[^ \t\r\n\xA0]/; var peg$r1 = /^[ \t\r\n\xA0,:=<>!]/; @@ -407,36 +409,38 @@ function peg$parse(input, options) { var peg$e96 = peg$literalExpectation("group-bank-account", true); var peg$e97 = peg$literalExpectation("group-withdrawn", true); var peg$e98 = peg$literalExpectation("group-withdrawal-id", true); - var peg$e99 = peg$literalExpectation("group-category", true); - var peg$e100 = peg$literalExpectation("group-tag", true); - var peg$e101 = peg$literalExpectation("group-merchant", true); - var peg$e102 = peg$literalExpectation("group-month", true); - var peg$e103 = peg$literalExpectation("group-week", true); - var peg$e104 = peg$literalExpectation("group-year", true); - var peg$e105 = peg$literalExpectation("group-quarter", true); - var peg$e106 = peg$otherExpectation("operator"); - var peg$e107 = peg$literalExpectation("!=", false); - var peg$e108 = peg$literalExpectation(">=", false); - var peg$e109 = peg$literalExpectation(">", false); - var peg$e110 = peg$literalExpectation("<=", false); - var peg$e111 = peg$literalExpectation("<", false); - var peg$e112 = peg$otherExpectation("word"); - var peg$e113 = peg$classExpectation([" ", ",", "\t", "\n", "\r", "\xA0"], true, false); - var peg$e114 = peg$otherExpectation("whitespace"); - var peg$e115 = peg$classExpectation([" ", "\t", "\r", "\n", "\xA0"], false, false); - var peg$e116 = peg$otherExpectation("quote"); - var peg$e117 = peg$classExpectation([" ", ",", "\"", "\u201D", "\u201C", "\t", "\n", "\r", "\xA0"], true, false); - var peg$e118 = peg$classExpectation(["\"", ["\u201C", "\u201D"]], false, false); - var peg$e119 = peg$classExpectation(["\"", "\u201D", "\u201C", "\r", "\n"], true, false); - var peg$e120 = peg$literalExpectation("\u201C", false); - var peg$e121 = peg$literalExpectation("\u201D", false); - var peg$e122 = peg$literalExpectation("\"", false); - var peg$e123 = peg$classExpectation([" ", "\t", "\n", "\r", "\xA0", ["a", "z"], ["A", "Z"], ["0", "9"]], false, false); - var peg$e124 = peg$classExpectation([["a", "z"], ["A", "Z"], ["0", "9"]], false, false); - var peg$e125 = peg$classExpectation([" ", "\t", "\n", "\r", "\xA0"], false, false); - var peg$e126 = peg$classExpectation([" ", "\t", "\n", "\r", "\xA0", ["a", "z"], ["A", "Z"]], false, false); - var peg$e127 = peg$classExpectation([","], false, false); - var peg$e128 = peg$classExpectation([" ", "\t", "\n", "\r", "\xA0", ","], false, false); + var peg$e99 = peg$literalExpectation("group-amount-debited", true); + var peg$e100 = peg$literalExpectation("group-amount-reimbursed", true); + var peg$e101 = peg$literalExpectation("group-category", true); + var peg$e102 = peg$literalExpectation("group-tag", true); + var peg$e103 = peg$literalExpectation("group-merchant", true); + var peg$e104 = peg$literalExpectation("group-month", true); + var peg$e105 = peg$literalExpectation("group-week", true); + var peg$e106 = peg$literalExpectation("group-year", true); + var peg$e107 = peg$literalExpectation("group-quarter", true); + var peg$e108 = peg$otherExpectation("operator"); + var peg$e109 = peg$literalExpectation("!=", false); + var peg$e110 = peg$literalExpectation(">=", false); + var peg$e111 = peg$literalExpectation(">", false); + var peg$e112 = peg$literalExpectation("<=", false); + var peg$e113 = peg$literalExpectation("<", false); + var peg$e114 = peg$otherExpectation("word"); + var peg$e115 = peg$classExpectation([" ", ",", "\t", "\n", "\r", "\xA0"], true, false); + var peg$e116 = peg$otherExpectation("whitespace"); + var peg$e117 = peg$classExpectation([" ", "\t", "\r", "\n", "\xA0"], false, false); + var peg$e118 = peg$otherExpectation("quote"); + var peg$e119 = peg$classExpectation([" ", ",", "\"", "\u201D", "\u201C", "\t", "\n", "\r", "\xA0"], true, false); + var peg$e120 = peg$classExpectation(["\"", ["\u201C", "\u201D"]], false, false); + var peg$e121 = peg$classExpectation(["\"", "\u201D", "\u201C", "\r", "\n"], true, false); + var peg$e122 = peg$literalExpectation("\u201C", false); + var peg$e123 = peg$literalExpectation("\u201D", false); + var peg$e124 = peg$literalExpectation("\"", false); + var peg$e125 = peg$classExpectation([" ", "\t", "\n", "\r", "\xA0", ["a", "z"], ["A", "Z"], ["0", "9"]], false, false); + var peg$e126 = peg$classExpectation([["a", "z"], ["A", "Z"], ["0", "9"]], false, false); + var peg$e127 = peg$classExpectation([" ", "\t", "\n", "\r", "\xA0"], false, false); + var peg$e128 = peg$classExpectation([" ", "\t", "\n", "\r", "\xA0", ["a", "z"], ["A", "Z"]], false, false); + var peg$e129 = peg$classExpectation([","], false, false); + var peg$e130 = peg$classExpectation([" ", "\t", "\n", "\r", "\xA0", ","], false, false); var peg$f0 = function(filters) { return applyDefaults(filters); }; var peg$f1 = function(head, tail) { @@ -603,39 +607,41 @@ function peg$parse(input, options) { var peg$f82 = function() { return "groupBankAccount"; }; var peg$f83 = function() { return "groupWithdrawn"; }; var peg$f84 = function() { return "groupWithdrawalID"; }; - var peg$f85 = function() { return "groupCategory"; }; - var peg$f86 = function() { return "groupTag"; }; - var peg$f87 = function() { return "groupMerchant"; }; - var peg$f88 = function() { return "groupmonth"; }; - var peg$f89 = function() { return "groupweek"; }; - var peg$f90 = function() { return "groupyear"; }; - var peg$f91 = function() { return "groupquarter"; }; - var peg$f92 = function() { return "eq"; }; - var peg$f93 = function() { return "neq"; }; - var peg$f94 = function() { return "gte"; }; - var peg$f95 = function() { return "gt"; }; - var peg$f96 = function() { return "lte"; }; - var peg$f97 = function() { return "lt"; }; - var peg$f98 = function(o) { + var peg$f85 = function() { return "groupAmountDebited"; }; + var peg$f86 = function() { return "groupAmountReimbursed"; }; + var peg$f87 = function() { return "groupCategory"; }; + var peg$f88 = function() { return "groupTag"; }; + var peg$f89 = function() { return "groupMerchant"; }; + var peg$f90 = function() { return "groupmonth"; }; + var peg$f91 = function() { return "groupweek"; }; + var peg$f92 = function() { return "groupyear"; }; + var peg$f93 = function() { return "groupquarter"; }; + var peg$f94 = function() { return "eq"; }; + var peg$f95 = function() { return "neq"; }; + var peg$f96 = function() { return "gte"; }; + var peg$f97 = function() { return "gt"; }; + var peg$f98 = function() { return "lte"; }; + var peg$f99 = function() { return "lt"; }; + var peg$f100 = function(o) { if (nameOperator) { expectingNestedQuote = (o === "eq"); // Use simple parser if no valid operator is found } isColumnsContext = false; return o; }; - var peg$f99 = function(chars) { return chars.join("").trim(); }; - var peg$f100 = function() { + var peg$f101 = function(chars) { return chars.join("").trim(); }; + var peg$f102 = function() { isColumnsContext = false; return "and"; }; - var peg$f101 = function() { return expectingNestedQuote; }; - var peg$f102 = function(start, inner, end) { //handle no-breaking space + var peg$f103 = function() { return expectingNestedQuote; }; + var peg$f104 = function(start, inner, end) { //handle no-breaking space return [...start, '"', ...inner, '"', ...end].join(""); }; - var peg$f103 = function(start) {return "“"}; - var peg$f104 = function(start) {return "”"}; - var peg$f105 = function(start) {return "\""}; - var peg$f106 = function(start, inner, end) { + var peg$f105 = function(start) {return "“"}; + var peg$f106 = function(start) {return "”"}; + var peg$f107 = function(start) {return "\""}; + var peg$f108 = function(start, inner, end) { return [...start, '"', ...inner, '"'].join(""); }; var peg$currPos = options.peg$currPos | 0; @@ -2726,19 +2732,25 @@ function peg$parse(input, options) { if (s0 === peg$FAILED) { s0 = peg$parsegroupWithdrawalId(); if (s0 === peg$FAILED) { - s0 = peg$parsegroupCategory(); + s0 = peg$parsegroupAmountDebited(); if (s0 === peg$FAILED) { - s0 = peg$parsegroupTag(); + s0 = peg$parsegroupAmountReimbursed(); if (s0 === peg$FAILED) { - s0 = peg$parsegroupMerchant(); + s0 = peg$parsegroupCategory(); if (s0 === peg$FAILED) { - s0 = peg$parsegroupMonth(); + s0 = peg$parsegroupTag(); if (s0 === peg$FAILED) { - s0 = peg$parsegroupWeek(); + s0 = peg$parsegroupMerchant(); if (s0 === peg$FAILED) { - s0 = peg$parsegroupYear(); + s0 = peg$parsegroupMonth(); if (s0 === peg$FAILED) { - s0 = peg$parsegroupQuarter(); + s0 = peg$parsegroupWeek(); + if (s0 === peg$FAILED) { + s0 = peg$parsegroupYear(); + if (s0 === peg$FAILED) { + s0 = peg$parsegroupQuarter(); + } + } } } } @@ -3687,13 +3699,13 @@ function peg$parse(input, options) { return s0; } - function peg$parsegroupCategory() { + function peg$parsegroupAmountDebited() { var s0, s1, s2, s3; s0 = peg$currPos; - s1 = input.substr(peg$currPos, 14); + s1 = input.substr(peg$currPos, 20); if (s1.toLowerCase() === peg$c93) { - peg$currPos += 14; + peg$currPos += 20; } else { s1 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$e99); } @@ -3724,13 +3736,13 @@ function peg$parse(input, options) { return s0; } - function peg$parsegroupTag() { + function peg$parsegroupAmountReimbursed() { var s0, s1, s2, s3; s0 = peg$currPos; - s1 = input.substr(peg$currPos, 9); + s1 = input.substr(peg$currPos, 23); if (s1.toLowerCase() === peg$c94) { - peg$currPos += 9; + peg$currPos += 23; } else { s1 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$e100); } @@ -3761,7 +3773,7 @@ function peg$parse(input, options) { return s0; } - function peg$parsegroupMerchant() { + function peg$parsegroupCategory() { var s0, s1, s2, s3; s0 = peg$currPos; @@ -3798,13 +3810,13 @@ function peg$parse(input, options) { return s0; } - function peg$parsegroupMonth() { + function peg$parsegroupTag() { var s0, s1, s2, s3; s0 = peg$currPos; - s1 = input.substr(peg$currPos, 11); + s1 = input.substr(peg$currPos, 9); if (s1.toLowerCase() === peg$c96) { - peg$currPos += 11; + peg$currPos += 9; } else { s1 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$e102); } @@ -3835,13 +3847,13 @@ function peg$parse(input, options) { return s0; } - function peg$parsegroupWeek() { + function peg$parsegroupMerchant() { var s0, s1, s2, s3; s0 = peg$currPos; - s1 = input.substr(peg$currPos, 10); + s1 = input.substr(peg$currPos, 14); if (s1.toLowerCase() === peg$c97) { - peg$currPos += 10; + peg$currPos += 14; } else { s1 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$e103); } @@ -3872,13 +3884,13 @@ function peg$parse(input, options) { return s0; } - function peg$parsegroupYear() { + function peg$parsegroupMonth() { var s0, s1, s2, s3; s0 = peg$currPos; - s1 = input.substr(peg$currPos, 10); + s1 = input.substr(peg$currPos, 11); if (s1.toLowerCase() === peg$c98) { - peg$currPos += 10; + peg$currPos += 11; } else { s1 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$e104); } @@ -3909,13 +3921,13 @@ function peg$parse(input, options) { return s0; } - function peg$parsegroupQuarter() { + function peg$parsegroupWeek() { var s0, s1, s2, s3; s0 = peg$currPos; - s1 = input.substr(peg$currPos, 13); + s1 = input.substr(peg$currPos, 10); if (s1.toLowerCase() === peg$c99) { - peg$currPos += 13; + peg$currPos += 10; } else { s1 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$e105); } @@ -3946,6 +3958,80 @@ function peg$parse(input, options) { return s0; } + function peg$parsegroupYear() { + var s0, s1, s2, s3; + + s0 = peg$currPos; + s1 = input.substr(peg$currPos, 10); + if (s1.toLowerCase() === peg$c100) { + peg$currPos += 10; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e106); } + } + if (s1 !== peg$FAILED) { + s2 = peg$currPos; + peg$silentFails++; + s3 = peg$parsewordBoundary(); + peg$silentFails--; + if (s3 !== peg$FAILED) { + peg$currPos = s2; + s2 = undefined; + } else { + s2 = peg$FAILED; + } + if (s2 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f92(); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parsegroupQuarter() { + var s0, s1, s2, s3; + + s0 = peg$currPos; + s1 = input.substr(peg$currPos, 13); + if (s1.toLowerCase() === peg$c101) { + peg$currPos += 13; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e107); } + } + if (s1 !== peg$FAILED) { + s2 = peg$currPos; + peg$silentFails++; + s3 = peg$parsewordBoundary(); + peg$silentFails--; + if (s3 !== peg$FAILED) { + peg$currPos = s2; + s2 = undefined; + } else { + s2 = peg$FAILED; + } + if (s2 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f93(); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + function peg$parseoperator() { var s0, s1; @@ -3960,77 +4046,77 @@ function peg$parse(input, options) { } if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$f92(); + s1 = peg$f94(); } s0 = s1; if (s0 === peg$FAILED) { s0 = peg$currPos; - if (input.substr(peg$currPos, 2) === peg$c100) { - s1 = peg$c100; + if (input.substr(peg$currPos, 2) === peg$c102) { + s1 = peg$c102; peg$currPos += 2; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e107); } + if (peg$silentFails === 0) { peg$fail(peg$e109); } } if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$f93(); + s1 = peg$f95(); } s0 = s1; if (s0 === peg$FAILED) { s0 = peg$currPos; - if (input.substr(peg$currPos, 2) === peg$c101) { - s1 = peg$c101; + if (input.substr(peg$currPos, 2) === peg$c103) { + s1 = peg$c103; peg$currPos += 2; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e108); } + if (peg$silentFails === 0) { peg$fail(peg$e110); } } if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$f94(); + s1 = peg$f96(); } s0 = s1; if (s0 === peg$FAILED) { s0 = peg$currPos; if (input.charCodeAt(peg$currPos) === 62) { - s1 = peg$c102; + s1 = peg$c104; peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e109); } + if (peg$silentFails === 0) { peg$fail(peg$e111); } } if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$f95(); + s1 = peg$f97(); } s0 = s1; if (s0 === peg$FAILED) { s0 = peg$currPos; - if (input.substr(peg$currPos, 2) === peg$c103) { - s1 = peg$c103; + if (input.substr(peg$currPos, 2) === peg$c105) { + s1 = peg$c105; peg$currPos += 2; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e110); } + if (peg$silentFails === 0) { peg$fail(peg$e112); } } if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$f96(); + s1 = peg$f98(); } s0 = s1; if (s0 === peg$FAILED) { s0 = peg$currPos; if (input.charCodeAt(peg$currPos) === 60) { - s1 = peg$c104; + s1 = peg$c106; peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e111); } + if (peg$silentFails === 0) { peg$fail(peg$e113); } } if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$f97(); + s1 = peg$f99(); } s0 = s1; } @@ -4041,7 +4127,7 @@ function peg$parse(input, options) { peg$silentFails--; if (s0 === peg$FAILED) { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e106); } + if (peg$silentFails === 0) { peg$fail(peg$e108); } } return s0; @@ -4054,7 +4140,7 @@ function peg$parse(input, options) { s1 = peg$parseoperator(); if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$f98(s1); + s1 = peg$f100(s1); } s0 = s1; @@ -4072,7 +4158,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e113); } + if (peg$silentFails === 0) { peg$fail(peg$e115); } } if (s2 !== peg$FAILED) { while (s2 !== peg$FAILED) { @@ -4082,7 +4168,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e113); } + if (peg$silentFails === 0) { peg$fail(peg$e115); } } } } else { @@ -4090,13 +4176,13 @@ function peg$parse(input, options) { } if (s1 !== peg$FAILED) { peg$savedPos = s0; - s1 = peg$f99(s1); + s1 = peg$f101(s1); } s0 = s1; peg$silentFails--; if (s0 === peg$FAILED) { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e112); } + if (peg$silentFails === 0) { peg$fail(peg$e114); } } return s0; @@ -4108,7 +4194,7 @@ function peg$parse(input, options) { s0 = peg$currPos; s1 = peg$parse_(); peg$savedPos = s0; - s1 = peg$f100(); + s1 = peg$f102(); s0 = s1; return s0; @@ -4124,7 +4210,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e115); } + if (peg$silentFails === 0) { peg$fail(peg$e117); } } while (s1 !== peg$FAILED) { s0.push(s1); @@ -4133,12 +4219,12 @@ function peg$parse(input, options) { peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e115); } + if (peg$silentFails === 0) { peg$fail(peg$e117); } } } peg$silentFails--; s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e114); } + if (peg$silentFails === 0) { peg$fail(peg$e116); } return s0; } @@ -4148,7 +4234,7 @@ function peg$parse(input, options) { s0 = peg$currPos; peg$savedPos = peg$currPos; - s1 = peg$f101(); + s1 = peg$f103(); if (s1) { s1 = undefined; } else { @@ -4185,7 +4271,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e117); } + if (peg$silentFails === 0) { peg$fail(peg$e119); } } while (s2 !== peg$FAILED) { s1.push(s2); @@ -4194,7 +4280,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e117); } + if (peg$silentFails === 0) { peg$fail(peg$e119); } } } s2 = input.charAt(peg$currPos); @@ -4202,7 +4288,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e118); } + if (peg$silentFails === 0) { peg$fail(peg$e120); } } if (s2 !== peg$FAILED) { s3 = []; @@ -4211,7 +4297,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s4 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e119); } + if (peg$silentFails === 0) { peg$fail(peg$e121); } } while (s4 !== peg$FAILED) { s3.push(s4); @@ -4220,7 +4306,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s4 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e119); } + if (peg$silentFails === 0) { peg$fail(peg$e121); } } } s4 = input.charAt(peg$currPos); @@ -4228,7 +4314,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s4 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e118); } + if (peg$silentFails === 0) { peg$fail(peg$e120); } } if (s4 !== peg$FAILED) { s5 = []; @@ -4237,7 +4323,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s6 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e113); } + if (peg$silentFails === 0) { peg$fail(peg$e115); } } while (s6 !== peg$FAILED) { s5.push(s6); @@ -4246,11 +4332,11 @@ function peg$parse(input, options) { peg$currPos++; } else { s6 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e113); } + if (peg$silentFails === 0) { peg$fail(peg$e115); } } } peg$savedPos = s0; - s0 = peg$f102(s1, s3, s5); + s0 = peg$f104(s1, s3, s5); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -4262,7 +4348,7 @@ function peg$parse(input, options) { peg$silentFails--; if (s0 === peg$FAILED) { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e116); } + if (peg$silentFails === 0) { peg$fail(peg$e118); } } return s0; @@ -4279,7 +4365,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e117); } + if (peg$silentFails === 0) { peg$fail(peg$e119); } } while (s2 !== peg$FAILED) { s1.push(s2); @@ -4288,7 +4374,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e117); } + if (peg$silentFails === 0) { peg$fail(peg$e119); } } } s2 = input.charAt(peg$currPos); @@ -4296,7 +4382,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e118); } + if (peg$silentFails === 0) { peg$fail(peg$e120); } } if (s2 !== peg$FAILED) { s3 = []; @@ -4305,7 +4391,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s4 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e119); } + if (peg$silentFails === 0) { peg$fail(peg$e121); } } if (s4 === peg$FAILED) { s4 = peg$currPos; @@ -4321,15 +4407,15 @@ function peg$parse(input, options) { } if (s5 !== peg$FAILED) { if (input.charCodeAt(peg$currPos) === 8220) { - s6 = peg$c105; + s6 = peg$c107; peg$currPos++; } else { s6 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e120); } + if (peg$silentFails === 0) { peg$fail(peg$e122); } } if (s6 !== peg$FAILED) { peg$savedPos = s4; - s4 = peg$f103(s1); + s4 = peg$f105(s1); } else { peg$currPos = s4; s4 = peg$FAILED; @@ -4352,15 +4438,15 @@ function peg$parse(input, options) { } if (s5 !== peg$FAILED) { if (input.charCodeAt(peg$currPos) === 8221) { - s6 = peg$c106; + s6 = peg$c108; peg$currPos++; } else { s6 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e121); } + if (peg$silentFails === 0) { peg$fail(peg$e123); } } if (s6 !== peg$FAILED) { peg$savedPos = s4; - s4 = peg$f104(s1); + s4 = peg$f106(s1); } else { peg$currPos = s4; s4 = peg$FAILED; @@ -4383,15 +4469,15 @@ function peg$parse(input, options) { } if (s5 !== peg$FAILED) { if (input.charCodeAt(peg$currPos) === 34) { - s6 = peg$c107; + s6 = peg$c109; peg$currPos++; } else { s6 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e122); } + if (peg$silentFails === 0) { peg$fail(peg$e124); } } if (s6 !== peg$FAILED) { peg$savedPos = s4; - s4 = peg$f105(s1); + s4 = peg$f107(s1); } else { peg$currPos = s4; s4 = peg$FAILED; @@ -4410,7 +4496,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s4 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e119); } + if (peg$silentFails === 0) { peg$fail(peg$e121); } } if (s4 === peg$FAILED) { s4 = peg$currPos; @@ -4426,15 +4512,15 @@ function peg$parse(input, options) { } if (s5 !== peg$FAILED) { if (input.charCodeAt(peg$currPos) === 8220) { - s6 = peg$c105; + s6 = peg$c107; peg$currPos++; } else { s6 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e120); } + if (peg$silentFails === 0) { peg$fail(peg$e122); } } if (s6 !== peg$FAILED) { peg$savedPos = s4; - s4 = peg$f103(s1); + s4 = peg$f105(s1); } else { peg$currPos = s4; s4 = peg$FAILED; @@ -4457,15 +4543,15 @@ function peg$parse(input, options) { } if (s5 !== peg$FAILED) { if (input.charCodeAt(peg$currPos) === 8221) { - s6 = peg$c106; + s6 = peg$c108; peg$currPos++; } else { s6 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e121); } + if (peg$silentFails === 0) { peg$fail(peg$e123); } } if (s6 !== peg$FAILED) { peg$savedPos = s4; - s4 = peg$f104(s1); + s4 = peg$f106(s1); } else { peg$currPos = s4; s4 = peg$FAILED; @@ -4488,15 +4574,15 @@ function peg$parse(input, options) { } if (s5 !== peg$FAILED) { if (input.charCodeAt(peg$currPos) === 34) { - s6 = peg$c107; + s6 = peg$c109; peg$currPos++; } else { s6 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e122); } + if (peg$silentFails === 0) { peg$fail(peg$e124); } } if (s6 !== peg$FAILED) { peg$savedPos = s4; - s4 = peg$f105(s1); + s4 = peg$f107(s1); } else { peg$currPos = s4; s4 = peg$FAILED; @@ -4512,7 +4598,7 @@ function peg$parse(input, options) { s4 = peg$parseclosingQuote(); if (s4 !== peg$FAILED) { peg$savedPos = s0; - s0 = peg$f106(s1, s3, s4); + s0 = peg$f108(s1, s3, s4); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -4524,7 +4610,7 @@ function peg$parse(input, options) { peg$silentFails--; if (s0 === peg$FAILED) { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e116); } + if (peg$silentFails === 0) { peg$fail(peg$e118); } } return s0; @@ -4539,7 +4625,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e118); } + if (peg$silentFails === 0) { peg$fail(peg$e120); } } if (s1 !== peg$FAILED) { s2 = peg$currPos; @@ -4577,7 +4663,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e123); } + if (peg$silentFails === 0) { peg$fail(peg$e125); } } while (s2 !== peg$FAILED) { s1.push(s2); @@ -4586,7 +4672,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e123); } + if (peg$silentFails === 0) { peg$fail(peg$e125); } } } s2 = []; @@ -4595,7 +4681,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s3 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e124); } + if (peg$silentFails === 0) { peg$fail(peg$e126); } } while (s3 !== peg$FAILED) { s2.push(s3); @@ -4604,7 +4690,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s3 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e124); } + if (peg$silentFails === 0) { peg$fail(peg$e126); } } } s3 = []; @@ -4613,7 +4699,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s4 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e125); } + if (peg$silentFails === 0) { peg$fail(peg$e127); } } while (s4 !== peg$FAILED) { s3.push(s4); @@ -4622,7 +4708,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s4 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e125); } + if (peg$silentFails === 0) { peg$fail(peg$e127); } } } s4 = peg$parseoperator(); @@ -4641,7 +4727,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e126); } + if (peg$silentFails === 0) { peg$fail(peg$e128); } } while (s2 !== peg$FAILED) { s1.push(s2); @@ -4650,7 +4736,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e126); } + if (peg$silentFails === 0) { peg$fail(peg$e128); } } } s2 = peg$currPos; @@ -4697,7 +4783,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s0 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e127); } + if (peg$silentFails === 0) { peg$fail(peg$e129); } } } } @@ -4713,7 +4799,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s0 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e128); } + if (peg$silentFails === 0) { peg$fail(peg$e130); } } if (s0 === peg$FAILED) { s0 = peg$currPos; diff --git a/src/types/onyx/SearchResults.ts b/src/types/onyx/SearchResults.ts index 2ab876d872d1..5e3661305ff7 100644 --- a/src/types/onyx/SearchResults.ts +++ b/src/types/onyx/SearchResults.ts @@ -196,6 +196,18 @@ type SearchWithdrawalIDGroup = { /** Settlement state (5/6/7=failed, 8=cleared, others=pending) */ state: number; + + /** What the company was debited, when the settlement converted currencies */ + debitedAmount?: number; + + /** Currency the company was debited in */ + debitedCurrency?: string; + + /** What the employee was credited, when the settlement converted currencies */ + creditedAmount?: number; + + /** Currency the employee was credited in */ + creditedCurrency?: string; }; /** Model of category grouped search result */ diff --git a/tests/unit/Search/ColumnAvailabilityTest.ts b/tests/unit/Search/ColumnAvailabilityTest.ts index fa1578228a41..a28fdb55ec0c 100644 --- a/tests/unit/Search/ColumnAvailabilityTest.ts +++ b/tests/unit/Search/ColumnAvailabilityTest.ts @@ -54,6 +54,8 @@ describe('Column availability single source of truth', () => { TABLE_COLUMNS.GROUP_YEAR, TABLE_COLUMNS.GROUP_QUARTER, TABLE_COLUMNS.GROUP_WITHDRAWAL_STATUS, + TABLE_COLUMNS.GROUP_AMOUNT_DEBITED, + TABLE_COLUMNS.GROUP_AMOUNT_REIMBURSED, ]); test('every TABLE_COLUMNS value is classified in COLUMN_AVAILABILITY or owned by another surface', () => { From 1174e9d29b49a032a12cc8eaab991ddd44d0e9e6 Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Tue, 28 Jul 2026 04:44:48 +0530 Subject: [PATCH 02/27] Render each conversion amount in its own currency and hide the columns until a settlement converts --- .../ListItem/WithdrawalIDListItemHeader.tsx | 30 ++++++++++ src/components/Search/SearchTableHeader.tsx | 36 +++++++----- src/libs/SearchUIUtils.ts | 37 +++++++++++- src/styles/utils/index.ts | 4 ++ tests/unit/Search/SearchUIUtilsTest.ts | 57 +++++++++++++++++++ 5 files changed, 150 insertions(+), 14 deletions(-) diff --git a/src/components/Search/SearchList/ListItem/WithdrawalIDListItemHeader.tsx b/src/components/Search/SearchList/ListItem/WithdrawalIDListItemHeader.tsx index 7327f9aa8b29..53b439494ca8 100644 --- a/src/components/Search/SearchList/ListItem/WithdrawalIDListItemHeader.tsx +++ b/src/components/Search/SearchList/ListItem/WithdrawalIDListItemHeader.tsx @@ -93,6 +93,10 @@ function WithdrawalIDListItemHeaderImpl({ withdrawalIDItem.debitPosted, DateUtils.doesDateBelongToAPastYear(withdrawalIDItem.debitPosted) ? CONST.DATE.MONTH_DAY_YEAR_ABBR_FORMAT : CONST.DATE.MONTH_DAY_ABBR_FORMAT, ); + // A settlement that converted currencies moved a different amount on each side, each in its own currency, so + // neither side is shown without the currency it is denominated in. + const {debitedAmount, debitedCurrency, creditedAmount, creditedCurrency} = withdrawalIDItem; + const badgeProps = getSettlementStatusBadgeProps(withdrawalIDItem.state, translate, theme); const settlementStatus = getSettlementStatus(withdrawalIDItem.state); const statusBadge = !!badgeProps && ( @@ -177,6 +181,32 @@ function WithdrawalIDListItemHeaderImpl({ ), + [CONST.SEARCH.TABLE_COLUMNS.GROUP_AMOUNT_DEBITED]: ( + + {!!debitedAmount && !!debitedCurrency && ( + + )} + + ), + [CONST.SEARCH.TABLE_COLUMNS.GROUP_AMOUNT_REIMBURSED]: ( + + {!!creditedAmount && !!creditedCurrency && ( + + )} + + ), [CONST.SEARCH.TABLE_COLUMNS.GROUP_TOTAL]: ( { - const commonGroupHeaders: SearchColumnConfig[] = [ - { - columnName: CONST.SEARCH.TABLE_COLUMNS.GROUP_EXPENSES, - translationKey: 'common.expenses' as TranslationPaths, - isColumnSortable: true, - }, - { - columnName: CONST.SEARCH.TABLE_COLUMNS.GROUP_TOTAL, - translationKey: 'common.total' as TranslationPaths, - isColumnSortable: true, - }, - ]; + const groupExpensesHeader: SearchColumnConfig = { + columnName: CONST.SEARCH.TABLE_COLUMNS.GROUP_EXPENSES, + translationKey: 'common.expenses' as TranslationPaths, + isColumnSortable: true, + }; + const groupTotalHeader: SearchColumnConfig = { + columnName: CONST.SEARCH.TABLE_COLUMNS.GROUP_TOTAL, + translationKey: 'common.total' as TranslationPaths, + isColumnSortable: true, + }; + const commonGroupHeaders: SearchColumnConfig[] = [groupExpensesHeader, groupTotalHeader]; switch (groupBy) { case CONST.SEARCH.GROUP_BY.FROM: return [ @@ -408,7 +407,18 @@ const getTransactionGroupHeaders = (groupBy: SearchGroupBy, icons: SearchHeaderI columnName: CONST.SEARCH.TABLE_COLUMNS.GROUP_WITHDRAWAL_ID, translationKey: 'common.withdrawalID', }, - ...commonGroupHeaders, + groupExpensesHeader, + { + columnName: CONST.SEARCH.TABLE_COLUMNS.GROUP_AMOUNT_DEBITED, + translationKey: 'common.amountDebited', + isColumnSortable: false, + }, + { + columnName: CONST.SEARCH.TABLE_COLUMNS.GROUP_AMOUNT_REIMBURSED, + translationKey: 'common.amountReimbursed', + isColumnSortable: false, + }, + groupTotalHeader, ]; case CONST.SEARCH.GROUP_BY.CATEGORY: return [ diff --git a/src/libs/SearchUIUtils.ts b/src/libs/SearchUIUtils.ts index 49d8b9402457..3c54df566c05 100644 --- a/src/libs/SearchUIUtils.ts +++ b/src/libs/SearchUIUtils.ts @@ -429,6 +429,11 @@ const nonSortableColumns = new Set([ CONST.SEARCH.TABLE_COLUMNS.ACTION, CONST.SEARCH.TABLE_COLUMNS.IN, CONST.SEARCH.TABLE_COLUMNS.AVATAR, + + // Each side of a conversion is denominated in its own currency, so ordering the raw numbers would rank + // amounts that are not comparable. + CONST.SEARCH.TABLE_COLUMNS.GROUP_AMOUNT_DEBITED, + CONST.SEARCH.TABLE_COLUMNS.GROUP_AMOUNT_REIMBURSED, ]); function isValidExpenseStatus(status: unknown): status is ValueOf { @@ -4459,6 +4464,10 @@ function getSearchColumnTranslationKey(column: SearchSortBy): TranslationPaths { return 'common.expenses'; case CONST.SEARCH.TABLE_COLUMNS.GROUP_TOTAL: return 'common.total'; + case CONST.SEARCH.TABLE_COLUMNS.GROUP_AMOUNT_DEBITED: + return 'common.amountDebited'; + case CONST.SEARCH.TABLE_COLUMNS.GROUP_AMOUNT_REIMBURSED: + return 'common.amountReimbursed'; case CONST.SEARCH.TABLE_COLUMNS.WITHDRAWAL_ID: return 'common.withdrawalID'; case CONST.SEARCH.TABLE_COLUMNS.SUBMITTER_USER_ID: @@ -5768,6 +5777,32 @@ function getPolicyTagsForPolicyID(policyTags: PolicyTagsLookup | undefined, poli return policyTagsKey ? policyTags[policyTagsKey] : undefined; } +// Only settlements that converted currencies carry these amounts, so each column stays hidden until a group reports one. +const conversionAmountGroupColumns = { + [CONST.SEARCH.TABLE_COLUMNS.GROUP_AMOUNT_DEBITED]: {amount: 'debitedAmount', currency: 'debitedCurrency'}, + [CONST.SEARCH.TABLE_COLUMNS.GROUP_AMOUNT_REIMBURSED]: {amount: 'creditedAmount', currency: 'creditedCurrency'}, +} as const satisfies Partial>; + +function isConversionAmountGroupColumn(column: SearchColumnType): column is keyof typeof conversionAmountGroupColumns { + return column in conversionAmountGroupColumns; +} + +function hasConversionAmount(column: SearchColumnType, data: OnyxTypes.SearchResults['data'] | OnyxTypes.Transaction[]): boolean { + if (!isConversionAmountGroupColumn(column) || Array.isArray(data)) { + return !isConversionAmountGroupColumn(column); + } + + const {amount, currency} = conversionAmountGroupColumns[column]; + return Object.keys(data).some((key) => { + if (!isGroupEntry(key)) { + return false; + } + + const group: Partial = data[key]; + return !!group[amount] && !!group[currency]; + }); +} + /** * Determines what columns to show based on available data * @param isExpenseReportView: true when we are inside an expense report view, false if we're in the Reports page. @@ -5926,7 +5961,7 @@ function getColumnsToShow({ result.push(col); } - return result; + return result.filter((column) => hasConversionAmount(column, data)); } } diff --git a/src/styles/utils/index.ts b/src/styles/utils/index.ts index 89716ac31b22..5fe57a117f55 100644 --- a/src/styles/utils/index.ts +++ b/src/styles/utils/index.ts @@ -1941,6 +1941,10 @@ const createStyleUtils = (theme: ThemeColors, styles: ThemeStyles) => ({ case CONST.SEARCH.TABLE_COLUMNS.GROUP_EXPENSES: columnWidth = {...getWidthStyle(variables.w130)}; break; + case CONST.SEARCH.TABLE_COLUMNS.GROUP_AMOUNT_DEBITED: + case CONST.SEARCH.TABLE_COLUMNS.GROUP_AMOUNT_REIMBURSED: + columnWidth = {...getWidthStyle(isAmountColumnWide ? variables.w130 : variables.w96), ...styles.alignItemsEnd}; + break; case CONST.SEARCH.TABLE_COLUMNS.REIMBURSABLE_TOTAL: case CONST.SEARCH.TABLE_COLUMNS.NON_REIMBURSABLE_TOTAL: case CONST.SEARCH.TABLE_COLUMNS.ORIGINAL_AMOUNT: diff --git a/tests/unit/Search/SearchUIUtilsTest.ts b/tests/unit/Search/SearchUIUtilsTest.ts index a018ccff1e9b..560e15584b7c 100644 --- a/tests/unit/Search/SearchUIUtilsTest.ts +++ b/tests/unit/Search/SearchUIUtilsTest.ts @@ -9473,6 +9473,63 @@ describe('SearchUIUtils', () => { expect(result).toContain(CONST.SEARCH.TABLE_COLUMNS.GROUP_FROM); }); + test('Should only show the conversion amount columns for withdrawal groups that converted currencies', () => { + const domesticGroup = { + entryID: 1, + count: 1, + total: -10000, + currency: 'USD', + accountNumber: '1234', + bankName: CONST.BANK_NAMES.CHASE, + debitPosted: '2026-01-02', + state: 8, + }; + const crossBorderGroup = { + ...domesticGroup, + entryID: 2, + debitedAmount: -10000, + debitedCurrency: 'USD', + creditedAmount: -9200, + creditedCurrency: 'EUR', + }; + + // @ts-expect-error minimal dataset for getColumnsToShow + const domesticData: OnyxTypes.SearchResults['data'] = {[`group_${domesticGroup.entryID}`]: domesticGroup}; + // @ts-expect-error minimal dataset for getColumnsToShow + const crossBorderData: OnyxTypes.SearchResults['data'] = {...domesticData, [`group_${crossBorderGroup.entryID}`]: crossBorderGroup}; + + const domesticColumns = SearchUIUtils.getColumnsToShow({currentAccountID: 1, data: domesticData, visibleColumns: [], groupBy: CONST.SEARCH.GROUP_BY.WITHDRAWAL_ID}); + expect(domesticColumns).not.toContain(CONST.SEARCH.TABLE_COLUMNS.GROUP_AMOUNT_DEBITED); + expect(domesticColumns).not.toContain(CONST.SEARCH.TABLE_COLUMNS.GROUP_AMOUNT_REIMBURSED); + + const crossBorderColumns = SearchUIUtils.getColumnsToShow({currentAccountID: 1, data: crossBorderData, visibleColumns: [], groupBy: CONST.SEARCH.GROUP_BY.WITHDRAWAL_ID}); + expect(crossBorderColumns).toContain(CONST.SEARCH.TABLE_COLUMNS.GROUP_AMOUNT_DEBITED); + expect(crossBorderColumns).toContain(CONST.SEARCH.TABLE_COLUMNS.GROUP_AMOUNT_REIMBURSED); + }); + + test('Should hide a conversion amount column when its currency is missing', () => { + const groupMissingCreditedCurrency = { + entryID: 3, + count: 1, + total: -10000, + currency: 'USD', + accountNumber: '1234', + bankName: CONST.BANK_NAMES.CHASE, + debitPosted: '2026-01-02', + state: 8, + debitedAmount: -10000, + debitedCurrency: 'USD', + creditedAmount: -9200, + }; + + // @ts-expect-error minimal dataset for getColumnsToShow + const data: OnyxTypes.SearchResults['data'] = {[`group_${groupMissingCreditedCurrency.entryID}`]: groupMissingCreditedCurrency}; + + const columns = SearchUIUtils.getColumnsToShow({currentAccountID: 1, data, visibleColumns: [], groupBy: CONST.SEARCH.GROUP_BY.WITHDRAWAL_ID}); + expect(columns).toContain(CONST.SEARCH.TABLE_COLUMNS.GROUP_AMOUNT_DEBITED); + expect(columns).not.toContain(CONST.SEARCH.TABLE_COLUMNS.GROUP_AMOUNT_REIMBURSED); + }); + test('Should hide To for strict default expense columns', () => { const baseTransaction = searchResults.data[`transactions_${transactionID}`]; const tx = { From b6792696db5397d052a9ffe916539c0c6387c7d1 Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Tue, 28 Jul 2026 05:12:51 +0530 Subject: [PATCH 03/27] Allow sorting the reconciliation dashboard by either conversion amount --- src/components/Search/SearchTableHeader.tsx | 2 -- src/libs/SearchUIUtils.ts | 7 ++----- tests/unit/Search/SearchUIUtilsTest.ts | 1 - 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/components/Search/SearchTableHeader.tsx b/src/components/Search/SearchTableHeader.tsx index 5dce37f9bc7b..13c736022193 100644 --- a/src/components/Search/SearchTableHeader.tsx +++ b/src/components/Search/SearchTableHeader.tsx @@ -411,12 +411,10 @@ const getTransactionGroupHeaders = (groupBy: SearchGroupBy, icons: SearchHeaderI { columnName: CONST.SEARCH.TABLE_COLUMNS.GROUP_AMOUNT_DEBITED, translationKey: 'common.amountDebited', - isColumnSortable: false, }, { columnName: CONST.SEARCH.TABLE_COLUMNS.GROUP_AMOUNT_REIMBURSED, translationKey: 'common.amountReimbursed', - isColumnSortable: false, }, groupTotalHeader, ]; diff --git a/src/libs/SearchUIUtils.ts b/src/libs/SearchUIUtils.ts index 3c54df566c05..2d24889e3e98 100644 --- a/src/libs/SearchUIUtils.ts +++ b/src/libs/SearchUIUtils.ts @@ -367,6 +367,8 @@ const transactionWithdrawalIDGroupColumnNamesToSortingProperty: TransactionWithd [CONST.SEARCH.TABLE_COLUMNS.WITHDRAWN]: 'debitPosted' as const, [CONST.SEARCH.TABLE_COLUMNS.GROUP_WITHDRAWAL_STATUS]: 'state' as const, [CONST.SEARCH.TABLE_COLUMNS.GROUP_WITHDRAWAL_ID]: 'formattedWithdrawalID' as const, + [CONST.SEARCH.TABLE_COLUMNS.GROUP_AMOUNT_DEBITED]: 'debitedAmount' as const, + [CONST.SEARCH.TABLE_COLUMNS.GROUP_AMOUNT_REIMBURSED]: 'creditedAmount' as const, ...transactionGroupBaseSortingProperties, }; @@ -429,11 +431,6 @@ const nonSortableColumns = new Set([ CONST.SEARCH.TABLE_COLUMNS.ACTION, CONST.SEARCH.TABLE_COLUMNS.IN, CONST.SEARCH.TABLE_COLUMNS.AVATAR, - - // Each side of a conversion is denominated in its own currency, so ordering the raw numbers would rank - // amounts that are not comparable. - CONST.SEARCH.TABLE_COLUMNS.GROUP_AMOUNT_DEBITED, - CONST.SEARCH.TABLE_COLUMNS.GROUP_AMOUNT_REIMBURSED, ]); function isValidExpenseStatus(status: unknown): status is ValueOf { diff --git a/tests/unit/Search/SearchUIUtilsTest.ts b/tests/unit/Search/SearchUIUtilsTest.ts index 560e15584b7c..63a052e9c6e5 100644 --- a/tests/unit/Search/SearchUIUtilsTest.ts +++ b/tests/unit/Search/SearchUIUtilsTest.ts @@ -9495,7 +9495,6 @@ describe('SearchUIUtils', () => { // @ts-expect-error minimal dataset for getColumnsToShow const domesticData: OnyxTypes.SearchResults['data'] = {[`group_${domesticGroup.entryID}`]: domesticGroup}; - // @ts-expect-error minimal dataset for getColumnsToShow const crossBorderData: OnyxTypes.SearchResults['data'] = {...domesticData, [`group_${crossBorderGroup.entryID}`]: crossBorderGroup}; const domesticColumns = SearchUIUtils.getColumnsToShow({currentAccountID: 1, data: domesticData, visibleColumns: [], groupBy: CONST.SEARCH.GROUP_BY.WITHDRAWAL_ID}); From 3d7bcaccf9314307824563c95cddca6ac860361a Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Tue, 28 Jul 2026 05:12:51 +0530 Subject: [PATCH 04/27] Keep the conversion amount headers inside their own columns --- src/components/Search/SortableTableHeader.tsx | 3 ++- src/styles/utils/index.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/Search/SortableTableHeader.tsx b/src/components/Search/SortableTableHeader.tsx index c535480b6d44..861b4b84abea 100644 --- a/src/components/Search/SortableTableHeader.tsx +++ b/src/components/Search/SortableTableHeader.tsx @@ -80,9 +80,10 @@ function SortableTableHeader({ const sortByColumnName = sortColumnName ?? columnName; const isActive = sortBy === sortByColumnName; const isReimbursableOrBillableColumn = columnName === CONST.SEARCH.TABLE_COLUMNS.REIMBURSABLE || columnName === CONST.SEARCH.TABLE_COLUMNS.BILLABLE; + const isConversionAmountColumn = columnName === CONST.SEARCH.TABLE_COLUMNS.GROUP_AMOUNT_DEBITED || columnName === CONST.SEARCH.TABLE_COLUMNS.GROUP_AMOUNT_REIMBURSED; const textStyle = [ columnName === CONST.SEARCH.TABLE_COLUMNS.RECEIPT ? StyleUtils.getTextOverflowStyle('clip') : null, - isReimbursableOrBillableColumn ? styles.flexShrink1 : null, + isReimbursableOrBillableColumn || isConversionAmountColumn ? styles.flexShrink1 : null, ]; return ( diff --git a/src/styles/utils/index.ts b/src/styles/utils/index.ts index 5fe57a117f55..d942b7f69744 100644 --- a/src/styles/utils/index.ts +++ b/src/styles/utils/index.ts @@ -1943,7 +1943,8 @@ const createStyleUtils = (theme: ThemeColors, styles: ThemeStyles) => ({ break; case CONST.SEARCH.TABLE_COLUMNS.GROUP_AMOUNT_DEBITED: case CONST.SEARCH.TABLE_COLUMNS.GROUP_AMOUNT_REIMBURSED: - columnWidth = {...getWidthStyle(isAmountColumnWide ? variables.w130 : variables.w96), ...styles.alignItemsEnd}; + // Fixed width: wide enough for the long headers these columns carry, so no amount-based widening is needed. + columnWidth = {...getWidthStyle(variables.w130), ...styles.alignItemsEnd}; break; case CONST.SEARCH.TABLE_COLUMNS.REIMBURSABLE_TOTAL: case CONST.SEARCH.TABLE_COLUMNS.NON_REIMBURSABLE_TOTAL: From 45c5e4a8983da317c10635237b82eddf91cf7df1 Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Tue, 28 Jul 2026 06:26:23 +0530 Subject: [PATCH 05/27] Let the two conversion amount columns be sorted SortableTableHeader only makes a header pressable when isColumnSortable is set, so without it the columns rendered as plain labels and the sort Auth supports could not be reached. Co-Authored-By: Claude Opus 5 (1M context) --- src/components/Search/SearchTableHeader.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/Search/SearchTableHeader.tsx b/src/components/Search/SearchTableHeader.tsx index 13c736022193..d6008df1f2b5 100644 --- a/src/components/Search/SearchTableHeader.tsx +++ b/src/components/Search/SearchTableHeader.tsx @@ -411,10 +411,12 @@ const getTransactionGroupHeaders = (groupBy: SearchGroupBy, icons: SearchHeaderI { columnName: CONST.SEARCH.TABLE_COLUMNS.GROUP_AMOUNT_DEBITED, translationKey: 'common.amountDebited', + isColumnSortable: true, }, { columnName: CONST.SEARCH.TABLE_COLUMNS.GROUP_AMOUNT_REIMBURSED, translationKey: 'common.amountReimbursed', + isColumnSortable: true, }, groupTotalHeader, ]; @@ -426,6 +428,7 @@ const getTransactionGroupHeaders = (groupBy: SearchGroupBy, icons: SearchHeaderI isColumnSortable: true, }, ...commonGroupHeaders, + x, ]; case CONST.SEARCH.GROUP_BY.MERCHANT: return [ From 09a5082271231253ac1ff691b37d9baf642f3345 Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Tue, 28 Jul 2026 06:26:40 +0530 Subject: [PATCH 06/27] Keep the backend order for the conversion amount sorts getSortedData re-sorts the page by the mapped property, which for these two columns is a raw amount in whichever currency that settlement used. Ranking GBP 10250 against USD 12000 that way puts them in the wrong order. Unmapped columns fall through with the data untouched, which is the order Auth already computed from a common-currency sort key. Co-Authored-By: Claude Opus 5 (1M context) --- src/libs/SearchUIUtils.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libs/SearchUIUtils.ts b/src/libs/SearchUIUtils.ts index 2d24889e3e98..6bf9262fe7e4 100644 --- a/src/libs/SearchUIUtils.ts +++ b/src/libs/SearchUIUtils.ts @@ -367,8 +367,9 @@ const transactionWithdrawalIDGroupColumnNamesToSortingProperty: TransactionWithd [CONST.SEARCH.TABLE_COLUMNS.WITHDRAWN]: 'debitPosted' as const, [CONST.SEARCH.TABLE_COLUMNS.GROUP_WITHDRAWAL_STATUS]: 'state' as const, [CONST.SEARCH.TABLE_COLUMNS.GROUP_WITHDRAWAL_ID]: 'formattedWithdrawalID' as const, - [CONST.SEARCH.TABLE_COLUMNS.GROUP_AMOUNT_DEBITED]: 'debitedAmount' as const, - [CONST.SEARCH.TABLE_COLUMNS.GROUP_AMOUNT_REIMBURSED]: 'creditedAmount' as const, + // The conversion amounts are each denominated in their own currency, so the backend ranks them by a hidden key + // that converts every group to one currency. Leaving them unmapped keeps that order instead of re-sorting the + // page by raw numbers that are not comparable. ...transactionGroupBaseSortingProperties, }; From 4754bb4207cddb2fbc9cef4ca5cccf8e68c245bd Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Tue, 28 Jul 2026 06:30:04 +0530 Subject: [PATCH 07/27] Translate the two new column headers typecheck and TranslateTest both fail on a key that only exists in en.ts. The generateTranslations bot skips pull requests from forks, so these are written by hand. Co-Authored-By: Claude Opus 5 (1M context) --- src/languages/de.ts | 2 ++ src/languages/es.ts | 2 ++ src/languages/fr.ts | 2 ++ src/languages/it.ts | 2 ++ src/languages/ja.ts | 2 ++ src/languages/nl.ts | 2 ++ src/languages/pl.ts | 2 ++ src/languages/pt-BR.ts | 2 ++ src/languages/zh-hans.ts | 2 ++ 9 files changed, 18 insertions(+) diff --git a/src/languages/de.ts b/src/languages/de.ts index ce65a8b932a3..f8088d63b64e 100644 --- a/src/languages/de.ts +++ b/src/languages/de.ts @@ -402,6 +402,8 @@ const translations: TranslationDeepObject = { longReportID: 'Langer Bericht-ID', withdrawalID: 'Auszahlungs-ID', internationalReimbursementIDs: 'Internationale Erstattungs-IDs', + amountDebited: 'Belasteter Betrag', + amountReimbursed: 'Erstatteter Betrag', withdrawalStatus: 'Auszahlungsstatus', paidStatus: 'Status: Bezahlt', bankAccounts: 'Bankkonten', diff --git a/src/languages/es.ts b/src/languages/es.ts index 7f1b9fed1c00..214bbfacf524 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -459,6 +459,8 @@ const translations: TranslationDeepObject = { reimbursableTotal: 'Total reembolsable', nonReimbursableTotal: 'Total no reembolsable', internationalReimbursementIDs: 'IDs de reembolso internacional', + amountDebited: 'Importe debitado', + amountReimbursed: 'Importe reembolsado', opensInNewTab: 'Se abre en una nueva pestaña', locked: 'Bloqueado', month: 'Monat', diff --git a/src/languages/fr.ts b/src/languages/fr.ts index 4deca52d75f1..27bbb61f24b6 100644 --- a/src/languages/fr.ts +++ b/src/languages/fr.ts @@ -401,6 +401,8 @@ const translations: TranslationDeepObject = { longReportID: 'ID de note de frais longue', withdrawalID: 'ID de retrait', internationalReimbursementIDs: 'ID de remboursement international', + amountDebited: 'Montant débité', + amountReimbursed: 'Montant remboursé', withdrawalStatus: 'Statut de retrait', paidStatus: 'Statut payé', bankAccounts: 'Comptes bancaires', diff --git a/src/languages/it.ts b/src/languages/it.ts index fa42e6be6a40..dedcd5339f86 100644 --- a/src/languages/it.ts +++ b/src/languages/it.ts @@ -402,6 +402,8 @@ const translations: TranslationDeepObject = { longReportID: 'ID report dettagliato', withdrawalID: 'ID prelievo', internationalReimbursementIDs: 'ID di rimborso internazionale', + amountDebited: 'Importo addebitato', + amountReimbursed: 'Importo rimborsato', withdrawalStatus: 'Stato del prelievo', paidStatus: 'Stato di pagamento', bankAccounts: 'Conti bancari', diff --git a/src/languages/ja.ts b/src/languages/ja.ts index bb7736a5d2e4..1436caf1f776 100644 --- a/src/languages/ja.ts +++ b/src/languages/ja.ts @@ -401,6 +401,8 @@ const translations: TranslationDeepObject = { longReportID: '長いレポートID', withdrawalID: '出金ID', internationalReimbursementIDs: '国際払い戻しID', + amountDebited: '引き落とし金額', + amountReimbursed: '払い戻し金額', withdrawalStatus: '出金ステータス', paidStatus: '支払済みステータス', bankAccounts: '銀行口座', diff --git a/src/languages/nl.ts b/src/languages/nl.ts index 4167f2dcda00..9967dd1b9249 100644 --- a/src/languages/nl.ts +++ b/src/languages/nl.ts @@ -401,6 +401,8 @@ const translations: TranslationDeepObject = { longReportID: 'Lang rapport-ID', withdrawalID: 'Opname-ID', internationalReimbursementIDs: 'Internationale terugbetalings-ID’s', + amountDebited: 'Afgeschreven bedrag', + amountReimbursed: 'Terugbetaald bedrag', withdrawalStatus: 'Opnamestatus', paidStatus: 'Betaald-status', bankAccounts: 'Bankrekeningen', diff --git a/src/languages/pl.ts b/src/languages/pl.ts index 62b508c159b8..411ef9dcb15c 100644 --- a/src/languages/pl.ts +++ b/src/languages/pl.ts @@ -401,6 +401,8 @@ const translations: TranslationDeepObject = { longReportID: 'Długi identyfikator raportu', withdrawalID: 'Identyfikator wypłaty', internationalReimbursementIDs: 'Identyfikatory zwrotów międzynarodowych', + amountDebited: 'Kwota obciążenia', + amountReimbursed: 'Kwota zwrotu', withdrawalStatus: 'Status wypłaty', paidStatus: 'Status: opłacono', bankAccounts: 'Konta bankowe', diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts index 16998d7b1e67..f316a9c844dd 100644 --- a/src/languages/pt-BR.ts +++ b/src/languages/pt-BR.ts @@ -401,6 +401,8 @@ const translations: TranslationDeepObject = { longReportID: 'ID de relatório longo', withdrawalID: 'ID do saque', internationalReimbursementIDs: 'IDs de reembolso internacional', + amountDebited: 'Valor debitado', + amountReimbursed: 'Valor reembolsado', withdrawalStatus: 'Status do saque', paidStatus: 'Status pago', bankAccounts: 'Contas bancárias', diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts index a71f2d2bec1a..fb74163dd0a5 100644 --- a/src/languages/zh-hans.ts +++ b/src/languages/zh-hans.ts @@ -401,6 +401,8 @@ const translations: TranslationDeepObject = { longReportID: '长报表 ID', withdrawalID: '提现编号', internationalReimbursementIDs: '国际报销 ID', + amountDebited: '扣款金额', + amountReimbursed: '报销金额', withdrawalStatus: '提现状态', paidStatus: '已付款状态', bankAccounts: '银行账户', From ad783d46011427882fabb76f061ac71e2c6a3a92 Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Tue, 28 Jul 2026 06:49:00 +0530 Subject: [PATCH 08/27] Drop a stray token from the category headers The list is built at module scope, so the name resolves the moment the file loads and grouping by category throws instead of rendering. --- src/components/Search/SearchTableHeader.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/Search/SearchTableHeader.tsx b/src/components/Search/SearchTableHeader.tsx index d6008df1f2b5..e46193c25eef 100644 --- a/src/components/Search/SearchTableHeader.tsx +++ b/src/components/Search/SearchTableHeader.tsx @@ -428,7 +428,6 @@ const getTransactionGroupHeaders = (groupBy: SearchGroupBy, icons: SearchHeaderI isColumnSortable: true, }, ...commonGroupHeaders, - x, ]; case CONST.SEARCH.GROUP_BY.MERCHANT: return [ From c89bf34cb433cfedf78a9f26f60e41ac84c76dd9 Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Tue, 28 Jul 2026 06:49:01 +0530 Subject: [PATCH 09/27] Say the column visibility rule the way the report list says it The helper answered "does this column have data" for every column, which read as a double negative for the ones it knows nothing about. Bail out on a transaction array first, then ask the question only of the two conversion columns, matching the expense report branch above. --- .../ListItem/WithdrawalIDListItemHeader.tsx | 3 +-- src/libs/SearchUIUtils.ts | 17 ++++++++--------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/components/Search/SearchList/ListItem/WithdrawalIDListItemHeader.tsx b/src/components/Search/SearchList/ListItem/WithdrawalIDListItemHeader.tsx index 53b439494ca8..67f72a025cdc 100644 --- a/src/components/Search/SearchList/ListItem/WithdrawalIDListItemHeader.tsx +++ b/src/components/Search/SearchList/ListItem/WithdrawalIDListItemHeader.tsx @@ -93,8 +93,6 @@ function WithdrawalIDListItemHeaderImpl({ withdrawalIDItem.debitPosted, DateUtils.doesDateBelongToAPastYear(withdrawalIDItem.debitPosted) ? CONST.DATE.MONTH_DAY_YEAR_ABBR_FORMAT : CONST.DATE.MONTH_DAY_ABBR_FORMAT, ); - // A settlement that converted currencies moved a different amount on each side, each in its own currency, so - // neither side is shown without the currency it is denominated in. const {debitedAmount, debitedCurrency, creditedAmount, creditedCurrency} = withdrawalIDItem; const badgeProps = getSettlementStatusBadgeProps(withdrawalIDItem.state, translate, theme); @@ -181,6 +179,7 @@ function WithdrawalIDListItemHeaderImpl({ ), + // A settlement that did not convert currencies reports neither amount, and an amount says nothing without the currency it moved in. [CONST.SEARCH.TABLE_COLUMNS.GROUP_AMOUNT_DEBITED]: ( ), + [CONST.SEARCH.TABLE_COLUMNS.AMOUNT_DEBITED]: ( + + {!!debitedAmount && !!debitedCurrency && ( + + )} + + ), + [CONST.SEARCH.TABLE_COLUMNS.AMOUNT_REIMBURSED]: ( + + {!!creditedAmount && !!creditedCurrency && ( + + )} + + ), [CONST.SEARCH.TABLE_COLUMNS.REPORT_ID]: ( diff --git a/src/components/Search/SearchTableHeader.tsx b/src/components/Search/SearchTableHeader.tsx index 06cf806653ad..0c7c88714c1d 100644 --- a/src/components/Search/SearchTableHeader.tsx +++ b/src/components/Search/SearchTableHeader.tsx @@ -168,6 +168,17 @@ const getExpenseHeaders = (groupBy?: SearchGroupBy): SearchColumnConfig[] => [ columnName: CONST.SEARCH.TABLE_COLUMNS.ORDER_DEAL_NUMBERS, translationKey: 'common.internationalReimbursementIDs', }, + // The backend has no sort key for these amounts on individual reports. + { + columnName: CONST.SEARCH.TABLE_COLUMNS.AMOUNT_DEBITED, + translationKey: 'common.amountDebited', + isColumnSortable: false, + }, + { + columnName: CONST.SEARCH.TABLE_COLUMNS.AMOUNT_REIMBURSED, + translationKey: 'common.amountReimbursed', + isColumnSortable: false, + }, { columnName: CONST.SEARCH.TABLE_COLUMNS.TOTAL_AMOUNT, translationKey: groupBy ? 'common.total' : 'iou.amount', @@ -316,6 +327,17 @@ const getExpenseReportHeaders = (profileIcon?: IconAsset): SearchColumnConfig[] columnName: CONST.SEARCH.TABLE_COLUMNS.ORDER_DEAL_NUMBERS, translationKey: 'common.internationalReimbursementIDs', }, + // The backend has no sort key for these amounts on individual reports. + { + columnName: CONST.SEARCH.TABLE_COLUMNS.AMOUNT_DEBITED, + translationKey: 'common.amountDebited', + isColumnSortable: false, + }, + { + columnName: CONST.SEARCH.TABLE_COLUMNS.AMOUNT_REIMBURSED, + translationKey: 'common.amountReimbursed', + isColumnSortable: false, + }, { columnName: CONST.SEARCH.TABLE_COLUMNS.BASE_62_REPORT_ID, translationKey: 'common.reportID', diff --git a/src/components/TransactionItemRow/TransactionItemRowWide.tsx b/src/components/TransactionItemRow/TransactionItemRowWide.tsx index 8dd2e39cb08e..a02c08b60e4b 100644 --- a/src/components/TransactionItemRow/TransactionItemRowWide.tsx +++ b/src/components/TransactionItemRow/TransactionItemRowWide.tsx @@ -155,6 +155,10 @@ function TransactionItemRowWide({ const submitterUserID = reportForCustomColumns?.submitterUserID; const submitterPayrollID = reportForCustomColumns?.submitterPayrollID; const orderDealNumbers = reportForCustomColumns?.orderDealNumbers; + const debitedAmount = reportForCustomColumns?.debitedAmount; + const debitedCurrency = reportForCustomColumns?.debitedCurrency; + const creditedAmount = reportForCustomColumns?.creditedAmount; + const creditedCurrency = reportForCustomColumns?.creditedCurrency; const hasValidationMessage = shouldShowErrors && (!!missingFieldError || !!violations?.length); let fullHeightMainRowStyle; if (shouldUseFullHeightEditableCellHoverTarget) { @@ -663,6 +667,34 @@ function TransactionItemRowWide({ ); + case CONST.SEARCH.TABLE_COLUMNS.AMOUNT_DEBITED: + return ( + + {!!debitedAmount && !!debitedCurrency && ( + + )} + + ); + case CONST.SEARCH.TABLE_COLUMNS.AMOUNT_REIMBURSED: + return ( + + {!!creditedAmount && !!creditedCurrency && ( + + )} + + ); default: return null; } diff --git a/src/styles/utils/index.ts b/src/styles/utils/index.ts index fd6fdae7aea4..6547d1ef3d77 100644 --- a/src/styles/utils/index.ts +++ b/src/styles/utils/index.ts @@ -2016,6 +2016,8 @@ const createStyleUtils = (theme: ThemeColors, styles: ThemeStyles) => ({ break; case CONST.SEARCH.TABLE_COLUMNS.GROUP_AMOUNT_DEBITED: case CONST.SEARCH.TABLE_COLUMNS.GROUP_AMOUNT_REIMBURSED: + case CONST.SEARCH.TABLE_COLUMNS.AMOUNT_DEBITED: + case CONST.SEARCH.TABLE_COLUMNS.AMOUNT_REIMBURSED: // Fixed width: wide enough for the long headers these columns carry, so no amount-based widening is needed. columnWidth = {...getWidthStyle(variables.w130), ...styles.alignItemsEnd}; break; From ab103c0a1464ba2fa7b4efa5986b6559915cef80 Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Mon, 3 Aug 2026 22:25:06 +0530 Subject: [PATCH 20/27] Cover the conversion amount filters and column visibility --- tests/unit/Search/SearchQueryUtilsTest.ts | 39 +++++++++++++++++++ tests/unit/Search/SearchUIUtilsTest.ts | 39 +++++++++++++++++++ .../ColumnAvailabilityTest.ts.snap | 4 ++ 3 files changed, 82 insertions(+) diff --git a/tests/unit/Search/SearchQueryUtilsTest.ts b/tests/unit/Search/SearchQueryUtilsTest.ts index fcf960cfa13b..67c63a40086a 100644 --- a/tests/unit/Search/SearchQueryUtilsTest.ts +++ b/tests/unit/Search/SearchQueryUtilsTest.ts @@ -594,6 +594,29 @@ describe('SearchQueryUtils', () => { expect(result).toEqual('type:expense total>1 total<1000'); }); + test('conversion amount filter values', () => { + const filterValues: Partial = { + type: 'expense-report', + amountDebitedGreaterThan: '1', + amountDebitedLessThan: '1000', + amountReimbursedEqualTo: '500', + }; + const result = buildQueryStringFromFilterFormValues(filterValues); + + expect(result).toEqual('type:expense-report amountDebited>1 amountDebited<1000 amountReimbursed:500'); + }); + + test('conversion amount filters survive on an expense search, where the expense reports its report amounts', () => { + const filterValues: Partial = { + type: 'expense', + amountDebitedEqualTo: '1694', + amountReimbursedGreaterThan: '1000', + }; + const result = buildQueryStringFromFilterFormValues(filterValues); + + expect(result).toEqual('type:expense amountDebited:1694 amountReimbursed>1000'); + }); + test('equal to filter values', () => { const filterValues: Partial = { type: 'expense', @@ -1182,6 +1205,22 @@ describe('SearchQueryUtils', () => { }); }); + test('conversion amount filters parse from either spelling of the key', () => { + const queryJSON = buildSearchQueryJSON('type:expense-report amount-debited>1000 amountReimbursed<2000'); + + if (!queryJSON) { + throw new Error('Failed to parse query string'); + } + + const result = buildFilterFormValuesFromQuery(queryJSON, {}, {}, {}, {}, {}, {}, {}); + + expect(result).toMatchObject({ + type: 'expense-report', + amountDebitedGreaterThan: '1000', + amountReimbursedLessThan: '2000', + }); + }); + test('withdrawal status filter parses valid values and drops invalid ones', () => { const policyCategories = {}; const policyTags = {}; diff --git a/tests/unit/Search/SearchUIUtilsTest.ts b/tests/unit/Search/SearchUIUtilsTest.ts index 6649664cc6f9..12ed593c8bf7 100644 --- a/tests/unit/Search/SearchUIUtilsTest.ts +++ b/tests/unit/Search/SearchUIUtilsTest.ts @@ -9650,6 +9650,45 @@ describe('SearchUIUtils', () => { expect(result).toContain(CONST.SEARCH.TABLE_COLUMNS.GROUP_FROM); }); + test('Should only show the conversion amount columns for reports whose reimbursement converted currencies', () => { + const visibleColumns = [CONST.SEARCH.TABLE_COLUMNS.DATE, CONST.SEARCH.TABLE_COLUMNS.AMOUNT_DEBITED, CONST.SEARCH.TABLE_COLUMNS.AMOUNT_REIMBURSED]; + const domesticReport = {reportID: '1', type: CONST.REPORT.TYPE.EXPENSE, total: -10000, currency: 'USD'}; + const crossBorderReport = { + ...domesticReport, + reportID: '2', + debitedAmount: 30000, + debitedCurrency: 'GBP', + creditedAmount: 37250, + creditedCurrency: 'USD', + }; + + // @ts-expect-error minimal dataset for getColumnsToShow + const domesticData: OnyxTypes.SearchResults['data'] = {[`${ONYXKEYS.COLLECTION.REPORT}${domesticReport.reportID}`]: domesticReport}; + const crossBorderData: OnyxTypes.SearchResults['data'] = {...domesticData, [`${ONYXKEYS.COLLECTION.REPORT}${crossBorderReport.reportID}`]: crossBorderReport}; + + const domesticColumns = SearchUIUtils.getColumnsToShow({currentAccountID: 1, data: domesticData, visibleColumns, type: CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT}); + expect(domesticColumns).not.toContain(CONST.SEARCH.TABLE_COLUMNS.AMOUNT_DEBITED); + expect(domesticColumns).not.toContain(CONST.SEARCH.TABLE_COLUMNS.AMOUNT_REIMBURSED); + + const crossBorderColumns = SearchUIUtils.getColumnsToShow({currentAccountID: 1, data: crossBorderData, visibleColumns, type: CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT}); + expect(crossBorderColumns).toContain(CONST.SEARCH.TABLE_COLUMNS.AMOUNT_DEBITED); + expect(crossBorderColumns).toContain(CONST.SEARCH.TABLE_COLUMNS.AMOUNT_REIMBURSED); + + // Without a currency the amount cannot be rendered, so that column stays hidden. + const missingCurrencyData: OnyxTypes.SearchResults['data'] = { + ...domesticData, + [`${ONYXKEYS.COLLECTION.REPORT}${crossBorderReport.reportID}`]: {...crossBorderReport, creditedCurrency: undefined}, + }; + const missingCurrencyColumns = SearchUIUtils.getColumnsToShow({ + currentAccountID: 1, + data: missingCurrencyData, + visibleColumns, + type: CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT, + }); + expect(missingCurrencyColumns).toContain(CONST.SEARCH.TABLE_COLUMNS.AMOUNT_DEBITED); + expect(missingCurrencyColumns).not.toContain(CONST.SEARCH.TABLE_COLUMNS.AMOUNT_REIMBURSED); + }); + test('Should only show the conversion amount columns for withdrawal groups that converted currencies', () => { const domesticGroup = { entryID: 1, diff --git a/tests/unit/Search/__snapshots__/ColumnAvailabilityTest.ts.snap b/tests/unit/Search/__snapshots__/ColumnAvailabilityTest.ts.snap index 23b2ccc516da..d8b41b90f2e8 100644 --- a/tests/unit/Search/__snapshots__/ColumnAvailabilityTest.ts.snap +++ b/tests/unit/Search/__snapshots__/ColumnAvailabilityTest.ts.snap @@ -28,6 +28,8 @@ exports[`Column availability single source of truth the derived picker lists mat "submitterUserID", "submitterPayrollID", "orderDealNumbers", + "amountDebited", + "amountReimbursed", ] `; @@ -70,5 +72,7 @@ exports[`Column availability single source of truth the derived picker lists mat "submitterUserID", "submitterPayrollID", "orderDealNumbers", + "amountDebited", + "amountReimbursed", ] `; From c1e5d3342a316d33e38e5911c49e7a455ef452a7 Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Tue, 4 Aug 2026 00:06:17 +0530 Subject: [PATCH 21/27] Keep the conversion amounts out of the expense view A payment pays a whole report, so an expense has no debited or reimbursed amount of its own to show or filter on. --- src/CONST/index.ts | 10 ------ src/components/Search/SearchTableHeader.tsx | 11 ------- .../TransactionItemRowWide.tsx | 32 ------------------- src/types/form/SearchAdvancedFiltersForm.ts | 8 ----- tests/unit/Search/ColumnAvailabilityTest.ts | 2 ++ tests/unit/Search/SearchQueryUtilsTest.ts | 4 +-- .../ColumnAvailabilityTest.ts.snap | 4 --- 7 files changed, 4 insertions(+), 67 deletions(-) diff --git a/src/CONST/index.ts b/src/CONST/index.ts index a05a75fc3485..a6b7d9a4617b 100644 --- a/src/CONST/index.ts +++ b/src/CONST/index.ts @@ -6982,16 +6982,6 @@ const CONST = { search: true, reportView: true, }, - AMOUNT_DEBITED: { - column: this.TABLE_COLUMNS.AMOUNT_DEBITED, - search: true, - reportView: true, - }, - AMOUNT_REIMBURSED: { - column: this.TABLE_COLUMNS.AMOUNT_REIMBURSED, - search: true, - reportView: true, - }, }; }, get TYPE_CUSTOM_COLUMNS() { diff --git a/src/components/Search/SearchTableHeader.tsx b/src/components/Search/SearchTableHeader.tsx index 0c7c88714c1d..47865a38892e 100644 --- a/src/components/Search/SearchTableHeader.tsx +++ b/src/components/Search/SearchTableHeader.tsx @@ -168,17 +168,6 @@ const getExpenseHeaders = (groupBy?: SearchGroupBy): SearchColumnConfig[] => [ columnName: CONST.SEARCH.TABLE_COLUMNS.ORDER_DEAL_NUMBERS, translationKey: 'common.internationalReimbursementIDs', }, - // The backend has no sort key for these amounts on individual reports. - { - columnName: CONST.SEARCH.TABLE_COLUMNS.AMOUNT_DEBITED, - translationKey: 'common.amountDebited', - isColumnSortable: false, - }, - { - columnName: CONST.SEARCH.TABLE_COLUMNS.AMOUNT_REIMBURSED, - translationKey: 'common.amountReimbursed', - isColumnSortable: false, - }, { columnName: CONST.SEARCH.TABLE_COLUMNS.TOTAL_AMOUNT, translationKey: groupBy ? 'common.total' : 'iou.amount', diff --git a/src/components/TransactionItemRow/TransactionItemRowWide.tsx b/src/components/TransactionItemRow/TransactionItemRowWide.tsx index a02c08b60e4b..8dd2e39cb08e 100644 --- a/src/components/TransactionItemRow/TransactionItemRowWide.tsx +++ b/src/components/TransactionItemRow/TransactionItemRowWide.tsx @@ -155,10 +155,6 @@ function TransactionItemRowWide({ const submitterUserID = reportForCustomColumns?.submitterUserID; const submitterPayrollID = reportForCustomColumns?.submitterPayrollID; const orderDealNumbers = reportForCustomColumns?.orderDealNumbers; - const debitedAmount = reportForCustomColumns?.debitedAmount; - const debitedCurrency = reportForCustomColumns?.debitedCurrency; - const creditedAmount = reportForCustomColumns?.creditedAmount; - const creditedCurrency = reportForCustomColumns?.creditedCurrency; const hasValidationMessage = shouldShowErrors && (!!missingFieldError || !!violations?.length); let fullHeightMainRowStyle; if (shouldUseFullHeightEditableCellHoverTarget) { @@ -667,34 +663,6 @@ function TransactionItemRowWide({ ); - case CONST.SEARCH.TABLE_COLUMNS.AMOUNT_DEBITED: - return ( - - {!!debitedAmount && !!debitedCurrency && ( - - )} - - ); - case CONST.SEARCH.TABLE_COLUMNS.AMOUNT_REIMBURSED: - return ( - - {!!creditedAmount && !!creditedCurrency && ( - - )} - - ); default: return null; } diff --git a/src/types/form/SearchAdvancedFiltersForm.ts b/src/types/form/SearchAdvancedFiltersForm.ts index cefe510b2fdf..68384a5161c2 100644 --- a/src/types/form/SearchAdvancedFiltersForm.ts +++ b/src/types/form/SearchAdvancedFiltersForm.ts @@ -277,14 +277,6 @@ const ALLOWED_TYPE_FILTERS: Record> = { FILTER_KEYS.TOTAL_NOT, FILTER_KEYS.TOTAL_GREATER_THAN, FILTER_KEYS.TOTAL_LESS_THAN, - FILTER_KEYS.AMOUNT_DEBITED_EQUAL_TO, - FILTER_KEYS.AMOUNT_DEBITED_NOT, - FILTER_KEYS.AMOUNT_DEBITED_GREATER_THAN, - FILTER_KEYS.AMOUNT_DEBITED_LESS_THAN, - FILTER_KEYS.AMOUNT_REIMBURSED_EQUAL_TO, - FILTER_KEYS.AMOUNT_REIMBURSED_NOT, - FILTER_KEYS.AMOUNT_REIMBURSED_GREATER_THAN, - FILTER_KEYS.AMOUNT_REIMBURSED_LESS_THAN, FILTER_KEYS.CURRENCY, FILTER_KEYS.CURRENCY_NOT, FILTER_KEYS.GROUP_CURRENCY, diff --git a/tests/unit/Search/ColumnAvailabilityTest.ts b/tests/unit/Search/ColumnAvailabilityTest.ts index a28fdb55ec0c..d960b00b2de0 100644 --- a/tests/unit/Search/ColumnAvailabilityTest.ts +++ b/tests/unit/Search/ColumnAvailabilityTest.ts @@ -33,6 +33,8 @@ describe('Column availability single source of truth', () => { TABLE_COLUMNS.FIRST_APPROVER, TABLE_COLUMNS.FIRST_APPROVED, TABLE_COLUMNS.PAID_STATUS, + TABLE_COLUMNS.AMOUNT_DEBITED, + TABLE_COLUMNS.AMOUNT_REIMBURSED, // Grouped search views. TABLE_COLUMNS.EXPENSES, TABLE_COLUMNS.FEED, diff --git a/tests/unit/Search/SearchQueryUtilsTest.ts b/tests/unit/Search/SearchQueryUtilsTest.ts index 67c63a40086a..4299e929c808 100644 --- a/tests/unit/Search/SearchQueryUtilsTest.ts +++ b/tests/unit/Search/SearchQueryUtilsTest.ts @@ -606,7 +606,7 @@ describe('SearchQueryUtils', () => { expect(result).toEqual('type:expense-report amountDebited>1 amountDebited<1000 amountReimbursed:500'); }); - test('conversion amount filters survive on an expense search, where the expense reports its report amounts', () => { + test('conversion amount filters are dropped on an expense search, since a payment pays a whole report', () => { const filterValues: Partial = { type: 'expense', amountDebitedEqualTo: '1694', @@ -614,7 +614,7 @@ describe('SearchQueryUtils', () => { }; const result = buildQueryStringFromFilterFormValues(filterValues); - expect(result).toEqual('type:expense amountDebited:1694 amountReimbursed>1000'); + expect(result).toEqual('type:expense'); }); test('equal to filter values', () => { diff --git a/tests/unit/Search/__snapshots__/ColumnAvailabilityTest.ts.snap b/tests/unit/Search/__snapshots__/ColumnAvailabilityTest.ts.snap index d8b41b90f2e8..23b2ccc516da 100644 --- a/tests/unit/Search/__snapshots__/ColumnAvailabilityTest.ts.snap +++ b/tests/unit/Search/__snapshots__/ColumnAvailabilityTest.ts.snap @@ -28,8 +28,6 @@ exports[`Column availability single source of truth the derived picker lists mat "submitterUserID", "submitterPayrollID", "orderDealNumbers", - "amountDebited", - "amountReimbursed", ] `; @@ -72,7 +70,5 @@ exports[`Column availability single source of truth the derived picker lists mat "submitterUserID", "submitterPayrollID", "orderDealNumbers", - "amountDebited", - "amountReimbursed", ] `; From a83b243b8c8faae91ce8820b5913ddd1d3a41b16 Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Tue, 4 Aug 2026 00:58:10 +0530 Subject: [PATCH 22/27] Sort reports by the conversion amounts now that the backend ranks them --- src/components/Search/SearchTableHeader.tsx | 3 --- src/components/Search/SortableTableHeader.tsx | 6 +++++- src/libs/SearchUIUtils.ts | 4 ++++ 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/components/Search/SearchTableHeader.tsx b/src/components/Search/SearchTableHeader.tsx index 47865a38892e..dce0eb64d31c 100644 --- a/src/components/Search/SearchTableHeader.tsx +++ b/src/components/Search/SearchTableHeader.tsx @@ -316,16 +316,13 @@ const getExpenseReportHeaders = (profileIcon?: IconAsset): SearchColumnConfig[] columnName: CONST.SEARCH.TABLE_COLUMNS.ORDER_DEAL_NUMBERS, translationKey: 'common.internationalReimbursementIDs', }, - // The backend has no sort key for these amounts on individual reports. { columnName: CONST.SEARCH.TABLE_COLUMNS.AMOUNT_DEBITED, translationKey: 'common.amountDebited', - isColumnSortable: false, }, { columnName: CONST.SEARCH.TABLE_COLUMNS.AMOUNT_REIMBURSED, translationKey: 'common.amountReimbursed', - isColumnSortable: false, }, { columnName: CONST.SEARCH.TABLE_COLUMNS.BASE_62_REPORT_ID, diff --git a/src/components/Search/SortableTableHeader.tsx b/src/components/Search/SortableTableHeader.tsx index 861b4b84abea..21daa1256854 100644 --- a/src/components/Search/SortableTableHeader.tsx +++ b/src/components/Search/SortableTableHeader.tsx @@ -80,7 +80,11 @@ function SortableTableHeader({ const sortByColumnName = sortColumnName ?? columnName; const isActive = sortBy === sortByColumnName; const isReimbursableOrBillableColumn = columnName === CONST.SEARCH.TABLE_COLUMNS.REIMBURSABLE || columnName === CONST.SEARCH.TABLE_COLUMNS.BILLABLE; - const isConversionAmountColumn = columnName === CONST.SEARCH.TABLE_COLUMNS.GROUP_AMOUNT_DEBITED || columnName === CONST.SEARCH.TABLE_COLUMNS.GROUP_AMOUNT_REIMBURSED; + const isConversionAmountColumn = + columnName === CONST.SEARCH.TABLE_COLUMNS.GROUP_AMOUNT_DEBITED || + columnName === CONST.SEARCH.TABLE_COLUMNS.GROUP_AMOUNT_REIMBURSED || + columnName === CONST.SEARCH.TABLE_COLUMNS.AMOUNT_DEBITED || + columnName === CONST.SEARCH.TABLE_COLUMNS.AMOUNT_REIMBURSED; const textStyle = [ columnName === CONST.SEARCH.TABLE_COLUMNS.RECEIPT ? StyleUtils.getTextOverflowStyle('clip') : null, isReimbursableOrBillableColumn || isConversionAmountColumn ? styles.flexShrink1 : null, diff --git a/src/libs/SearchUIUtils.ts b/src/libs/SearchUIUtils.ts index eeef250df510..11b5ebc8a583 100644 --- a/src/libs/SearchUIUtils.ts +++ b/src/libs/SearchUIUtils.ts @@ -340,6 +340,10 @@ const expenseReportColumnNamesToSortingProperty: ExpenseReportSorting = { [CONST.SEARCH.TABLE_COLUMNS.FROM]: 'formattedFrom' as const, [CONST.SEARCH.TABLE_COLUMNS.TO]: 'formattedTo' as const, [CONST.SEARCH.TABLE_COLUMNS.TOTAL]: 'total' as const, + // Both the backend page selection and this local sort rank the amounts as stored, without converting between + // currencies, so a report can outrank one that is worth more in another currency. + [CONST.SEARCH.TABLE_COLUMNS.AMOUNT_DEBITED]: 'debitedAmount' as const, + [CONST.SEARCH.TABLE_COLUMNS.AMOUNT_REIMBURSED]: 'creditedAmount' as const, [CONST.SEARCH.TABLE_COLUMNS.ACTION]: 'action' as const, }; From 141124a5823b4aaa57a08fcd0164bd7384fdcc3e Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Tue, 4 Aug 2026 00:58:10 +0530 Subject: [PATCH 23/27] Cover the report-level conversion amount sorts --- tests/unit/Search/SearchUIUtilsTest.ts | 43 ++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/tests/unit/Search/SearchUIUtilsTest.ts b/tests/unit/Search/SearchUIUtilsTest.ts index 12ed593c8bf7..dfd4d37ba9d9 100644 --- a/tests/unit/Search/SearchUIUtilsTest.ts +++ b/tests/unit/Search/SearchUIUtilsTest.ts @@ -6955,6 +6955,49 @@ describe('SearchUIUtils', () => { expect(sortGroups(CONST.SEARCH.TABLE_COLUMNS.GROUP_AMOUNT_REIMBURSED, CONST.SEARCH.SORT_ORDER.DESC)).toStrictEqual(['group_large', 'group_small', 'group_domestic']); }); + it('should sort expense reports by each conversion amount, leaving the reports that did not convert at the empty end', () => { + const withConversion = (keyForList: string, debitedAmount?: number, creditedAmount?: number) => + createMock({ + groupedBy: 'expense-report', + keyForList, + reportID: keyForList, + created: '2025-08-12 17:11:22', + currency: 'USD', + transactions: [], + ...(debitedAmount ? {debitedAmount, debitedCurrency: 'GBP'} : {}), + ...(creditedAmount ? {creditedAmount, creditedCurrency: 'USD'} : {}), + }); + const domestic = withConversion('report_domestic'); + const smallConversion = withConversion('report_small', 1694, 2000); + const largeConversion = withConversion('report_large', 32200, 40000); + // Debited the most but reimbursed the least, so the two sides rank the reports differently + const reversedConversion = withConversion('report_reversed', 90000, 100); + const reports = [smallConversion, domestic, largeConversion, reversedConversion]; + + const sortReports = (sortBy: SearchColumnType, sortOrder: SortOrder) => + SearchUIUtils.getSortedSections(CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT, [...reports], localeCompare, translateLocal, sortBy, sortOrder).map((report) => report.keyForList); + + expect(sortReports(CONST.SEARCH.TABLE_COLUMNS.AMOUNT_DEBITED, CONST.SEARCH.SORT_ORDER.ASC)).toStrictEqual(['report_domestic', 'report_small', 'report_large', 'report_reversed']); + expect(sortReports(CONST.SEARCH.TABLE_COLUMNS.AMOUNT_DEBITED, CONST.SEARCH.SORT_ORDER.DESC)).toStrictEqual([ + 'report_reversed', + 'report_large', + 'report_small', + 'report_domestic', + ]); + expect(sortReports(CONST.SEARCH.TABLE_COLUMNS.AMOUNT_REIMBURSED, CONST.SEARCH.SORT_ORDER.ASC)).toStrictEqual([ + 'report_domestic', + 'report_reversed', + 'report_small', + 'report_large', + ]); + expect(sortReports(CONST.SEARCH.TABLE_COLUMNS.AMOUNT_REIMBURSED, CONST.SEARCH.SORT_ORDER.DESC)).toStrictEqual([ + 'report_large', + 'report_small', + 'report_reversed', + 'report_domestic', + ]); + }); + it('should sort category group data when type is EXPENSE and groupBy is category', () => { expect( SearchUIUtils.getSortedSections( From c5645cafc7533375f8287d7ecabe2134fdb87652 Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Tue, 4 Aug 2026 01:00:43 +0530 Subject: [PATCH 24/27] Keep the conversion amount column the reports are sorted by Sorting ascending puts the reports without that amount first, so an all-domestic first page would drop the column, taking the header and the Sort by option with it. --- src/libs/SearchUIUtils.ts | 6 ++++++ tests/unit/Search/SearchUIUtilsTest.ts | 10 ++++++++++ 2 files changed, 16 insertions(+) diff --git a/src/libs/SearchUIUtils.ts b/src/libs/SearchUIUtils.ts index 2f0e2af6b2ed..97d8fdfcfe50 100644 --- a/src/libs/SearchUIUtils.ts +++ b/src/libs/SearchUIUtils.ts @@ -6053,6 +6053,12 @@ function getColumnsToShow({ return true; } + // The column the results are sorted by has to stay, even with nothing to show in it. Both the header and the + // Sort by option come from this list, so dropping it would leave the sort applied with no way to change it. + if (column === sortBy) { + return true; + } + return Object.keys(data).some((key) => { if (!isReportEntry(key)) { return false; diff --git a/tests/unit/Search/SearchUIUtilsTest.ts b/tests/unit/Search/SearchUIUtilsTest.ts index 5040523cf1a8..5dcb016316bb 100644 --- a/tests/unit/Search/SearchUIUtilsTest.ts +++ b/tests/unit/Search/SearchUIUtilsTest.ts @@ -9769,6 +9769,16 @@ describe('SearchUIUtils', () => { }); expect(missingCurrencyColumns).toContain(CONST.SEARCH.TABLE_COLUMNS.AMOUNT_DEBITED); expect(missingCurrencyColumns).not.toContain(CONST.SEARCH.TABLE_COLUMNS.AMOUNT_REIMBURSED); + + const sortedColumns = SearchUIUtils.getColumnsToShow({ + currentAccountID: 1, + data: domesticData, + visibleColumns, + type: CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT, + sortBy: CONST.SEARCH.TABLE_COLUMNS.AMOUNT_DEBITED, + }); + expect(sortedColumns).toContain(CONST.SEARCH.TABLE_COLUMNS.AMOUNT_DEBITED); + expect(sortedColumns).not.toContain(CONST.SEARCH.TABLE_COLUMNS.AMOUNT_REIMBURSED); }); test('Should only show the conversion amount columns for withdrawal groups that converted currencies', () => { From d86d7a395d56c883d0b5b48b7600eac888883bef Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Tue, 4 Aug 2026 01:07:20 +0530 Subject: [PATCH 25/27] Restore the currency formatter the cross-border payment messages use The currency list refactor removed the bare convertToDisplayString these three call sites relied on, leaving main unable to typecheck. --- src/libs/ReportActionsUtils.ts | 10 ++++++++-- src/libs/ReportUtils.ts | 2 +- tests/unit/ReportUtilsTest.ts | 10 +++++----- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index 86da6a3fb1bc..caaa6f33ae52 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -55,7 +55,13 @@ import type {OptimisticIOUReportAction, PartialReportAction} from './ReportUtils import {getBankName, isCardPendingActivate} from './CardUtils'; import {getDecodedCategoryName} from './CategoryUtils'; -import {convertAmountToDisplayString, convertToBackendAmount, convertToDisplayStringWithExplicitCurrency, convertToShortDisplayString} from './CurrencyUtils'; +import { + convertAmountToDisplayString, + convertToBackendAmount, + convertToDisplayString as convertToDisplayStringUtil, + convertToDisplayStringWithExplicitCurrency, + convertToShortDisplayString, +} from './CurrencyUtils'; import DateUtils from './DateUtils'; import {getEnvironmentURL, getOldDotEnvironmentURL} from './Environment/Environment'; import getBase62ReportID from './getBase62ReportID'; @@ -491,7 +497,7 @@ function getCrossBorderReimbursedMessage( } return translate('iou.reimbursedCrossBorder', { - amount: convertToDisplayString(creditedAmount, creditedCurrency), + amount: convertToDisplayStringUtil(creditedAmount, creditedCurrency), debitBankAccount: originalMessage.debitBankAccountLast4 ?? fallbackDebitBankAccountLast4 ?? '', creditBankAccount: originalMessage.creditBankAccountLast4 ?? '', }); diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index e6758ca7115b..6d4f1df83d3d 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -6084,7 +6084,7 @@ function getReportPreviewReportActionMessage(params: GetReportPreviewMessageBase // This variant returns raw English to match the surrounding non-localized preview strings. if (originalMessage?.creditedAmount && originalMessage.creditedCurrency) { - const creditedAmountDisplay = convertToDisplayString(originalMessage.creditedAmount, originalMessage.creditedCurrency); + const creditedAmountDisplay = convertToDisplayStringUtil(originalMessage.creditedAmount, originalMessage.creditedCurrency); return `paid ${creditedAmountDisplay} from account ${originalMessage.debitBankAccountLast4 ?? last4Digits} to account ${originalMessage.creditBankAccountLast4 ?? ''}`; } return `paid with bank account ${last4Digits}`; diff --git a/tests/unit/ReportUtilsTest.ts b/tests/unit/ReportUtilsTest.ts index f53cca0da3e7..0b888302d233 100644 --- a/tests/unit/ReportUtilsTest.ts +++ b/tests/unit/ReportUtilsTest.ts @@ -601,7 +601,7 @@ describe('ReportUtils', () => { debitBankAccount: '6789', creditBankAccount: '3335', }); - expect(getIOUReportActionDisplayMessage(translateLocal, crossBorderAction, undefined, iouReport)).toBe(expectedMessage); + expect(getIOUReportActionDisplayMessage(translateLocal, crossBorderAction, convertToDisplayString, undefined, iouReport)).toBe(expectedMessage); }); it('should return received payment when submitter marked payment received', () => { @@ -16511,7 +16511,7 @@ describe('ReportUtils', () => { it('names the credited amount, falling back to the policy default for the debited account', () => { // Given a converted payment that recorded the employee's account but not the account it was paid from - const result = getReportPreviewMessage(englishTranslate, crossBorderParams); + const result = getReportPreviewMessage(englishTranslate, convertToDisplayString, crossBorderParams); // Then the debited account comes from the policy default, the same fallback the non-converted wording uses expect(result).toBe( @@ -16525,13 +16525,13 @@ describe('ReportUtils', () => { it('stores the same wording on the report action as the localized preview shows', () => { // The hardcoded English copy must not drift from the localized function - expect(getReportPreviewReportActionMessage(crossBorderParams)).toBe(getReportPreviewMessage(englishTranslate, crossBorderParams)); + expect(getReportPreviewReportActionMessage(crossBorderParams)).toBe(getReportPreviewMessage(englishTranslate, convertToDisplayString, crossBorderParams)); }); it('still names the report total in the parent chat preview', () => { // Given the parent chat preview, which summarizes the report rather than describing the payment const params = {...crossBorderParams, isPreviewMessageForParentChatReport: true}; - const paymentWithoutConversion = getReportPreviewMessage(englishTranslate, { + const paymentWithoutConversion = getReportPreviewMessage(englishTranslate, convertToDisplayString, { reportOrID: settledReport, iouReportAction: payReportAction, originalReportAction: payReportAction, @@ -16539,7 +16539,7 @@ describe('ReportUtils', () => { }); // Then the credited amount does not replace the report total, which is what the report is denominated in - expect(getReportPreviewMessage(englishTranslate, params)).toBe(paymentWithoutConversion); + expect(getReportPreviewMessage(englishTranslate, convertToDisplayString, params)).toBe(paymentWithoutConversion); }); }); }); From 9a7841b596c8c65e64627a87f8b3b574d0583815 Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Tue, 4 Aug 2026 12:55:13 +0530 Subject: [PATCH 26/27] Add the Greek strings for the two conversion amount columns --- src/languages/el.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/languages/el.ts b/src/languages/el.ts index bf92231ac01f..f4670a58e355 100644 --- a/src/languages/el.ts +++ b/src/languages/el.ts @@ -411,6 +411,8 @@ const translations: TranslationDeepObject = { longReportID: 'Μακρύ αναγνωριστικό αναφοράς', withdrawalID: 'Αναγνωριστικό ανάληψης', internationalReimbursementIDs: 'Διεθνή αναγνωριστικά αποζημιώσεων', + amountDebited: 'Ποσό χρέωσης', + amountReimbursed: 'Ποσό αποζημίωσης', withdrawalStatus: 'Κατάσταση ανάληψης', paidStatus: 'Κατάσταση πληρωμής', bankAccounts: 'Τραπεζικοί λογαριασμοί', From baf9b30ed404a869f45642317f0c93603e39272c Mon Sep 17 00:00:00 2001 From: Ishpaul Singh Date: Tue, 4 Aug 2026 13:11:10 +0530 Subject: [PATCH 27/27] Regenerate the two conversion amount strings with the translation script --- src/languages/el.ts | 2 +- src/languages/ja.ts | 4 ++-- src/languages/pl.ts | 2 +- src/languages/zh-hans.ts | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/languages/el.ts b/src/languages/el.ts index f4670a58e355..f610d0f60205 100644 --- a/src/languages/el.ts +++ b/src/languages/el.ts @@ -412,7 +412,7 @@ const translations: TranslationDeepObject = { withdrawalID: 'Αναγνωριστικό ανάληψης', internationalReimbursementIDs: 'Διεθνή αναγνωριστικά αποζημιώσεων', amountDebited: 'Ποσό χρέωσης', - amountReimbursed: 'Ποσό αποζημίωσης', + amountReimbursed: 'Ποσό που αποζημιώθηκε', withdrawalStatus: 'Κατάσταση ανάληψης', paidStatus: 'Κατάσταση πληρωμής', bankAccounts: 'Τραπεζικοί λογαριασμοί', diff --git a/src/languages/ja.ts b/src/languages/ja.ts index e50d93dd8cf9..de0a702f0322 100644 --- a/src/languages/ja.ts +++ b/src/languages/ja.ts @@ -404,8 +404,8 @@ const translations: TranslationDeepObject = { longReportID: '長いレポートID', withdrawalID: '出金ID', internationalReimbursementIDs: '国際払い戻しID', - amountDebited: '引き落とし金額', - amountReimbursed: '払い戻し金額', + amountDebited: '引き落とし額', + amountReimbursed: '精算済み金額', withdrawalStatus: '出金ステータス', paidStatus: '支払済みステータス', bankAccounts: '銀行口座', diff --git a/src/languages/pl.ts b/src/languages/pl.ts index 26e23fc4cdc9..e38d465843c6 100644 --- a/src/languages/pl.ts +++ b/src/languages/pl.ts @@ -405,7 +405,7 @@ const translations: TranslationDeepObject = { withdrawalID: 'Identyfikator wypłaty', internationalReimbursementIDs: 'Identyfikatory zwrotów międzynarodowych', amountDebited: 'Kwota obciążenia', - amountReimbursed: 'Kwota zwrotu', + amountReimbursed: 'Kwota zwrócona', withdrawalStatus: 'Status wypłaty', paidStatus: 'Status: opłacono', bankAccounts: 'Konta bankowe', diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts index 1be0a356029d..21824aee2580 100644 --- a/src/languages/zh-hans.ts +++ b/src/languages/zh-hans.ts @@ -405,7 +405,7 @@ const translations: TranslationDeepObject = { withdrawalID: '提现编号', internationalReimbursementIDs: '国际报销 ID', amountDebited: '扣款金额', - amountReimbursed: '报销金额', + amountReimbursed: '已报销金额', withdrawalStatus: '提现状态', paidStatus: '已付款状态', bankAccounts: '银行账户',