Skip to content
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

[Advanced Approval Workflows] Cleanup the logic responsible for building policy.employeeList #48411

60 changes: 55 additions & 5 deletions src/libs/WorkflowUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,19 +169,35 @@ type ConvertApprovalWorkflowToPolicyEmployeesParams = {
*/
approvalWorkflow: ApprovalWorkflow;

/**
* The previous employee list before the approval workflow was created
*/
previousEmployeeList: PolicyEmployeeList;

/**
* Members to remove from the approval workflow
*/
membersToRemove?: Member[];

/**
* Approvers to remove from the approval workflow
*/
approversToRemove?: Approver[];

/**
* Mode to use when converting the approval workflow
*/
type: ValueOf<typeof CONST.APPROVAL_WORKFLOW.TYPE>;
};

/** Convert an approval workflow to a list of policy employees */
function convertApprovalWorkflowToPolicyEmployees({approvalWorkflow, membersToRemove, type}: ConvertApprovalWorkflowToPolicyEmployeesParams): PolicyEmployeeList {
function convertApprovalWorkflowToPolicyEmployees({
approvalWorkflow,
previousEmployeeList,
membersToRemove,
approversToRemove,
type,
}: ConvertApprovalWorkflowToPolicyEmployeesParams): PolicyEmployeeList {
const updatedEmployeeList: PolicyEmployeeList = {};
const firstApprover = approvalWorkflow.approvers.at(0);

Expand All @@ -191,26 +207,60 @@ function convertApprovalWorkflowToPolicyEmployees({approvalWorkflow, membersToRe

approvalWorkflow.approvers.forEach((approver, index) => {
blazejkustra marked this conversation as resolved.
Show resolved Hide resolved
const nextApprover = approvalWorkflow.approvers.at(index + 1);
const forwardsTo = type === CONST.APPROVAL_WORKFLOW.TYPE.REMOVE ? '' : nextApprover?.email ?? '';

if (previousEmployeeList[approver.email]?.forwardsTo === forwardsTo) {
return;
}

updatedEmployeeList[approver.email] = {
email: approver.email,
forwardsTo: type === CONST.APPROVAL_WORKFLOW.TYPE.REMOVE ? '' : nextApprover?.email ?? '',
forwardsTo,
};
});

approvalWorkflow.members.forEach(({email}) => {
const submitsTo = type === CONST.APPROVAL_WORKFLOW.TYPE.REMOVE ? '' : firstApprover.email ?? '';
Copy link
Contributor

Choose a reason for hiding this comment

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

When removing the workflow, should we set the submitTo to default approver?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We could, but couple of weeks ago we agreed with Tim Golen that it will be easier to use an empty string, here is an issue where it was implemented on the BE.


if (previousEmployeeList[email]?.submitsTo === submitsTo) {
return;
}

if (updatedEmployeeList[email]) {
updatedEmployeeList[email].submitsTo = submitsTo;
return;
}

updatedEmployeeList[email] = {
...(updatedEmployeeList[email] ? updatedEmployeeList[email] : {email}),
submitsTo: type === CONST.APPROVAL_WORKFLOW.TYPE.REMOVE ? '' : firstApprover.email ?? '',
email,
submitsTo,
};
});

membersToRemove?.forEach(({email}) => {
if (updatedEmployeeList[email]) {
updatedEmployeeList[email].submitsTo = '';
return;
}

updatedEmployeeList[email] = {
...(updatedEmployeeList[email] ? updatedEmployeeList[email] : {email}),
email,
submitsTo: '',
};
});

approversToRemove?.forEach(({email}) => {
if (updatedEmployeeList[email]) {
updatedEmployeeList[email].forwardsTo = '';
return;
}

updatedEmployeeList[email] = {
email,
forwardsTo: '',
};
});

return updatedEmployeeList;
}

Expand Down
14 changes: 10 additions & 4 deletions src/libs/actions/Workflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ function createApprovalWorkflow(policyID: string, approvalWorkflow: ApprovalWork

const previousEmployeeList = {...policy.employeeList};
const previousApprovalMode = policy.approvalMode;
const updatedEmployees = convertApprovalWorkflowToPolicyEmployees({approvalWorkflow, type: CONST.APPROVAL_WORKFLOW.TYPE.CREATE});
const updatedEmployees = convertApprovalWorkflowToPolicyEmployees({previousEmployeeList, approvalWorkflow, type: CONST.APPROVAL_WORKFLOW.TYPE.CREATE});

const optimisticData: OnyxUpdate[] = [
{
Expand Down Expand Up @@ -111,7 +111,7 @@ function createApprovalWorkflow(policyID: string, approvalWorkflow: ApprovalWork
API.write(WRITE_COMMANDS.CREATE_WORKSPACE_APPROVAL, parameters, {optimisticData, failureData, successData});
}

function updateApprovalWorkflow(policyID: string, approvalWorkflow: ApprovalWorkflow, membersToRemove: Member[]) {
function updateApprovalWorkflow(policyID: string, approvalWorkflow: ApprovalWorkflow, membersToRemove: Member[], approversToRemove: Approver[]) {
const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`];

if (!authToken || !policy) {
Expand All @@ -121,7 +121,13 @@ function updateApprovalWorkflow(policyID: string, approvalWorkflow: ApprovalWork
const previousDefaultApprover = policy.approver ?? policy.owner;
const newDefaultApprover = approvalWorkflow.isDefault ? approvalWorkflow.approvers[0].email : undefined;
const previousEmployeeList = {...policy.employeeList};
const updatedEmployees = convertApprovalWorkflowToPolicyEmployees({approvalWorkflow, type: CONST.APPROVAL_WORKFLOW.TYPE.UPDATE, membersToRemove});
const updatedEmployees = convertApprovalWorkflowToPolicyEmployees({
previousEmployeeList,
approvalWorkflow,
type: CONST.APPROVAL_WORKFLOW.TYPE.UPDATE,
membersToRemove,
approversToRemove,
});

const optimisticData: OnyxUpdate[] = [
{
Expand Down Expand Up @@ -191,7 +197,7 @@ function removeApprovalWorkflow(policyID: string, approvalWorkflow: ApprovalWork
}

const previousEmployeeList = {...policy.employeeList};
const updatedEmployees = convertApprovalWorkflowToPolicyEmployees({approvalWorkflow, type: CONST.APPROVAL_WORKFLOW.TYPE.REMOVE});
const updatedEmployees = convertApprovalWorkflowToPolicyEmployees({previousEmployeeList, approvalWorkflow, type: CONST.APPROVAL_WORKFLOW.TYPE.REMOVE});
const updatedEmployeeList = {...previousEmployeeList, ...updatedEmployees};

const defaultApprover = policy.approver ?? policy.owner;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,10 @@ function WorkspaceWorkflowsApprovalsEditPage({policy, isLoadingReportData = true
return;
}

// We need to remove members and approvers that are no longer in the updated workflow
const membersToRemove = initialApprovalWorkflow.members.filter((initialMember) => !approvalWorkflow.members.some((member) => member.email === initialMember.email));
Workflow.updateApprovalWorkflow(route.params.policyID, approvalWorkflow, membersToRemove);
const approversToRemove = initialApprovalWorkflow.approvers.filter((initialApprover) => !approvalWorkflow.approvers.some((approver) => approver.email === initialApprover.email));
Workflow.updateApprovalWorkflow(route.params.policyID, approvalWorkflow, membersToRemove, approversToRemove);
Navigation.dismissModal();
}, [approvalWorkflow, initialApprovalWorkflow, route.params.policyID]);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ function WorkspaceWorkflowsApprovalsExpensesFromPage({policy, isLoadingReportDat
Navigation.navigate(ROUTES.WORKSPACE_WORKFLOWS_APPROVALS_APPROVER.getRoute(route.params.policyID, 0));
} else {
const firstApprover = approvalWorkflow?.approvers?.[0]?.email ?? '';
Navigation.navigate(ROUTES.WORKSPACE_WORKFLOWS_APPROVALS_EDIT.getRoute(route.params.policyID, firstApprover));
Navigation.goBack(ROUTES.WORKSPACE_WORKFLOWS_APPROVALS_EDIT.getRoute(route.params.policyID, firstApprover));
}
}, [approvalWorkflow, route.params.backTo, route.params.policyID, selectedMembers]);

Expand Down
6 changes: 3 additions & 3 deletions tests/unit/WorkflowUtilsTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,7 @@ describe('WorkflowUtils', () => {
isDefault: true,
};

const convertedEmployees = WorkflowUtils.convertApprovalWorkflowToPolicyEmployees({approvalWorkflow, type: 'create'});
const convertedEmployees = WorkflowUtils.convertApprovalWorkflowToPolicyEmployees({previousEmployeeList: {}, approvalWorkflow, type: 'create'});

expect(convertedEmployees).toEqual({
'1@example.com': buildPolicyEmployee(1, {forwardsTo: '', submitsTo: '1@example.com'}),
Expand All @@ -395,7 +395,7 @@ describe('WorkflowUtils', () => {
isDefault: false,
};

const convertedEmployees = WorkflowUtils.convertApprovalWorkflowToPolicyEmployees({approvalWorkflow, type: 'create'});
const convertedEmployees = WorkflowUtils.convertApprovalWorkflowToPolicyEmployees({previousEmployeeList: {}, approvalWorkflow, type: 'create'});

expect(convertedEmployees).toEqual({
'1@example.com': buildPolicyEmployee(1, {forwardsTo: '2@example.com'}),
Expand All @@ -414,7 +414,7 @@ describe('WorkflowUtils', () => {
isDefault: false,
};

const convertedEmployees = WorkflowUtils.convertApprovalWorkflowToPolicyEmployees({approvalWorkflow, type: 'remove'});
const convertedEmployees = WorkflowUtils.convertApprovalWorkflowToPolicyEmployees({previousEmployeeList: {}, approvalWorkflow, type: 'remove'});

expect(convertedEmployees).toEqual({
'1@example.com': buildPolicyEmployee(1, {forwardsTo: ''}),
Expand Down
Loading