Merged PR 2000: open tasks

Related work items: #5309
This commit is contained in:
Lorenz Hilpert
2025-11-06 10:01:41 +00:00
committed by Nino Righi
parent 1d4c900d3a
commit 89b3d9aa60
136 changed files with 5088 additions and 4798 deletions

View File

@@ -1,4 +1,8 @@
import type { Preview } from '@storybook/angular';
import { registerLocaleData } from '@angular/common';
import localeDe from '@angular/common/locales/de';
registerLocaleData(localeDe);
const preview: Preview = {
tags: ['autodocs'],

View File

@@ -5,7 +5,7 @@ import {
VATValueDTO,
} from '@generated/swagger/checkout-api';
import { PurchaseOption } from './store';
import { OrderType } from '@isa/checkout/data-access';
import { OrderTypeFeature } from '@isa/checkout/data-access';
export const PURCHASE_OPTIONS: PurchaseOption[] = [
'in-store',
@@ -23,7 +23,7 @@ export const DELIVERY_PURCHASE_OPTIONS: PurchaseOption[] = [
];
export const PURCHASE_OPTION_TO_ORDER_TYPE: {
[purchaseOption: string]: OrderType;
[purchaseOption: string]: OrderTypeFeature;
} = {
'in-store': 'Rücklage',
'pickup': 'Abholung',

View File

@@ -13,7 +13,7 @@ import {
ItemPayloadWithSourceId,
PurchaseOption,
} from './purchase-options.types';
import { OrderType } from '@isa/checkout/data-access';
import { OrderTypeFeature } from '@isa/checkout/data-access';
export function isItemDTO(item: any, type: ActionType): item is ItemDTO {
return type === 'add';
@@ -145,7 +145,7 @@ export function mapToOlaAvailability({
export function getOrderTypeForPurchaseOption(
purchaseOption: PurchaseOption,
): OrderType | undefined {
): OrderTypeFeature | undefined {
switch (purchaseOption) {
case 'delivery':
case 'dig-delivery':
@@ -163,7 +163,7 @@ export function getOrderTypeForPurchaseOption(
}
export function getPurchaseOptionForOrderType(
orderType: OrderType,
orderType: OrderTypeFeature,
): PurchaseOption | undefined {
switch (orderType) {
case 'Versand':

View File

@@ -17,7 +17,10 @@ import { memorize } from '@utils/common';
import { AuthService } from '@core/auth';
import { ApplicationService } from '@core/application';
import { DomainOmsService } from '@domain/oms';
import { OrderType, PurchaseOptionsFacade } from '@isa/checkout/data-access';
import {
OrderTypeFeature,
PurchaseOptionsFacade,
} from '@isa/checkout/data-access';
@Injectable({ providedIn: 'root' })
export class PurchaseOptionsService {
@@ -122,7 +125,7 @@ export class PurchaseOptionsService {
fetchCanAdd(
shoppingCartId: number,
orderType: OrderType,
orderType: OrderTypeFeature,
payload: ItemPayload[],
customerFeatures: Record<string, string>,
): Promise<ItemsResult[]> {

View File

@@ -40,8 +40,11 @@ import { uniqueId } from 'lodash';
import { VATDTO } from '@generated/swagger/oms-api';
import { DomainCatalogService } from '@domain/catalog';
import { ItemDTO } from '@generated/swagger/cat-search-api';
import { Loyalty, OrderType, Promotion } from '@isa/checkout/data-access';
import { ensureCurrencyDefaults } from '@isa/common/data-access';
import {
Loyalty,
OrderTypeFeature,
Promotion,
} from '@isa/checkout/data-access';
@Injectable()
export class PurchaseOptionsStore extends ComponentStore<PurchaseOptionsState> {
@@ -724,7 +727,7 @@ export class PurchaseOptionsStore extends ComponentStore<PurchaseOptionsState> {
try {
const res = await this._service.fetchCanAdd(
this.shoppingCartId,
key as OrderType,
key as OrderTypeFeature,
itemPayloads,
this.customerFeatures,
);
@@ -733,7 +736,9 @@ export class PurchaseOptionsStore extends ComponentStore<PurchaseOptionsState> {
this._addCanAddResult({
canAdd: canAdd.status === 0,
itemId: item.sourceId,
purchaseOption: getPurchaseOptionForOrderType(key as OrderType),
purchaseOption: getPurchaseOptionForOrderType(
key as OrderTypeFeature,
),
message: canAdd.message,
});
});

View File

@@ -1,53 +1,47 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ArticleDetailsComponent } from './article-details.component';
import { ProductImageModule } from '@cdn/product-image';
import { UiIconModule } from '@ui/icon';
import { RouterModule } from '@angular/router';
import { UiStarsModule } from '@ui/stars';
import { UiSliderModule } from '@ui/slider';
import { ArticleRecommendationsComponent } from './recommendations/article-recommendations.component';
import { PipesModule } from '../shared/pipes/pipes.module';
import { UiTooltipModule } from '@ui/tooltip';
import { UiCommonModule } from '@ui/common';
import { OrderDeadlinePipeModule } from '@shared/pipes/order-deadline';
import { IconModule } from '@shared/components/icon';
import { ArticleDetailsTextComponent } from './article-details-text/article-details-text.component';
import { IconBadgeComponent } from 'apps/isa-app/src/shared/components/icon/badge/icon-badge.component';
import { MatomoModule } from 'ngx-matomo-client';
import {
SelectedRewardShoppingCartResource,
SelectedShoppingCartResource,
} from '@isa/checkout/data-access';
import {
RewardSelectionService,
RewardSelectionPopUpService,
} from '@isa/checkout/shared/reward-selection-dialog';
@NgModule({
imports: [
CommonModule,
ProductImageModule,
UiIconModule,
RouterModule,
UiStarsModule,
UiSliderModule,
UiCommonModule,
UiTooltipModule,
IconModule,
PipesModule,
OrderDeadlinePipeModule,
ArticleDetailsTextComponent,
IconBadgeComponent,
MatomoModule,
],
exports: [ArticleDetailsComponent, ArticleRecommendationsComponent],
declarations: [ArticleDetailsComponent, ArticleRecommendationsComponent],
providers: [
SelectedShoppingCartResource,
SelectedRewardShoppingCartResource,
RewardSelectionService,
RewardSelectionPopUpService,
],
})
export class ArticleDetailsModule {}
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ArticleDetailsComponent } from './article-details.component';
import { ProductImageModule } from '@cdn/product-image';
import { UiIconModule } from '@ui/icon';
import { RouterModule } from '@angular/router';
import { UiStarsModule } from '@ui/stars';
import { UiSliderModule } from '@ui/slider';
import { ArticleRecommendationsComponent } from './recommendations/article-recommendations.component';
import { PipesModule } from '../shared/pipes/pipes.module';
import { UiTooltipModule } from '@ui/tooltip';
import { UiCommonModule } from '@ui/common';
import { OrderDeadlinePipeModule } from '@shared/pipes/order-deadline';
import { IconModule } from '@shared/components/icon';
import { ArticleDetailsTextComponent } from './article-details-text/article-details-text.component';
import { IconBadgeComponent } from 'apps/isa-app/src/shared/components/icon/badge/icon-badge.component';
import { MatomoModule } from 'ngx-matomo-client';
import {
RewardSelectionService,
RewardSelectionPopUpService,
} from '@isa/checkout/shared/reward-selection-dialog';
@NgModule({
imports: [
CommonModule,
ProductImageModule,
UiIconModule,
RouterModule,
UiStarsModule,
UiSliderModule,
UiCommonModule,
UiTooltipModule,
IconModule,
PipesModule,
OrderDeadlinePipeModule,
ArticleDetailsTextComponent,
IconBadgeComponent,
MatomoModule,
],
exports: [ArticleDetailsComponent, ArticleRecommendationsComponent],
declarations: [ArticleDetailsComponent, ArticleRecommendationsComponent],
providers: [
RewardSelectionService,
RewardSelectionPopUpService,
],
})
export class ArticleDetailsModule {}

View File

@@ -1,32 +1,31 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { CheckoutSummaryComponent } from './checkout-summary.component';
import { PageCheckoutPipeModule } from '../pipes/page-checkout-pipe.module';
import { ProductImageModule } from '@cdn/product-image';
import { RouterModule } from '@angular/router';
import { UiCommonModule } from '@ui/common';
import { UiSpinnerModule } from '@ui/spinner';
import { UiDatepickerModule } from '@ui/datepicker';
import { IconModule } from '@shared/components/icon';
import { AuthModule } from '@core/auth';
import { SelectedRewardShoppingCartResource } from '@isa/checkout/data-access';
@NgModule({
imports: [
CommonModule,
RouterModule,
PageCheckoutPipeModule,
ProductImageModule,
IconModule,
UiCommonModule,
UiSpinnerModule,
UiDatepickerModule,
AuthModule,
UiSpinnerModule,
],
exports: [CheckoutSummaryComponent],
declarations: [CheckoutSummaryComponent],
providers: [SelectedRewardShoppingCartResource],
})
export class CheckoutSummaryModule {}
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { CheckoutSummaryComponent } from './checkout-summary.component';
import { PageCheckoutPipeModule } from '../pipes/page-checkout-pipe.module';
import { ProductImageModule } from '@cdn/product-image';
import { RouterModule } from '@angular/router';
import { UiCommonModule } from '@ui/common';
import { UiSpinnerModule } from '@ui/spinner';
import { UiDatepickerModule } from '@ui/datepicker';
import { IconModule } from '@shared/components/icon';
import { AuthModule } from '@core/auth';
@NgModule({
imports: [
CommonModule,
RouterModule,
PageCheckoutPipeModule,
ProductImageModule,
IconModule,
UiCommonModule,
UiSpinnerModule,
UiDatepickerModule,
AuthModule,
UiSpinnerModule,
],
exports: [CheckoutSummaryComponent],
declarations: [CheckoutSummaryComponent],
providers: [],
})
export class CheckoutSummaryModule {}

View File

@@ -120,7 +120,7 @@ export class CustomerOrderDetailsHeaderComponent implements OnChanges {
),
);
openAddresses: boolean = false;
openAddresses = false;
get digOrderNumber(): string {
return this.order?.linkedRecords?.find((_) => true)?.number;

View File

@@ -21,6 +21,4 @@ export class CustomerResultListItemFullComponent {
@Input()
customer: CustomerInfoDTO;
constructor() {}
}

View File

@@ -21,6 +21,4 @@ export class CustomerResultListItemComponent {
@Input()
customer: CustomerInfoDTO;
constructor() {}
}

View File

@@ -1,47 +1,41 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { CustomerSearchComponent } from './customer-search.component';
import { CustomerResultsSideViewModule } from './results-side-view/results-side-view.module';
import { RouterModule } from '@angular/router';
import { CustomerResultsMainViewModule } from './results-main-view/results-main-view.module';
import { CustomerDetailsMainViewModule } from './details-main-view/details-main-view.module';
import { CustomerHistoryMainViewModule } from './history-main-view/history-main-view.module';
import { CustomerFilterMainViewModule } from './filter-main-view/filter-main-view.module';
import { MainSideViewModule } from './main-side-view/main-side-view.module';
import { OrderDetailsSideViewComponent } from './order-details-side-view/order-details-side-view.component';
import { CustomerMainViewComponent } from './main-view/main-view.component';
import { SharedSplitscreenComponent } from '@shared/components/splitscreen';
import {
SelectedShoppingCartResource,
SelectedRewardShoppingCartResource,
} from '@isa/checkout/data-access';
import {
RewardSelectionService,
RewardSelectionPopUpService,
} from '@isa/checkout/shared/reward-selection-dialog';
@NgModule({
imports: [
CommonModule,
RouterModule,
SharedSplitscreenComponent,
CustomerResultsSideViewModule,
CustomerResultsMainViewModule,
CustomerDetailsMainViewModule,
CustomerHistoryMainViewModule,
CustomerFilterMainViewModule,
MainSideViewModule,
OrderDetailsSideViewComponent,
CustomerMainViewComponent,
],
exports: [CustomerSearchComponent],
declarations: [CustomerSearchComponent],
providers: [
SelectedShoppingCartResource,
SelectedRewardShoppingCartResource,
RewardSelectionService,
RewardSelectionPopUpService,
],
})
export class CustomerSearchModule {}
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { CustomerSearchComponent } from './customer-search.component';
import { CustomerResultsSideViewModule } from './results-side-view/results-side-view.module';
import { RouterModule } from '@angular/router';
import { CustomerResultsMainViewModule } from './results-main-view/results-main-view.module';
import { CustomerDetailsMainViewModule } from './details-main-view/details-main-view.module';
import { CustomerHistoryMainViewModule } from './history-main-view/history-main-view.module';
import { CustomerFilterMainViewModule } from './filter-main-view/filter-main-view.module';
import { MainSideViewModule } from './main-side-view/main-side-view.module';
import { OrderDetailsSideViewComponent } from './order-details-side-view/order-details-side-view.component';
import { CustomerMainViewComponent } from './main-view/main-view.component';
import { SharedSplitscreenComponent } from '@shared/components/splitscreen';
import {
RewardSelectionService,
RewardSelectionPopUpService,
} from '@isa/checkout/shared/reward-selection-dialog';
@NgModule({
imports: [
CommonModule,
RouterModule,
SharedSplitscreenComponent,
CustomerResultsSideViewModule,
CustomerResultsMainViewModule,
CustomerDetailsMainViewModule,
CustomerHistoryMainViewModule,
CustomerFilterMainViewModule,
MainSideViewModule,
OrderDetailsSideViewComponent,
CustomerMainViewComponent,
],
exports: [CustomerSearchComponent],
declarations: [CustomerSearchComponent],
providers: [
RewardSelectionService,
RewardSelectionPopUpService,
],
})
export class CustomerSearchModule {}

View File

@@ -11,7 +11,6 @@ import {
inject,
computed,
input,
effect,
} from '@angular/core';
import { Router } from '@angular/router';
import { ApplicationProcess, ApplicationService } from '@core/application';
@@ -27,7 +26,10 @@ import {
} from 'rxjs';
import { map, tap } from 'rxjs/operators';
import { TabService } from '@isa/core/tabs';
import { CheckoutMetadataService } from '@isa/checkout/data-access';
import {
CheckoutMetadataService,
ShoppingCartResource,
} from '@isa/checkout/data-access';
@Component({
selector: 'shell-process-bar-item',
@@ -35,6 +37,7 @@ import { CheckoutMetadataService } from '@isa/checkout/data-access';
styleUrls: ['process-bar-item.component.css'],
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: false,
providers: [ShoppingCartResource],
})
export class ShellProcessBarItemComponent
implements OnInit, OnDestroy, OnChanges
@@ -72,11 +75,8 @@ export class ShellProcessBarItemComponent
});
cartCount = computed(() => {
const tab = this.tab();
const pdata = tab.metadata?.process_data as { count?: number };
return pdata?.count ?? 0;
// TODO: Use implementation from develop
return 0;
});
currentLocationUrlTree = computed(() => {

View File

@@ -1,72 +1,74 @@
<div
class="flex flex-row justify-start items-center h-full max-w-[1920px] desktop-xx-large:max-w-[2448px] relative"
(mouseenter)="hovered = true"
(mouseleave)="hovered = false"
>
@if (showScrollArrows) {
<button
class="scroll-button prev-button"
[class.invisible]="!this.hovered || showArrowLeft"
(click)="scrollLeft()"
>
<ui-icon icon="arrow_head" size="22px" rotate="180deg"></ui-icon>
</button>
}
<div
#processContainer
class="grid grid-flow-col max-w-[calc(100vw-9.5rem)] overflow-x-scroll"
(wheel)="onMouseWheel($event)"
(scroll)="checkScrollArrowVisibility()"
>
@for (process of processes$ | async; track trackByFn($index, process)) {
<shell-process-bar-item
[process]="process"
(closed)="checkScrollArrowVisibility()"
></shell-process-bar-item>
}
</div>
@if (showScrollArrows) {
<button
class="scroll-button next-button"
[class.invisible]="!this.hovered || showArrowRight"
(click)="scrollRight()"
>
<ui-icon icon="arrow_head" size="22px"></ui-icon>
</button>
}
<button
type="button"
class="grid px-3 shell-process-bar__create-process-btn-desktop start-process-btn grid-flow-col items-center justify-center gap-[0.625rem] grow-0 shrink-0"
(click)="createProcess('product')"
type="button"
>
<ng-container *ngTemplateOutlet="createProcessButtonContent"></ng-container>
</button>
<div class="grow"></div>
<button
type="button"
[disabled]="!(processes$ | async)?.length"
class="grow-0 shrink-0 px-3 mr-[.125rem] shell-process-bar__close-processes"
(click)="closeAllProcesses()"
>
<div
class="rounded border border-solid flex flex-row pl-3 pr-[0.625rem] py-[0.375rem]"
[class.text-brand]="(processes$ | async)?.length"
[class.border-brand]="(processes$ | async)?.length"
[class.text-[#AEB7C1]]="!(processes$ | async)?.length"
[class.border-[#AEB7C1]]="!(processes$ | async)?.length"
>
<span class="mr-1">{{ (processes$ | async)?.length }}</span>
<shared-icon icon="close"></shared-icon>
</div>
</button>
</div>
<ng-template #createProcessButtonContent>
<div class="bg-brand text-white w-[2.375rem] h-[2.375rem] rounded-full grid items-center justify-center mx-auto mb-1">
<shared-icon icon="add"></shared-icon>
</div>
@if (showStartProcessText$ | async) {
<span class="text-brand create-process-btn-text">Vorgang starten</span>
}
</ng-template>
<div
class="flex flex-row justify-start items-center h-full max-w-[1920px] desktop-xx-large:max-w-[2448px] relative"
(mouseenter)="hovered = true"
(mouseleave)="hovered = false"
>
@if (showScrollArrows) {
<button
class="scroll-button prev-button"
[class.invisible]="!this.hovered || showArrowLeft"
(click)="scrollLeft()"
>
<ui-icon icon="arrow_head" size="22px" rotate="180deg"></ui-icon>
</button>
}
<div
#processContainer
class="grid grid-flow-col max-w-[calc(100vw-9.5rem)] overflow-x-scroll"
(wheel)="onMouseWheel($event)"
(scroll)="checkScrollArrowVisibility()"
>
@for (process of processes$ | async; track process.id) {
<shell-process-bar-item
[process]="process"
(closed)="checkScrollArrowVisibility()"
></shell-process-bar-item>
}
</div>
@if (showScrollArrows) {
<button
class="scroll-button next-button"
[class.invisible]="!this.hovered || showArrowRight"
(click)="scrollRight()"
>
<ui-icon icon="arrow_head" size="22px"></ui-icon>
</button>
}
<button
type="button"
class="grid px-3 shell-process-bar__create-process-btn-desktop start-process-btn grid-flow-col items-center justify-center gap-[0.625rem] grow-0 shrink-0"
(click)="createProcess('product')"
type="button"
>
<ng-container *ngTemplateOutlet="createProcessButtonContent"></ng-container>
</button>
<div class="grow"></div>
<button
type="button"
[disabled]="!(processes$ | async)?.length"
class="grow-0 shrink-0 px-3 mr-[.125rem] shell-process-bar__close-processes"
(click)="closeAllProcesses()"
>
<div
class="rounded border border-solid flex flex-row pl-3 pr-[0.625rem] py-[0.375rem]"
[class.text-brand]="(processes$ | async)?.length"
[class.border-brand]="(processes$ | async)?.length"
[class.text-[#AEB7C1]]="!(processes$ | async)?.length"
[class.border-[#AEB7C1]]="!(processes$ | async)?.length"
>
<span class="mr-1">{{ (processes$ | async)?.length }}</span>
<shared-icon icon="close"></shared-icon>
</div>
</button>
</div>
<ng-template #createProcessButtonContent>
<div
class="bg-brand text-white w-[2.375rem] h-[2.375rem] rounded-full grid items-center justify-center mx-auto mb-1"
>
<shared-icon icon="add"></shared-icon>
</div>
@if (showStartProcessText$ | async) {
<span class="text-brand create-process-btn-text">Vorgang starten</span>
}
</ng-template>

View File

@@ -1,4 +1,3 @@
import { coerceArray } from '@angular/cdk/coercion';
import {
Component,
ChangeDetectionStrategy,
@@ -65,6 +64,7 @@ export class ShellProcessBarComponent implements OnInit {
}
initProcesses$() {
// TODO: Use implementation from develop
this.processes$ = this.section$.pipe(
switchMap((section) => this._app.getProcesses$(section)),
// TODO: Nach Prämie release kann der Filter rausgenommen werden

View File

@@ -86,7 +86,15 @@
sharedRegexRouterLinkActive="active"
sharedRegexRouterLinkActiveTest="^\/\d*\/reward"
>
<span class="side-menu-group-item-icon"> </span>
<span class="side-menu-group-item-icon">
<shell-reward-shopping-cart-indicator />
@if (hasShoppingCartItems()) {
<span
class="w-2 h-2 bg-isa-accent-red rounded-full"
data-what="open-reward-tasks-indicator"
></span>
}
</span>
<span class="side-menu-group-item-label">Prämienshop</span>
</a>
}
@@ -272,11 +280,7 @@
<a
class="side-menu-group-item"
(click)="closeSideMenu(); focusSearchBox()"
[routerLink]="[
'/',
tabId(),
'remission',
]"
[routerLink]="['/', tabId(), 'remission']"
(isActiveChange)="focusSearchBox(); remissionExpanded.set($event)"
routerLinkActive="active"
#rlActive="routerLinkActive"

View File

@@ -35,6 +35,7 @@ import { TabService } from '@isa/core/tabs';
import { NgIconComponent, provideIcons } from '@ng-icons/core';
import { isaNavigationRemission2, isaNavigationReturn } from '@isa/icons';
import z from 'zod';
import { SelectedRewardShoppingCartResource } from '@isa/checkout/data-access';
@Component({
selector: 'shell-side-menu',
@@ -71,6 +72,7 @@ export class ShellSideMenuComponent {
#cdr = inject(ChangeDetectorRef);
#document = inject(DOCUMENT);
tabService = inject(TabService);
#shoppingCartResource = inject(SelectedRewardShoppingCartResource);
staticTabIds = Object.values(
this.#config.get('process.ids', z.record(z.coerce.number())),
@@ -151,6 +153,10 @@ export class ShellSideMenuComponent {
return this.#router.createUrlTree(['/', tabId || this.nextId(), routeName]);
});
hasShoppingCartItems = computed(() => {
return this.#shoppingCartResource.resource.value()?.items?.length > 0;
});
pickUpShelfOutRoutePath$ = this.getLastActivatedCustomerProcessId$().pipe(
map((processId) => {
if (processId) {

View File

@@ -1,9 +1,9 @@
import { NgModule } from '@angular/core';
import { ShellSideMenuComponent } from './side-menu.component';
@NgModule({
imports: [ShellSideMenuComponent],
exports: [ShellSideMenuComponent],
})
export class ShellSideMenuModule {}
import { NgModule } from '@angular/core';
import { ShellSideMenuComponent } from './side-menu.component';
@NgModule({
imports: [ShellSideMenuComponent],
exports: [ShellSideMenuComponent],
})
export class ShellSideMenuModule {}

View File

@@ -2,9 +2,9 @@ import {
type Meta,
type StoryObj,
applicationConfig,
argsToTemplate,
moduleMetadata,
} from '@storybook/angular';
import { provideHttpClient } from '@angular/common/http';
import { DestinationInfoComponent } from '@isa/checkout/shared/product-info';
import { ShippingTarget } from '@isa/checkout/data-access';
@@ -14,7 +14,7 @@ const meta: Meta<DestinationInfoComponent> = {
component: DestinationInfoComponent,
decorators: [
applicationConfig({
providers: [],
providers: [provideHttpClient()],
}),
moduleMetadata({
imports: [],
@@ -29,6 +29,7 @@ type Story = StoryObj<DestinationInfoComponent>;
export const Delivery: Story = {
args: {
underline: true,
shoppingCartItem: {
availability: {
estimatedDelivery: {
@@ -83,6 +84,12 @@ export const Pickup: Story = {
export const InStore: Story = {
args: {
shoppingCartItem: {
availability: {
estimatedDelivery: {
start: '2024-06-10T00:00:00+02:00',
stop: '2024-06-12T00:00:00+02:00',
},
},
destination: {
data: {
target: ShippingTarget.Branch,