import { Component, Input, Output, OnInit, OnChanges, ViewChild, EventEmitter } from '@angular/core'; import { CurrencyConverterPipe } from '../currency-converter/currency-converter.pipe'; import { BuyingOptionModel, BomItemService, PermissionsService } from '@arrow/bom/core'; import { ConfigService } from '@arrow/bom/config'; import { Observable, Subscription, zip, of } from 'rxjs'; import { switchMap } from 'rxjs/operators'; @Component({ selector: 'bom-pricing', templateUrl: 'pricing.component.html', styleUrls: ['pricing.component.scss'] }) export class PricingComponent implements OnInit, OnChanges { @Input() itemLoading: boolean = false; @Input() limitPricesDisplay: number; @Input() rowId: string; @Input() isAlt: boolean = false; @Input() priceRanges: any; @Input() isPriceError: boolean = false; @Input() buyingOptions: any; @Input() itemQty: number; @Input() inCart: number; @Input() increment: number; @Input() minQuantity: number; @Input() priceSourceLoading: boolean = false; @Input() isDefaultView: boolean = false; @Input() hasTieredPricing: boolean = false; @Output() priceBreaksToggled: EventEmitter = new EventEmitter(); @Output() showBuyingOptions: EventEmitter = new EventEmitter(); @ViewChild('viewBuyingOptionsButton') viewBuyingOptionsButton: any; isSinglePrice: boolean; hasMBO: boolean; selectedPriceIndex: number = 0; displayedPriceRanges$: Observable; lowestPrice: number; inputTotal: number; hasUnallocated: boolean; permissions: any = {}; subscriptions: Array = []; currencyCode: string; hasPriceGroup: boolean = false; constructor( private bomItemService: BomItemService, private config: ConfigService, private ps: PermissionsService, private currencyConverter: CurrencyConverterPipe ) {} ngOnInit() { this.subscriptions.push( this.ps .getPermissions() .subscribe((permissions: any) => (this.permissions = permissions)) ); this.currencyCode = this.config.userSelectedCurrency ? this.config.userSelectedCurrency : this.config.defaultCurrency; this.hasPriceGroup = this.hasTieredPricing; } OnDestroy() { this.subscriptions.forEach((sub: Subscription) => sub.unsubscribe()); } /** * On price changes updates singlePrice/ranges display, reload price ranges array and truncated array * update price ranges display. Needs to be on changes coz alternates get prices later and for now we don't * have any other price update notification (event) */ ngOnChanges(changes: any) { if (changes.priceRanges || changes.itemQty) { this.isSinglePrice = this.determineSinglePrice(this.priceRanges); this.hasMBO = this.buyingOptions ? this.buyingOptions.length > 1 : false; this.selectedPriceIndex = this.bomItemService.getPriceSelectedIndex( this.itemQty, this.priceRanges ); this.displayedPriceRanges$ = zip( ...this.priceRanges.map(range => { return this.currencyConverter .transform(range.rangePrice, '1.2-5', false) .pipe( switchMap(price => of({ quantity: range.rangeLimit, price: price }) ) ); }) ); // Check for "unallocated qty" only on part rows (not alts) if (!this.isAlt) { const prevUnallocated = this.hasUnallocated; this.hasUnallocated = this.checkUnallocated(this.buyingOptions); // Let any subscribers know that unallocated qty status has changed if (prevUnallocated !== this.hasUnallocated) this.bomItemService.updateUnallocatedBuyingOptions( this.rowId, this.hasUnallocated ); } this.lowestPrice = this.determineLowestPrice( this.buyingOptions, this.priceRanges ); } } /** * Filters the buying options to only selected prices, then reduces those selected prices to the lowest prices, * then returns lowest selected price value. * @param buyingOptions * @param priceRanges * @returns is lowest selected price */ determineLowestPrice(buyingOptions: any, priceRanges: any): number { if (!buyingOptions) return; const filteredPrices = buyingOptions.filter( (option: BuyingOptionModel) => option.selectedPrice && option.quantity > 0 ); let lowestPrice: number; if (filteredPrices.length) { lowestPrice = filteredPrices.reduce((acc: any, option: any) => acc.selectedPrice >= option.selectedPrice ? option : acc ).selectedPrice; } else if (priceRanges.length) { lowestPrice = priceRanges.reduce((acc: any, option: any) => acc.rangePrice >= option.rangePrice ? option : acc ).rangePrice; } return lowestPrice; } /** * Sets isSinglePrice to true/false to determine price ranges vs. single price display * Single price if only 1 price range 1+ (but displays price ranges if 1 range but min 10+ for example) * @param priceRanges * @returns is single price true/false */ determineSinglePrice(priceRanges: any): boolean { return ( priceRanges && priceRanges.length === 1 && priceRanges[0].rangeLimit === 1 ); } /** * Calculate the item's row height depending on the price range list height & (view more arrow + buying options link + padding) * Emits the changed row height event (list item is listening to it) */ updateRowHeight(): void { this.priceBreaksToggled.emit(this.priceRanges.length); } /** * check un allocated items * @param buyingOptions */ checkUnallocated(buyingOptions: any): boolean { if (!buyingOptions) return; this.inputTotal = buyingOptions.reduce( (acc: any, option: any) => option.selected && option.input ? acc + +option.input : acc, 0 ); return this.itemQty > this.inputTotal; } showBuyingOptionsModal(): void { this.showBuyingOptions.emit(true); } }