/// <summary>
        /// Retrieve external pre product asynchronously by using specific conditions.
        /// </summary>
        /// <param name="terminalNo"></param>
        /// <param name="commandNo"></param>
        /// <param name="preProductCode"></param>
        /// <param name="prePdtLotNo"></param>
        /// <param name="tableListingLine"></param>
        /// <param name="row"></param>
        /// <param name="bay"></param>
        /// <param name="level"></param>
        /// <returns></returns>
        public async Task RetrievalExternalPreProductAsync(string terminalNo, string commandNo, string preProductCode, string prePdtLotNo, string tableListingLine)
        {
            // o	Retrieve [f05_strrtrsts] in TM05_CONVEYOR, where [f05_terminalno] = Terminal No. which is retrieved from section “server” in “toshiba.ini” configurable file.
            var conveyor = await _commonDomain.FindConveyorCodeAsync(terminalNo);

            // Conveyor count.
            var statusError = Constants.F05_StrRtrSts.Error.ToString("D");

            if (conveyor == null || statusError.Equals(conveyor.F05_StrRtrSts))
            {
                throw new Exception(HttpMessages.InvalidConveyorStatus);
            }

            /*
             * o	Retrieve [f14_devicestatus] and [f14_usepermitclass] from TM14_DEVICE, where [f14_devicecode] = Pre-product Device Code under “Device” section of “toshiba.ini” configurable file.
             * If retrieval is failed OR retrieved status is “Error” (or 2) OR “Offline” (or 1) OR retrieved permit is “Prohibited” (or 1), system shows the message MSG 9, stop the use case.
             */
            var device = await _commonDomain.FindDeviceAvailabilityAsync(_configurationService.ProductDeviceCode);

            // Enum conversion.
            var statusOffline     = Constants.F14_DeviceStatus.Offline.ToString("D");
            var statusDeviceError = Constants.F14_DeviceStatus.Error.ToString("D");
            var statusProhibited  = Constants.F14_UsePermitClass.Prohibited.ToString("D");

            if (device == null || statusOffline.Equals(device.F14_DeviceStatus) ||
                statusDeviceError.Equals(device.F14_DeviceStatus) || device.F14_UsePermitClass.Equals(statusProhibited))
            {
                throw new Exception(HttpMessages.InvalidDeviceAvailability);
            }

            /*
             * 	System will get tablet line from “Line” column of selected row.
             * o	If it is not blank, then continue the use case.
             * o	If it is blank, then update TX41_TBTCMD, in which:
             * •	[f41_kndcmdno] = “Command No” column of selected line,
             * •	[f41_prepdtlotno] = “Pre-product Lot No" column of selected line.
             * Then:
             * •	Set [f41_tabletline] = "TAB0" + [Tabletising Line].
             * If no record is updated after this query, system shows the message MSG 47 and stops the use case.
             */

            var tableListingCommands = _unitOfWork.TabletCommandRepository.GetAll();

            tableListingCommands = tableListingCommands.Where(x => x.F41_KndCmdNo.Trim().Equals(commandNo));
            tableListingCommands = tableListingCommands.Where(x => x.F41_PrePdtLotNo.Trim().Equals(prePdtLotNo));

            var totalTableListingLines = await tableListingCommands.CountAsync();

            if (totalTableListingLines < 1)
            {
                throw new Exception(HttpMessages.RecordIsBeingModified);
            }

            foreach (var tableListingCommand in tableListingCommands)
            {
                if (string.IsNullOrWhiteSpace(tableListingCommand.F41_TabletLine))
                {
                    tableListingCommand.F41_TabletLine = "TAB0" + tableListingLine.Substring(tableListingLine.Length - 2);
                    _unitOfWork.TabletCommandRepository.Update(tableListingCommand);
                }
            }

            /*
             * System starts the Retrieval process by doing as follow:
             * o	Get Command No, Pre-product Code and Pre-product Lot No of selected row.
             * o	System will get pallet no for the pre-product by doing as follow:
             * •	Suppose Pallet No and Updated Date are temporary variables which are retrieved respectively from [f53_palletno] and [f53_updatedate] in TX53_OUTSIDEPREPDTSTK, in which:
             * 	[f53_outsideprepdtcode] = Pre-product Code of selected row,
             * 	AND [f53_outsideprepdtlotno] = Pre-product Lot No of selected row,
             * 	AND [f53_kndcmdno] = Command No of selected row,
             * 	AND [f53_stockflag] = “In Stock” (or 3),
             * 	Ascending order by [f53_palletseqno].
             * If no matched record from the query, system shows the message MSG 30 and stops the use case.
             */
            var outsidePrePdtStks = _unitOfWork.OutSidePrePdtStkRepository.GetAll();

            outsidePrePdtStks = outsidePrePdtStks.Where(x => x.F53_OutSidePrePdtCode.Trim().Equals(preProductCode.Trim()));
            outsidePrePdtStks = outsidePrePdtStks.Where(x => x.F53_OutSidePrePdtLotNo.Trim().Equals(prePdtLotNo.Trim()));
            outsidePrePdtStks = outsidePrePdtStks.Where(x => x.F53_KndCmdNo.Trim().Equals(commandNo.Trim()));
            outsidePrePdtStks =
                outsidePrePdtStks.Where(x => Constants.F53_StokcFlag.TX53_StkFlg_Stk.Equals(x.F53_StockFlag.Trim()));
            outsidePrePdtStks = outsidePrePdtStks.OrderBy(x => x.F53_PalletSeqNo);

            var totalOutsidePrePdtStk = await outsidePrePdtStks.CountAsync();

            if (totalOutsidePrePdtStk < 1)
            {
                throw new Exception(HttpMessages.RecordIsBeingModified);
            }

            foreach (var outsidePrePdtStk in outsidePrePdtStks)
            {
                outsidePrePdtStk.F53_StockFlag = Constants.F53_StokcFlag.TX53_StkFlg_Rtr;
            }

            var outsidepPdtStk = await outsidePrePdtStks.FirstOrDefaultAsync();

            /*
             * o	System will assign shelf by doing as follow:
             * •	Suppose Row, Bay, Level and Updated Date1 are temporary variables which are retrieved respectively from
             * [f51_shelfrow], [f51_shelfbay], [f51_shelflevel] and [f51_updatedate] in TX51_PDTSHFSTS, in which:
             * 	[f51_palletno] = Pallet No above,
             * 	AND [f51_shelfstatus] = “External Pre-Products Stocked” (or 8).
             * If no record found, system shows the message MSG 47 and stops the use case.
             * •	(Reference xxiv) Update TX51_PDTSHFSTS, in which:
             * 	[f51_shelfrow] = Row above,
             * 	AND [f51_shelfbay] = Bay above,
             * 	AND [f51_shelflevel] = Level above,
             * 	AND [f51_shelfstatus] = “External Pre-Products Stocked” (or 8),
             * 	AND [f51_updatedate] = Updated Date 1 above.
             * If no record updated from the query, system shows the message MSG 47 and stops the use case.
             */

            var productShelfStatuses = _unitOfWork.ProductShelfStatusRepository.GetAll();

            productShelfStatuses = productShelfStatuses.Where(x => x.F51_PalletNo.Trim().Equals(outsidepPdtStk.F53_PalletNo.Trim()));
            productShelfStatuses =
                productShelfStatuses.Where(
                    x => Constants.F51_ShelfStatus.TX51_ShfSts_ExtPrePdt.Equals(x.F51_ShelfStatus));

            var totalProductShelfStatuses = await productShelfStatuses.CountAsync();

            if (totalProductShelfStatuses < 1)
            {
                throw new Exception(HttpMessages.RecordIsBeingModified);
            }

            foreach (var productShelfStatuse in productShelfStatuses)
            {
                productShelfStatuse.F51_ShelfStatus = "5";
                _unitOfWork.ProductShelfStatusRepository.Update(productShelfStatuse);

                break;
            }

            TX51_PdtShfSts pdtShfSts = null;

            foreach (var productShelfStatus in productShelfStatuses)
            {
                pdtShfSts = productShelfStatus;
            }

            /*
             * o	System will assign Tablet command by updating TX41_TBTCMD, in which:
             * •	[f41_kndcmdno] = Command No of selected row,
             * •	AND [f41_prepdtlotno] = Pre-product Lot No of selected row,
             * •	AND [f41_status] = “not yet tabletised” (or 3), OR “Retrieval but stopped” (or 5), OR “Retrieval process” (or 4)
             * If no record updated from the query, system shows the message MSG 47 and stops the use case.
             */
            var tbtCmds = _unitOfWork.TabletCommandRepository.GetAll();

            tbtCmds = tbtCmds.Where(x => x.F41_KndCmdNo.Trim().Equals(commandNo.Trim()) &&
                                    x.F41_PrePdtLotNo.Trim().Equals(prePdtLotNo.Trim())
                                    &&
                                    (Constants.F41_Status.NotTablet.Equals(x.F41_Status.Trim()) ||
                                     Constants.F41_Status.RetrievalingStoped.Equals(x.F41_Status.Trim())));

            if (await tbtCmds.CountAsync() < 1)
            {
                throw new Exception(HttpMessages.RecordIsBeingModified);
            }

            var totalNoManageItems = await _commonDomain.InsertIntoNoManageAsync();

            await _unitOfWork.CommitAsync();

            var serialNo = string.Format("0000{0}", totalNoManageItems);

            serialNo = serialNo.Substring(serialNo.Length - 4);

            /*
             * •	Insert data to TX47_PDTWHSCMD:
             * 	[f47_commandno] = “2000” (for Retrieval),
             * 	[f47_cmdseqno] = Serial No above,
             * 	[f47_commandtype] = “0000”,
             * 	[f47_strrtrtype] = “External pre-product” (or 2),
             * 	[f47_status] = “An instruction” (or 0),
             * 	[f47_priority] = 0,
             * 	[f47_palletno] = Pallet No above,
             * 	[f47_from] = Row + Bay + Level above,
             * 	[f47_to] = [f05_conveyorcode] from “tm05_conveyor” table, where [f05_terminalno] = Terminal No. which is retrieved from section “server” in “toshiba.ini” configurable file,
             * 	[f47_commandsenddate] = blank,
             * 	[f47_commandenddate] = blank,
             * 	[f47_terminalno] = Terminal No. which is retrieved from section “server” in “toshiba.ini” configurable file,
             * 	[f47_pictureno] = “TCPR101F”,
             * 	[f47_abnormalcode] = blank,
             * 	[f47_adddate] = current date time,
             * 	[f47_updatedate] = current date time,
             * 	[f47_updatecount] = blank.
             */
            var f47To = GetConveyorCode(terminalNo);
            var productWarehouseCommand = new TX47_PdtWhsCmd();

            productWarehouseCommand.F47_CommandNo   = Constants.F47_CommandNo.Retrieval.ToString("D");
            productWarehouseCommand.F47_CmdSeqNo    = serialNo;
            productWarehouseCommand.F47_CommandType = "0000";
            productWarehouseCommand.F47_StrRtrType  = Constants.F47_StrRtrType.ExternalPreProduct.ToString("D");
            productWarehouseCommand.F47_Status      = Constants.F47_Status.AnInstruction.ToString("D");
            productWarehouseCommand.F47_Priority    = 0;
            productWarehouseCommand.F47_PalletNo    = outsidepPdtStk.F53_PalletNo;
            if (pdtShfSts != null)
            {
                productWarehouseCommand.F47_From = string.Format("{0}{1}{2}", pdtShfSts.F51_ShelfRow, pdtShfSts.F51_ShelfBay, pdtShfSts.F51_ShelfLevel);
            }
            productWarehouseCommand.F47_To           = f47To;
            productWarehouseCommand.F47_TerminalNo   = terminalNo;
            productWarehouseCommand.F47_PictureNo    = Constants.PictureNo.TCPR101F;
            productWarehouseCommand.F47_AddDate      = DateTime.Now;
            productWarehouseCommand.F47_UpdateDate   = DateTime.Now;
            productWarehouseCommand.F47_AbnormalCode = "";

            _unitOfWork.ProductWarehouseCommandRepository.Add(productWarehouseCommand);

            // Save changes into database.
            await _unitOfWork.CommitAsync();

            // Broadcast message to third communication.
            _notificationService.SendMessageToC3("TCPR101F", _notificationService.FormatThirdCommunicationMessageResponse(productWarehouseCommand));
        }
        /// <summary>
        /// Retrieve product details asynchronously.
        /// </summary>
        /// <returns></returns>
        public async Task <ResponseResult <RetrievalItem> > RetrieveProductDetailsAsync(string palletNo, string row, string bay, string level, string terminalNo)
        {
            //o	System will get Serial No by doing as follow:
            //•	Suppose Sequence No is selected from [f48_pdtwhscmdno] in TX48_NOMANAGE, where [f48_systemid] = “00000”.
            var seqNo1 = string.Format("0000{0}", await _commonDomain.InsertIntoNoManageAsync());
            var seqNo  = seqNo1.Substring(seqNo1.Length - 4, 4);

            /*
             * o	Update TX40_PDTSHFSTK, in which:
             * •	[f40_palletno] = Pallet No column value of selected row,
             * •	AND [f40_stockflag] = “Stock take” (or 3).
             * Then:
             * •	Set [f40_stockflag] = “Retrieval” (or 2).
             */
            var productShelfStocks = _unitOfWork.ProductShelfStockRepository.GetAll();
            var stockFlag          = Constants.F40_StockFlag.TX40_StkFlg_Stk;

            productShelfStocks =
                productShelfStocks.Where(x => x.F40_StockFlag.Trim().Equals(stockFlag) &&
                                         x.F40_PalletNo.Trim().Equals(palletNo));

            foreach (var productShelfStock in productShelfStocks)
            {
                productShelfStock.F40_StockFlag = Constants.F40_StockFlag.TX40_StkFlg_Rtr;
                _unitOfWork.ProductShelfStockRepository.Update(productShelfStock);
            }



            //o	Suppose Row, Bay and Level are 2 first ## characters, 2 middle ## characters and 2 last ## characters of Shelf No of selected record

            /*
             * o	Update TX51_PDTSHFSTS, in which:
             * •	[f51_shelfrow] = Row above,
             * •	AND [f51_shelfbay] = Bay above,
             * •	AND [f51_shelflevel] = Level above,
             * •	AND [f51_shelfstatus] = “Products Stocked’ (or 2),
             * •	AND [f51_stocktakingflag] = “Yet to stock-take” (or 0).
             * */
            var productShelfStatuses = _unitOfWork.ProductShelfStatusRepository.GetAll();

            productShelfStatuses = productShelfStatuses.Where(x => x.F51_ShelfRow.Trim().Equals(row));
            productShelfStatuses = productShelfStatuses.Where(x => x.F51_ShelfBay.Trim().Equals(bay));
            productShelfStatuses = productShelfStatuses.Where(x => x.F51_ShelfLevel.Trim().Equals(level));
            productShelfStatuses = productShelfStatuses.Where(x => Constants.F51_ShelfStatus.TX51_ShfSts_Pdt.Equals(x.F51_ShelfStatus.Trim()));
            productShelfStatuses = productShelfStatuses.Where(x => Constants.F51_StockTakingFlag.TX51_StkTkgFlg_InvNotChk.Equals(x.F51_StockTakingFlag.Trim()));
            //productShelfStatuses = productShelfStatuses.Where(x => x.F51_ShelfRow.Trim().Equals(row));

            // Count total records.
            var productShelfStatusesTotal = await productShelfStatuses.CountAsync();

            //* If there is no record updated from the update query, system shows message MSG 47, stops the use case and reloads the current page.
            if (productShelfStatusesTotal == 0)
            {
                //throw new Exception("MSG47");
                return(new ResponseResult <RetrievalItem>(null, false, "MSG47"));
            }


            /*
             * Then:
             * •	Set [f51_shelfstatus] = “Assigned for Retrieval” (or 5),
             * •	Set [f51_terminalno] = Terminal No. which is retrieved from section “server” in “toshiba.ini” configurable file,
             * •	Set [f51_updatedate] = current date time.
             */
            foreach (var productShelfStatus in productShelfStatuses)
            {
                productShelfStatus.F51_ShelfStatus = Constants.F51_ShelfStatus.TX51_ShfSts_RsvRtr;
                productShelfStatus.F51_TerminalNo  = terminalNo;
                productShelfStatus.F51_UpdateDate  = DateTime.Now;
                _unitOfWork.ProductShelfStatusRepository.Update(productShelfStatus);
                break;
            }

            var condition  = Constants.F47_Status.AnInstruction.ToString("D");
            var condition2 = Constants.F47_StrRtrType.Product.ToString("D");

            var pdtWhsCmd = new TX47_PdtWhsCmd();

            pdtWhsCmd.F47_CommandNo   = "7000";
            pdtWhsCmd.F47_CmdSeqNo    = seqNo;
            pdtWhsCmd.F47_CommandType = "0000";
            pdtWhsCmd.F47_StrRtrType  = condition2;
            pdtWhsCmd.F47_Status      = condition;

            pdtWhsCmd.F47_Priority = 0;
            pdtWhsCmd.F47_PalletNo = palletNo;
            pdtWhsCmd.F47_From     = string.Format("{0}{1}{2}", row, bay, level);

            var conveyor = await _unitOfWork.ConveyorRepository.GetAll().FirstOrDefaultAsync(i => i.F05_TerminalNo.Trim().Equals(terminalNo));

            pdtWhsCmd.F47_To         = conveyor.F05_ConveyorCode;
            pdtWhsCmd.F47_TerminalNo = terminalNo;
            pdtWhsCmd.F47_PictureNo  = "TCPR061F";
            pdtWhsCmd.F47_RetryCount = 0;
            pdtWhsCmd.F47_AddDate    = DateTime.Now;
            pdtWhsCmd.F47_UpdateDate = DateTime.Now;
            _unitOfWork.ProductWarehouseCommandRepository.Add(pdtWhsCmd);

            // Save changes into database asynchronously.
            await _unitOfWork.CommitAsync();



            var productWarehouses = _unitOfWork.ProductWarehouseCommandRepository.GetAll();

            productWarehouses =
                productWarehouses.Where(x => x.F47_CommandNo.Trim().Equals("7000") && x.F47_CmdSeqNo.Trim().Equals(seqNo) &&
                                        x.F47_Status.Trim().Equals(condition));

            foreach (var item in productWarehouses)
            {
                var text1   = item.F47_TerminalNo;
                var text2   = item.F47_PictureNo;
                var text3   = item.F47_CommandType;
                var text4   = item.F47_Status;
                var text5   = item.F47_From;
                var text6   = item.F47_To;
                var text7   = item.F47_PalletNo;
                var message = "0011" + text1 + text2 + "0066" + seqNo + "7000" + text3 + text4 + text5 + text6 + text7 +
                              DateTime.Now.ToString("d");
                //_notificationService.SendMessageToC3(new
                //{
                //    "screenName",
                //    message
                //});
                _notificationService.SendMessageToC3("", message);
            }
            var assignPalletItem = new RetrievalItem
            {
                Row   = row,
                Bay   = bay,
                Level = level
            };

            return(new ResponseResult <RetrievalItem>(assignPalletItem, true, ""));
            // TODO : Display the waiting notification message: “Shelf No [" + Shelf No of selected record + "] retrieving ..."
        }