-
Notifications
You must be signed in to change notification settings - Fork 3.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[No QA] Bootstrap secondary actions getter #57678
Changes from 7 commits
ad51938
4dfd0ce
8c98298
265eeb3
60532af
1f7eb7b
e45b471
19e344a
3108f37
cd79a0f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,332 @@ | ||||||||||
import type {OnyxCollection} from 'react-native-onyx'; | ||||||||||
import type {ValueOf} from 'type-fest'; | ||||||||||
import CONST from '@src/CONST'; | ||||||||||
import type {Policy, Report, Transaction, TransactionViolation} from '@src/types/onyx'; | ||||||||||
import {isApprover as isApprovedMember} from './actions/Policy/Member'; | ||||||||||
import {getCurrentUserAccountID} from './actions/Report'; | ||||||||||
import {arePaymentsEnabled, getCorrectedAutoReportingFrequency, hasAccountingConnections, hasNoPolicyOtherThanPersonalType, isAutoSyncEnabled, isPrefferedExporter} from './PolicyUtils'; | ||||||||||
import {getReportActions} from './ReportActionsUtils'; | ||||||||||
import { | ||||||||||
isClosedReport, | ||||||||||
isCurrentUserSubmitter, | ||||||||||
isExpenseReport, | ||||||||||
isExported, | ||||||||||
isInvoiceReport, | ||||||||||
isIOUReport, | ||||||||||
isOpenReport, | ||||||||||
isPayer, | ||||||||||
isProcessingReport, | ||||||||||
isReportApproved, | ||||||||||
isReportManager, | ||||||||||
isSettled, | ||||||||||
} from './ReportUtils'; | ||||||||||
import {getSession} from './SessionUtils'; | ||||||||||
import {allHavePendingRTERViolation, isDuplicate, isOnHold as isOnHoldTransactionUtils, shouldShowBrokenConnectionViolationForMultipleTransactions} from './TransactionUtils'; | ||||||||||
|
||||||||||
function isSubmitAction(report: Report, policy: Policy): boolean { | ||||||||||
const isExpense = isExpenseReport(report); | ||||||||||
|
||||||||||
if (!isExpense) { | ||||||||||
return false; | ||||||||||
} | ||||||||||
|
||||||||||
const isSubmitter = isCurrentUserSubmitter(report.reportID); | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same as ^ |
||||||||||
const isApprover = isApprovedMember(policy, getCurrentUserAccountID()); | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't we use existing isApprover util here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm using existing |
||||||||||
|
||||||||||
if (!isSubmitter && !isApprover) { | ||||||||||
return false; | ||||||||||
} | ||||||||||
|
||||||||||
const isOpen = isOpenReport(report); | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same as ^, please name the report as isOpenReport and adjust the util accordingly |
||||||||||
|
||||||||||
if (!isOpen) { | ||||||||||
return false; | ||||||||||
} | ||||||||||
|
||||||||||
const autoReportingFrequency = getCorrectedAutoReportingFrequency(policy); | ||||||||||
|
||||||||||
const isScheduledSubmitEnabled = policy?.harvesting?.enabled && autoReportingFrequency !== CONST.POLICY.AUTO_REPORTING_FREQUENCIES.MANUAL; | ||||||||||
|
||||||||||
return !!isScheduledSubmitEnabled; | ||||||||||
} | ||||||||||
|
||||||||||
function isApproveAction(report: Report, policy: Policy, reportTransactions: Transaction[], violations: OnyxCollection<TransactionViolation[]>): boolean { | ||||||||||
const isExpense = isExpenseReport(report); | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same as ^ |
||||||||||
const isApprover = isApprovedMember(policy, getCurrentUserAccountID()); | ||||||||||
const isProcessing = isProcessingReport(report); | ||||||||||
const hasDuplicates = reportTransactions.some((transaction) => isDuplicate(transaction.transactionID)); | ||||||||||
|
||||||||||
if (isExpense && isApprover && isProcessing && hasDuplicates) { | ||||||||||
return true; | ||||||||||
} | ||||||||||
|
||||||||||
const transactionIDs = reportTransactions.map((t) => t.transactionID); | ||||||||||
|
||||||||||
const hasAllPendingRTERViolations = allHavePendingRTERViolation(transactionIDs, violations); | ||||||||||
|
||||||||||
if (hasAllPendingRTERViolations) { | ||||||||||
return true; | ||||||||||
} | ||||||||||
|
||||||||||
const isAdmin = policy?.role === CONST.POLICY.ROLE.ADMIN; | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this should be compared against the current user right? , @luacmartins is this correct? i'm not sure There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||||||
|
||||||||||
const shouldShowBrokenConnectionViolation = shouldShowBrokenConnectionViolationForMultipleTransactions(transactionIDs, report, policy, violations); | ||||||||||
|
||||||||||
const userControllsReport = isApprover || isAdmin; | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this name is fine and we're already using it in the primary actions. Let's just fix the typo |
||||||||||
return userControllsReport && shouldShowBrokenConnectionViolation; | ||||||||||
} | ||||||||||
|
||||||||||
function isUnapproveAction(report: Report, policy: Policy): boolean { | ||||||||||
const isExpense = isExpenseReport(report); | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same as ^ |
||||||||||
const isApprover = isApprovedMember(policy, getCurrentUserAccountID()); | ||||||||||
const isApproved = isReportApproved({report}); | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please name this as isReportApproved, and rename util accordingly |
||||||||||
|
||||||||||
return isExpense && isApprover && isApproved; | ||||||||||
} | ||||||||||
|
||||||||||
function isCancelPaymentAction(report: Report): boolean { | ||||||||||
const isExpense = isExpenseReport(report); | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same as ^ |
||||||||||
|
||||||||||
if (!isExpense) { | ||||||||||
return false; | ||||||||||
} | ||||||||||
|
||||||||||
const isPaidElsewhere = report.stateNum === CONST.REPORT.STATE_NUM.APPROVED && report.statusNum === CONST.REPORT.STATUS_NUM.REIMBURSED; | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please rename this as isReportPaidElsewhere |
||||||||||
|
||||||||||
if (isPaidElsewhere) { | ||||||||||
return true; | ||||||||||
} | ||||||||||
|
||||||||||
const isPaymentProcessing = true; // TODO | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @luacmartins I'm not sure how to determine this value There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can check if There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @luacmartins but the payment is processing and not settled ? do we assume processing payments as settled ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @luacmartins current condition for showing cancel payment button is this: App/src/pages/ReportDetailsPage.tsx Line 367 in 237759d
And Lines 8287 to 8289 in 237759d
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Well, in this case the processing state is given by the combination of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jnowakow yes, we use |
||||||||||
const hasDailyNachaCutoffPassed = false; // TODO | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @luacmartins I'm not sure how to determine this value There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The cutoff is 23:45 PM UTC the day the payment was made There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm so here I have to find payment transaction and get it's creation date and check if it's after 23:45 on that day? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, you can probably use getIOUActionForReportID to get the IOU action and check if it's a pay action and then use the reportAction date |
||||||||||
return isPaymentProcessing && !hasDailyNachaCutoffPassed; | ||||||||||
} | ||||||||||
|
||||||||||
function isExportAction(report: Report, policy: Policy): boolean { | ||||||||||
const isInvoice = isInvoiceReport(report); | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same as ^ |
||||||||||
const isSender = isCurrentUserSubmitter(report.reportID); | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same as ^ |
||||||||||
|
||||||||||
if (isInvoice && isSender) { | ||||||||||
return true; | ||||||||||
} | ||||||||||
|
||||||||||
const isExpense = isExpenseReport(report); | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same as ^ |
||||||||||
|
||||||||||
const hasAccountingConnection = hasAccountingConnections(policy); | ||||||||||
|
||||||||||
if (!isExpense || !hasAccountingConnection) { | ||||||||||
return false; | ||||||||||
} | ||||||||||
|
||||||||||
const isApproved = isReportApproved({report}); | ||||||||||
const isReportPayer = isPayer(getSession(), report, false, policy); | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same as ^ |
||||||||||
const isPaymentsEnabled = arePaymentsEnabled(policy); | ||||||||||
const isClosed = isClosedReport(report); | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this should be names as isReportClosed |
||||||||||
|
||||||||||
if (isReportPayer && isPaymentsEnabled && (isApproved || isClosed)) { | ||||||||||
return true; | ||||||||||
} | ||||||||||
|
||||||||||
const isAdmin = policy?.role === CONST.POLICY.ROLE.ADMIN; | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not sure if this check is correct, same @luacmartins we should check this with the current user right? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. policy.role is specific to the current user, so this is comparing against the current user |
||||||||||
const isReimbursed = report.statusNum === CONST.REPORT.STATUS_NUM.REIMBURSED; | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. isReportReimbursed please |
||||||||||
const syncEnabled = isAutoSyncEnabled(policy); | ||||||||||
const isReportExported = isExported(getReportActions(report)); | ||||||||||
const isFinished = isApproved || isReimbursed || isClosed; | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||||||
|
||||||||||
return isAdmin && isFinished && syncEnabled && !isReportExported; | ||||||||||
} | ||||||||||
|
||||||||||
function isMarkAsExportedAction(report: Report, policy: Policy): boolean { | ||||||||||
const isInvoice = isInvoiceReport(report); | ||||||||||
const isSender = isCurrentUserSubmitter(report.reportID); | ||||||||||
|
||||||||||
if (isInvoice && isSender) { | ||||||||||
return true; | ||||||||||
} | ||||||||||
|
||||||||||
const isExpense = isExpenseReport(report); | ||||||||||
|
||||||||||
if (!isExpense) { | ||||||||||
return false; | ||||||||||
} | ||||||||||
|
||||||||||
const isReportPayer = isPayer(getSession(), report, false, policy); | ||||||||||
const isPaymentsEnabled = arePaymentsEnabled(policy); | ||||||||||
const isApproved = isReportApproved({report}); | ||||||||||
const isClosed = isClosedReport(report); | ||||||||||
const isClosedOrApproved = isClosed || isApproved; | ||||||||||
|
||||||||||
if (isReportPayer && isPaymentsEnabled && isClosedOrApproved) { | ||||||||||
return true; | ||||||||||
} | ||||||||||
|
||||||||||
const isAdmin = policy?.role === CONST.POLICY.ROLE.ADMIN; | ||||||||||
const isReimbursed = isSettled(report); | ||||||||||
const hasAccountingConnection = hasAccountingConnections(policy); | ||||||||||
const syncEnabled = isAutoSyncEnabled(policy); | ||||||||||
const isFinished = isClosedOrApproved || isReimbursed; | ||||||||||
|
||||||||||
if (isAdmin && isFinished && hasAccountingConnection && syncEnabled) { | ||||||||||
return true; | ||||||||||
} | ||||||||||
|
||||||||||
const isExporter = isPrefferedExporter(policy); | ||||||||||
|
||||||||||
if (isExporter && isFinished && hasAccountingConnection && !syncEnabled) { | ||||||||||
return true; | ||||||||||
} | ||||||||||
|
||||||||||
return false; | ||||||||||
} | ||||||||||
|
||||||||||
function isHoldAction(report: Report, reportTransactions: Transaction[]): boolean { | ||||||||||
const isExpense = isExpenseReport(report); | ||||||||||
|
||||||||||
if (!isExpense) { | ||||||||||
return false; | ||||||||||
} | ||||||||||
|
||||||||||
const isOnHold = reportTransactions.some(isOnHoldTransactionUtils); | ||||||||||
|
||||||||||
if (isOnHold) { | ||||||||||
return false; | ||||||||||
} | ||||||||||
|
||||||||||
const isOpen = isOpenReport(report); | ||||||||||
const isProcessing = isProcessingReport(report); | ||||||||||
const isApproved = isReportApproved({report}); | ||||||||||
|
||||||||||
return isOpen || isProcessing || isApproved; | ||||||||||
} | ||||||||||
|
||||||||||
function isChangeWorkspaceAction(report: Report, policy: Policy, reportTransactions: Transaction[], violations: OnyxCollection<TransactionViolation[]>): boolean { | ||||||||||
const isExpense = isExpenseReport(report); | ||||||||||
const isSubmitter = isCurrentUserSubmitter(report.reportID); | ||||||||||
const areWorkflowsEnabled = policy.areWorkflowsEnabled; | ||||||||||
const isClosed = isClosedReport(report); | ||||||||||
|
||||||||||
if (isExpense && isSubmitter && !areWorkflowsEnabled && isClosed) { | ||||||||||
return true; | ||||||||||
} | ||||||||||
|
||||||||||
const isOpen = isOpenReport(report); | ||||||||||
const isProcessing = isProcessingReport(report); | ||||||||||
|
||||||||||
if (isSubmitter && (isOpen || isProcessing)) { | ||||||||||
return true; | ||||||||||
} | ||||||||||
|
||||||||||
const isApprover = isApprovedMember(policy, getCurrentUserAccountID()); | ||||||||||
|
||||||||||
if (isApprover && isProcessing) { | ||||||||||
return true; | ||||||||||
} | ||||||||||
|
||||||||||
const isReportPayer = isPayer(getSession(), report, false, policy); | ||||||||||
const isApproved = isReportApproved({report}); | ||||||||||
|
||||||||||
if (isReportPayer && (isApproved || isClosed)) { | ||||||||||
return true; | ||||||||||
} | ||||||||||
|
||||||||||
const isAdmin = policy?.role === CONST.POLICY.ROLE.ADMIN; | ||||||||||
const isReimbursed = isSettled(report); | ||||||||||
const transactionIDs = reportTransactions.map((t) => t.transactionID); | ||||||||||
const hasAllPendingRTERViolations = allHavePendingRTERViolation(transactionIDs, violations); | ||||||||||
|
||||||||||
const shouldShowBrokenConnectionViolation = shouldShowBrokenConnectionViolationForMultipleTransactions(transactionIDs, report, policy, violations); | ||||||||||
|
||||||||||
const userControlsReport = isSubmitter || isApprover || isAdmin; | ||||||||||
const hasReceiptMatchViolation = hasAllPendingRTERViolations || (userControlsReport && shouldShowBrokenConnectionViolation); | ||||||||||
const isReportExported = isExported(getReportActions(report)); | ||||||||||
|
||||||||||
if (isAdmin && ((!isReportExported && (isApproved || isReimbursed || isClosed)) || hasReceiptMatchViolation)) { | ||||||||||
return true; | ||||||||||
} | ||||||||||
|
||||||||||
const isIOU = isIOUReport(report); | ||||||||||
const hasOnlyPersonalWorkspace = hasNoPolicyOtherThanPersonalType(); | ||||||||||
const isReceiver = isReportManager(report); | ||||||||||
if (isIOU && !hasOnlyPersonalWorkspace && isReceiver && isReimbursed) { | ||||||||||
return true; | ||||||||||
} | ||||||||||
|
||||||||||
const isSender = isCurrentUserSubmitter(report.reportID); | ||||||||||
// it's already satisified in line 215 | ||||||||||
if (isSender && isProcessing) { | ||||||||||
return true; | ||||||||||
} | ||||||||||
luacmartins marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
|
||||||||||
return false; | ||||||||||
} | ||||||||||
|
||||||||||
function isDeleteAction(report: Report): boolean { | ||||||||||
const isExpense = isExpenseReport(report); | ||||||||||
|
||||||||||
if (!isExpense) { | ||||||||||
return false; | ||||||||||
} | ||||||||||
|
||||||||||
const isSubmitter = isCurrentUserSubmitter(report.reportID); | ||||||||||
|
||||||||||
if (!isSubmitter) { | ||||||||||
return false; | ||||||||||
} | ||||||||||
|
||||||||||
const isOpen = isOpenReport(report); | ||||||||||
const isProcessing = isProcessingReport(report); | ||||||||||
const isApproved = isReportApproved({report}); | ||||||||||
|
||||||||||
return isOpen || isProcessing || isApproved; | ||||||||||
} | ||||||||||
|
||||||||||
function getSecondaryAction( | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jnowakow shouldn't we just return if one of the conditions below are true? will there be a case where report will have many actions ? checking for each action isn't optimal right, unless i'm missing something There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @allgandalf assumption is that there will be only one primary action and it was already implemented here. There can be many secondary actions so we're creating array of them as stated in design doc. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Correct. We can have many secondary actions, which is what we're implementing in this PR. |
||||||||||
report: Report, | ||||||||||
policy: Policy, | ||||||||||
reportTransactions: Transaction[], | ||||||||||
violations: OnyxCollection<TransactionViolation[]>, | ||||||||||
): Array<ValueOf<typeof CONST.REPORT.SECONDARY_ACTIONS>> { | ||||||||||
const options: Array<ValueOf<typeof CONST.REPORT.SECONDARY_ACTIONS>> = []; | ||||||||||
options.push(CONST.REPORT.SECONDARY_ACTIONS.DOWNLOAD); | ||||||||||
options.push(CONST.REPORT.SECONDARY_ACTIONS.VIEW_DETAILS); | ||||||||||
|
||||||||||
if (isSubmitAction(report, policy)) { | ||||||||||
options.push(CONST.REPORT.SECONDARY_ACTIONS.SUBMIT); | ||||||||||
} | ||||||||||
|
||||||||||
if (isApproveAction(report, policy, reportTransactions, violations)) { | ||||||||||
options.push(CONST.REPORT.SECONDARY_ACTIONS.APPROVE); | ||||||||||
} | ||||||||||
|
||||||||||
if (isUnapproveAction(report, policy)) { | ||||||||||
options.push(CONST.REPORT.SECONDARY_ACTIONS.UNAPPROVE); | ||||||||||
} | ||||||||||
|
||||||||||
if (isCancelPaymentAction(report)) { | ||||||||||
options.push(CONST.REPORT.SECONDARY_ACTIONS.CANCEL_PAYMENT); | ||||||||||
} | ||||||||||
|
||||||||||
if (isExportAction(report, policy)) { | ||||||||||
options.push(CONST.REPORT.SECONDARY_ACTIONS.EXPORT_TO_ACCOUNTING); | ||||||||||
} | ||||||||||
|
||||||||||
if (isMarkAsExportedAction(report, policy)) { | ||||||||||
options.push(CONST.REPORT.SECONDARY_ACTIONS.MARK_AS_EXPORTED); | ||||||||||
} | ||||||||||
|
||||||||||
if (isHoldAction(report, reportTransactions)) { | ||||||||||
options.push(CONST.REPORT.SECONDARY_ACTIONS.HOLD); | ||||||||||
} | ||||||||||
|
||||||||||
if (isChangeWorkspaceAction(report, policy, reportTransactions, violations)) { | ||||||||||
options.push(CONST.REPORT.SECONDARY_ACTIONS.CHANGE_WORKSPACE); | ||||||||||
} | ||||||||||
|
||||||||||
if (isDeleteAction(report)) { | ||||||||||
options.push(CONST.REPORT.SECONDARY_ACTIONS.DELETE); | ||||||||||
} | ||||||||||
|
||||||||||
return options; | ||||||||||
} | ||||||||||
|
||||||||||
export default getSecondaryAction; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1265,7 +1265,7 @@ function isSettled(reportOrID: OnyxInputOrEntry<Report> | SearchReport | string | |
return false; | ||
} | ||
|
||
if (isEmptyObject(report) || report.isWaitingOnBankAccount) { | ||
if (isEmptyObject(report)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jnowakow i don't understand the change here? why do we need to remove this change? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is contradiction in this function. Discussion is here |
||
return false; | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please have a look at this slack post for details about when variable and util names are the same