Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/CONST/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6901,6 +6901,11 @@ const CONST = {
reportView: true,
},
TAG: {column: this.TABLE_COLUMNS.TAG, search: true, reportView: true},
VIOLATIONS: {
column: this.TABLE_COLUMNS.VIOLATIONS,
search: false,
reportView: false,
},
TAG_GL_CODE: {
column: this.TABLE_COLUMNS.TAG_GL_CODE,
search: true,
Expand Down Expand Up @@ -7212,6 +7217,7 @@ const CONST = {
TO: 'to',
CATEGORY: 'category',
TAG: 'tag',
VIOLATIONS: 'violations',
ORIGINAL_AMOUNT: 'originalamount',
REIMBURSABLE: 'reimbursable',
BILLABLE: 'billable',
Expand Down Expand Up @@ -7437,6 +7443,7 @@ const CONST = {
[this.TABLE_COLUMNS.TO]: 'to',
[this.TABLE_COLUMNS.CATEGORY]: 'category',
[this.TABLE_COLUMNS.TAG]: 'tag',
[this.TABLE_COLUMNS.VIOLATIONS]: 'violations',
[this.TABLE_COLUMNS.ORIGINAL_AMOUNT]: 'purchase-amount',
[this.TABLE_COLUMNS.REIMBURSABLE]: 'reimbursable',
[this.TABLE_COLUMNS.BILLABLE]: 'billable',
Expand Down Expand Up @@ -7535,6 +7542,7 @@ const CONST = {
TOP_CATEGORIES: 'topCategories',
TOP_MERCHANTS: 'topMerchants',
SPEND_OVER_TIME: 'spendOverTime',
VIOLATIONS_BY_SUBMITTER: 'violationsBySubmitter',
},
SAVED_SEARCH_PREFIX: 'savedSearch_',
GROUP_PREFIX: 'group_',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ function TransactionGroupListExpandedImpl({
)}
{visibleTransactions.map((transaction, index) => {
const shouldShowBottomBorder = !isLastTransaction(index);
const exportedReportActions = Object.values(transactionsSnapshot?.data?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transaction?.reportID}`] ?? {});
const reportActions = Object.values(transactionsSnapshot?.data?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transaction?.reportID}`] ?? {});
const isDeletedOrPendingDelete = isDeletedTransaction(transaction) || isTransactionPendingDelete(transaction);

return (
Expand Down Expand Up @@ -378,7 +378,7 @@ function TransactionGroupListExpandedImpl({
shouldShowBottomBorder={shouldShowBottomBorder}
onArrowRightPress={isDeletedOrPendingDelete ? undefined : (event) => openReportInRHP(transaction, event)}
shouldShowArrowRightOnNarrowLayout
reportActions={exportedReportActions}
reportActions={reportActions}
nonPersonalAndWorkspaceCards={nonPersonalAndWorkspaceCards}
isActionColumnWide={isActionColumnWide}
isHover={hovered}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ function TransactionListItemNarrow<TItem extends ListItem>({
handleActionButtonPress,
shouldDisableActionPointerEvents,
transactionPreviewData,
exportedReportActions,
reportActions,
nonPersonalAndWorkspaceCards,
isAttendeesEnabledForMovingPolicy,
}: TransactionListItemNarrowProps<TItem>) {
Expand Down Expand Up @@ -170,7 +170,7 @@ function TransactionListItemNarrow<TItem extends ListItem>({
onArrowRightPress={isDeletedTransaction ? undefined : (event) => onSelectRow(item, transactionPreviewData, event)}
isHover={false}
nonPersonalAndWorkspaceCards={nonPersonalAndWorkspaceCards}
reportActions={exportedReportActions}
reportActions={reportActions}
isAttendeesEnabledForMovingPolicy={isAttendeesEnabledForMovingPolicy}
/>
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ function TransactionListItemWide<TItem extends ListItem>({
handleActionButtonPress,
shouldDisableActionPointerEvents,
transactionPreviewData,
exportedReportActions,
reportActions,
policyCategories,
policyTagLists,
nonPersonalAndWorkspaceCards,
Expand Down Expand Up @@ -210,7 +210,7 @@ function TransactionListItemWide<TItem extends ListItem>({
onArrowRightPress={isDeletedTransaction ? undefined : (event) => onSelectRow(item, transactionPreviewData, event)}
isHover={hovered}
nonPersonalAndWorkspaceCards={nonPersonalAndWorkspaceCards}
reportActions={exportedReportActions}
reportActions={reportActions}
isAttendeesEnabledForMovingPolicy={isAttendeesEnabledForMovingPolicy}
onEditDate={onEditDate}
onEditMerchant={onEditMerchant}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ function TransactionListItemInner<TItem extends ListItem>({
const snapshotPolicy = (currentSearchResults?.data?.[`${ONYXKEYS.COLLECTION.POLICY}${transactionItem.policyID}`] ?? {}) as Policy;

const actionsData = currentSearchResults?.data?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionItem.reportID}`];
const exportedReportActions = actionsData ? Object.values(actionsData) : [];
const reportActions = actionsData ? Object.values(actionsData) : [];

// Fetch policy categories directly from Onyx since they are not included in the search snapshot
const [policyCategories] = originalUseOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${getNonEmptyStringOnyxID(policyID)}`);
Expand All @@ -134,7 +134,7 @@ function TransactionListItemInner<TItem extends ListItem>({
const [transactionViolationsForRow] = originalUseOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${getNonEmptyStringOnyxID(transactionItem.transactionID)}`);
const parentReportActionID = transactionItem?.reportAction?.reportActionID;
const [parentReportAction] = originalUseOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${getNonEmptyStringOnyxID(transactionItem.reportID)}`, {
selector: (reportActions: OnyxEntry<ReportActions>): OnyxEntry<ReportAction> => reportActions?.[`${parentReportActionID}`],
selector: (actions: OnyxEntry<ReportActions>): OnyxEntry<ReportAction> => actions?.[`${parentReportActionID}`],
});
const currentUserDetails = useCurrentUserPersonalDetails();
const chatReportID = snapshotReport?.chatReportID ?? snapshotReport?.parentReportID;
Expand All @@ -156,7 +156,7 @@ function TransactionListItemInner<TItem extends ListItem>({
reportID: transactionItem.reportID,
itemKey: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionItem.transactionID}`,
snapshotData,
snapshotActions: exportedReportActions,
snapshotActions: reportActions,
enabled: !!snapshotData,
});
const transactionPreviewData: TransactionPreviewData = {
Expand Down Expand Up @@ -260,7 +260,7 @@ function TransactionListItemInner<TItem extends ListItem>({
handleActionButtonPress,
shouldDisableActionPointerEvents: shouldDisableSearchSubmitPress,
transactionPreviewData,
exportedReportActions,
reportActions,
policyCategories,
policyTagLists,
nonPersonalAndWorkspaceCards,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ type TransactionListItemSharedProps<TItem extends ListItem> = {
handleActionButtonPress: (event?: ModifiedMouseEvent) => void;
shouldDisableActionPointerEvents?: boolean;
transactionPreviewData: TransactionPreviewData;
exportedReportActions: ReportAction[];
reportActions: ReportAction[];
policyCategories?: PolicyCategories;
policyTagLists?: PolicyTagLists;
nonPersonalAndWorkspaceCards?: CardList;
Expand Down
5 changes: 5 additions & 0 deletions src/components/Search/SearchTableHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,11 @@ const getExpenseHeaders = (groupBy?: SearchGroupBy): SearchColumnConfig[] => [
translationKey: 'common.tag',
canEdit: true,
},
{
columnName: CONST.SEARCH.TABLE_COLUMNS.VIOLATIONS,
translationKey: 'common.violations',
isColumnSortable: false,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is this behavior expected according to the design? Since this is a string, I think we should enable sorting for consistency with other columns

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think it's ok to do that in a follow up. The backend doesn't support sorting by it yet.

Comment thread
nkdengineer marked this conversation as resolved.
},
{
columnName: CONST.SEARCH.TABLE_COLUMNS.TAG_GL_CODE,
translationKey: 'common.tagGLCode',
Expand Down
11 changes: 11 additions & 0 deletions src/components/TransactionItemRow/TransactionItemRowWide.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import getBase62ReportID from '@libs/getBase62ReportID';
import {getTagGLCode} from '@libs/PolicyUtils';
import {getReportName} from '@libs/ReportNameUtils';
import {getReimbursableTotal, isExpenseReport} from '@libs/ReportUtils';
import {getSubmittedViolationsForTransaction} from '@libs/SearchUIUtils';
import {getShiftKeyFromEvent} from '@libs/shiftRangeSelection';
import {
getAmount,
Expand Down Expand Up @@ -155,6 +156,7 @@ function TransactionItemRowWide({
const submitterUserID = reportForCustomColumns?.submitterUserID;
const submitterPayrollID = reportForCustomColumns?.submitterPayrollID;
const orderDealNumbers = reportForCustomColumns?.orderDealNumbers;
const submittedViolations = getSubmittedViolationsForTransaction(reportActions, transactionItem.transactionID, translate);
const hasValidationMessage = shouldShowErrors && (!!missingFieldError || !!violations?.length);
let fullHeightMainRowStyle;
if (shouldUseFullHeightEditableCellHoverTarget) {
Expand Down Expand Up @@ -205,6 +207,15 @@ function TransactionItemRowWide({
/>
</View>
);
case CONST.SEARCH.TABLE_COLUMNS.VIOLATIONS:
return (
<View
key={column}
style={[StyleUtils.getReportTableColumnStyles(CONST.SEARCH.TABLE_COLUMNS.VIOLATIONS)]}
>
<TextCell text={submittedViolations} />
</View>
);
case CONST.SEARCH.TABLE_COLUMNS.TAG_GL_CODE:
return (
<View
Expand Down
1 change: 1 addition & 0 deletions src/hooks/useSearchShouldCalculateTotals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ function useSearchShouldCalculateTotals(searchKey: SearchKey | undefined, search
CONST.SEARCH.SEARCH_KEYS.TOP_MERCHANTS,
CONST.SEARCH.SEARCH_KEYS.TOP_SPENDERS,
CONST.SEARCH.SEARCH_KEYS.SPEND_OVER_TIME,
CONST.SEARCH.SEARCH_KEYS.VIOLATIONS_BY_SUBMITTER,
];

const isSuggestedSearchWithTotals = eligibleSearchKeys.includes(searchKey);
Expand Down
1 change: 1 addition & 0 deletions src/hooks/useSearchTypeMenuSections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ const policyMapper = (policy: OnyxEntry<Policy>): OnyxEntry<Policy> =>
achAccount: policy.achAccount,
areCategoriesEnabled: policy.areCategoriesEnabled,
areWorkflowsEnabled: policy.areWorkflowsEnabled,
areRulesEnabled: policy.areRulesEnabled,
};

const currentUserLoginAndAccountIDSelector = (session: OnyxEntry<Session>) => ({
Expand Down
47 changes: 47 additions & 0 deletions src/languages/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,7 @@ const translations: TranslationDeepObject<typeof en> = {
billable: 'Abrechenbar',
nonBillable: 'Nicht abrechenbar',
tag: 'Tag',
violations: 'Violations',
receipt: 'Beleg',
verified: 'Verifiziert',
replace: 'Ersetzen',
Expand Down Expand Up @@ -9168,6 +9169,7 @@ Fügen Sie weitere Ausgabelimits hinzu, um den Cashflow Ihres Unternehmens zu sc
topSpenders: 'Höchste Ausgaben',
topCategories: 'Topkategorien',
topMerchants: 'Top-Händler',
violationsBySubmitter: 'Violations by submitter',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Untranslated copy

},
},
genericErrorPage: {
Expand Down Expand Up @@ -9833,7 +9835,52 @@ Fügen Sie weitere Ausgabelimits hinzu, um den Cashflow Ihres Unternehmens zu sc
customUnitRateOutOfDateRangeStartOnly: ({startDate}: {startDate: string}) => `Der Satz gilt erst ab dem ${startDate}`,
customUnitRateOutOfDateRangeEndOnly: ({endDate}: {endDate: string}) => `Satz gilt nur bis ${endDate}`,
cannotMergeDuplicates: 'Sie können Ausgaben nur in Entwürfen oder offenen Berichten zusammenführen. Ziehen Sie den Bericht zurück und versuchen Sie es erneut.',
shortName: {
allTagLevelsRequired: 'All tags required',
autoReportedRejectedExpense: 'Expense rejected',
billableExpense: 'Billable no longer valid',
cashExpenseWithNoReceipt: 'Receipt required',
categoryOutOfPolicy: 'Category no longer valid',
companyCardRequired: 'Company card required',
conversionSurcharge: 'Conversion surcharge applied',
customUnitOutOfPolicy: 'Rate not valid for workspace',
customUnitRateOutOfDateRange: 'Rate outside valid dates',
duplicatedTransaction: 'Potential duplicate',
fieldRequired: 'Report field required',
futureDate: 'Future date not allowed',
hold: 'Expense on hold',
inactiveVendor: 'Vendor no longer valid',
increasedDistance: 'Distance exceeds route',
invoiceMarkup: 'Invoice marked up',
itemizedReceiptRequired: 'Itemized receipt required',
maxAge: 'Expense too old',
missingAttendees: 'Attendees required',
missingCategory: 'Missing category',
missingComment: 'Description required',
missingTag: 'Missing tag',
modifiedAmount: 'Amount modified',
modifiedDate: 'Date modified',
noRoute: 'No valid route',
nonExpensiworksExpense: 'Non-Expensiworks expense',
overAutoApprovalLimit: 'Over auto-approval limit',
overCategoryLimit: 'Over category limit',
overLimit: 'Over limit',
overTripLimit: 'Over trip limit',
perDayLimit: 'Over daily limit',
prohibitedExpense: 'Prohibited expense',
receiptGeneratedWithAI: 'Possible AI-generated receipt',
receiptNotSmartScanned: 'Receipt added manually',
receiptRequired: 'Receipt required',
rter: 'Awaiting card match',
smartscanFailed: 'Receipt scanning failed',
someTagLevelsRequired: 'Tag required',
taxAmountChanged: 'Tax amount modified',
taxOutOfPolicy: 'Tax rate no longer valid',
taxRateChanged: 'Tax rate modified',
taxRequired: 'Missing tax rate',
},
},

reportViolations: {
[CONST.REPORT_VIOLATIONS.FIELD_REQUIRED]: (fieldName: string) => `${fieldName} ist erforderlich`,
reportContainsExpensesWithViolations: 'Der Bericht enthält Ausgaben mit Verstößen.',
Expand Down
50 changes: 50 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,7 @@ const translations = {
billable: 'Billable',
nonBillable: 'Non-billable',
tag: 'Tag',
violations: 'Violations',
receipt: 'Receipt',
verified: 'Verified',
replace: 'Replace',
Expand Down Expand Up @@ -9025,6 +9026,7 @@ const translations = {
topSpenders: 'Top spenders',
topCategories: 'Top categories',
topMerchants: 'Top merchants',
violationsBySubmitter: 'Violations by submitter',
},
resultsAreLimited: 'Search results are limited.',
viewResults: 'View results',
Expand Down Expand Up @@ -9975,6 +9977,54 @@ const translations = {
resolvedDuplicates: 'resolved the duplicate',
companyCardRequired: 'Company card purchases required',
noRoute: 'Please select a valid address',
/**
* Parameter-free labels for submitted violations shown in Search table columns.
* Prefer these over sibling `violations.*` keys when violation data/context is unavailable.
*/
shortName: {
allTagLevelsRequired: 'All tags required',
autoReportedRejectedExpense: 'Expense rejected',
billableExpense: 'Billable no longer valid',
cashExpenseWithNoReceipt: 'Receipt required',
categoryOutOfPolicy: 'Category no longer valid',
companyCardRequired: 'Company card required',
conversionSurcharge: 'Conversion surcharge applied',
customUnitOutOfPolicy: 'Rate not valid for workspace',
customUnitRateOutOfDateRange: 'Rate outside valid dates',
duplicatedTransaction: 'Potential duplicate',
fieldRequired: 'Report field required',
futureDate: 'Future date not allowed',
hold: 'Expense on hold',
inactiveVendor: 'Vendor no longer valid',
increasedDistance: 'Distance exceeds route',
invoiceMarkup: 'Invoice marked up',
itemizedReceiptRequired: 'Itemized receipt required',
maxAge: 'Expense too old',
missingAttendees: 'Attendees required',
missingCategory: 'Missing category',
missingComment: 'Description required',
missingTag: 'Missing tag',
modifiedAmount: 'Amount modified',
modifiedDate: 'Date modified',
noRoute: 'No valid route',
nonExpensiworksExpense: 'Non-Expensiworks expense',
overAutoApprovalLimit: 'Over auto-approval limit',
overCategoryLimit: 'Over category limit',
overLimit: 'Over limit',
overTripLimit: 'Over trip limit',
perDayLimit: 'Over daily limit',
prohibitedExpense: 'Prohibited expense',
receiptGeneratedWithAI: 'Possible AI-generated receipt',
receiptNotSmartScanned: 'Receipt added manually',
receiptRequired: 'Receipt required',
rter: 'Awaiting card match',
smartscanFailed: 'Receipt scanning failed',
someTagLevelsRequired: 'Tag required',
taxAmountChanged: 'Tax amount modified',
taxOutOfPolicy: 'Tax rate no longer valid',
taxRateChanged: 'Tax rate modified',
taxRequired: 'Missing tax rate',
},
},
reportViolations: {
[CONST.REPORT_VIOLATIONS.FIELD_REQUIRED]: (fieldName: string) => `${fieldName} is required`,
Expand Down
Loading
Loading