Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
8888d06
changes for deliveryMode=pickup
Diksha-28 Aug 19, 2025
1152a99
test cases
Diksha-28 Aug 20, 2025
a17a828
Add license header
github-actions[bot] Aug 20, 2025
4bdd9f4
fixing the cart item count
Diksha-28 Sep 3, 2025
aacefa0
chore(ui): update shipping address and delivery method components as …
Sep 5, 2025
8291d2b
adding pickup address card
Diksha-28 Sep 7, 2025
9aab726
changes for billing address and deliverymode for pickup items
Sep 8, 2025
13968b4
Merge branch 'develop' into BOPISChange
Diksha-28 Sep 10, 2025
a07f42e
Test cases for BOPIS changes
Sep 10, 2025
7208b7e
removed comments
Diksha-28 Sep 10, 2025
b68e9f6
adding @spartacus/pickup-in-store in dependency
Diksha-28 Sep 10, 2025
3d5f289
Update dependencies.json
Diksha-28 Sep 10, 2025
1a39d2b
adding properties to tsconfig.lib.json
Diksha-28 Sep 10, 2025
db0b921
Fix Sonarqube scan error
Sep 10, 2025
e24ca25
fixing sonarQube error
Diksha-28 Sep 10, 2025
d955179
Update user-address.service.ts
Diksha-28 Sep 10, 2025
a048d82
fixing code formatting error
Diksha-28 Sep 10, 2025
4229fbf
fix dependency order
Sep 11, 2025
1185d6c
Merge branch 'develop' into BOPISChange
Matejk00 Sep 15, 2025
0168adc
addressing review comments.
Diksha-28 Sep 16, 2025
900b3fc
Merge branch 'develop' into BOPISChange
Diksha-28 Sep 16, 2025
2d54082
Addressing review comments
Diksha-28 Sep 16, 2025
8ed39d9
Addressing review comments(deliveryMode)
Sep 16, 2025
800e497
fixed prettier formatting error
Sep 16, 2025
089ed92
Merge branch 'develop' into BOPISChange
Diksha-28 Sep 18, 2025
7c15215
variable rename
Sep 18, 2025
ddd293c
fixed testcases
Sep 18, 2025
3821d25
fixed formatting issue
Sep 18, 2025
a9134d1
Merge branch 'develop' into BOPISChange
Diksha-28 Sep 19, 2025
830b5e2
Merge branch 'develop' into BOPISChange
ijitendrasap Sep 19, 2025
42d6df6
changes reverted
Sep 19, 2025
2713436
Merge branch 'develop' into BOPISChange
Diksha-28 Sep 21, 2025
264cbfd
Merge branch 'develop' into BOPISChange
Matejk00 Sep 22, 2025
2b06d52
fix: no billing address disables payment options
Sep 24, 2025
b4ae065
Merge branch 'develop' into BOPISChange
ijitendrasap Sep 24, 2025
2e884c1
build error fix
Sep 24, 2025
cf782ac
build error fix
Sep 24, 2025
1b474cc
update dependencies
Sep 24, 2025
6eb315f
fix: B2B address card, add backtocart , show error when no billing ad…
Sep 30, 2025
eaf7719
fixing sonarQube issue
Diksha-28 Sep 30, 2025
0d20f4b
static code check fix
Diksha-28 Sep 30, 2025
fdef38a
Merge branch 'develop' into BOPISChange
Diksha-28 Sep 30, 2025
f139519
fix: noBillingAddress error message
Oct 1, 2025
6432ccc
unit testcases fixed
Oct 1, 2025
a35853c
fixed formatting
Oct 1, 2025
feab14b
fixed sonarqube and formatting
Oct 1, 2025
43f28be
fixed formatting
Oct 1, 2025
c882a8b
Merge branch 'develop' into BOPISChange
Matejk00 Oct 2, 2025
97b5d39
addressing review comments
Diksha-28 Oct 3, 2025
e06decf
addressing review comments
Oct 3, 2025
f2c9db5
fixed formatting
Oct 3, 2025
5e1b180
fix build error
Oct 3, 2025
e9c69b6
Merge branch 'develop' into BOPISChange
Diksha-28 Oct 6, 2025
fa56d6a
fixed error 'cxfeature' can't bind
Oct 6, 2025
4a32600
chore: add pickup in store lib to be built before OPF
Matejk00 Oct 6, 2025
128d1a3
Merge branch 'develop' into BOPISChange
Diksha-28 Oct 6, 2025
978ed22
Merge branch 'develop' into BOPISChange
Diksha-28 Oct 7, 2025
b2bb5f6
Add license header
github-actions[bot] Oct 7, 2025
04d3c28
addressing review comments
Oct 7, 2025
ce897a2
Add license header
github-actions[bot] Oct 7, 2025
7beff58
Merge branch 'develop' into BOPISChange
Diksha-28 Oct 8, 2025
6ea545b
addressing review comments
Oct 8, 2025
d0d477b
Merge branch 'develop' into BOPISChange
ijitendrasap Oct 8, 2025
d9ca9b4
Merge branch 'develop' into BOPISChange
ijitendrasap Oct 8, 2025
172d6d9
Merge branch 'develop' into BOPISChange
ijitendrasap Oct 8, 2025
c9ac70f
fix dependencies error
Oct 9, 2025
673aebf
fix dependency order
Oct 9, 2025
f1687c0
Merge branch 'develop' into BOPISChange
Matejk00 Oct 15, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { RouterModule } from '@angular/router';
import {
CmsConfig,
ConfigModule,
FeaturesConfigModule,
I18nModule,
UrlModule,
} from '@spartacus/core';
Expand All @@ -28,6 +29,7 @@ import { PickUpItemsDetailsComponent } from './pickup-items-details.component';
StoreModule,
CardModule,
MediaModule,
FeaturesConfigModule,
ConfigModule.withConfig({
cmsComponents: {
OrderConfirmationPickUpComponent: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@
"errors": {
"loadActiveConfigurations": "We are unable to load payment options at this time. Please try again later.",
"noActiveConfigurations": "There are no payment options available at this time. Please try again later or contact support.",
"updateBillingAddress": "The address could not be updated. Please check that the address information is correct and that your device is connected to the internet. If the problem persists, you may need to clear your cart and start the checkout again."
"updateBillingAddress": "The address could not be updated. Please check that the address information is correct and that your device is connected to the internet. If the problem persists, you may need to clear your cart and start the checkout again.",
"noBillingAddress": "Billing address is required"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ <h3>{{ 'opfCheckout.billingAddress' | cxTranslate }}</h3>
} as addressData"
>
<div class="form-group">
<div class="form-check">
<div class="form-check" *ngIf="cart?.deliveryItemsQuantity">
<label>
<input
type="checkbox"
Expand Down Expand Up @@ -50,9 +50,13 @@ <h3>{{ 'opfCheckout.billingAddress' | cxTranslate }}</h3>
[showTitleCode]="true"
[showCancelBtn]="true"
actionBtnLabel="{{ 'common.save' | cxTranslate }}"
cancelBtnLabel="{{ 'common.cancel' | cxTranslate }}"
[cancelBtnLabel]="
(service.paymentOptionsDisabled$ | async)
? ('common.back' | cxTranslate)
: ('common.cancel' | cxTranslate)
"
(submitAddress)="onSubmitAddress($event)"
(backToAddress)="cancelAndHideForm()"
(backToAddress)="onBackToAddress()"
[setAsDefaultField]="false"
[countries]="countries$"
></cx-address-form>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,25 @@

import { Pipe, PipeTransform } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { Address, Country } from '@spartacus/core';
import { BehaviorSubject, EMPTY, Observable, of } from 'rxjs';
import {
Address,
Country,
BaseSiteService,
UserAddressAdapter,
} from '@spartacus/core';
import { BehaviorSubject, EMPTY, Observable, of, Subject } from 'rxjs';
import { OpfCheckoutBillingAddressFormComponent } from './opf-checkout-billing-address-form.component';
import { OpfCheckoutBillingAddressFormService } from './opf-checkout-billing-address-form.service';
import { Store } from '@ngrx/store';
import { ActiveCartFacade } from '@spartacus/cart/base/root';
import { CheckoutStepService } from '@spartacus/checkout/base/components';
import { ActivatedRoute } from '@angular/router';

class Service {
billingAddress$ = new BehaviorSubject<Address | undefined>(undefined);
isLoadingAddress$ = new BehaviorSubject<boolean>(false);
isSameAsDelivery$ = new BehaviorSubject<boolean>(true);
pickupNoDefaultAddress$ = new Subject<void>();

getCountries(): Observable<Country[]> {
return EMPTY;
Expand All @@ -38,6 +48,7 @@ class Service {
setIsSameAsDeliveryValue(value: boolean): void {
this.isSameAsDelivery$.next(value);
}
setDefaultBillingAddress(): void {}
}

@Pipe({
Expand All @@ -61,6 +72,17 @@ describe('OpfCheckoutBillingAddressFormComponent', () => {
provide: OpfCheckoutBillingAddressFormService,
useClass: Service,
},
{
provide: ActiveCartFacade,
useValue: {
getActive: () => of({ code: '123', totalItems: 2 }),
},
},
{ provide: Store, useValue: {} },
{ provide: UserAddressAdapter, useValue: {} },
{ provide: CheckoutStepService, useValue: {} },
{ provide: BaseSiteService, useValue: {} },
{ provide: ActivatedRoute, useValue: { params: of({}) } },
],
}).compileComponents();

Expand All @@ -78,12 +100,14 @@ describe('OpfCheckoutBillingAddressFormComponent', () => {
const countries = [{ id: '1', name: 'Country 1' }];
spyOn(service, 'getCountries').and.returnValue(of(countries));
spyOn(service, 'getAddresses');
spyOn(service, 'setDefaultBillingAddress');

component.ngOnInit();

expect(component.countries$).toBeDefined();
expect(service.getCountries).toHaveBeenCalled();
expect(service.getAddresses).toHaveBeenCalled();
expect(service.setDefaultBillingAddress).toHaveBeenCalled();
});

it('should cancel and hide form on cancelAndHideForm', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,35 @@ import {
Component,
OnInit,
inject,
OnDestroy,
} from '@angular/core';
import { Address, Country } from '@spartacus/core';
import { Address, Country, UserAddressService } from '@spartacus/core';
import { ICON_TYPE } from '@spartacus/storefront';
import { Observable } from 'rxjs';
import { Observable, Subscription } from 'rxjs';
import { OpfCheckoutBillingAddressFormService } from './opf-checkout-billing-address-form.service';
import { ActiveCartFacade, Cart } from '@spartacus/cart/base/root';
import { ActivatedRoute } from '@angular/router';
import { CheckoutStepService } from '@spartacus/checkout/base/components';

@Component({
selector: 'cx-opf-checkout-billing-address-form',
templateUrl: './opf-checkout-billing-address-form.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: false,
})
export class OpfCheckoutBillingAddressFormComponent implements OnInit {
export class OpfCheckoutBillingAddressFormComponent
implements OnInit, OnDestroy
{
protected service = inject(OpfCheckoutBillingAddressFormService);
protected userAddressService = inject(UserAddressService);
protected activeCartFacade = inject(ActiveCartFacade);
protected checkoutStepService = inject(CheckoutStepService);
protected activatedRoute = inject(ActivatedRoute);

protected cart: Cart | null = null;

iconTypes = ICON_TYPE;
subscription = new Subscription();

billingAddress$ = this.service.billingAddress$;
isLoadingAddress$ = this.service.isLoadingAddress$;
Expand All @@ -36,8 +49,19 @@ export class OpfCheckoutBillingAddressFormComponent implements OnInit {
countries$: Observable<Country[]>;

ngOnInit() {
this.subscription.add(
this.activeCartFacade.getActive().subscribe((cart) => (this.cart = cart))
);
this.countries$ = this.service.getCountries();
this.userAddressService.loadAddresses();
this.service.setDefaultBillingAddress();
this.service.getAddresses();
this.subscription.add(
this.service.pickupNoDefaultAddress$.subscribe(() => {
this.isEditBillingAddress = true;
this.isAddingBillingAddressInProgress = true;
})
);
}

cancelAndHideForm(): void {
Expand All @@ -47,6 +71,17 @@ export class OpfCheckoutBillingAddressFormComponent implements OnInit {
this.isAddingBillingAddressInProgress = false;
}
}
back(): void {
this.checkoutStepService.back(this.activatedRoute);
}

onBackToAddress(): void {
this.subscription.add(
this.service.paymentOptionsDisabled$.subscribe((isDisabled) =>
isDisabled ? this.back() : this.cancelAndHideForm()
)
);
}

editCustomBillingAddress(): void {
this.isEditBillingAddress = true;
Expand Down Expand Up @@ -78,6 +113,17 @@ export class OpfCheckoutBillingAddressFormComponent implements OnInit {
return;
}

this.service.setBillingAddress(address).subscribe();
this.service.setBillingAddress(address).subscribe({
next: () => {
this.service.setPaymentOptionsDisabled(false);
},
error: () => {
this.service.setPaymentOptionsDisabled(true);
},
});
}

ngOnDestroy(): void {
this.subscription.unsubscribe();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,14 @@ import {
Address,
GlobalMessageService,
HttpErrorModel,
UserAddressAdapter,
UserAddressService,
UserPaymentService,
} from '@spartacus/core';
import { of, throwError } from 'rxjs';
import { BehaviorSubject, Observable, of, throwError } from 'rxjs';
import { OpfCheckoutPaymentWrapperService } from '../opf-checkout-payment-wrapper';
import { OpfCheckoutBillingAddressFormService } from './opf-checkout-billing-address-form.service';
import { Store } from '@ngrx/store';

describe('OpfCheckoutBillingAddressFormService', () => {
let service: OpfCheckoutBillingAddressFormService;
Expand All @@ -27,6 +30,8 @@ describe('OpfCheckoutBillingAddressFormService', () => {
let mockActiveCartFacade: Partial<ActiveCartFacade>;
let mockGlobalMessageService: Partial<GlobalMessageService>;
let mockOpfCheckoutPaymentWrapperService: Partial<OpfCheckoutPaymentWrapperService>;
let mockPickupNoDefaultAddress$: BehaviorSubject<void>;
let mockUserAddressService: Partial<UserAddressService>;

const mockDeliveryAddress: Address = {
id: '123',
Expand Down Expand Up @@ -54,6 +59,7 @@ describe('OpfCheckoutBillingAddressFormService', () => {
reloadActiveCart: () => of(true),
isStable: () => of(true),
getActive: () => of({ sapBillingAddress: mockPaymentAddress } as Cart),
hasDeliveryItems: () => of(false),
};

mockGlobalMessageService = {
Expand All @@ -63,6 +69,11 @@ describe('OpfCheckoutBillingAddressFormService', () => {
mockOpfCheckoutPaymentWrapperService = {
reloadPaymentMode: () => {},
};
mockPickupNoDefaultAddress$ = new BehaviorSubject<void>(undefined);

mockUserAddressService = {
getDefaultAddress: () => of(undefined),
};

TestBed.configureTestingModule({
providers: [
Expand All @@ -82,6 +93,13 @@ describe('OpfCheckoutBillingAddressFormService', () => {
provide: OpfCheckoutPaymentWrapperService,
useValue: mockOpfCheckoutPaymentWrapperService,
},
{ provide: Store, useValue: { pipe: () => of(undefined) } },
{ provide: UserAddressAdapter, useValue: {} },
{
provide: '_noDefaultAddressFoundForPickupMode$',
useValue: mockPickupNoDefaultAddress$,
},
{ provide: UserAddressService, useValue: mockUserAddressService },
],
});

Expand Down Expand Up @@ -251,4 +269,67 @@ describe('OpfCheckoutBillingAddressFormService', () => {

expect(service.setBillingAddress).not.toHaveBeenCalled();
});

it('should return an observable from pickupNoDefaultAddress$', () => {
spyOn(mockPickupNoDefaultAddress$, 'asObservable').and.callThrough();

(service as any)._noDefaultAddressFoundForPickupMode$ =
mockPickupNoDefaultAddress$;

const result: Observable<void> = service.pickupNoDefaultAddress$;

expect(mockPickupNoDefaultAddress$.asObservable).toHaveBeenCalled();
expect(result).toEqual(mockPickupNoDefaultAddress$.asObservable());
});

it('should handle no default address by setting isSameAsDelivery=false and emitting pickupNoDefaultAddress$', (done) => {
spyOn(service, 'setIsSameAsDeliveryValue').and.callThrough();
service.pickupNoDefaultAddress$.subscribe(() => {
expect(service.setIsSameAsDeliveryValue).toHaveBeenCalledWith(false);
done();
});
(service as any).handleNoDefaultAddress();
});

it('should handle error when setting default billing address fails', fakeAsync(() => {
spyOn(mockActiveCartFacade, 'hasDeliveryItems').and.returnValue(of(false));
spyOn(mockUserAddressService, 'getDefaultAddress').and.returnValue(
of(mockDeliveryAddress)
);
spyOn(service, 'setBillingAddress').and.returnValue(
throwError(() => new Error('Error'))
);

service.setDefaultBillingAddress();
flush();

expect(service['_$isLoadingAddress'].value).toBeFalsy();
}));

it('should handle the absence of a default address by invoking handleNoDefaultAddress', fakeAsync(() => {
spyOn(mockActiveCartFacade, 'hasDeliveryItems').and.returnValue(of(false));
spyOn(mockUserAddressService, 'getDefaultAddress').and.returnValue(
of(undefined)
);
const handleNoDefaultAddressSpy = spyOn(
service as any,
'handleNoDefaultAddress'
).and.callThrough();

service.setDefaultBillingAddress();
flush();

expect(handleNoDefaultAddressSpy).toHaveBeenCalled();
}));
it('should handle errors when loading the default address', fakeAsync(() => {
spyOn(mockActiveCartFacade, 'hasDeliveryItems').and.returnValue(of(false));
spyOn(mockUserAddressService, 'getDefaultAddress').and.returnValue(
throwError(() => new Error('Error loading default address'))
);

service.setDefaultBillingAddress();
flush();

expect(service['_$isLoadingAddress'].value).toBeFalsy();
}));
});
Loading
Loading