-
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
Add checks for editing APPROVED reports #33633
Merged
Merged
Changes from 26 commits
Commits
Show all changes
36 commits
Select commit
Hold shift + click to select a range
ba930e4
Add support for editing approved reports
youssef-lr bcb8dc4
Cleanup
youssef-lr c41b554
Fix bugs
youssef-lr 0459190
Lint
youssef-lr 394c82b
Merge branch 'main' into youssef_edit_approveed_reports
youssef-lr d273fc6
Fix typescript commplaining
youssef-lr 7ba5146
Add safety check
youssef-lr d85421d
Remove useMemo
youssef-lr 07e2148
Remove useMemo
youssef-lr 9a2ef5c
Remove unnecessary call to canEditFieldOfMoneyRequest
youssef-lr bdfa319
Address comments
youssef-lr de8ea54
Remove unneeded check
youssef-lr 982ba48
Remove code causing regression
youssef-lr cfaac6f
Add comment
youssef-lr f672b91
Update src/components/ReportActionItem/MoneyRequestView.js
youssef-lr 31e6a15
Update comment
youssef-lr ba8506d
Remove transaction dependency from canEditFieldOfMoneyRequest
youssef-lr c51f250
Update comment
youssef-lr 6ebe315
Fix typescript errors
youssef-lr eda03f7
Conflicts
youssef-lr c5e9c58
Fix condition
youssef-lr 1bea72c
Update comment
youssef-lr a5397e5
Merge branch 'main' into youssef_edit_approveed_reports
youssef-lr 22ec1dd
Fi comment
youssef-lr 7021d27
Add policy to proptypes with default
youssef-lr a5cdf15
Use isGroupPolicy
youssef-lr 7c894e2
Conflicts
youssef-lr 53e263a
Cleanup
youssef-lr 7de81d4
Cleanup
youssef-lr 4123a31
Remove unneeded check
youssef-lr a130cb3
Update src/libs/ReportUtils.ts
youssef-lr b6ba955
use isDraftExpenseReport
youssef-lr 1707e03
Move variables into the scope where they're needed
youssef-lr 8491e86
Rename methods
youssef-lr 3525c5e
Add code removed by mistake
youssef-lr 316b718
Make code clearer
youssef-lr File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -541,7 +541,8 @@ function isExpenseReport(report: OnyxEntry<Report> | EmptyObject): boolean { | |
/** | ||
* Checks if a report is an IOU report. | ||
*/ | ||
function isIOUReport(report: OnyxEntry<Report>): boolean { | ||
function isIOUReport(reportOrID: OnyxEntry<Report> | string | EmptyObject): boolean { | ||
const report = typeof reportOrID === 'string' ? allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportOrID}`] ?? null : reportOrID; | ||
return report?.type === CONST.REPORT.TYPE.IOU; | ||
} | ||
|
||
|
@@ -597,7 +598,8 @@ function isReportManager(report: OnyxEntry<Report>): boolean { | |
/** | ||
* Checks if the supplied report has been approved | ||
*/ | ||
function isReportApproved(report: OnyxEntry<Report> | EmptyObject): boolean { | ||
function isReportApproved(reportOrID: OnyxEntry<Report> | string | EmptyObject): boolean { | ||
const report = typeof reportOrID === 'string' ? allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportOrID}`] ?? null : reportOrID; | ||
return report?.stateNum === CONST.REPORT.STATE_NUM.SUBMITTED && report?.statusNum === CONST.REPORT.STATUS.APPROVED; | ||
} | ||
|
||
|
@@ -814,10 +816,17 @@ function isConciergeChatReport(report: OnyxEntry<Report>): boolean { | |
/** | ||
* Returns true if report is still being processed | ||
*/ | ||
function isProcessingReport(report: OnyxEntry<Report>): boolean { | ||
function isProcessingReport(report: OnyxEntry<Report> | EmptyObject): boolean { | ||
return report?.stateNum === CONST.REPORT.STATE_NUM.PROCESSING && report?.statusNum === CONST.REPORT.STATUS.SUBMITTED; | ||
} | ||
|
||
/** | ||
* Returns true if report is still open | ||
*/ | ||
function isOpenReport(report: OnyxEntry<Report> | EmptyObject): boolean { | ||
return report?.stateNum === CONST.REPORT.STATE_NUM.OPEN && report?.statusNum === CONST.REPORT.STATUS.OPEN; | ||
} | ||
|
||
mountiny marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/** | ||
* Check if the report is a single chat report that isn't a thread | ||
* and personal detail of participant is optimistic data | ||
|
@@ -1845,9 +1854,13 @@ function getTransactionDetails(transaction: OnyxEntry<Transaction>, createdDateF | |
* - the current user is the requestor and is not settled yet | ||
* - in case of expense report | ||
* - the current user is the requestor and is not settled yet | ||
* - or the user is an admin on the policy the expense report is tied to | ||
* - the current user is the manager of the report | ||
* - or the current user is an admin on the policy the expense report is tied to | ||
* | ||
* This is used in conjuction with canEditRestrictedField to control editing of specific fields like amount, currency, created, and receipt. | ||
* On its own, it only controls allowing/disallowing navigating to the editing pages or showing/hiding the 'Edit' icon on report actions | ||
*/ | ||
function canEditMoneyRequest(reportAction: OnyxEntry<ReportAction>, fieldToEdit = ''): boolean { | ||
function canEditMoneyRequest(reportAction: OnyxEntry<ReportAction>): boolean { | ||
const isDeleted = ReportActionsUtils.isDeletedAction(reportAction); | ||
|
||
if (isDeleted) { | ||
|
@@ -1870,52 +1883,59 @@ function canEditMoneyRequest(reportAction: OnyxEntry<ReportAction>, fieldToEdit | |
} | ||
|
||
const moneyRequestReport = getReport(String(moneyRequestReportID)); | ||
const isReportSettled = isSettled(moneyRequestReport?.reportID); | ||
const isApproved = isReportApproved(moneyRequestReport); | ||
const isAdmin = isExpenseReport(moneyRequestReport) && (getPolicy(moneyRequestReport?.policyID ?? '')?.role ?? '') === CONST.POLICY.ROLE.ADMIN; | ||
const policy = getPolicy(moneyRequestReport?.policyID ?? ''); | ||
const isAdmin = isExpenseReport(moneyRequestReport) && policy.role === CONST.POLICY.ROLE.ADMIN; | ||
const isManager = isExpenseReport(moneyRequestReport) && currentUserAccountID === moneyRequestReport?.managerID; | ||
const isRequestor = currentUserAccountID === reportAction?.actorAccountID; | ||
|
||
if (isAdmin && !isRequestor && fieldToEdit === CONST.EDIT_REQUEST_FIELD.RECEIPT) { | ||
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 logic is moved to |
||
return false; | ||
} | ||
|
||
if (isAdmin) { | ||
// Admin & managers can always edit coding fields such as tag, category, billable, etc. As long as the report has a state higher than OPEN. | ||
if ((isAdmin || isManager) && !isOpenReport(moneyRequestReport)) { | ||
return true; | ||
} | ||
|
||
return !isApproved && !isReportSettled && isRequestor; | ||
return !isReportApproved(moneyRequestReport) && !isSettled(moneyRequestReport?.reportID) && isRequestor; | ||
} | ||
|
||
/** | ||
* Checks if the current user can edit the provided property of a money request | ||
* | ||
*/ | ||
function canEditFieldOfMoneyRequest( | ||
reportAction: OnyxEntry<ReportAction>, | ||
reportID: string, | ||
fieldToEdit: ValueOf<typeof CONST.EDIT_REQUEST_FIELD>, | ||
transaction: OnyxEntry<Transaction>, | ||
): boolean { | ||
function canEditFieldOfMoneyRequest(reportAction: OnyxEntry<ReportAction>, fieldToEdit: ValueOf<typeof CONST.EDIT_REQUEST_FIELD>): boolean { | ||
// A list of fields that cannot be edited by anyone, once a money request has been settled | ||
const nonEditableFieldsWhenSettled: string[] = [ | ||
const restrictedFields: string[] = [ | ||
CONST.EDIT_REQUEST_FIELD.AMOUNT, | ||
CONST.EDIT_REQUEST_FIELD.CURRENCY, | ||
CONST.EDIT_REQUEST_FIELD.MERCHANT, | ||
CONST.EDIT_REQUEST_FIELD.DATE, | ||
CONST.EDIT_REQUEST_FIELD.RECEIPT, | ||
CONST.EDIT_REQUEST_FIELD.DISTANCE, | ||
]; | ||
|
||
// Checks if this user has permissions to edit this money request | ||
if (!canEditMoneyRequest(reportAction, fieldToEdit)) { | ||
return false; // User doesn't have permission to edit | ||
if (!canEditMoneyRequest(reportAction)) { | ||
return false; | ||
} | ||
if (!isEmpty(transaction) && fieldToEdit === CONST.EDIT_REQUEST_FIELD.RECEIPT && TransactionUtils.hasReceipt(transaction) && TransactionUtils.isReceiptBeingScanned(transaction)) { | ||
|
||
// If we're editing fields such as category, tag, description, etc. the check above should be enough for handling the permission | ||
if (!restrictedFields.includes(fieldToEdit)) { | ||
return true; | ||
} | ||
|
||
const iouMessage = reportAction?.originalMessage as IOUMessage; | ||
if (isSettled(String(iouMessage.IOUReportID)) || isReportApproved(String(iouMessage.IOUReportID))) { | ||
return false; | ||
} | ||
|
||
// Checks if the report is settled | ||
// Checks if the provided property is a restricted one | ||
return !isSettled(reportID) || !nonEditableFieldsWhenSettled.includes(fieldToEdit); | ||
const transaction = allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${iouMessage?.IOUTransactionID}`] ?? ({} as Transaction); | ||
if (fieldToEdit === CONST.EDIT_REQUEST_FIELD.AMOUNT || fieldToEdit === CONST.EDIT_REQUEST_FIELD.CURRENCY) { | ||
return !TransactionUtils.isCardTransaction(transaction); | ||
} | ||
|
||
if (fieldToEdit === CONST.EDIT_REQUEST_FIELD.RECEIPT && TransactionUtils.hasReceipt(transaction)) { | ||
const isRequestor = currentUserAccountID === reportAction?.actorAccountID; | ||
return !TransactionUtils.isReceiptBeingScanned(transaction) && !TransactionUtils.isDistanceRequest(transaction) && isRequestor; | ||
} | ||
|
||
return true; | ||
} | ||
|
||
/** | ||
|
@@ -4307,6 +4327,7 @@ export { | |
formatReportLastMessageText, | ||
chatIncludesConcierge, | ||
isPolicyExpenseChat, | ||
isGroupPolicy, | ||
isControlPolicyExpenseChat, | ||
isControlPolicyExpenseReport, | ||
isGroupPolicyExpenseChat, | ||
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
@mountiny woops this doesn't take into account Free policies, I'll revert this back to testing the policy is not personal
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.
Can you also rename it to isPaidGroupPolicy?
And create a new one name isGroupPolicy checking for free too? it can be done in a follow up too
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.
yeah that sounds good
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.
done, though I just found out we have the same method in
PolicyUtils
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.
I think we're creating a lot of redundant helper methods we should probably some day clean things up :D