Skip to content

Commit

Permalink
feat: Requisition Management / Order Approval functionality (#326)
Browse files Browse the repository at this point in the history
* requisition-management sub project
* checkout with order approval additions
* manage budgets for B2B users in organization-management
* My Account overview widgets (budget, requisition, approval)
* introduce Angular CDK Table for requisition listing with mobile card view

BREAKING CHANGE: Required ICM Version 7.10.26.2-LTS

Co-authored-by: Silke Grueber <SGrueber@intershop.com>
Co-authored-by: Susanne Schneider <s.schneider@intershop.de>
Co-authored-by: Francisco Brito <brito@evident.nl>
Co-authored-by: Danilo Hoffmann <d.hoffmann@intershop.de>
Co-authored-by: MKless <mkless@intershop.de>
  • Loading branch information
6 people authored Dec 17, 2020
1 parent a53de1a commit eee665f
Show file tree
Hide file tree
Showing 209 changed files with 7,168 additions and 421 deletions.
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ COPY schematics /workspace/schematics/
COPY package.json package-lock.json /workspace/
RUN npm i --ignore-scripts
COPY projects/organization-management/src/app /workspace/projects/organization-management/src/app/
COPY projects/requisition-management/src/app /workspace/projects/requisition-management/src/app/
COPY src /workspace/src/
COPY tsconfig.app.json tsconfig.base.json ngsw-config.json .browserslistrc angular.json /workspace/
RUN npm run build:schematics && npm run synchronize-lazy-components -- --ci
Expand Down
46 changes: 46 additions & 0 deletions angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,52 @@
}
}
}
},
"requisition-management": {
"projectType": "application",
"cli": {
"defaultCollection": "intershop-schematics"
},
"schematics": {
"intershop-schematics:page": {
"lazy": false
}
},
"root": "projects/requisition-management",
"sourceRoot": "projects/requisition-management/src",
"prefix": "ish",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/requisition-management",
"index": "projects/requisition-management/src/index.html",
"main": "projects/requisition-management/src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "projects/requisition-management/tsconfig.app.json",
"aot": false,
"styles": ["src/styles/themes/default/style.scss"],
"assets": [
{ "glob": "**/*", "input": "src/assets/", "output": "/assets/" }
],
"scripts": [],
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.local.ts"
}
]
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "requisition-management:build",
"disableHostCheck": true,
"host": "0.0.0.0"
}
}
}
}
},
"defaultProject": "intershop-pwa",
Expand Down
16 changes: 4 additions & 12 deletions e2e/cypress/integration/pages/account/my-account.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,17 @@ export class MyAccountPage {
}

navigateToQuoting() {
cy.get('a[data-testing-id="quoute-list-link"]').click();
cy.get('a[data-testing-id="quote-list-link"]').click();
}

get newQuoteLabel() {
return cy.get('[data-testing-id="new-counter"]');
get respondedQuotesCount() {
return cy.get('[data-testing-id="responded-counter"]');
}

get submittedQuoteLabel() {
get submittedQuotesCount() {
return cy.get('[data-testing-id="submitted-counter"]');
}

get acceptedQuoteLabel() {
return cy.get('[data-testing-id="accepted-counter"]');
}

get rejectedQuoteLabel() {
return cy.get('[data-testing-id="rejected-counter"]');
}

navigateToAddresses() {
cy.get('a[data-testing-id="addresses-link"]').click();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,11 @@ export class CheckoutPaymentPage {
},

formError(key: string) {
return cy.get(`[data-testing-id=payment-parameter-form-${method}]`).find(`[data-testing-id='${key}']`).next();
return cy
.get(`[data-testing-id=payment-parameter-form-${method}]`)
.find(`[data-testing-id='${key}']`)
.parent()
.next();
},
};
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { fillFormField } from '../../framework';

declare interface B2BUserBudgetForm {
budget: number;
orderSpentLimit: number;
budgetPeriod: string;
}

export class UserEditBudgetPage {
readonly tag = 'ish-user-edit-budget-page';

private submitButton = () => cy.get('[data-testing-id="edit-budget-submit"]');

fillForm(content: B2BUserBudgetForm) {
Object.keys(content)
.filter(key => content[key] !== undefined)
.forEach((key: keyof B2BUserBudgetForm) => {
fillFormField(this.tag, key, content[key]);
});

return this;
}

submit() {
this.submitButton().click();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,27 @@ export class UsersDetailPage {
return cy.get('[data-testing-id="edit-roles"]').click();
}

editBudget() {
return cy.get('[data-testing-id="edit-budget"]').click();
}

goToUserManagement() {
cy.get('[data-testing-id="back-to-user-management"]').click();
}

get rolesAndPermissions() {
return cy.get(this.tag).find('[data-testing-id="user-roles-fields"]');
}

get orderSpendLimit() {
return cy.get(this.tag).find('[data-testing-id="order-spend-limit-field"]');
}

get budget() {
return cy.get(this.tag).find('[data-testing-id="budget-field"]');
}

get userBudget() {
return cy.get(this.tag).find('[data-testing-id="user-budget"]');
}
}
10 changes: 10 additions & 0 deletions e2e/cypress/integration/pages/organizationmanagement/users.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,14 @@ export class UsersPage {
rolesOfUser(id: string) {
return cy.get(`[data-testing-id="user-roles-${id}"]`);
}

hoverUserBudgetProgressBar(id: string) {
cy.get(`[data-testing-id="user-budget-${id}"]`)
.find('[data-testing-id="user-budget-popover"]')
.trigger('mouseover');
}

getUserBudgetPopover(id: string) {
return cy.get(`[data-testing-id="user-budget-${id}"]`).find('[data-testing-id="user-budget-popover"]');
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ const _ = {
name: 'Patricia Miller',
email: 'pmiller@test.intershop.de',
role: 'Buyer',
permission: 'View cost objects',
permission: 'Manage purchases',
orderSpendLimit: '$500.00',
budget: '$10,000.00',
},
};

Expand All @@ -34,6 +36,13 @@ describe('User Management', () => {
});
});

it('should show budget popover on progress bar mouseover', () => {
at(UsersPage, page => {
page.hoverUserBudgetProgressBar(_.selectedUser.email);
page.getUserBudgetPopover(_.selectedUser.email).should('exist');
});
});

it('should be able to see user details', () => {
at(UsersPage, page => {
page.goToUserDetailLink(_.selectedUser.email);
Expand All @@ -44,6 +53,8 @@ describe('User Management', () => {
page.rolesAndPermissions.should('not.contain', _.user.role);
page.rolesAndPermissions.should('contain', _.selectedUser.role);
page.rolesAndPermissions.should('contain', _.selectedUser.permission);
page.orderSpendLimit.should('contain', `${_.selectedUser.orderSpendLimit}`);
page.budget.should('contain', `${_.selectedUser.budget}`);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { createB2BUserViaREST } from '../../framework/b2b-user';
import { LoginPage } from '../../pages/account/login.page';
import { sensibleDefaults } from '../../pages/account/registration.page';
import { UserCreatePage } from '../../pages/organizationmanagement/user-create.page';
import { UserEditBudgetPage } from '../../pages/organizationmanagement/user-edit-budget.page';
import { UserEditRolesPage } from '../../pages/organizationmanagement/user-edit-roles.page';
import { UserEditPage } from '../../pages/organizationmanagement/user-edit.page';
import { UsersDetailPage } from '../../pages/organizationmanagement/users-detail.page';
Expand All @@ -21,11 +22,20 @@ const _ = {
lastName: 'Doe',
phone: '5551234',
email: `j.joe${new Date().getTime()}@testcity.de`,

orderSpentLimit: 100,
budget: 800,
budgetPeriod: 'monthly',
},
roles: {
autoRole: 'Buyer',
assignedRole: 'Account Admin',
},
budget: {
orderSpentLimit: 200,
budget: 900,
budgetPeriod: 'weekly',
},
editUser: {
title: 'Ms.',
firstName: 'Jane',
Expand Down Expand Up @@ -57,6 +67,7 @@ describe('User Management - CRUD', () => {
at(UsersDetailPage, page => {
page.name.should('contain', `${_.newUser.firstName} ${_.newUser.lastName}`);
page.rolesAndPermissions.should('contain', _.roles.autoRole);
page.budget.should('contain', `${_.newUser.budget}`);
});
});

Expand Down Expand Up @@ -84,6 +95,19 @@ describe('User Management - CRUD', () => {
});
});

it('should be able to edit budget', () => {
at(UsersDetailPage, page => page.editBudget());
at(UserEditBudgetPage, page => {
page.fillForm(_.budget);
page.submit();
});
at(UsersDetailPage, page => {
page.budget.should('contain', `${_.budget.budget}`);
page.orderSpendLimit.should('contain', `${_.budget.orderSpentLimit}`);
page.userBudget.should('contain', `${_.budget.budgetPeriod}`);
});
});

it('should be able to delete user', () => {
at(UsersDetailPage, page => page.goToUserManagement());
at(UsersPage, page => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,8 @@ describe('Quote Handling', () => {

it('should check number of quotes', () => {
at(MyAccountPage, page => {
page.newQuoteLabel.should('have.text', '0');
page.submittedQuoteLabel.should('have.text', '0');
page.acceptedQuoteLabel.should('have.text', '0');
page.rejectedQuoteLabel.should('have.text', '0');
page.submittedQuotesCount.should('have.text', ' 0 ');
page.respondedQuotesCount.should('have.text', ' 0 ');
});
});

Expand Down Expand Up @@ -127,10 +125,7 @@ describe('Quote Handling', () => {
it('should check number of quotes again', () => {
at(FamilyPage, page => page.header.goToMyAccount());
at(MyAccountPage, page => {
page.newQuoteLabel.should('have.text', '2');
page.submittedQuoteLabel.should('have.text', '1');
page.acceptedQuoteLabel.should('have.text', '0');
page.rejectedQuoteLabel.should('have.text', '0');
page.submittedQuotesCount.should('have.text', ' 1 ');
});
});
});
1 change: 1 addition & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ module.exports = {
moduleNameMapper: {
'^ish-(.*)$': '<rootDir>/src/app/$1',
'^organization-management$': '<rootDir>/projects/organization-management/src/app/exports',
'^requisition-management$': '<rootDir>/projects/requisition-management/src/app/exports',
},
snapshotSerializers: [
'./src/jest-serializer/AngularHTMLSerializer.js',
Expand Down
12 changes: 10 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
},
"dependencies": {
"@angular/animations": "~11.0.3",
"@angular/cdk": "^11.0.2",
"@angular/common": "~11.0.3",
"@angular/compiler": "~11.0.3",
"@angular/core": "~11.0.3",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<ish-error-message [error]="error$ | async"></ish-error-message>
<ish-info-box heading="account.requisitions.widget.budget_title" class="infobox-wrapper" cssClass="infobox-widget">
<div class="loading-container">
<div *ngIf="!(budgetLoading$ | async); else loading">
<div class="row">
<div class="col-xs-12 col-md-8">
<ish-user-budget progressBarClass="background-inverse" [budget]="userBudget$ | async"></ish-user-budget>
</div>
</div>
<div *ishIsAuthorizedTo="'APP_B2B_MANAGE_USERS'" class="row">
<div class="col">
<a routerLink="/account/organization/users">
{{ 'account.requisitions.widget.manage_budgets' | translate }}
</a>
</div>
</div>
</div>

<ng-template #loading>
<ish-loading></ish-loading>
</ng-template>
</div>
</ish-info-box>
Loading

0 comments on commit eee665f

Please sign in to comment.