/// <summary>
        /// Process all Schedules (FSSchedule) in each Contract (FSContract).
        /// </summary>
        public virtual void processServiceContract(PXCache cache, FSContractSchedule fsScheduleRow, DateTime?fromDate, DateTime?toDate)
        {
            List <Schedule> mapScheduleResults = new List <Schedule>();

            FSServiceContract fsServiceContractRow_Current = ServiceContractSelected.Select(fsScheduleRow.EntityID);

            if (fsServiceContractRow_Current != null)
            {
                fsScheduleRow.ContractDescr = fsServiceContractRow_Current.DocDesc;
            }

            mapScheduleResults = MapFSScheduleToSchedule.convertFSScheduleToSchedule(cache, fsScheduleRow, toDate, ID.RecordType_ServiceContract.SERVICE_CONTRACT);
            generate_APP_SO_UpdateContracts(mapScheduleResults, ID.RecordType_ServiceContract.SERVICE_CONTRACT, fromDate, toDate, fsScheduleRow);
        }
        public virtual IEnumerable GenerateProjection(PXAdapter adapter)
        {
            if (RouteAppointmentForecastingFilter.Current != null &&
                RouteAppointmentForecastingFilter.Current.DateBegin != null &&
                RouteAppointmentForecastingFilter.Current.DateEnd != null &&
                RouteAppointmentForecastingFilter.Current.DateBegin < RouteAppointmentForecastingFilter.Current.DateEnd)
            {
                DateTime beginFromFilter = RouteAppointmentForecastingFilter.Current.DateBegin.Value;
                DateTime endToFilter     = RouteAppointmentForecastingFilter.Current.DateEnd.Value;
                string   recordType      = ID.RecordType_ServiceContract.ROUTE_SERVICE_CONTRACT;

                int?serviceID          = RouteAppointmentForecastingFilter.Current.ServiceID;
                int?customerID         = RouteAppointmentForecastingFilter.Current.CustomerID;
                int?customerLocationID = RouteAppointmentForecastingFilter.Current.CustomerLocationID;
                int?routeID            = RouteAppointmentForecastingFilter.Current.RouteID;

                PXLongOperation.StartOperation(
                    this,
                    delegate
                {
                    using (PXTransactionScope ts = new PXTransactionScope())
                    {
                        if (beginFromFilter != null && endToFilter != null)
                        {
                            DateHandler requestDate = new DateHandler(endToFilter);

                            var period = new Period((DateTime)beginFromFilter, (DateTime)endToFilter);

                            List <Scheduler.Schedule> scheduleListToProcess = new List <Scheduler.Schedule>();
                            var generator = new TimeSlotGenerator();

                            var fsRouteContractScheduleRecords =
                                PXSelectJoinGroupBy <FSRouteContractSchedule,
                                                     InnerJoin <FSServiceContract,
                                                                On <FSServiceContract.serviceContractID, Equal <FSRouteContractSchedule.entityID> >,
                                                                InnerJoin <FSScheduleRoute,
                                                                           On <FSScheduleRoute.scheduleID, Equal <FSRouteContractSchedule.scheduleID> >,
                                                                           InnerJoin <FSScheduleDet,
                                                                                      On <FSScheduleDet.scheduleID, Equal <FSRouteContractSchedule.scheduleID> > > > >,
                                                     Where <
                                                         FSRouteContractSchedule.entityType, Equal <FSSchedule.entityType.Contract>,
                                                         And <FSServiceContract.recordType, Equal <FSServiceContract.recordType.RouteServiceContract>,
                                                              And <FSRouteContractSchedule.active, Equal <True>,
                                                                   And <FSServiceContract.status, Equal <FSServiceContract.status.Active>,
                                                                        And <FSRouteContractSchedule.startDate, LessEqual <Required <FSRouteContractSchedule.startDate> >,
                                                                             And2 <
                                                                                 Where <
                                                                                     Required <FSScheduleDet.inventoryID>, IsNull,
                                                                                     Or <FSScheduleDet.inventoryID, Equal <Required <FSScheduleDet.inventoryID> > > >,
                                                                                 And2 <
                                                                                     Where <
                                                                                         Required <FSRouteContractSchedule.customerID>, IsNull,
                                                                                         Or <FSRouteContractSchedule.customerID, Equal <Required <FSRouteContractSchedule.customerID> > > >,
                                                                                     And2 <
                                                                                         Where <
                                                                                             Required <FSRouteContractSchedule.customerLocationID>, IsNull,
                                                                                             Or <FSRouteContractSchedule.customerLocationID, Equal <Required <FSRouteContractSchedule.customerLocationID> > > >,
                                                                                         And2 <
                                                                                             Where <
                                                                                                 Required <FSScheduleRoute.dfltRouteID>, IsNull,
                                                                                                 Or <FSScheduleRoute.dfltRouteID, Equal <Required <FSScheduleRoute.dfltRouteID> > > >,
                                                                                             And <
                                                                                                 Where <FSRouteContractSchedule.endDate, IsNull,
                                                                                                        Or <FSRouteContractSchedule.endDate, GreaterEqual <Required <FSRouteContractSchedule.startDate> > > > > > > > > > > > > >,
                                                     Aggregate <
                                                         GroupBy <FSRouteContractSchedule.scheduleID> > >
                                .Select(this, beginFromFilter, serviceID, serviceID, customerID, customerID, customerLocationID, customerLocationID, routeID, routeID, endToFilter);

                            foreach (FSRouteContractSchedule fsRouteContractScheduleRecord in fsRouteContractScheduleRecords)
                            {
                                List <Scheduler.Schedule> mapScheduleResults = MapFSScheduleToSchedule.convertFSScheduleToSchedule(RouteAppointmentForecastingFilter.Cache, fsRouteContractScheduleRecord, endToFilter, recordType);

                                foreach (Scheduler.Schedule mapSchedule in mapScheduleResults)
                                {
                                    scheduleListToProcess.Add(mapSchedule);
                                }
                            }

                            if (recordType == ID.RecordType_ServiceContract.ROUTE_SERVICE_CONTRACT)
                            {
                                foreach (Scheduler.Schedule schedule in scheduleListToProcess)
                                {
                                    schedule.Priority      = (int?)RouteScheduleProcess.SetSchedulePriority(schedule, this);
                                    schedule.RouteInfoList = RouteScheduleProcess.getRouteListFromSchedule(schedule, this);
                                }
                            }

                            List <TimeSlot> timeSlots = generator.GenerateCalendar(period, scheduleListToProcess);

                            var fsRouteAppointmentForecastingRecords =
                                PXSelect <FSRouteAppointmentForecasting,
                                          Where <
                                              FSRouteAppointmentForecasting.startDate, GreaterEqual <Required <FSRouteAppointmentForecasting.startDate> >,
                                              And <
                                                  FSRouteAppointmentForecasting.startDate, LessEqual <Required <FSRouteAppointmentForecasting.startDate> > > > >
                                .Select(this, beginFromFilter, endToFilter);

                            PXCache <FSRouteAppointmentForecasting> cacheRouteForecasting = new PXCache <FSRouteAppointmentForecasting>(this);

                            foreach (FSRouteAppointmentForecasting fsRouteAppointmentDetailProjectionRecord in fsRouteAppointmentForecastingRecords)
                            {
                                cacheRouteForecasting.PersistDeleted(fsRouteAppointmentDetailProjectionRecord);
                            }

                            foreach (TimeSlot timeSlot in timeSlots)
                            {
                                requestDate.setDate(timeSlot.DateTimeBegin);

                                FSSchedule fsScheduleRow = ScheduleSelected.Select(timeSlot.ScheduleID);

                                FSScheduleRoute fsScheduleRouteRow = ScheduleRouteSelected.Select(fsScheduleRow.ScheduleID);

                                FSServiceContract fsServiceContractRow = ServiceContractSelected.Select(fsScheduleRow.EntityID);

                                FSRouteAppointmentForecasting fsRouteAppointmentForecastingRow = new FSRouteAppointmentForecasting();

                                fsRouteAppointmentForecastingRow.RouteID            = fsScheduleRouteRow.DfltRouteID;
                                fsRouteAppointmentForecastingRow.StartDate          = requestDate.StartOfDay();
                                fsRouteAppointmentForecastingRow.ScheduleID         = fsScheduleRow.ScheduleID;
                                fsRouteAppointmentForecastingRow.ServiceContractID  = fsServiceContractRow.ServiceContractID;
                                fsRouteAppointmentForecastingRow.CustomerLocationID = fsServiceContractRow.CustomerLocationID;
                                fsRouteAppointmentForecastingRow.CustomerID         = fsServiceContractRow.CustomerID;
                                fsRouteAppointmentForecastingRow.SequenceOrder      = int.Parse(fsScheduleRouteRow.GlobalSequence);

                                cacheRouteForecasting.PersistInserted(fsRouteAppointmentForecastingRow);
                            }
                        }

                        ts.Complete();
                    }
                });
            }

            return(adapter.Get());
        }
        /// <summary>
        /// Create a Service Order from a TimeSlot.
        /// </summary>
        protected void CreateServiceOrder(TimeSlot timeSlotServiceOrder, bool createAppointmentFlag = false, bool appointmentsBelongToRoute = false)
        {
            // This action allows to avoid errors related to cache
            if (graphServiceOrderEntry != null)
            {
                graphServiceOrderEntry.Clear(PXClearOption.ClearAll);
            }
            else
            {
                graphServiceOrderEntry = PXGraph.CreateInstance <ServiceOrderEntry>();
                graphServiceOrderEntry.DisableServiceOrderUnboundFieldCalc = true;
            }

            FSSchedule        fsScheduleRow        = ScheduleSelected.Select(timeSlotServiceOrder.ScheduleID);
            FSServiceContract fsServiceContractRow = ServiceContractSelected.Select(fsScheduleRow.EntityID);

            bool isPrepaidContract = fsServiceContractRow.BillingType == ID.Contract_BillingType.STANDARDIZED_BILLINGS;

            //Services from the Schedule
            var fsScheduleDetSet = ScheduleLinesSelected.Select(timeSlotServiceOrder.ScheduleID);

            //Templates from the Schedule
            var fsScheduleDetTemplateSet = ScheduleTemplatesSelected.Select(timeSlotServiceOrder.ScheduleID);

            FSServiceOrder fsServiceOrderRow = new FSServiceOrder();

            fsServiceOrderRow.SrvOrdType = fsScheduleRow.SrvOrdType;
            graphServiceOrderEntry.ServiceOrderRecords.Insert(fsServiceOrderRow);

            fsServiceOrderRow = graphServiceOrderEntry.ServiceOrderRecords.Cache.CreateCopy(graphServiceOrderEntry.ServiceOrderRecords.Current) as FSServiceOrder;

            fsServiceOrderRow.BranchID         = fsScheduleRow.BranchID;
            fsServiceOrderRow.BranchLocationID = fsScheduleRow.BranchLocationID;
            fsServiceOrderRow.OrderDate        = timeSlotServiceOrder.DateTimeBegin.Date;
            fsServiceOrderRow.CustomerID       = fsServiceContractRow.CustomerID;
            fsServiceOrderRow.LocationID       = fsScheduleRow.CustomerLocationID;

            graphServiceOrderEntry.ServiceOrderRecords.Update(fsServiceOrderRow);
            fsServiceOrderRow = graphServiceOrderEntry.ServiceOrderRecords.Cache.CreateCopy(graphServiceOrderEntry.ServiceOrderRecords.Current) as FSServiceOrder;

            // Currently Service-Management's contracts DO NOT support multi-currency specification
            // therefore their ServiceOrders MUST be created in customer base currency
            if (PXAccess.FeatureInstalled <FeaturesSet.multicurrency>())
            {
                string curyID = null;

                Customer customer = (Customer)PXSelect <Customer,
                                                        Where <Customer.bAccountID, Equal <Required <Customer.bAccountID> > > >
                                    .Select(this, fsServiceContractRow.CustomerID);

                if (customer != null)
                {
                    curyID = customer.CuryID;
                }

                if (string.IsNullOrEmpty(curyID) == true)
                {
                    curyID = this.Accessinfo.BaseCuryID ?? new PXSetup <PX.Objects.GL.Company>(this).Current?.BaseCuryID;
                }

                fsServiceOrderRow.CuryID = curyID;
            }

            fsServiceOrderRow.DocDesc           = timeSlotServiceOrder.Descr;
            fsServiceOrderRow.BillCustomerID    = fsServiceContractRow.BillCustomerID;
            fsServiceOrderRow.BillLocationID    = fsServiceContractRow.BillLocationID;
            fsServiceOrderRow.ServiceContractID = fsServiceContractRow.ServiceContractID;
            fsServiceOrderRow.ProjectID         = fsServiceContractRow.ProjectID;
            fsServiceOrderRow.DfltProjectTaskID = fsServiceContractRow.DfltProjectTaskID;

            if (isPrepaidContract == true &&
                graphServiceOrderEntry.BillingCycleRelated.Current != null &&
                graphServiceOrderEntry.BillingCycleRelated.Current.BillingBy == ID.Billing_By.SERVICE_ORDER)
            {
                fsServiceOrderRow.BillServiceContractID = fsServiceContractRow.ServiceContractID;
            }

            fsServiceOrderRow.ScheduleID        = timeSlotServiceOrder.ScheduleID;
            fsServiceOrderRow.ServiceContractID = fsServiceContractRow.ServiceContractID;
            fsServiceOrderRow.GenerationID      = timeSlotServiceOrder.GenerationID;

            fsServiceOrderRow.SalesPersonID  = fsServiceContractRow.SalesPersonID;
            fsServiceOrderRow.Commissionable = fsServiceContractRow.Commissionable;

            fsServiceOrderRow = graphServiceOrderEntry.ServiceOrderRecords.Update(fsServiceOrderRow);

            if (fsServiceOrderRow.SalesPersonID == null)
            {
                object salesPersonID;
                graphServiceOrderEntry.ServiceOrderRecords.Cache.RaiseFieldDefaulting <FSServiceOrder.salesPersonID>(fsServiceOrderRow, out salesPersonID);
                fsServiceOrderRow.SalesPersonID = (int?)salesPersonID;

                object commissionable;
                graphServiceOrderEntry.ServiceOrderRecords.Cache.RaiseFieldDefaulting <FSServiceOrder.commissionable>(fsServiceOrderRow, out commissionable);
                fsServiceOrderRow.Commissionable = (bool?)commissionable;
            }

            graphServiceOrderEntry.Answers.CopyAllAttributes(fsServiceOrderRow, fsScheduleRow);

            #region Setting Service Order Details
            foreach (FSScheduleDet fsScheduleDetRow in fsScheduleDetSet)
            {
                if (fsScheduleDetRow.LineType == ID.LineType_ServiceContract.SERVICE_TEMPLATE)
                {
                    foreach (FSScheduleDet fsScheduleDetLocalRow in fsScheduleDetTemplateSet.Where(x => ((FSScheduleDet)x).ServiceTemplateID == fsScheduleDetRow.ServiceTemplateID))
                    {
                        var fsServiceTemplateDetSet_Service = ServiceTemplateSelected.Select(fsScheduleDetRow.ServiceTemplateID);

                        foreach (FSServiceTemplateDet fsServiceTemplateDetRow_Service in fsServiceTemplateDetSet_Service)
                        {
                            FSSODet fsSODetRow = new FSSODet();

                            // Insert the new row with key fields
                            fsSODetRow.ScheduleID    = fsScheduleDetLocalRow.ScheduleID;
                            fsSODetRow.ScheduleDetID = fsScheduleDetLocalRow.ScheduleDetID;
                            fsSODetRow.LineType      = fsServiceTemplateDetRow_Service.LineType;

                            fsSODetRow = graphServiceOrderEntry.ServiceOrderDetails.Insert(fsSODetRow);

                            fsSODetRow.InventoryID = fsServiceTemplateDetRow_Service.InventoryID;

                            fsSODetRow = graphServiceOrderEntry.ServiceOrderDetails.Update(fsSODetRow);

                            PXNoteAttribute.CopyNoteAndFiles(ScheduleLinesSelected.Cache,
                                                             fsScheduleDetLocalRow,
                                                             graphServiceOrderEntry.ServiceOrderDetails.Cache,
                                                             fsSODetRow,
                                                             copyNotes: true,
                                                             copyFiles: true);

                            // Create a copy to update the other fields
                            fsSODetRow = (FSSODet)graphServiceOrderEntry.ServiceOrderDetails.Cache.CreateCopy(fsSODetRow);

                            fsSODetRow.TranDesc = fsServiceTemplateDetRow_Service.TranDesc;

                            InventoryItem inventoryItemRow = SharedFunctions.GetInventoryItemRow(this, fsServiceTemplateDetRow_Service.InventoryID);

                            if (inventoryItemRow != null)
                            {
                                FSxService fsxServiceRow = PXCache <InventoryItem> .GetExtension <FSxService>(inventoryItemRow);

                                if (fsxServiceRow != null && fsxServiceRow.BillingRule == ID.BillingRule.TIME)
                                {
                                    int?estimatedDuration = CalculateEstimatedDuration(fsSODetRow, fsServiceTemplateDetRow_Service.Qty);
                                    fsSODetRow.EstimatedDuration = estimatedDuration;
                                }
                                else
                                {
                                    fsSODetRow.EstimatedQty = fsServiceTemplateDetRow_Service.Qty;
                                }
                            }

                            if (fsServiceContractRow.SourcePrice == ID.SourcePrice.CONTRACT)
                            {
                                fsSODetRow.ManualPrice = true;
                                // TODO: Check where is assigned the contract price?
                            }

                            if (createAppointmentFlag)
                            {
                                fsSODetRow.Scheduled = true;
                                fsSODetRow.Status    = ID.Status_SODet.SCHEDULED;
                            }

                            fsSODetRow.EquipmentAction  = fsScheduleDetRow.EquipmentAction;
                            fsSODetRow.SMEquipmentID    = fsScheduleDetRow.SMEquipmentID;
                            fsSODetRow.ComponentID      = fsScheduleDetRow.ComponentID;
                            fsSODetRow.EquipmentLineRef = fsScheduleDetRow.EquipmentLineRef;
                            fsSODetRow.ProjectTaskID    = fsScheduleDetRow.ProjectTaskID;
                            fsSODetRow.CostCodeID       = fsScheduleDetRow.CostCodeID;

                            if (fsSODetRow.InventoryID != null)
                            {
                                fsSODetRow.ProjectTaskID = fsScheduleDetRow.ProjectTaskID;
                                fsSODetRow.CostCodeID    = fsScheduleDetRow.CostCodeID;
                            }

                            graphServiceOrderEntry.ServiceOrderDetails.Update(fsSODetRow);
                        }
                    }
                }
                else
                {
                    FSSODet fsSODetRow = new FSSODet();

                    // Insert the new row with key fields
                    fsSODetRow.ScheduleID    = fsScheduleDetRow.ScheduleID;
                    fsSODetRow.ScheduleDetID = fsScheduleDetRow.ScheduleDetID;
                    fsSODetRow.LineType      = fsScheduleDetRow.LineType;

                    fsSODetRow = graphServiceOrderEntry.ServiceOrderDetails.Insert(fsSODetRow);

                    fsSODetRow.InventoryID = fsScheduleDetRow.InventoryID;

                    fsSODetRow = graphServiceOrderEntry.ServiceOrderDetails.Update(fsSODetRow);

                    PXNoteAttribute.CopyNoteAndFiles(ScheduleLinesSelected.Cache,
                                                     fsScheduleDetRow,
                                                     graphServiceOrderEntry.ServiceOrderDetails.Cache,
                                                     fsSODetRow,
                                                     copyNotes: true,
                                                     copyFiles: true);

                    // Create a copy to update the other fields
                    fsSODetRow = (FSSODet)graphServiceOrderEntry.ServiceOrderDetails.Cache.CreateCopy(fsSODetRow);

                    fsSODetRow.TranDesc    = fsScheduleDetRow.TranDesc;
                    fsSODetRow.BillingRule = fsScheduleDetRow.BillingRule;

                    if (fsSODetRow.BillingRule == ID.BillingRule.TIME)
                    {
                        int?estimatedDuration = CalculateEstimatedDuration(fsSODetRow, fsScheduleDetRow?.Qty);
                        fsSODetRow.EstimatedDuration = estimatedDuration;
                    }
                    else
                    {
                        fsSODetRow.EstimatedQty = fsScheduleDetRow.Qty;
                    }

                    if (fsServiceContractRow.SourcePrice == ID.SourcePrice.CONTRACT)
                    {
                        fsSODetRow.ManualPrice = true;
                        // TODO: AC-142850 Check where is assigned the contract price?
                    }

                    if (createAppointmentFlag)
                    {
                        fsSODetRow.Scheduled = true;
                        fsSODetRow.Status    = ID.Status_SODet.SCHEDULED;
                    }

                    fsSODetRow.EquipmentAction  = fsScheduleDetRow.EquipmentAction;
                    fsSODetRow.SMEquipmentID    = fsScheduleDetRow.SMEquipmentID;
                    fsSODetRow.ComponentID      = fsScheduleDetRow.ComponentID;
                    fsSODetRow.EquipmentLineRef = fsScheduleDetRow.EquipmentLineRef;
                    fsSODetRow.ProjectTaskID    = fsScheduleDetRow.ProjectTaskID;
                    fsSODetRow.CostCodeID       = fsScheduleDetRow.CostCodeID;

                    if (fsSODetRow.InventoryID != null)
                    {
                        fsSODetRow.ProjectTaskID = fsScheduleDetRow.ProjectTaskID;
                        fsSODetRow.CostCodeID    = fsScheduleDetRow.CostCodeID;
                    }

                    graphServiceOrderEntry.ServiceOrderDetails.Update(fsSODetRow);
                }
            }

            if (fsScheduleRow.VendorID != null)
            {
                FSSOEmployee fsSOEmployeeRow = new FSSOEmployee();
                fsSOEmployeeRow.EmployeeID = fsScheduleRow.VendorID;
                graphServiceOrderEntry.ServiceOrderEmployees.Insert(fsSOEmployeeRow);
            }

            #endregion

            graphServiceOrderEntry.Save.Press();

            if (createAppointmentFlag)
            {
                string origStatus = graphServiceOrderEntry.ServiceOrderRecords.Current.Status;

                if (origStatus != ID.Status_ServiceOrder.OPEN)
                {
                    //Due to FSAppointment.sORefNbr selector, Service Order status must be OPEN to allow the SetValueExt<SORefNbr> inside createAppointment() work properly.
                    //PXUpdate is used to avoid raising any ServiceOrderEntry event.
                    PXUpdate <
                        Set <FSServiceOrder.status, FSServiceOrder.status.Open>,
                        FSServiceOrder,
                        Where <
                            FSServiceOrder.sOID, Equal <Required <FSServiceOrder.sOID> > > >
                    .Update(this, graphServiceOrderEntry.ServiceOrderRecords.Current.SOID);
                }

                CreateAppointment(graphServiceOrderEntry.ServiceOrderRecords.Current, timeSlotServiceOrder, fsScheduleRow, true, appointmentsBelongToRoute, isPrepaidContract);

                if (origStatus != ID.Status_ServiceOrder.OPEN)
                {
                    PXUpdate <
                        Set <FSServiceOrder.status, Required <FSServiceOrder.status> >,
                        FSServiceOrder,
                        Where <
                            FSServiceOrder.sOID, Equal <Required <FSServiceOrder.sOID> > > >
                    .Update(this, origStatus, graphServiceOrderEntry.ServiceOrderRecords.Current.SOID);
                }
            }
        }