diff --git a/cypress/e2e/acquisition-units/user-not-in-au-can-assign-au-to-new-title-with-restrictions.cy.js b/cypress/e2e/acquisition-units/user-not-in-au-can-assign-au-to-new-title-with-restrictions.cy.js new file mode 100644 index 0000000000..07d7dfb894 --- /dev/null +++ b/cypress/e2e/acquisition-units/user-not-in-au-can-assign-au-to-new-title-with-restrictions.cy.js @@ -0,0 +1,172 @@ +import uuid from 'uuid'; +import permissions from '../../support/dictionary/permissions'; +import NewOrder from '../../support/fragments/orders/newOrder'; +import OrderLines from '../../support/fragments/orders/orderLines'; +import Orders from '../../support/fragments/orders/orders'; +import NewOrganization from '../../support/fragments/organizations/newOrganization'; +import Organizations from '../../support/fragments/organizations/organizations'; +import Receiving from '../../support/fragments/receiving/receiving'; +import ReceivingDetails from '../../support/fragments/receiving/receivingDetails'; +import ReceivingEditForm from '../../support/fragments/receiving/receivingEditForm'; +import AcquisitionUnits from '../../support/fragments/settings/acquisitionUnits/acquisitionUnits'; +import TopMenu from '../../support/fragments/topMenu'; +import Users from '../../support/fragments/users/users'; +import BasicOrderLine from '../../support/fragments/orders/basicOrderLine'; +import InventoryInstances from '../../support/fragments/inventory/inventoryInstances'; +import InventoryInstance from '../../support/fragments/inventory/inventoryInstance'; +import { ACQUISITION_METHOD_NAMES_IN_PROFILE, ORDER_STATUSES } from '../../support/constants'; +import InteractorsTools from '../../support/utils/interactorsTools'; +import getRandomPostfix from '../../support/utils/stringTools'; + +describe('Receiving', () => { + describe('Acquisition units', () => { + const organization = { ...NewOrganization.defaultUiOrganizations }; + const randomPostfix = getRandomPostfix(); + const instanceTitle = `C430263_Title_${randomPostfix}`; + const packageName = `C430263_Package_${randomPostfix}`; + const quantity = 1; + let order; + let orderLine; + let user; + let acquisitionUnit; + let location; + let instance; + + before('Create test data', () => { + cy.getAdminToken(); + + acquisitionUnit = AcquisitionUnits.getDefaultAcquisitionUnit({ + protectRead: false, + protectUpdate: true, + protectCreate: false, + protectDelete: true, + }); + + AcquisitionUnits.createAcquisitionUnitViaApi(acquisitionUnit).then((auResponse) => { + acquisitionUnit.id = auResponse.id; + + InventoryInstance.createInstanceViaApi({ + instanceTitle, + }).then((instanceData) => { + instance = instanceData.instanceData; + }); + + cy.createTempUser([ + permissions.uiReceivingAssignAcquisitionUnitsToNewTitle.gui, + permissions.uiReceivingViewEditCreate.gui, + ]).then((userProperties) => { + user = userProperties; + + InventoryInstances.getLocations({ limit: 1 }).then((locations) => { + location = locations[0]; + + Organizations.createOrganizationViaApi(organization).then((orgResponse) => { + organization.id = orgResponse; + + cy.getDefaultMaterialType().then((materialType) => { + cy.getAcquisitionMethodsApi({ + query: `value="${ACQUISITION_METHOD_NAMES_IN_PROFILE.PURCHASE_AT_VENDOR_SYSTEM}"`, + }).then((acquisitionMethodResponse) => { + const orderData = { + ...NewOrder.getDefaultOrder({ vendorId: organization.id }), + orderType: 'One-Time', + approved: true, + }; + + Orders.createOrderViaApi(orderData).then((orderResponse) => { + order = orderResponse; + + const orderLineData = { + ...BasicOrderLine.defaultOrderLine, + id: uuid(), + purchaseOrderId: orderResponse.id, + isPackage: true, + titleOrPackage: packageName, + checkinItems: false, + cost: { + listUnitPrice: 100.0, + currency: 'USD', + discountType: 'percentage', + quantityPhysical: quantity, + poLineEstimatedPrice: 100.0, + }, + fundDistribution: [], + locations: [ + { + locationId: location.id, + quantity, + quantityPhysical: quantity, + }, + ], + acquisitionMethod: acquisitionMethodResponse.body.acquisitionMethods[0].id, + physical: { + createInventory: 'Instance, Holding, Item', + materialType: materialType.id, + materialSupplier: organization.id, + volumes: [], + }, + }; + + OrderLines.createOrderLineViaApi(orderLineData).then((orderLineResponse) => { + orderLine = orderLineResponse; + + Orders.updateOrderViaApi({ + ...orderResponse, + workflowStatus: ORDER_STATUSES.OPEN, + }).then(() => { + cy.login(user.username, user.password, { + path: TopMenu.receivingPath, + waiter: Receiving.waitLoading, + }); + }); + }); + }); + }); + }); + }); + }); + }); + }); + }); + + after('Delete test data', () => { + cy.getAdminToken(); + Orders.deleteOrderViaApi(order.id); + Organizations.deleteOrganizationViaApi(organization.id); + Users.deleteViaApi(user.userId); + AcquisitionUnits.deleteAcquisitionUnitViaApi(acquisitionUnit.id); + InventoryInstance.deleteInstanceViaApi(instance.instanceId); + }); + + it( + 'C430263 User not included in Acq unit is able to assign an Acq unit to a new Title with appropriate Acq unit restrictions (thunderjet)', + { tags: ['criticalPath', 'thunderjet', 'C430263'] }, + () => { + Receiving.clickNewTitleOption(); + ReceivingEditForm.waitLoading(); + Receiving.verifyNewTitlePageOpened(); + + ReceivingEditForm.openAcquisitionUnitsDropdown(); + ReceivingEditForm.verifyAcquisitionUnitsInDropdown([acquisitionUnit.name]); + + ReceivingEditForm.selectAcquisitionUnit(acquisitionUnit.name); + ReceivingEditForm.verifyAcquisitionUnitDisplayed(acquisitionUnit.name, true); + + Receiving.fillTitleLookup(instanceTitle); + Receiving.fillPOLNumberLookup(orderLine.poLineNumber); + ReceivingEditForm.verifySaveButtonEnabled(true); + + ReceivingEditForm.clickSaveButton({ itemSaved: false }); + ReceivingDetails.waitLoading(); + InteractorsTools.checkCalloutMessage( + `The title ${instanceTitle} has been successfully added for PO line ${orderLine.poLineNumber}`, + ); + + cy.wait(1000); + + ReceivingDetails.expandTitleInformationAccordion(); + ReceivingDetails.verifyAcquisitionUnitInTitleInformation(acquisitionUnit.name, true); + }, + ); + }); +}); diff --git a/cypress/e2e/invoices/pay-invoice-with-total-value-zero.cy.js b/cypress/e2e/invoices/pay-invoice-with-total-value-zero.cy.js new file mode 100644 index 0000000000..082d879179 --- /dev/null +++ b/cypress/e2e/invoices/pay-invoice-with-total-value-zero.cy.js @@ -0,0 +1,319 @@ +import { APPLICATION_NAMES, INVOICE_STATUSES, ORDER_STATUSES } from '../../support/constants'; +import { Permissions } from '../../support/dictionary'; +import { + Budgets, + FinanceHelper, + FundDetails, + Funds, + Transactions, +} from '../../support/fragments/finance'; +import { InvoiceView, Invoices } from '../../support/fragments/invoices'; +import BasicOrderLine from '../../support/fragments/orders/basicOrderLine'; +import NewOrder from '../../support/fragments/orders/newOrder'; +import OrderLines from '../../support/fragments/orders/orderLines'; +import Orders from '../../support/fragments/orders/orders'; +import NewOrganization from '../../support/fragments/organizations/newOrganization'; +import Organizations from '../../support/fragments/organizations/organizations'; +import { Approvals } from '../../support/fragments/settings/invoices'; +import TopMenu from '../../support/fragments/topMenu'; +import TopMenuNavigation from '../../support/fragments/topMenuNavigation'; +import Users from '../../support/fragments/users/users'; + +describe('Invoices', () => { + const invoiceLineAmount1 = 20; + const invoiceLineAmount2 = 30; + const invoiceLineAmount3 = invoiceLineAmount1 + invoiceLineAmount2; + + const testData = { + organization: NewOrganization.getDefaultOrganization(), + fiscalYear: {}, + ledger: {}, + fundAlpha: {}, + fundBeta: {}, + budgetAlpha: {}, + budgetBeta: {}, + orders: [], + orderLines: [], + invoice: {}, + user: {}, + originalApprovalSettings: null, + }; + + before('Create test data', () => { + cy.getAdminToken(); + + const { + fiscalYear, + ledger, + fund: fundAlpha, + budget: budgetAlpha, + } = Budgets.createBudgetWithFundLedgerAndFYViaApi({ + budget: { allocated: 100 }, + }); + testData.fiscalYear = fiscalYear; + testData.ledger = ledger; + testData.fundAlpha = fundAlpha; + testData.budgetAlpha = budgetAlpha; + + const fundBeta = { ...Funds.getDefaultFund(), ledgerId: ledger.id }; + testData.fundBeta = fundBeta; + Funds.createViaApi(fundBeta).then((fundResponse) => { + testData.fundBeta = fundResponse.fund; + + const budgetBeta = { + ...Budgets.getDefaultBudget(), + fiscalYearId: fiscalYear.id, + fundId: fundResponse.fund.id, + allocated: 100, + }; + Budgets.createViaApi(budgetBeta); + testData.budgetBeta = budgetBeta; + }); + + Organizations.createOrganizationViaApi(testData.organization).then(() => { + const fundDistributionAlpha = [{ code: fundAlpha.code, fundId: fundAlpha.id, value: 100 }]; + const fundDistributionBeta = [{ code: fundBeta.code, fundId: fundBeta.id, value: 100 }]; + + const order1 = { + ...NewOrder.getDefaultOngoingOrder({ vendorId: testData.organization.id }), + reEncumber: true, + approved: true, + }; + const orderLine1 = BasicOrderLine.getDefaultOrderLine({ + listUnitPrice: invoiceLineAmount1, + fundDistribution: fundDistributionAlpha, + }); + + const order2 = { + ...NewOrder.getDefaultOngoingOrder({ vendorId: testData.organization.id }), + reEncumber: true, + approved: true, + }; + const orderLine2 = BasicOrderLine.getDefaultOrderLine({ + listUnitPrice: invoiceLineAmount2, + fundDistribution: fundDistributionAlpha, + }); + + const order3 = { + ...NewOrder.getDefaultOngoingOrder({ vendorId: testData.organization.id }), + reEncumber: true, + approved: true, + }; + const orderLine3 = BasicOrderLine.getDefaultOrderLine({ + listUnitPrice: invoiceLineAmount3, + fundDistribution: fundDistributionBeta, + }); + + Orders.createOrderWithOrderLineViaApi(order1, orderLine1).then((orderResponse1) => { + testData.orders.push(orderResponse1); + testData.orderLines.push(orderLine1); + Orders.updateOrderViaApi({ ...orderResponse1, workflowStatus: ORDER_STATUSES.OPEN }); + + Orders.createOrderWithOrderLineViaApi(order2, orderLine2).then((orderResponse2) => { + testData.orders.push(orderResponse2); + testData.orderLines.push(orderLine2); + Orders.updateOrderViaApi({ ...orderResponse2, workflowStatus: ORDER_STATUSES.OPEN }); + + Orders.createOrderWithOrderLineViaApi(order3, orderLine3).then((orderResponse3) => { + testData.orders.push(orderResponse3); + testData.orderLines.push(orderLine3); + Orders.updateOrderViaApi({ + ...orderResponse3, + workflowStatus: ORDER_STATUSES.OPEN, + }); + + cy.getBatchGroups().then(({ id: batchGroupId }) => { + Invoices.createInvoiceViaApi({ + vendorId: testData.organization.id, + fiscalYearId: fiscalYear.id, + batchGroupId, + accountingCode: testData.organization.erpCode, + }).then((invoiceResponse) => { + testData.invoice = invoiceResponse; + + OrderLines.getOrderLineViaApi({ + query: `purchaseOrderId=="${orderResponse1.id}"`, + }).then((lines1) => { + const invoiceLine1 = Invoices.getDefaultInvoiceLine({ + invoiceId: invoiceResponse.id, + invoiceLineStatus: invoiceResponse.status, + poLineId: lines1[0].id, + fundDistributions: lines1[0].fundDistribution, + subTotal: invoiceLineAmount1, + accountingCode: testData.organization.erpCode, + releaseEncumbrance: true, + }); + Invoices.createInvoiceLineViaApi(invoiceLine1); + + OrderLines.getOrderLineViaApi({ + query: `purchaseOrderId=="${orderResponse2.id}"`, + }).then((lines2) => { + const invoiceLine2 = Invoices.getDefaultInvoiceLine({ + invoiceId: invoiceResponse.id, + invoiceLineStatus: invoiceResponse.status, + poLineId: lines2[0].id, + fundDistributions: lines2[0].fundDistribution, + subTotal: invoiceLineAmount2, + accountingCode: testData.organization.erpCode, + releaseEncumbrance: true, + }); + Invoices.createInvoiceLineViaApi(invoiceLine2); + + OrderLines.getOrderLineViaApi({ + query: `purchaseOrderId=="${orderResponse3.id}"`, + }).then((lines3) => { + const invoiceLine3 = Invoices.getDefaultInvoiceLine({ + invoiceId: invoiceResponse.id, + invoiceLineStatus: invoiceResponse.status, + poLineId: lines3[0].id, + fundDistributions: lines3[0].fundDistribution, + subTotal: invoiceLineAmount3, + accountingCode: testData.organization.erpCode, + releaseEncumbrance: true, + }); + Invoices.createInvoiceLineViaApi(invoiceLine3); + }); + }); + }); + }); + }); + }); + }); + }); + }); + + Approvals.getApprovalConfigViaApi().then((settings) => { + testData.originalApprovalSettings = settings; + }); + + Approvals.setApprovePayValueViaApi(false); + + cy.createTempUser([ + Permissions.uiFinanceViewFundAndBudget.gui, + Permissions.uiInvoicesApproveInvoices.gui, + Permissions.uiInvoicesCanViewAndEditInvoicesAndInvoiceLines.gui, + Permissions.uiInvoicesPayInvoices.gui, + ]).then((userProperties) => { + testData.user = userProperties; + + cy.login(userProperties.username, userProperties.password); + TopMenuNavigation.navigateToApp(APPLICATION_NAMES.INVOICES); + Invoices.waitLoading(); + }); + }); + + after('Delete test data', () => { + cy.getAdminToken(); + + if (testData.originalApprovalSettings?.length > 0) { + const originalValue = JSON.parse(testData.originalApprovalSettings[0].value); + Approvals.setApprovePayValueViaApi(originalValue.isApprovePayEnabled); + } + + Users.deleteViaApi(testData.user.userId); + Organizations.deleteOrganizationViaApi(testData.organization.id); + }); + + it( + 'C357038 Pay Invoice with total value = 0 (one of the fund distribution has negative amount) (thunderjet) (TaaS)', + { tags: ['extendedPath', 'thunderjet', 'C357038'] }, + () => { + Invoices.searchByNumber(testData.invoice.vendorInvoiceNo); + Invoices.selectInvoice(testData.invoice.vendorInvoiceNo); + InvoiceView.waitLoading(); + + const InvoiceLineDetailsPane = InvoiceView.selectInvoiceLine(2); + InvoiceLineDetailsPane.waitLoading(); + + const InvoiceLineEditForm = InvoiceLineDetailsPane.openInvoiceLineEditForm(); + + InvoiceLineEditForm.setNegativeSubTotal(invoiceLineAmount3); + + InvoiceLineEditForm.clickSaveButton(); + InvoiceView.waitLoading(); + + InvoiceView.checkInvoiceDetails({ + invoiceInformation: [ + { key: 'Sub-total', value: '$0.00' }, + { key: 'Calculated total amount', value: '$0.00' }, + ], + }); + + const InvoiceLineDetailsAfterSave = InvoiceView.selectInvoiceLine(2); + InvoiceLineDetailsAfterSave.waitLoading(); + InvoiceLineDetailsAfterSave.checkFundDistibutionTableContent([ + { + name: testData.fundBeta.name, + amount: `-$${invoiceLineAmount3}.00`, + }, + ]); + cy.go('back'); + InvoiceView.waitLoading(); + + InvoiceView.approveInvoice(); + InvoiceView.checkInvoiceDetails({ + invoiceInformation: [{ key: 'Status', value: INVOICE_STATUSES.APPROVED }], + }); + + TopMenuNavigation.navigateToApp(APPLICATION_NAMES.FINANCE); + FinanceHelper.clickFundButton(); + FinanceHelper.searchByName(testData.fundAlpha.name); + Funds.selectFund(testData.fundAlpha.name); + FundDetails.waitLoading(); + + FundDetails.viewTransactionsForCurrentBudget(); + Funds.verifyTransactionWithAmountExist('Pending payment', `($${invoiceLineAmount1}.00)`); + Funds.verifyTransactionWithAmountExist('Pending payment', `($${invoiceLineAmount2}.00)`); + Transactions.waitLoading(); + + Transactions.closeTransactionsPage(); + + FinanceHelper.searchByName(testData.fundBeta.name); + Funds.selectFund(testData.fundBeta.name); + FundDetails.waitLoading(); + + FundDetails.viewTransactionsForCurrentBudget(); + Funds.verifyTransactionWithAmountExist('Pending payment', `$${invoiceLineAmount3}.00`); + Transactions.waitLoading(); + + cy.visit(TopMenu.invoicesPath); + Invoices.waitLoading(); + Invoices.searchByNumber(testData.invoice.vendorInvoiceNo); + Invoices.selectInvoice(testData.invoice.vendorInvoiceNo); + InvoiceView.waitLoading(); + + InvoiceView.payInvoice(); + InvoiceView.checkInvoiceDetails({ + invoiceInformation: [{ key: 'Status', value: INVOICE_STATUSES.PAID }], + }); + + TopMenuNavigation.navigateToApp(APPLICATION_NAMES.FINANCE); + FinanceHelper.searchByName(testData.fundBeta.name); + Funds.selectFund(testData.fundBeta.name); + FundDetails.waitLoading(); + + FundDetails.viewTransactionsForCurrentBudget(); + Transactions.checkTransactionsList({ + records: [{ type: 'Pending payment' }], + present: false, + }); + Transactions.checkTransactionsByTypeAndAmount({ + records: [{ type: 'Credit', amount: `$${invoiceLineAmount3}.00` }], + }); + + Transactions.closeTransactionsPage(); + + FinanceHelper.searchByName(testData.fundAlpha.name); + Funds.selectFund(testData.fundAlpha.name); + FundDetails.waitLoading(); + + FundDetails.viewTransactionsForCurrentBudget(); + Transactions.checkTransactionsList({ + records: [{ type: 'Pending payment' }], + present: false, + }); + Funds.verifyTransactionWithAmountExist('Payment', `($${invoiceLineAmount1}.00)`); + Funds.verifyTransactionWithAmountExist('Payment', `($${invoiceLineAmount2}.00)`); + }, + ); +}); diff --git a/cypress/e2e/receiving/user-in-acq-unit-cannot-edit-acq-unit-of-title-without-manage-permission.cy.js b/cypress/e2e/receiving/user-in-acq-unit-cannot-edit-acq-unit-of-title-without-manage-permission.cy.js new file mode 100644 index 0000000000..6639c56c02 --- /dev/null +++ b/cypress/e2e/receiving/user-in-acq-unit-cannot-edit-acq-unit-of-title-without-manage-permission.cy.js @@ -0,0 +1,193 @@ +import uuid from 'uuid'; +import permissions from '../../support/dictionary/permissions'; +import NewOrder from '../../support/fragments/orders/newOrder'; +import OrderLines from '../../support/fragments/orders/orderLines'; +import Orders from '../../support/fragments/orders/orders'; +import NewOrganization from '../../support/fragments/organizations/newOrganization'; +import Organizations from '../../support/fragments/organizations/organizations'; +import Receiving from '../../support/fragments/receiving/receiving'; +import ReceivingDetails from '../../support/fragments/receiving/receivingDetails'; +import ReceivingEditForm from '../../support/fragments/receiving/receivingEditForm'; +import AcquisitionUnits from '../../support/fragments/settings/acquisitionUnits/acquisitionUnits'; +import TopMenu from '../../support/fragments/topMenu'; +import Users from '../../support/fragments/users/users'; +import BasicOrderLine from '../../support/fragments/orders/basicOrderLine'; +import InventoryInstances from '../../support/fragments/inventory/inventoryInstances'; +import { ACQUISITION_METHOD_NAMES_IN_PROFILE, ORDER_STATUSES } from '../../support/constants'; +import InteractorsTools from '../../support/utils/interactorsTools'; +import getRandomPostfix from '../../support/utils/stringTools'; + +describe('Receiving', () => { + describe('Acquisition units', () => { + const organization = { ...NewOrganization.defaultUiOrganizations }; + const randomPostfix = getRandomPostfix(); + const instanceTitle = `C430248_Title_${randomPostfix}`; + const editionValue = `C430248_Edition_${randomPostfix}`; + const quantity = 1; + let order; + let orderLine; + let user; + let firstAcquisitionUnit; + let secondAcquisitionUnit; + let location; + + before('Create test data', () => { + cy.getAdminToken(); + + firstAcquisitionUnit = AcquisitionUnits.getDefaultAcquisitionUnit({ + protectRead: false, + protectUpdate: true, + protectCreate: true, + protectDelete: true, + }); + secondAcquisitionUnit = AcquisitionUnits.getDefaultAcquisitionUnit({ + protectRead: false, + protectUpdate: true, + protectCreate: true, + protectDelete: true, + }); + + AcquisitionUnits.createAcquisitionUnitViaApi(firstAcquisitionUnit).then((firstAUResponse) => { + firstAcquisitionUnit.id = firstAUResponse.id; + + AcquisitionUnits.createAcquisitionUnitViaApi(secondAcquisitionUnit).then( + (secondAUResponse) => { + secondAcquisitionUnit.id = secondAUResponse.id; + + cy.createTempUser([ + permissions.inventoryAll.gui, + permissions.uiReceivingViewEditCreate.gui, + ]).then((userProperties) => { + user = userProperties; + + AcquisitionUnits.assignUserViaApi(user.userId, firstAcquisitionUnit.id).then(() => { + AcquisitionUnits.assignUserViaApi(user.userId, secondAcquisitionUnit.id); + }); + + InventoryInstances.getLocations({ limit: 1 }).then((locations) => { + location = locations[0]; + + Organizations.createOrganizationViaApi(organization).then((orgResponse) => { + organization.id = orgResponse; + + cy.getDefaultMaterialType().then((materialType) => { + cy.getAcquisitionMethodsApi({ + query: `value="${ACQUISITION_METHOD_NAMES_IN_PROFILE.PURCHASE_AT_VENDOR_SYSTEM}"`, + }).then((acquisitionMethodResponse) => { + const orderData = { + ...NewOrder.getDefaultOrder({ vendorId: organization.id }), + orderType: 'One-Time', + approved: true, + }; + + Orders.createOrderViaApi(orderData).then((orderResponse) => { + order = orderResponse; + + const orderLineData = { + ...BasicOrderLine.defaultOrderLine, + id: uuid(), + purchaseOrderId: orderResponse.id, + titleOrPackage: instanceTitle, + checkinItems: false, + cost: { + listUnitPrice: 100.0, + currency: 'USD', + discountType: 'percentage', + quantityPhysical: quantity, + poLineEstimatedPrice: 100.0, + }, + fundDistribution: [], + locations: [ + { + locationId: location.id, + quantity, + quantityPhysical: quantity, + }, + ], + acquisitionMethod: + acquisitionMethodResponse.body.acquisitionMethods[0].id, + physical: { + createInventory: 'Instance, Holding, Item', + materialType: materialType.id, + materialSupplier: organization.id, + volumes: [], + }, + }; + + OrderLines.createOrderLineViaApi(orderLineData).then( + (orderLineResponse) => { + orderLine = orderLineResponse; + + Orders.updateOrderViaApi({ + ...orderResponse, + workflowStatus: ORDER_STATUSES.OPEN, + }).then(() => { + cy.getAdminToken(); + Receiving.getTitleByPoLineIdViaApi(orderLine.id).then((titleData) => { + Receiving.updateTitleViaApi({ + ...titleData, + acqUnitIds: [firstAcquisitionUnit.id], + }); + }); + + cy.login(user.username, user.password, { + path: TopMenu.receivingPath, + waiter: Receiving.waitLoading, + }); + }); + }, + ); + }); + }); + }); + }); + }); + }); + }, + ); + }); + }); + + after('Delete test data', () => { + cy.getAdminToken(); + Orders.deleteOrderViaApi(order.id); + Organizations.deleteOrganizationViaApi(organization.id); + Users.deleteViaApi(user.userId); + AcquisitionUnits.deleteAcquisitionUnitViaApi(firstAcquisitionUnit.id); + AcquisitionUnits.deleteAcquisitionUnitViaApi(secondAcquisitionUnit.id); + }); + + it( + 'C430248 User included in Acq unit can not edit Acq unit of a Title without Receiving: Manage acquisition units permission (thunderjet)', + { tags: ['criticalPath', 'thunderjet', 'C430248'] }, + () => { + Receiving.searchByParameter({ parameter: 'Keyword', value: instanceTitle }); + Receiving.selectFromResultsList(instanceTitle); + ReceivingDetails.waitLoading(); + + ReceivingDetails.expandTitleInformationAccordion(); + ReceivingDetails.verifyAcquisitionUnitInTitleInformation(firstAcquisitionUnit.name, true); + + ReceivingDetails.openReceivingEditForm(); + ReceivingEditForm.waitLoading(); + ReceivingEditForm.verifyAcquisitionUnitsDropdownDisabled(); + ReceivingEditForm.verifyAcquisitionUnitDisplayed(firstAcquisitionUnit.name, true); + + ReceivingEditForm.tryRemoveAcquisitionUnit(firstAcquisitionUnit.name); + + ReceivingEditForm.tryExpandAcquisitionUnitsDropdown(); + + ReceivingEditForm.fillItemDetailsFields({ edition: editionValue }); + ReceivingEditForm.verifySaveButtonEnabled(true); + + ReceivingEditForm.clickSaveButton({ itemSaved: false }); + ReceivingDetails.waitLoading(); + InteractorsTools.checkCalloutMessage( + `The title ${instanceTitle} has been successfully added for PO line ${orderLine.poLineNumber}`, + ); + + ReceivingDetails.verifyAcquisitionUnitInTitleInformation(firstAcquisitionUnit.name, true); + }, + ); + }); +}); diff --git a/cypress/e2e/receiving/user-not-included-in-acq-unit-is-not-able-to-unreceive-piece-having-acq-unit.cy.js b/cypress/e2e/receiving/user-not-included-in-acq-unit-is-not-able-to-unreceive-piece-having-acq-unit.cy.js new file mode 100644 index 0000000000..e6219c49ed --- /dev/null +++ b/cypress/e2e/receiving/user-not-included-in-acq-unit-is-not-able-to-unreceive-piece-having-acq-unit.cy.js @@ -0,0 +1,162 @@ +import uuid from 'uuid'; +import permissions from '../../support/dictionary/permissions'; +import NewOrder from '../../support/fragments/orders/newOrder'; +import OrderLines from '../../support/fragments/orders/orderLines'; +import Orders from '../../support/fragments/orders/orders'; +import NewOrganization from '../../support/fragments/organizations/newOrganization'; +import Organizations from '../../support/fragments/organizations/organizations'; +import Receiving from '../../support/fragments/receiving/receiving'; +import ReceivingDetails from '../../support/fragments/receiving/receivingDetails'; +import AcquisitionUnits from '../../support/fragments/settings/acquisitionUnits/acquisitionUnits'; +import TopMenu from '../../support/fragments/topMenu'; +import Users from '../../support/fragments/users/users'; +import BasicOrderLine from '../../support/fragments/orders/basicOrderLine'; +import InventoryInstances from '../../support/fragments/inventory/inventoryInstances'; +import { ACQUISITION_METHOD_NAMES_IN_PROFILE, ORDER_STATUSES } from '../../support/constants'; +import getRandomPostfix from '../../support/utils/stringTools'; +import EditPieceModal from '../../support/fragments/receiving/modals/editPieceModal'; + +describe('Receiving', () => { + describe('Acquisition units', () => { + const organization = { ...NewOrganization.defaultUiOrganizations }; + const randomPostfix = getRandomPostfix(); + const instanceTitle = `C436815_Title_${randomPostfix}`; + const quantity = 1; + let order; + let orderLine; + let user; + let acquisitionUnit; + let location; + + before('Create test data', () => { + cy.getAdminToken(); + + acquisitionUnit = AcquisitionUnits.getDefaultAcquisitionUnit({ + protectRead: false, + protectUpdate: true, + protectCreate: true, + protectDelete: false, + }); + + AcquisitionUnits.createAcquisitionUnitViaApi(acquisitionUnit).then((auResponse) => { + acquisitionUnit.id = auResponse.id; + + cy.createTempUser([ + permissions.uiInventoryViewInstances.gui, + permissions.uiReceivingViewEditCreate.gui, + ]).then((userProperties) => { + user = userProperties; + + InventoryInstances.getLocations({ limit: 1 }).then((locations) => { + location = locations[0]; + + Organizations.createOrganizationViaApi(organization).then((orgResponse) => { + organization.id = orgResponse; + + cy.getDefaultMaterialType().then((materialType) => { + cy.getAcquisitionMethodsApi({ + query: `value="${ACQUISITION_METHOD_NAMES_IN_PROFILE.PURCHASE_AT_VENDOR_SYSTEM}"`, + }).then((acquisitionMethodResponse) => { + const orderData = { + ...NewOrder.getDefaultOrder({ vendorId: organization.id }), + orderType: 'One-Time', + approved: true, + }; + + Orders.createOrderViaApi(orderData).then((orderResponse) => { + order = orderResponse; + + const orderLineData = { + ...BasicOrderLine.defaultOrderLine, + id: uuid(), + purchaseOrderId: orderResponse.id, + titleOrPackage: instanceTitle, + checkinItems: false, + cost: { + listUnitPrice: 100.0, + currency: 'USD', + discountType: 'percentage', + quantityPhysical: quantity, + poLineEstimatedPrice: 100.0, + }, + fundDistribution: [], + locations: [ + { + locationId: location.id, + quantity, + quantityPhysical: quantity, + }, + ], + acquisitionMethod: acquisitionMethodResponse.body.acquisitionMethods[0].id, + physical: { + createInventory: 'Instance, Holding, Item', + materialType: materialType.id, + materialSupplier: organization.id, + volumes: [], + }, + }; + + OrderLines.createOrderLineViaApi(orderLineData).then((orderLineResponse) => { + orderLine = orderLineResponse; + + Orders.updateOrderViaApi({ + ...orderResponse, + workflowStatus: ORDER_STATUSES.OPEN, + }).then(() => { + cy.getAdminToken(); + Receiving.getTitleByPoLineIdViaApi(orderLine.id).then((titleData) => { + Receiving.getPiecesViaApi(orderLine.id).then((pieces) => { + Receiving.receivePieceViaApi({ + poLineId: orderLine.id, + pieces: [{ id: pieces[0].id }], + }).then(() => { + Receiving.updateTitleViaApi({ + ...titleData, + acqUnitIds: [acquisitionUnit.id], + }).then(() => { + cy.login(user.username, user.password, { + path: TopMenu.receivingPath, + waiter: Receiving.waitLoading, + }); + }); + }); + }); + }); + }); + }); + }); + }); + }); + }); + }); + }); + }); + }); + + after('Delete test data', () => { + cy.getAdminToken(); + Orders.deleteOrderViaApi(order.id); + Organizations.deleteOrganizationViaApi(organization.id); + Users.deleteViaApi(user.userId); + AcquisitionUnits.deleteAcquisitionUnitViaApi(acquisitionUnit.id); + }); + + it( + 'C436815 User NOT included in Acq unit is not able to unreceive a piece having Acq unit (thunderjet)', + { tags: ['extendedPath', 'thunderjet', 'C436815'] }, + () => { + Receiving.searchByParameter({ parameter: 'Keyword', value: instanceTitle }); + Receiving.selectFromResultsList(instanceTitle); + ReceivingDetails.waitLoading(); + ReceivingDetails.verifyReceivedRecordsCount(1); + + ReceivingDetails.expandTitleInformationAccordion(); + ReceivingDetails.verifyAcquisitionUnitInTitleInformation(acquisitionUnit.name, true); + + ReceivingDetails.openEditPieceModal({ row: 0, section: 'Received' }); + EditPieceModal.waitLoading(); + EditPieceModal.verifyUnreceiveOptionState({ disabled: true }); + }, + ); + }); +}); diff --git a/cypress/support/fragments/invoices/invoiceLineDetails.js b/cypress/support/fragments/invoices/invoiceLineDetails.js index 308c8b4df8..876f2c50ad 100644 --- a/cypress/support/fragments/invoices/invoiceLineDetails.js +++ b/cypress/support/fragments/invoices/invoiceLineDetails.js @@ -102,6 +102,13 @@ export default { .has({ content: including(record.expenseClass) }), ); } + if (record.amount) { + cy.expect( + fundDistributionsSection + .find(MultiColumnListCell({ row: index, column: 'Amount' })) + .has({ content: including(record.amount) }), + ); + } if (record.initialEncumbrance) { cy.expect( fundDistributionsSection diff --git a/cypress/support/fragments/invoices/invoiceLineEditForm.js b/cypress/support/fragments/invoices/invoiceLineEditForm.js index 8a9f18fa21..6275c1b29e 100644 --- a/cypress/support/fragments/invoices/invoiceLineEditForm.js +++ b/cypress/support/fragments/invoices/invoiceLineEditForm.js @@ -22,6 +22,7 @@ const fundDistributionSection = Section({ id: 'invoiceLineForm-fundDistribution' const cancelButtom = Button('Cancel'); const saveButton = Button('Save & close'); +const subTotalSelector = '#subTotal'; const infoFields = { description: informationSection.find(TextField({ id: 'description' })), @@ -145,6 +146,12 @@ export default { cy.wait(1000); }, + setNegativeSubTotal(amount) { + cy.get(subTotalSelector).clear(); + cy.get(subTotalSelector).type(`-${amount}`); + cy.get(subTotalSelector).should('have.value', `-${amount}`); + }, + checkSelectionOptions(selectionName, expectedOptions) { cy.do([Selection(selectionName).open()]); cy.expect([SelectionList().has({ optionList: expectedOptions })]); diff --git a/cypress/support/fragments/receiving/modals/editPieceModal.js b/cypress/support/fragments/receiving/modals/editPieceModal.js index c81190bc64..36ea302f45 100644 --- a/cypress/support/fragments/receiving/modals/editPieceModal.js +++ b/cypress/support/fragments/receiving/modals/editPieceModal.js @@ -23,6 +23,8 @@ const cancelButton = editPieceModal.find(Button('Cancel')); const deleteButton = Button('Delete'); const quickReceiveButton = Button('Quick receive'); const saveAndCloseButton = editPieceModal.find(Button('Save & close')); +const actionsDropdownButton = Button({ dataTestID: 'dropdown-trigger-button' }); +const unreceiveButton = Button('Unreceive'); const editPieceFields = { Caption: editPieceModal.find(TextField({ name: 'displaySummary' })), @@ -103,6 +105,10 @@ export default { cy.expect(saveAndCloseButton.has({ disabled })); }, verifyActionsMenuState({ disabled = true } = {}) { - cy.expect(Button({ dataTestID: 'dropdown-trigger-button' }).has({ disabled })); + cy.expect(actionsDropdownButton.has({ disabled })); + }, + verifyUnreceiveOptionState({ disabled = true } = {}) { + cy.do(actionsDropdownButton.click()); + cy.expect(unreceiveButton.has({ disabled })); }, }; diff --git a/cypress/support/fragments/receiving/receivingDetails.js b/cypress/support/fragments/receiving/receivingDetails.js index 8a2e34a1ec..bc533a9cd9 100644 --- a/cypress/support/fragments/receiving/receivingDetails.js +++ b/cypress/support/fragments/receiving/receivingDetails.js @@ -25,6 +25,7 @@ const orderLineDetailsSection = receivingDetailsSection.find(Section({ id: 'polD const expectedSection = receivingDetailsSection.find(Section({ id: 'expected' })); const receivedSection = receivingDetailsSection.find(Section({ id: 'received' })); const expectedRowsSelector = '#expected [class*="mclRowFormatterContainer"]'; +const receivedRowsSelector = '#received [class*="mclRowFormatterContainer"]'; const buttons = { Actions: receinvingDetailsHeader.find(Button('Actions')), @@ -108,6 +109,13 @@ export default { cy.get(expectedRowsSelector).should('have.length', expectedCount); } }, + verifyReceivedRecordsCount(receivedCount) { + if (receivedCount === 0) { + cy.expect(receivedSection.has({ text: including('The list contains no items') })); + } else { + cy.get(receivedRowsSelector).should('have.length', receivedCount); + } + }, checkExpectedTableContent(records = []) { records.forEach((record, index) => { if (record.copyNumber) { diff --git a/cypress/support/fragments/receiving/receivingEditForm.js b/cypress/support/fragments/receiving/receivingEditForm.js index 2af5069407..1446955152 100644 --- a/cypress/support/fragments/receiving/receivingEditForm.js +++ b/cypress/support/fragments/receiving/receivingEditForm.js @@ -5,6 +5,7 @@ import { MultiSelect, MultiSelectOption, including, + ValueChipRoot, not, matching, } from '../../../../interactors'; @@ -30,6 +31,13 @@ const lineDetailsFields = { const cancelButton = receivingEditForm.find(Button('Cancel')); const saveAndCloseButton = receivingEditForm.find(Button('Save & close')); +const acqUnitsInputSelector = '#title-acq-units input'; +const acqUnitsRemoveButton = itemDetailsFields.acquisitionUnits.find( + ValueChipRoot().find(Button({ icon: 'times' })), +); +const acqUnitsToggleButton = itemDetailsFields.acquisitionUnits.find( + Button({ className: including('multiSelectToggleButton') }), +); export default { waitLoading(ms = DEFAULT_WAIT_TIME) { @@ -122,4 +130,15 @@ export default { verifySaveButtonEnabled(isEnabled = true) { cy.expect(saveAndCloseButton.has({ disabled: !isEnabled })); }, + verifyAcquisitionUnitsDropdownDisabled() { + cy.get(acqUnitsInputSelector).should('be.disabled'); + }, + tryRemoveAcquisitionUnit(acquisitionUnitName) { + cy.expect(acqUnitsRemoveButton.has({ disabled: true })); + cy.expect(itemDetailsFields.acquisitionUnits.has({ selected: including(acquisitionUnitName) })); + }, + tryExpandAcquisitionUnitsDropdown() { + cy.expect(acqUnitsToggleButton.has({ disabled: true })); + cy.expect(itemDetailsFields.acquisitionUnits.has({ open: false })); + }, };