| Quintiq file version 2.0 | 
| #parent: #root | 
| Method GetUpstreamPISPIPs ( | 
|   Number depth, | 
|   ProductInStockingPointInPeriods pispips_o | 
| ) | 
| { | 
|   Description: | 
|   [* | 
|     Selects all upstream PISPIPs related to this pispip.  | 
|     Please note that this will create the required PeriodTaskOperations/Trips to be able to follow the dependent demand.  | 
|     Therefore, it is recommended to delete any unused PeriodTaskOperations/Trips after calling this method | 
|   *] | 
|   TextBody: | 
|   [* | 
|     // Martijn Oct-18-2016 (created) | 
|     // Martijn: I think we could remove some code duplication between this method and the SetOptimizerInputSmartPlan methods | 
|      | 
|     depth := depth - 1; | 
|      | 
|     if( depth > 0 ) | 
|     { | 
|       pispips_o.Add( this ); | 
|       period := this.Period_MP(); | 
|       // Traverse over the operations that produce this product as an output | 
|       // If this product is a byproduct, we do not want to add its operations, unless they only produce byproduct. | 
|       // This is to avoid adding products to the smart plan that are only related to the smart plan input pispips because they are produced together with the same byproduct | 
|       traverse( this, ProductInStockingPoint_MP.OperationOutput.Operation, operation, | 
|                 operation.GetIsAvailableForOptimization()                                     // The operation must be available | 
|                 and PeriodTaskOperation::GetIsValidPeriodTask( operation, period )  // The resulting period task must be valid | 
|                 and ( not this.ProductInStockingPoint_MP().Product_MP().IsByProduct()       // This product is not a byproduct | 
|                       or forall( operation, OperationOutput.ProductInStockingPoint_MP.Product_MP, product, product.IsByProduct() ) ) )  // or all outputs are byproducts | 
|       { | 
|         // Create/Select the related periodtaskoperation | 
|         periodtaskoperation := PeriodTaskOperation::FindPeriodTaskOperationTypeIndex( period.Start() , operation.ID() ); | 
|      | 
|         if( isnull( periodtaskoperation ) ) | 
|         { | 
|           unit := operation.Unit(); | 
|           unitperiod := UnitPeriod::FindUnitPeriodTypeIndex( unit.ID(), period.Start(), period.End() ); | 
|           periodtaskoperation := PeriodTaskOperation::Create( operation, unitperiod, 0.0, false ); | 
|           // I think this transaction propagate is necessary, we need to get the dependent demand from the newly created periodtask | 
|           Transaction::Transaction().Propagate( relation( PeriodTaskOperation, DependentDemand ) ) | 
|         } | 
|          | 
|         traverse( periodtaskoperation, DependentDemand.ProductInStockingPointInPeriodPlanningLeaf, newpispip ) | 
|         { | 
|           if( pispips_o.Find( newpispip ) < 0 )  //PISPIP not already added to the set | 
|           { | 
|             newpispip.GetUpstreamPISPIPs( depth, pispips_o ); | 
|           } | 
|         } | 
|       } | 
|      | 
|      | 
|       // Traverse over the lanelegs that supply this PISPIP | 
|       traverse( this, ProductInStockingPoint_MP.LaneLegOutput.LaneLeg, laneleg, laneleg.GetIsAvailableForOptimization() ) | 
|       {     | 
|         unit := laneleg.Lane().Unit(); | 
|         unitperiod := UnitPeriod::FindUnitPeriodTypeIndex( unit.ID(), period.Start(), period.End() ); | 
|      | 
|         // Select the existing trips of this laneleg on this unit period | 
|         // Normally there should only be a single trip, but the user may have created multiple trips for a single unit period | 
|         trips := selectset( laneleg, Trip, trip, trip.ArrivalUnitPeriod() = unitperiod ); | 
|          | 
|         // If no such trip exists and it would be a valid nonfrozen trip, then create a new trip | 
|         if( trips.Elements( relsize ) = 0 | 
|             and Trip::GetIsValidNonFrozenTrip( laneleg, period ) ) | 
|         { | 
|           trips.Add( Trip::Create( laneleg, period ) ); | 
|           // Without the Transaction.Propagate() the trips will not have the required relations | 
|           Transaction::Transaction().Propagate( relation( Trip, PeriodTaskLaneLeg ) ); | 
|         } | 
|      | 
|         // Add the current product to the trips (if it is not yet part of these trips) | 
|         traverse( trips, Elements, trip, | 
|                   not exists( trip, ProductInTrip.Product_MP, product, | 
|                               product = this.ProductInStockingPoint_MP().Product_MP() ) ) | 
|         { | 
|           trip.AddProduct( this.ProductInStockingPoint_MP().Product_MP(), 0.0, false ); | 
|            | 
|           // Without the Transaction.Propagate() the trips will not have the required relations | 
|           Transaction::Transaction().Propagate( relation( ProductInTrip, DependentDemand ) ); | 
|         } | 
|         | 
|         // Add the trips to the optimizer run | 
|         traverse( trips, Elements, trip, trip.GetIsValidNonFrozenTrip() ) | 
|         { | 
|           // Add the related productintrip to productintripsforoptimization | 
|           traverse( trip, ProductInTrip, productintrip, | 
|                     productintrip.ProductID() = this.ProductInStockingPoint_MP().ProductID() ) | 
|           { | 
|             newpispip := null(ProductInStockingPointInPeriod ) | 
|             newpispip := guard( productintrip.DependentDemand().ProductInStockingPointInPeriodPlanningLeaf(), null(ProductInStockingPointInPeriodPlanningLeaf) ); | 
|             if( isnull( newpispip ) ) | 
|             { | 
|               newpispip := this; | 
|             } | 
|             if( pispips_o.Find( newpispip ) < 0 )  //PISPIP not already added to the set | 
|             { | 
|               newpispip.GetUpstreamPISPIPs( depth, pispips_o ); | 
|             } | 
|           } | 
|         } | 
|       } | 
|     } | 
|   *] | 
| } |