import { ChangeDetectionStrategy, Component, Directive, Inject, Input, OnDestroy, OnInit, Output } from '@angular/core'; import { NavigationEnd, Router } from '@angular/router'; import { BomItemService, SocketsService, DialogService, BuyingOptionModel, ApiSocketListenAction, ApiSocketEmitAction, Utils, PaginationParamsModel } from '@arrow/bom/core'; import { WPT_DIALOG_DATA, WptDialogRef } from '@arrow/warpaint/dialog'; import { WptTableDataSource } from '@arrow/warpaint/table'; import { of, Subject, Subscription, zip } from 'rxjs'; import { switchMap, takeUntil } from 'rxjs/operators'; import { CurrencyConverterPipe } from '@arrow/bom/shared'; import { LoggerService } from '@arrow/bom/logger'; import { cloneDeep } from 'lodash'; import { PackageValidator } from '../validator/packaging-validator'; import { PackageValidatorFactory } from '../validator/packaging-validator-factory'; import { Row } from '../../ORM/types/custom/row.interface'; import { IBuyingOption, IBuyingOptionsColumns, PackagingFilterOption } from '../buying-options.interfaces'; import { BuyingOptionService } from '../buying-option.service'; @Component({ selector: 'bom-buying-options-modal', templateUrl: 'buying-options.component.html', styleUrls: [ '../../../../core/src/style/modal.scss', './buying-options.component.scss' ], changeDetection: ChangeDetectionStrategy.OnPush }) export class BuyingOptionsComponent implements OnInit, OnDestroy { columnDefs: Array = [ IBuyingOptionsColumns.QUANTITY, IBuyingOptionsColumns.PACKAGE, IBuyingOptionsColumns.PRICE, IBuyingOptionsColumns.MINIMUM_INCREMENT, IBuyingOptionsColumns.TARIFF, IBuyingOptionsColumns.STOCK, IBuyingOptionsColumns.PIPELINE ]; priceRanges: any[]; selectedPriceIndex: number; dataSource = new WptTableDataSource(); subscriptions: Array = []; currentOption: BuyingOptionModel; permissions: any; data: IBuyingOption; showWarningMessage: boolean = false; hiddenOptions: number; showHiddenBo = true; savingBO: boolean = false; unallocatedQty: number; showAllHiddenBOMessage: boolean; isValidPackageTypeCombination: boolean = true; disablePartPerReelInput: boolean = false; disableCutStripInput: boolean = false; userSelectedCurrency: string; // if there are no global inventory items the count will be 0 and the fee message will not be displayed GlobalInventoryItems = []; readonly isArrowcom: boolean; private _packageValidator: PackageValidator; private _rowDataCopy: Row; private _killSig$$: Subject = new Subject(); private _rowData: Row; private _paginationParams: PaginationParamsModel; constructor( public dialogRef: WptDialogRef, public utils: Utils, private _currencyConverter: CurrencyConverterPipe, private _socketsService: SocketsService, private _boService: BuyingOptionService, private _router: Router, private _dialog: DialogService, private _bomItemService: BomItemService, private _logService: LoggerService, private _packageValidatorFactory: PackageValidatorFactory, @Inject(WPT_DIALOG_DATA) private _dialogData: any ) { this._rowData = _dialogData.row; this._paginationParams = _dialogData.paginationParams; this.isArrowcom = this._boService.isTargetArrowcom(); } ngOnInit() { this._rowDataCopy = cloneDeep(this._rowData); this._updatePricingComponent(); this._getUnallocatedQty(); this.getPackageValidator(); this.userSelectedCurrency = this._boService.getUserSelectedCurrency(); // If this is a shared bom and Owner delete the row we close this modal. this.hiddenOptions = this._getTotalHiddenBO(); this.showAllHiddenBOMessage = this.hiddenOptions === this.data.buyingOptions.length; this._socketsService .observeAction(ApiSocketListenAction.ROW_DELETED) .pipe(takeUntil(this._killSig$$)) .subscribe((rowId: any) => { if (this.data.rowId === rowId) { this.cancel(); } }); this._router.events.pipe(takeUntil(this._killSig$$)).subscribe(val => { if (val instanceof NavigationEnd) { this.cancel(); } }); // check if there is any option with global inventory to set the fee message this.data.buyingOptions.forEach(bo => { this.checkGlobalInventory(bo.sourcePartId, bo.input, bo.isGlobalInventory) }); } ngOnDestroy(): void { this._killSig$$.next(); this._killSig$$.complete(); } private getPackageValidator(): void { this._packageValidator = this._packageValidatorFactory.getValidator(); } private _updatePricingComponent(): void { this.data = this._getBuyingOptionData(this._rowDataCopy); this.data.buyingOptions.forEach(buyingOption => { buyingOption.selectedPriceIndex = this._bomItemService.getPriceSelectedIndex( buyingOption.input, buyingOption.priceRanges ); buyingOption.displayedPriceRanges = zip( ...buyingOption.priceRanges.map(range => { return this._currencyConverter .transform(range.rangePrice, '1.2-5', false) .pipe( switchMap(price => of({ quantity: range.rangeLimit, price: price }) ) ); }) ); }); } private _getTotalHiddenBO() { return this.data.buyingOptions.filter(bo => bo.isHidden === true).length; } private _getUnallocatedQty(): void { const sumOfAllWarehouses = this.data.buyingOptions .filter(bo => bo.input) .map(qty => qty.input) .reduce((prev, next) => +prev + +next, 0); const unallocated = this.data.singleBomQuantity - sumOfAllWarehouses; this.unallocatedQty = unallocated > 0 ? unallocated : 0; } private _getBuyingOptionData( row: Row, extended: boolean = true ): IBuyingOption { return { multiplier: row.item.multiplier, singleBomQuantity: row.item.singleBomQuantity, buyingOptions: row.item.buyingOptions, partName: row.item.partName, manufacturer: row.item.manufacturer, bomId: row.bomId, rowId: row.item.rowId, extended, pricing: row.pricing, packageTypeRestrictions: row.packageTypeRestrictions, stockInfo: { priceSourceLoading: row.item.priceSourceLoading, matchStatus: row.item.matchStatus, stock: row.item.stock, packaging: row.item.packaging, shipIn: row.item.shipIn, increment: row.item.increment, minQuantity: row.item.minQuantity, totalPrice: row.item.totalPrice } }; } private _handleSocketResponse(response: { action: any; result: any }) { switch (response.action) { case ApiSocketEmitAction.ADD_SBO: { if (response.result) { this.savingBO = false; this.cancel(); } else { this._dialog.alert('Error'); } } } } private _resetSelectedAndWarningStates( buyingOption: BuyingOptionModel, requestedQty: number ): void { this._togglePackageCombinationMessages(buyingOption, +requestedQty); buyingOption.selected = false; this.isValidPackageTypeCombination = true; } private _togglePackageCombinationMessages( selectedWarehouse: BuyingOptionModel, requestedQty: number ): void { const selectedWarehouses = this.data.buyingOptions.filter( bo => bo.selected ); const hasRequestedQty = requestedQty > 0; this.disablePartPerReelInput = selectedWarehouse.isCutStrip() && selectedWarehouses.map( bo => bo.packagingTypeId === PackagingFilterOption.arrowReel ).length > 0 && hasRequestedQty; this.disableCutStripInput = selectedWarehouse.isArrowReelPackage() && selectedWarehouses.map( bo => bo.packagingTypeId === PackagingFilterOption.cutStrip ).length > 0 && hasRequestedQty; } private _cleanArrowReel(warehouse: BuyingOptionModel) { warehouse.reelOptions = null; warehouse.selected = false; warehouse.input = 0; warehouse.quantity = 0; } public getPackageTypeName(buyingOption: BuyingOptionModel) : string{ const nameId = buyingOption.getPackageNameId(); const nameFallback = buyingOption.packaging; return this.utils.translateByKeyWithFallback(nameId, nameFallback); } public canAssignBuyingOption(buyingOption: BuyingOptionModel): boolean { const packagesIdSelected = this.data.buyingOptions .filter( bo => bo.selected && bo.sourcePartId !== buyingOption.sourcePartId ) .map(bo => bo.packagingTypeId); packagesIdSelected.push(buyingOption.packagingTypeId); return this._packageValidator.validate( packagesIdSelected, this.data.pricing.packageTypeRestrictions ); } checkGlobalInventory(sourcePartId, requestedQty, isGlobalInventory) { if (requestedQty && isGlobalInventory) { if(!this.GlobalInventoryItems.includes(sourcePartId)) { this.GlobalInventoryItems.push(sourcePartId); } } else if (isGlobalInventory) { var partIndex = this.GlobalInventoryItems.indexOf(sourcePartId) if(partIndex !== -1) { this.GlobalInventoryItems.splice(partIndex); } } } public onChangeRequestedQty( sourcePartId: string, requestedQty: number, isGlobalInventory: boolean ): void { // check if the part is from global inventory to show/hide the fee message this.checkGlobalInventory(sourcePartId, requestedQty, isGlobalInventory); const warehouse = this._boService.getWarehouseById( sourcePartId, this.data.buyingOptions ); if (+requestedQty > 0) { this.isValidPackageTypeCombination = this.canAssignBuyingOption( warehouse ); if (this.isValidPackageTypeCombination) { warehouse.selected = +requestedQty > 0; this.showWarningMessage = warehouse && warehouse.quantity !== requestedQty; } else { this._togglePackageCombinationMessages(warehouse, +requestedQty); } } else { this._resetSelectedAndWarningStates(warehouse, +requestedQty); } this.showWarningMessage = true; this._getUnallocatedQty(); } public onArrowReelChange(sourcePartId: string, event: any, requestedQty: number, isGlobalInventory: boolean) { if (!sourcePartId && !event.target) return; // check if the part is from global inventory to show/hide the fee message this.checkGlobalInventory(sourcePartId, requestedQty, isGlobalInventory); const numberOfReelsId = 'reels'; const partsPerReelId = 'partsReel'; const value = event.target.value; const warehouse = this._boService.getWarehouseById( sourcePartId, this.data.buyingOptions ); warehouse.reelOptions = warehouse.reelOptions ? warehouse.reelOptions : { reelCount: 0, reelLength: 0 }; if (+value > 0) { this.isValidPackageTypeCombination = this.canAssignBuyingOption( warehouse ); warehouse.selected = +value > 0; if ( event.target.id === numberOfReelsId && warehouse.reelOptions.reelCount != +value ) { warehouse.reelOptions.reelCount = +value; } if ( event.target.id === partsPerReelId && warehouse.reelOptions.reelLength != +value ) { warehouse.reelOptions.reelLength = +value; } this.showWarningMessage = this._boService.isValidArrowReel(warehouse); if (!this.isValidPackageTypeCombination) { this._togglePackageCombinationMessages(warehouse, +value); } } else { this.showWarningMessage = true; this._cleanArrowReel(warehouse); this._resetSelectedAndWarningStates(warehouse, +value); } this._boService.updateReelsTotalQuantity(warehouse); this._getUnallocatedQty(); } public onSaveChanges(): void { this.savingBO = true; this._boService.saveBuyingOptions( this.data.buyingOptions, this.data.bomId.toString(), this.data.rowId, this.data.multiplier > 1, this._paginationParams ); this._socketsService .observeAction(ApiSocketListenAction.ACTION_RESULT) .pipe(takeUntil(this._killSig$$)) .subscribe( response => this._handleSocketResponse(response), error => this._logService.error(error) ); } public toggleHiddenOptions(): void { if (this.hiddenOptions === this.data.buyingOptions.length) { this.showAllHiddenBOMessage = !this.showAllHiddenBOMessage; } this.showHiddenBo = !this.showHiddenBo; const hiddenBO = this.data.buyingOptions.filter(bo => bo.isHidden === true); hiddenBO.map(bo => { bo.isTemporalHidden = this.showHiddenBo; }); } public cancel(): void { this.dialogRef.close(); } public translatePackaging(packaging) { let key = packaging?.replace(/\s+/g,"-").toLowerCase() || ''; return this.utils.translateByKeyWithFallback(key, packaging); } public setPositiveQuantity(quantity) { if (quantity < 0) { return Math.abs(quantity); } return quantity; } }