diff --git a/src/libs/WorkflowUtils.ts b/src/libs/WorkflowUtils.ts index e45e14311d71..4682654d3583 100644 --- a/src/libs/WorkflowUtils.ts +++ b/src/libs/WorkflowUtils.ts @@ -116,12 +116,17 @@ function convertPolicyEmployeesToApprovalWorkflows({employees, defaultApprover, // Add each employee to the appropriate workflow Object.values(employees).forEach((employee) => { - const {email, submitsTo} = employee; + const {email, submitsTo, pendingAction} = employee; if (!email || !submitsTo) { return; } - const member: Member = {email, avatar: personalDetailsByEmail[email]?.avatar, displayName: personalDetailsByEmail[email]?.displayName ?? email}; + const member: Member = { + email, + avatar: personalDetailsByEmail[email]?.avatar, + displayName: personalDetailsByEmail[email]?.displayName ?? email, + }; + if (!approvalWorkflows[submitsTo]) { const approvers = calculateApprovers({employees, firstEmail: submitsTo, personalDetailsByEmail}); if (submitsTo !== firstApprover) { @@ -132,9 +137,14 @@ function convertPolicyEmployeesToApprovalWorkflows({employees, defaultApprover, members: [], approvers, isDefault: defaultApprover === submitsTo, + pendingAction, }; } + approvalWorkflows[submitsTo].members.push(member); + if (pendingAction) { + approvalWorkflows[submitsTo].pendingAction = pendingAction; + } }); // Sort the workflows by the first approver's name (default workflow has priority) @@ -205,6 +215,8 @@ function convertApprovalWorkflowToPolicyEmployees({ throw new Error('Approval workflow must have at least one approver'); } + const pendingAction = type === CONST.APPROVAL_WORKFLOW.TYPE.CREATE ? CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD : CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE; + approvalWorkflow.approvers.forEach((approver, index) => { const nextApprover = approvalWorkflow.approvers.at(index + 1); const forwardsTo = type === CONST.APPROVAL_WORKFLOW.TYPE.REMOVE ? '' : nextApprover?.email ?? ''; @@ -216,6 +228,7 @@ function convertApprovalWorkflowToPolicyEmployees({ updatedEmployeeList[approver.email] = { email: approver.email, forwardsTo, + pendingAction, }; }); @@ -226,38 +239,26 @@ function convertApprovalWorkflowToPolicyEmployees({ return; } - if (updatedEmployeeList[email]) { - updatedEmployeeList[email].submitsTo = submitsTo; - return; - } - updatedEmployeeList[email] = { - email, + ...(updatedEmployeeList[email] ? updatedEmployeeList[email] : {email}), submitsTo, + pendingAction, }; }); membersToRemove?.forEach(({email}) => { - if (updatedEmployeeList[email]) { - updatedEmployeeList[email].submitsTo = ''; - return; - } - updatedEmployeeList[email] = { - email, + ...(updatedEmployeeList[email] ? updatedEmployeeList[email] : {email}), submitsTo: '', + pendingAction, }; }); approversToRemove?.forEach(({email}) => { - if (updatedEmployeeList[email]) { - updatedEmployeeList[email].forwardsTo = ''; - return; - } - updatedEmployeeList[email] = { - email, + ...(updatedEmployeeList[email] ? updatedEmployeeList[email] : {email}), forwardsTo: '', + pendingAction, }; }); diff --git a/src/libs/actions/Workflow.ts b/src/libs/actions/Workflow.ts index 5a83f3c9cf77..4adb692919ee 100644 --- a/src/libs/actions/Workflow.ts +++ b/src/libs/actions/Workflow.ts @@ -52,7 +52,7 @@ function createApprovalWorkflow(policyID: string, approvalWorkflow: ApprovalWork return; } - const previousEmployeeList = {...policy.employeeList}; + const previousEmployeeList = Object.fromEntries(Object.entries(policy.employeeList ?? {}).map(([key, value]) => [key, {...value, pendingAction: null}])); const previousApprovalMode = policy.approvalMode; const updatedEmployees = convertApprovalWorkflowToPolicyEmployees({previousEmployeeList, approvalWorkflow, type: CONST.APPROVAL_WORKFLOW.TYPE.CREATE}); @@ -70,7 +70,6 @@ function createApprovalWorkflow(policyID: string, approvalWorkflow: ApprovalWork value: { employeeList: updatedEmployees, approvalMode: CONST.POLICY.APPROVAL_MODE.ADVANCED, - pendingFields: {employeeList: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD}, }, }, ]; @@ -87,7 +86,6 @@ function createApprovalWorkflow(policyID: string, approvalWorkflow: ApprovalWork value: { employeeList: previousEmployeeList, approvalMode: previousApprovalMode, - pendingFields: {employeeList: null}, }, }, ]; @@ -102,7 +100,7 @@ function createApprovalWorkflow(policyID: string, approvalWorkflow: ApprovalWork onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { - pendingFields: {employeeList: null}, + employeeList: Object.fromEntries(Object.keys(updatedEmployees).map((key) => [key, {pendingAction: null}])), }, }, ]; @@ -120,7 +118,7 @@ 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 previousEmployeeList = Object.fromEntries(Object.entries(policy.employeeList ?? {}).map(([key, value]) => [key, {...value, pendingAction: null}])); const updatedEmployees = convertApprovalWorkflowToPolicyEmployees({ previousEmployeeList, approvalWorkflow, @@ -129,6 +127,10 @@ function updateApprovalWorkflow(policyID: string, approvalWorkflow: ApprovalWork approversToRemove, }); + if (isEmptyObject(updatedEmployees) && !newDefaultApprover) { + return; + } + const optimisticData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, @@ -142,7 +144,6 @@ function updateApprovalWorkflow(policyID: string, approvalWorkflow: ApprovalWork key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { employeeList: updatedEmployees, - pendingFields: {employeeList: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}, ...(newDefaultApprover ? {approver: newDefaultApprover} : {}), }, }, @@ -175,7 +176,7 @@ function updateApprovalWorkflow(policyID: string, approvalWorkflow: ApprovalWork onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { - pendingFields: {employeeList: null}, + employeeList: Object.fromEntries(Object.keys(updatedEmployees).map((key) => [key, {pendingAction: null}])), }, }, ]; @@ -196,7 +197,7 @@ function removeApprovalWorkflow(policyID: string, approvalWorkflow: ApprovalWork return; } - const previousEmployeeList = {...policy.employeeList}; + const previousEmployeeList = Object.fromEntries(Object.entries(policy.employeeList ?? {}).map(([key, value]) => [key, {...value, pendingAction: null}])); const updatedEmployees = convertApprovalWorkflowToPolicyEmployees({previousEmployeeList, approvalWorkflow, type: CONST.APPROVAL_WORKFLOW.TYPE.REMOVE}); const updatedEmployeeList = {...previousEmployeeList, ...updatedEmployees}; @@ -218,7 +219,6 @@ function removeApprovalWorkflow(policyID: string, approvalWorkflow: ApprovalWork value: { employeeList: updatedEmployees, approvalMode: hasMoreThanOneWorkflow ? CONST.POLICY.APPROVAL_MODE.ADVANCED : CONST.POLICY.APPROVAL_MODE.BASIC, - pendingFields: {employeeList: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}, }, }, ]; @@ -237,13 +237,6 @@ function removeApprovalWorkflow(policyID: string, approvalWorkflow: ApprovalWork approvalMode: CONST.POLICY.APPROVAL_MODE.ADVANCED, }, }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - pendingFields: {employeeList: null}, - }, - }, ]; const successData: OnyxUpdate[] = [ @@ -256,7 +249,7 @@ function removeApprovalWorkflow(policyID: string, approvalWorkflow: ApprovalWork onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { - pendingFields: {employeeList: null}, + employeeList: Object.fromEntries(Object.keys(updatedEmployees).map((key) => [key, {pendingAction: null}])), }, }, ]; diff --git a/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx b/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx index bd0f2775a884..9c03e5001a28 100644 --- a/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx +++ b/src/pages/workspace/workflows/WorkspaceWorkflowsPage.tsx @@ -176,7 +176,7 @@ function WorkspaceWorkflowsPage({policy, betas, route}: WorkspaceWorkflowsPagePr ; /** * Approval workflow for a group of employees with additional properties for the Onyx store diff --git a/src/types/onyx/Policy.ts b/src/types/onyx/Policy.ts index 661c6d9a8a82..7a596e7fe524 100644 --- a/src/types/onyx/Policy.ts +++ b/src/types/onyx/Policy.ts @@ -1582,7 +1582,7 @@ type Policy = OnyxCommon.OnyxValueWithOfflineFeedback< /** Workspace account ID configured for Expensify Card */ workspaceAccountID?: number; } & Partial, - 'addWorkspaceRoom' | 'employeeList' | keyof ACHAccount | keyof Attributes + 'addWorkspaceRoom' | keyof ACHAccount | keyof Attributes >; /** Stages of policy connection sync */ diff --git a/tests/unit/WorkflowUtilsTest.ts b/tests/unit/WorkflowUtilsTest.ts index bc3d0f479944..9400fc34375b 100644 --- a/tests/unit/WorkflowUtilsTest.ts +++ b/tests/unit/WorkflowUtilsTest.ts @@ -13,6 +13,7 @@ const personalDetailsByEmail: PersonalDetailsList = {}; function buildPolicyEmployee(accountID: number, policyEmployee: Partial = {}): PolicyEmployee { return { email: `${accountID}@example.com`, + pendingAction: 'add', ...policyEmployee, }; } @@ -417,12 +418,12 @@ describe('WorkflowUtils', () => { const convertedEmployees = WorkflowUtils.convertApprovalWorkflowToPolicyEmployees({previousEmployeeList: {}, approvalWorkflow, type: 'remove'}); expect(convertedEmployees).toEqual({ - '1@example.com': buildPolicyEmployee(1, {forwardsTo: ''}), - '2@example.com': buildPolicyEmployee(2, {forwardsTo: ''}), - '3@example.com': buildPolicyEmployee(3, {forwardsTo: ''}), - '4@example.com': buildPolicyEmployee(4, {submitsTo: ''}), - '5@example.com': buildPolicyEmployee(5, {submitsTo: ''}), - '6@example.com': buildPolicyEmployee(6, {submitsTo: ''}), + '1@example.com': buildPolicyEmployee(1, {forwardsTo: '', pendingAction: 'update'}), + '2@example.com': buildPolicyEmployee(2, {forwardsTo: '', pendingAction: 'update'}), + '3@example.com': buildPolicyEmployee(3, {forwardsTo: '', pendingAction: 'update'}), + '4@example.com': buildPolicyEmployee(4, {submitsTo: '', pendingAction: 'update'}), + '5@example.com': buildPolicyEmployee(5, {submitsTo: '', pendingAction: 'update'}), + '6@example.com': buildPolicyEmployee(6, {submitsTo: '', pendingAction: 'update'}), }); }); });