Skip to content

Commit 927e59b

Browse files
committed
add button
1 parent 04c4647 commit 927e59b

19 files changed

+376
-8
lines changed

extra-webpack.config.js

+10-3
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,10 @@ module.exports = {
3838
__dirname,
3939
'feature-libs/product-configurator'
4040
),
41-
'@spartacus/product-multi-dimensional': path.join(__dirname, 'feature-libs/product-multi-dimensional'),
41+
'@spartacus/product-multi-dimensional': path.join(
42+
__dirname,
43+
'feature-libs/product-multi-dimensional'
44+
),
4245
'@spartacus/storefinder': path.join(
4346
__dirname,
4447
'feature-libs/storefinder'
@@ -58,7 +61,7 @@ module.exports = {
5861
'@spartacus/tracking': path.join(__dirname, 'feature-libs/tracking'),
5962
'@spartacus/cart': path.join(__dirname, 'feature-libs/cart'),
6063
'@spartacus/order': path.join(__dirname, 'feature-libs/order'),
61-
'@spartacus/quote': path.join( __dirname, 'feature-libs/quote'),
64+
'@spartacus/quote': path.join(__dirname, 'feature-libs/quote'),
6265
'@spartacus/epd-visualization': path.join(
6366
__dirname,
6467
'integration-libs/epd-visualization'
@@ -73,8 +76,12 @@ module.exports = {
7376
),
7477
'@spartacus/s4om': path.join(__dirname, 'integration-libs/s4om'),
7578
'@spartacus/opf': path.join(__dirname, 'integration-libs/opf'),
76-
'@spartacus/s4-service': path.join(__dirname, 'integration-libs/s4-service'),
79+
'@spartacus/s4-service': path.join(
80+
__dirname,
81+
'integration-libs/s4-service'
82+
),
7783
'@spartacus/omf': path.join(__dirname, 'integration-libs/omf'),
84+
'@spartacus/punchout': path.join(__dirname, 'integration-libs/punchout'),
7885
},
7986
},
8087
};

integration-libs/punchout/_index.scss

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
@import '@spartacus/styles/scss/core';
2+
@import 'styles/punchout-close-session';
3+
@import 'styles/punchout-buttons';
4+
5+
$punchout-components-allowlist: cx-punchout-close-session, cx-punchout-buttons !default;
6+
7+
$skipComponentStyles: () !default;
8+
9+
@each $selector in $punchout-components-allowlist {
10+
#{$selector} {
11+
// skip selectors if they're added to the $skipComponentStyles list
12+
@if (index($skipComponentStyles, $selector) == null) {
13+
@extend %#{$selector} !optional;
14+
}
15+
}
16+
}
17+
18+
// add body specific selectors
19+
body {
20+
@each $selector in $punchout-components-allowlist {
21+
@if (index($skipComponentStyles, $selector) == null) {
22+
@extend %#{$selector}__body !optional;
23+
}
24+
}
25+
}

integration-libs/punchout/assets/translations/en/punchout.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
"punchout": {
33
"backToRequisition": "Back to requisition",
44
"cancel": "Cancel",
5-
"redirectToProcurementSystem": "Return to Procurement System"
5+
"redirectToProcurementSystem": "Return to Procurement System",
6+
"closeSession": "Close Session"
67
}
78
}

integration-libs/punchout/components/public_api.ts

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
*/
66

77
export * from './punchout-buttons/punchout-buttons.component';
8+
export * from './punchout-close-session/punchout-close-session.component';
89
export * from './punchout-components.module';
910
export * from './punchout-error/punchout-error.component';
1011
export * from './punchout-requisition/punchout-requisition.component';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<ng-container *ngIf="(hasSessionId$ | async) === true">
2+
<div class="cx-punchout-close-session" (click)="clickCloseSessionButton()">
3+
{{ 'punchout.closeSession' | cxTranslate }}
4+
</div>
5+
</ng-container>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2025 SAP Spartacus team <[email protected]>
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
8+
import { ActiveCartFacade, MultiCartFacade } from '@spartacus/cart/base/root';
9+
import { AuthService, EventService, RoutingService } from '@spartacus/core';
10+
import { PunchoutFacade, PunchoutStoreService } from '@spartacus/punchout/root';
11+
import { map, Observable, of, Subscription, switchMap } from 'rxjs';
12+
13+
@Component({
14+
selector: 'cx-punchout-close-session',
15+
templateUrl: './punchout-close-session.component.html',
16+
changeDetection: ChangeDetectionStrategy.OnPush,
17+
standalone: false,
18+
})
19+
export class PunchoutCloseSessionComponent {
20+
protected punchoutStoreService = inject(PunchoutStoreService);
21+
protected routingService = inject(RoutingService);
22+
protected authService = inject(AuthService);
23+
protected multiCartFacade = inject(MultiCartFacade);
24+
protected activeCartFacade = inject(ActiveCartFacade);
25+
protected eventService = inject(EventService);
26+
protected subscription = new Subscription();
27+
protected punchoutFacade = inject(PunchoutFacade);
28+
29+
hasSessionId$: Observable<boolean> = this.authService.isUserLoggedIn().pipe(
30+
switchMap((isLoggedIn) => {
31+
return isLoggedIn
32+
? this.punchoutStoreService.getPunchoutState()
33+
: of({ punchoutSessionId: undefined });
34+
}),
35+
map((punchoutState) => {
36+
return !!punchoutState.punchoutSessionId;
37+
})
38+
);
39+
40+
clickCloseSessionButton(): void {
41+
this.punchoutFacade.closePunchoutSession().subscribe();
42+
}
43+
}

integration-libs/punchout/components/punchout-components.module.ts

+6
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { NgModule } from '@angular/core';
99
import { ReactiveFormsModule } from '@angular/forms';
1010
import { CmsConfig, I18nModule, provideDefaultConfig } from '@spartacus/core';
1111
import { PunchoutButtonsComponent } from './punchout-buttons/punchout-buttons.component';
12+
import { PunchoutCloseSessionComponent } from './punchout-close-session/punchout-close-session.component';
1213
import { PunchoutErrorComponent } from './punchout-error/punchout-error.component';
1314
import { PunchoutRequisitionComponent } from './punchout-requisition/punchout-requisition.component';
1415
import { PunchoutSessionComponent } from './punchout-session/punchout-session.component';
@@ -19,12 +20,14 @@ import { PunchoutSessionComponent } from './punchout-session/punchout-session.co
1920
PunchoutErrorComponent,
2021
PunchoutRequisitionComponent,
2122
PunchoutButtonsComponent,
23+
PunchoutCloseSessionComponent,
2224
],
2325
exports: [
2426
PunchoutSessionComponent,
2527
PunchoutErrorComponent,
2628
PunchoutRequisitionComponent,
2729
PunchoutButtonsComponent,
30+
PunchoutCloseSessionComponent,
2831
],
2932
imports: [CommonModule, ReactiveFormsModule, I18nModule],
3033
providers: [
@@ -42,6 +45,9 @@ import { PunchoutSessionComponent } from './punchout-session/punchout-session.co
4245
PunchoutRequisitionComponent: {
4346
component: PunchoutRequisitionComponent,
4447
},
48+
PunchoutCloseSessionComponent: {
49+
component: PunchoutCloseSessionComponent,
50+
},
4551
},
4652
}),
4753
],

integration-libs/punchout/core/facade/punchout.service.spec.ts

+83-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import { TestBed } from '@angular/core/testing';
2-
import { CommandService, RoutingService } from '@spartacus/core';
2+
import { MultiCartFacade, OrderEntry } from '@spartacus/cart/base/root';
3+
import { CommandService, RoutingService, UserIdService } from '@spartacus/core';
34
import {
45
PUNCHOUT_ERROR_PAGE_URL,
6+
PunchoutInitialCart,
57
PunchOutLevel,
68
PunchOutOperation,
79
PunchoutRequisition,
@@ -51,8 +53,39 @@ const mockPunchoutSession: PunchoutSession = {
5153
const mockPunchoutState: PunchoutState = {
5254
punchoutSessionId: mockSessionId,
5355
punchoutSession: mockPunchoutSession,
56+
punchoutInitialCart: undefined,
57+
cancelRequisition: undefined,
5458
};
5559

60+
const mockEntries: OrderEntry[] = [
61+
{
62+
quantity: 1,
63+
product: { name: 'product1', code: 'code1' },
64+
},
65+
{
66+
quantity: 1,
67+
product: { name: 'product2', code: 'cod2' },
68+
},
69+
];
70+
71+
const mockStateEntries: { productCode: string; quantity: number }[] = [
72+
{
73+
quantity: 1,
74+
productCode: 'code1',
75+
},
76+
{
77+
quantity: 1,
78+
productCode: 'code2',
79+
},
80+
{
81+
quantity: 2,
82+
productCode: 'code3',
83+
},
84+
];
85+
86+
const mockInitialCart: PunchoutInitialCart = { entries: mockStateEntries };
87+
const mockCart = { entries: mockEntries };
88+
5689
class MockPunchoutStoreService implements Partial<PunchoutStoreService> {
5790
setPunchoutState = () => {};
5891
getPunchoutState = () => of(mockPunchoutState);
@@ -83,12 +116,32 @@ class MockRoutingService implements Partial<RoutingService> {
83116
go = () => Promise.resolve(true);
84117
}
85118

119+
// class MockMultiCartFacade implements Partial<MultiCartFacade> {
120+
// loadCart = () => {};
121+
// removeEntry = () => {};
122+
// addEntries = () => {};
123+
// getCart = () => of(mockCart);
124+
// isStable = () => of(true);
125+
// }
126+
127+
class MockMultiCartFacade implements Partial<MultiCartFacade> {
128+
getCart = createSpy().and.returnValue(of(mockCart));
129+
addEntry = createSpy();
130+
removeEntry = createSpy();
131+
isStable = createSpy().and.returnValue(of(true));
132+
}
133+
134+
class MockUserIdService implements Partial<UserIdService> {
135+
takeUserId = () => of(mockPunchoutSession.customerId);
136+
}
137+
86138
describe('Punchoutservice', () => {
87139
let service: PunchoutService;
88140
let connector: PunchoutConnector;
89141
let routingService: RoutingService;
90142
let punchoutStoreService: PunchoutStoreService;
91143
let punchoutAuthService: PunchoutAuthService;
144+
let multiCartFacade: MultiCartFacade;
92145

93146
beforeEach(() => {
94147
TestBed.configureTestingModule({
@@ -99,13 +152,16 @@ describe('Punchoutservice', () => {
99152
{ provide: CommandService, useValue: commandServiceMock },
100153
{ provide: RoutingService, useClass: MockRoutingService },
101154
{ provide: MockPunchoutStoreService, useClass: PunchoutStoreService },
155+
{ provide: MockMultiCartFacade, useClass: MultiCartFacade },
156+
{ provide: MockUserIdService, useClass: UserIdService },
102157
],
103158
});
104159
service = TestBed.inject(PunchoutService);
105160
connector = TestBed.inject(PunchoutConnector);
106161
routingService = TestBed.inject(RoutingService);
107162
punchoutStoreService = TestBed.inject(PunchoutStoreService);
108163
punchoutAuthService = TestBed.inject(PunchoutAuthService);
164+
multiCartFacade = TestBed.inject(MultiCartFacade);
109165
});
110166

111167
it('should be created', () => {
@@ -278,4 +334,30 @@ describe('Punchoutservice', () => {
278334
},
279335
});
280336
});
337+
338+
it('should closePunchoutSession ', (done) => {
339+
const mockState: PunchoutState = {
340+
...mockPunchoutState,
341+
punchoutInitialCart: mockInitialCart,
342+
punchoutSession: {
343+
...mockPunchoutSession,
344+
punchOutOperation: PunchOutOperation.EDIT,
345+
},
346+
};
347+
spyOn(routingService, 'go').and.returnValue(Promise.resolve(true));
348+
spyOn(punchoutStoreService, 'getPunchoutState').and.returnValue(
349+
of(mockState)
350+
);
351+
// spyOn(multiCartFacade, 'deleteCart').and.callThrough();
352+
spyOn(punchoutStoreService, 'updatePunchoutState').and.callThrough();
353+
spyOn(multiCartFacade, 'addEntries').and.callThrough();
354+
spyOn(multiCartFacade, 'isStable').and.callThrough();
355+
356+
service.closePunchoutSession().subscribe({
357+
next: () => {
358+
expect(punchoutStoreService.updatePunchoutState).toHaveBeenCalled();
359+
done();
360+
},
361+
});
362+
});
281363
});

0 commit comments

Comments
 (0)