Generates timephased costs from timephased work where multiple cost rates
apply to the assignment.

@param standardWorkList timephased work
@param overtimeWorkList timephased work
@return timephased cost
6918
private List<TimephasedCost> getTimephasedCostMultipleRates(List<TimephasedWork> standardWorkList, List<TimephasedWork> overtimeWorkList)
   {
      List<TimephasedWork> standardWorkResult = new LinkedList<TimephasedWork>();
      List<TimephasedWork> overtimeWorkResult = new LinkedList<TimephasedWork>();
      CostRateTable table = getCostRateTable();
      ProjectCalendar calendar = getCalendar();

      Iterator<TimephasedWork> iter = overtimeWorkList.iterator();
      for (TimephasedWork standardWork : standardWorkList)
      {
         TimephasedWork overtimeWork = iter.hasNext() ? iter.next() : null;

         int startIndex = getCostRateTableEntryIndex(standardWork.getStart());
         int finishIndex = getCostRateTableEntryIndex(standardWork.getFinish());

         if (startIndex == finishIndex)
         {
            standardWorkResult.add(standardWork);
            if (overtimeWork != null)
            {
               overtimeWorkResult.add(overtimeWork);
            }
         }
         else
         {
            standardWorkResult.addAll(splitWork(table, calendar, standardWork, startIndex));
            if (overtimeWork != null)
            {
               overtimeWorkResult.addAll(splitWork(table, calendar, overtimeWork, startIndex));
            }
         }
      }

      return getTimephasedCostSingleRate(standardWorkResult, overtimeWorkResult);
   }
500243
private void readCostRateTables(Resource resource, Rates rates)
   {
      if (rates == null)
      {
         CostRateTable table = new CostRateTable();
         table.add(CostRateTableEntry.DEFAULT_ENTRY);
         resource.setCostRateTable(0, table);

         table = new CostRateTable();
         table.add(CostRateTableEntry.DEFAULT_ENTRY);
         resource.setCostRateTable(1, table);

         table = new CostRateTable();
         table.add(CostRateTableEntry.DEFAULT_ENTRY);
         resource.setCostRateTable(2, table);

         table = new CostRateTable();
         table.add(CostRateTableEntry.DEFAULT_ENTRY);
         resource.setCostRateTable(3, table);

         table = new CostRateTable();
         table.add(CostRateTableEntry.DEFAULT_ENTRY);
         resource.setCostRateTable(4, table);
      }
      else
      {
         Set<CostRateTable> tables = new HashSet<CostRateTable>();

         for (net.sf.mpxj.mspdi.schema.Project.Resources.Resource.Rates.Rate rate : rates.getRate())
         {
            Rate standardRate = DatatypeConverter.parseRate(rate.getStandardRate());
            TimeUnit standardRateFormat = DatatypeConverter.parseTimeUnit(rate.getStandardRateFormat());
            Rate overtimeRate = DatatypeConverter.parseRate(rate.getOvertimeRate());
            TimeUnit overtimeRateFormat = DatatypeConverter.parseTimeUnit(rate.getOvertimeRateFormat());
            Double costPerUse = DatatypeConverter.parseCurrency(rate.getCostPerUse());
            Date endDate = rate.getRatesTo();

            CostRateTableEntry entry = new CostRateTableEntry(standardRate, standardRateFormat, overtimeRate, overtimeRateFormat, costPerUse, endDate);

            int tableIndex = rate.getRateTable().intValue();
            CostRateTable table = resource.getCostRateTable(tableIndex);
            if (table == null)
            {
               table = new CostRateTable();
               resource.setCostRateTable(tableIndex, table);
            }
            table.add(entry);
            tables.add(table);
         }

         for (CostRateTable table : tables)
         {
            Collections.sort(table);
         }
      }
   }
801669
public JodaWorkingWeek withWorkingDayFromDateTimeConstant(final boolean working, final int givenDayOfWeek) {
        final int dayOfWeek = jodaToCalendarDayConstant(givenDayOfWeek);
        return new JodaWorkingWeek(super.withWorkingDayFromCalendar(working, dayOfWeek));
    }
489984
private void splitDays(ProjectCalendar calendar, LinkedList<TimephasedCost> list)
   {
      LinkedList<TimephasedCost> result = new LinkedList<TimephasedCost>();
      boolean remainderInserted = false;

      for (TimephasedCost assignment : list)
      {
         if (remainderInserted)
         {
            assignment.setStart(DateHelper.addDays(assignment.getStart(), 1));
            remainderInserted = false;
         }

         while (assignment != null)
         {
            Date startDay = DateHelper.getDayStartDate(assignment.getStart());
            Date finishDay = DateHelper.getDayStartDate(assignment.getFinish());

            // special case - when the finishday time is midnight, it's really the previous day...
            if (assignment.getFinish().getTime() == finishDay.getTime())
            {
               finishDay = DateHelper.addDays(finishDay, -1);
            }

            if (startDay.getTime() == finishDay.getTime())
            {
               result.add(assignment);
               break;
            }

            TimephasedCost[] split = splitFirstDay(calendar, assignment);
            if (split[0] != null)
            {
               result.add(split[0]);
            }

            if (assignment.equals(split[1]))
            {
               break;
            }

            assignment = split[1];
         }
      }
      
      list.clear();
      list.addAll(result);
   }
878684
public static final TimeUnit parseWorkUnits(BigInteger value)
   {
      TimeUnit result = TimeUnit.HOURS;

      if (value != null)
      {
         switch (value.intValue())
         {
            case 1:
            {
               result = TimeUnit.MINUTES;
               break;
            }

            case 3:
            {
               result = TimeUnit.DAYS;
               break;
            }

            case 4:
            {
               result = TimeUnit.WEEKS;
               break;
            }

            case 5:
            {
               result = TimeUnit.MONTHS;
               break;
            }

            case 7:
            {
               result = TimeUnit.YEARS;
               break;
            }

            default:
            case 2:
            {
               result = TimeUnit.HOURS;
               break;
            }
         }
      }

      return (result);
   }
101240
public static Object repeat(List<Object> list, int times) {
        if ( times < 1 )
            if (list instanceof Queue)
                return new LinkedList<>();
            else
                return new ArrayList<>(0);
        List<Object> lst = list instanceof Queue
                            ? new LinkedList<>(list)
                            : new ArrayList<>(list);
        if ( times == 1 )
            return lst;
        while ( times-- > 1 )
            lst.addAll(list);
        return lst;
    }
1350239
private void writeAssignmentTimephasedData(BigInteger assignmentID, List<TimephasedDataType> list, List<TimephasedWork> data, int type)
   {
      for (TimephasedWork mpx : data)
      {
         TimephasedDataType xml = m_factory.createTimephasedDataType();
         list.add(xml);

         xml.setStart(mpx.getStart());
         xml.setFinish(mpx.getFinish());
         xml.setType(BigInteger.valueOf(type));
         xml.setUID(assignmentID);
         xml.setUnit(DatatypeConverter.printDurationTimeUnits(mpx.getTotalAmount(), false));
         xml.setValue(DatatypeConverter.printDuration(this, mpx.getTotalAmount()));
      }
   }
584381
public void process(Resource resource, int index, byte[] data)
   {
      CostRateTable result = new CostRateTable();

      if (data != null)
      {
         for (int i = 16; i + 44 <= data.length; i += 44)
         {
            Rate standardRate = new Rate(MPPUtility.getDouble(data, i), TimeUnit.HOURS);
            TimeUnit standardRateFormat = getFormat(MPPUtility.getShort(data, i + 8));
            Rate overtimeRate = new Rate(MPPUtility.getDouble(data, i + 16), TimeUnit.HOURS);
            TimeUnit overtimeRateFormat = getFormat(MPPUtility.getShort(data, i + 24));
            Double costPerUse = NumberHelper.getDouble(MPPUtility.getDouble(data, i + 32) / 100.0);
            Date endDate = MPPUtility.getTimestampFromTenths(data, i + 40);
            CostRateTableEntry entry = new CostRateTableEntry(standardRate, standardRateFormat, overtimeRate, overtimeRateFormat, costPerUse, endDate);
            result.add(entry);
         }
         Collections.sort(result);
      }
      else
      {
         //
         // MS Project economises by not actually storing the first cost rate
         // table if it doesn't need to, so we take this into account here.
         //
         if (index == 0)
         {
            Rate standardRate = resource.getStandardRate();
            Rate overtimeRate = resource.getOvertimeRate();
            Number costPerUse = resource.getCostPerUse();
            CostRateTableEntry entry = new CostRateTableEntry(standardRate, standardRate.getUnits(), overtimeRate, overtimeRate.getUnits(), costPerUse, CostRateTableEntry.DEFAULT_ENTRY.getEndDate());
            result.add(entry);
         }
         else
         {
            result.add(CostRateTableEntry.DEFAULT_ENTRY);
         }
      }

      resource.setCostRateTable(index, result);
   }
652990
private boolean costRateTableWriteRequired(CostRateTableEntry entry, Date from)
   {
      boolean fromDate = (DateHelper.compare(from, DateHelper.FIRST_DATE) > 0);
      boolean toDate = (DateHelper.compare(entry.getEndDate(), DateHelper.LAST_DATE) > 0);
      boolean costPerUse = (NumberHelper.getDouble(entry.getCostPerUse()) != 0);
      boolean overtimeRate = (entry.getOvertimeRate() != null && entry.getOvertimeRate().getAmount() != 0);
      boolean standardRate = (entry.getStandardRate() != null && entry.getStandardRate().getAmount() != 0);
      return (fromDate || toDate || costPerUse || overtimeRate || standardRate);
   }
771767
private void mergeSameDay(ProjectCalendar calendar, LinkedList<TimephasedWork> list)
   {
      LinkedList<TimephasedWork> result = new LinkedList<TimephasedWork>();

      TimephasedWork previousAssignment = null;
      for (TimephasedWork assignment : list)
      {
         if (previousAssignment == null)
         {
            assignment.setAmountPerDay(assignment.getTotalAmount());
            result.add(assignment);
         }
         else
         {
            Date previousAssignmentStart = previousAssignment.getStart();
            Date previousAssignmentStartDay = DateHelper.getDayStartDate(previousAssignmentStart);
            Date assignmentStart = assignment.getStart();
            Date assignmentStartDay = DateHelper.getDayStartDate(assignmentStart);

            if (previousAssignmentStartDay.getTime() == assignmentStartDay.getTime())
            {
               Duration previousAssignmentWork = previousAssignment.getTotalAmount();
               Duration assignmentWork = assignment.getTotalAmount();

               if (previousAssignmentWork.getDuration() != 0 && assignmentWork.getDuration() == 0)
               {
                  continue;
               }

               result.removeLast();

               if (previousAssignmentWork.getDuration() != 0 && assignmentWork.getDuration() != 0)
               {
                  double work = previousAssignment.getTotalAmount().getDuration();
                  work += assignment.getTotalAmount().getDuration();
                  Duration totalWork = Duration.getInstance(work, TimeUnit.MINUTES);

                  TimephasedWork merged = new TimephasedWork();
                  merged.setStart(previousAssignment.getStart());
                  merged.setFinish(assignment.getFinish());
                  merged.setTotalAmount(totalWork);
                  assignment = merged;
               }
               else
               {
                  if (assignmentWork.getDuration() == 0)
                  {
                     assignment = previousAssignment;
                  }
               }
            }

            assignment.setAmountPerDay(assignment.getTotalAmount());
            result.add(assignment);
         }

         Duration calendarWork = calendar.getWork(assignment.getStart(), assignment.getFinish(), TimeUnit.MINUTES);
         Duration assignmentWork = assignment.getTotalAmount();
         if (calendarWork.getDuration() == 0 && assignmentWork.getDuration() == 0)
         {
            result.removeLast();
         }
         else
         {
            previousAssignment = assignment;
         }
      }

      list.clear();
      list.addAll(result);
   }
644423
public TimedPhase getNextTimedPhase(final DefaultSubscriptionBase subscription, final DateTime effectiveDate, final Catalog catalog, final InternalTenantContext context) {

        try {
            final SubscriptionBaseTransition pendingOrLastPlanTransition;
            if (subscription.getState() == EntitlementState.PENDING) {
                pendingOrLastPlanTransition = subscription.getPendingTransition();
            } else {
                pendingOrLastPlanTransition = subscription.getLastTransitionForCurrentPlan();
            }

            switch (pendingOrLastPlanTransition.getTransitionType()) {
                // If we never had any Plan change, borrow the logic for createPlan alignment
                case CREATE:
                case TRANSFER:
                    final List<TimedPhase> timedPhases = getTimedPhaseOnCreate(subscription.getAlignStartDate(),
                                                                               subscription.getBundleStartDate(),
                                                                               pendingOrLastPlanTransition.getNextPlan(),
                                                                               pendingOrLastPlanTransition.getNextPhase().getPhaseType(),
                                                                               catalog,
                                                                               // Use the catalog version at subscription creation time: this allows
                                                                               // for PHASE events and uncancel operations for plans/products/pricelists already retired
                                                                               // This is not 100% correct in a scenario where the catalog was updated and the alignment rules changed since
                                                                               // See https://github.com/killbill/killbill/issues/784
                                                                               subscription.getAlignStartDate(),
                                                                               context);
                    return getTimedPhase(timedPhases, effectiveDate, WhichPhase.NEXT);
                case CHANGE:
                    return getTimedPhaseOnChange(subscription.getAlignStartDate(),
                                                 subscription.getBundleStartDate(),
                                                 pendingOrLastPlanTransition.getPreviousPhase(),
                                                 pendingOrLastPlanTransition.getPreviousPlan(),
                                                 pendingOrLastPlanTransition.getNextPlan(),
                                                 effectiveDate,
                                                 // Same remark as above
                                                 subscription.getAlignStartDate(),
                                                 pendingOrLastPlanTransition.getEffectiveTransitionTime(),
                                                 subscription.getAllTransitions().get(0).getNextPhase().getPhaseType(),
                                                 null,
                                                 WhichPhase.NEXT,
                                                 catalog,
                                                 context);
                default:
                    throw new SubscriptionBaseError(String.format("Unexpected initial transition %s for current plan %s on subscription %s",
                                                                  pendingOrLastPlanTransition.getTransitionType(), subscription.getCurrentPlan(), subscription.getId()));
            }
        } catch (Exception /* SubscriptionBaseApiException, CatalogApiException */ e) {
            throw new SubscriptionBaseError(String.format("Could not compute next phase change for subscription %s", subscription.getId()), e);
        }
    }
102828
protected void mergeSameWork(LinkedList<TimephasedWork> list)
   {
      LinkedList<TimephasedWork> result = new LinkedList<TimephasedWork>();

      TimephasedWork previousAssignment = null;
      for (TimephasedWork assignment : list)
      {
         if (previousAssignment == null)
         {
            assignment.setAmountPerDay(assignment.getTotalAmount());
            result.add(assignment);
         }
         else
         {
            Duration previousAssignmentWork = previousAssignment.getAmountPerDay();
            Duration assignmentWork = assignment.getTotalAmount();

            if (NumberHelper.equals(previousAssignmentWork.getDuration(), assignmentWork.getDuration(), 0.01))
            {
               Date assignmentStart = previousAssignment.getStart();
               Date assignmentFinish = assignment.getFinish();
               double total = previousAssignment.getTotalAmount().getDuration();
               total += assignmentWork.getDuration();
               Duration totalWork = Duration.getInstance(total, TimeUnit.MINUTES);

               TimephasedWork merged = new TimephasedWork();
               merged.setStart(assignmentStart);
               merged.setFinish(assignmentFinish);
               merged.setAmountPerDay(assignmentWork);
               merged.setTotalAmount(totalWork);

               result.removeLast();
               assignment = merged;
            }
            else
            {
               assignment.setAmountPerDay(assignment.getTotalAmount());
            }
            result.add(assignment);
         }

         previousAssignment = assignment;
      }

      list.clear();
      list.addAll(result);
   }
12968
public void processSplitData(Task task, List<TimephasedWork> timephasedComplete, List<TimephasedWork> timephasedPlanned)
   {
      Date splitsComplete = null;
      TimephasedWork lastComplete = null;
      TimephasedWork firstPlanned = null;
      if (!timephasedComplete.isEmpty())
      {
         lastComplete = timephasedComplete.get(timephasedComplete.size() - 1);
         splitsComplete = lastComplete.getFinish();
      }

      if (!timephasedPlanned.isEmpty())
      {
         firstPlanned = timephasedPlanned.get(0);
      }

      LinkedList<DateRange> splits = new LinkedList<DateRange>();
      TimephasedWork lastAssignment = null;
      DateRange lastRange = null;
      for (TimephasedWork assignment : timephasedComplete)
      {
         if (lastAssignment != null && lastRange != null && lastAssignment.getTotalAmount().getDuration() != 0 && assignment.getTotalAmount().getDuration() != 0)
         {
            splits.removeLast();
            lastRange = new DateRange(lastRange.getStart(), assignment.getFinish());
         }
         else
         {
            lastRange = new DateRange(assignment.getStart(), assignment.getFinish());
         }
         splits.add(lastRange);
         lastAssignment = assignment;
      }

      //
      // We may not have a split, we may just have a partially
      // complete split.
      //
      Date splitStart = null;
      if (lastComplete != null && firstPlanned != null && lastComplete.getTotalAmount().getDuration() != 0 && firstPlanned.getTotalAmount().getDuration() != 0)
      {
         lastRange = splits.removeLast();
         splitStart = lastRange.getStart();
      }

      lastAssignment = null;
      lastRange = null;
      for (TimephasedWork assignment : timephasedPlanned)
      {
         if (splitStart == null)
         {
            if (lastAssignment != null && lastRange != null && lastAssignment.getTotalAmount().getDuration() != 0 && assignment.getTotalAmount().getDuration() != 0)
            {
               splits.removeLast();
               lastRange = new DateRange(lastRange.getStart(), assignment.getFinish());
            }
            else
            {
               lastRange = new DateRange(assignment.getStart(), assignment.getFinish());
            }
         }
         else
         {
            lastRange = new DateRange(splitStart, assignment.getFinish());
         }
         splits.add(lastRange);
         splitStart = null;
         lastAssignment = assignment;
      }

      //
      // We must have a minimum of 3 entries for this to be a valid split task
      //
      if (splits.size() > 2)
      {
         task.getSplits().addAll(splits);
         task.setSplitCompleteDuration(splitsComplete);
      }
      else
      {
         task.setSplits(null);
         task.setSplitCompleteDuration(null);
      }
   }
717942
@Override public List<TimephasedWork> getData()
   {
      if (m_raw)
      {
         m_normaliser.normalise(m_calendar, m_data);
         m_raw = false;
      }
      return m_data;
   }
983892
private Map<Integer, List<Row>> createTimeEntryMap(List<Row> rows) throws ParseException
   {
      Map<Integer, List<Row>> map = new HashMap<Integer, List<Row>>();
      for (Row row : rows)
      {
         Integer workPatternID = row.getInteger("ID");
         String shifts = row.getString("SHIFTS");
         map.put(workPatternID, createTimeEntryRowList(shifts));
      }
      return map;
   }
26085
public <T> T getWorkContext(Class<T> workContextClass)
   {
      T instance = null;

      if (workContexts != null && workContexts.containsKey(workContextClass))
      {
         instance = workContextClass.cast(workContexts.get(workContextClass));
      }

      return instance;
   }
514861
public List<TaskAction> filterStandardActions(List<TaskAction> standardTaskActions)
    throws ServiceException, DataAccessException {

        CodeTimer timer = new CodeTimer("TaskManager.filterStandardTaskActions()", true);

        List<TaskAction> filteredTaskActions = standardTaskActions;
        try {
            ActivityInstance activityInstance = getActivityInstance(true);

            if (activityInstance != null) {
                Long processInstanceId = activityInstance.getProcessInstanceId();
                ProcessInstance processInstance = ServiceLocator.getWorkflowServices().getProcess(processInstanceId);

                if (processInstance.isEmbedded()) {
                    // remove RETRY since no default behavior is defined for inline tasks
                    TaskAction retryAction = null;
                    for (TaskAction taskAction : standardTaskActions) {
                        if (taskAction.getTaskActionName().equalsIgnoreCase("Retry"))
                            retryAction = taskAction;
                        }
                    if (retryAction != null)
                        standardTaskActions.remove(retryAction);
                }

                Process processVO = null;
                if (processInstance.getProcessInstDefId() > 0L)
                    processVO = ProcessCache.getProcessInstanceDefiniton(processInstance.getProcessId(), processInstance.getProcessInstDefId());
                if (processVO == null)
                    processVO = ProcessCache.getProcess(processInstance.getProcessId());
                if (processInstance.isEmbedded())
                    processVO = processVO.getSubProcessVO(new Long(processInstance.getComment()));
                List<Transition> outgoingWorkTransVOs = processVO.getAllTransitions(activityInstance.getActivityId());
                boolean foundNullResultCode = false;
                for (Transition workTransVO : outgoingWorkTransVOs) {
                    Integer eventType = workTransVO.getEventType();
                    if ((eventType.equals(EventType.FINISH) || eventType.equals(EventType.RESUME))
                        && workTransVO.getCompletionCode() == null) {
                      foundNullResultCode = true;
                      break;
                    }
                }
                if (!foundNullResultCode) {
                    TaskAction cancelAction = null;
                    TaskAction completeAction = null;
                    for (TaskAction taskAction : standardTaskActions) {
                        if (taskAction.getTaskActionName().equalsIgnoreCase("Cancel"))
                            cancelAction = taskAction;
                        if (taskAction.getTaskActionName().equalsIgnoreCase("Complete"))
                            completeAction = taskAction;
                    }
                    if (cancelAction != null)
                        standardTaskActions.remove(cancelAction);
                    if (completeAction != null)
                      standardTaskActions.remove(completeAction);
                }
            }
        }
        catch (Exception ex) {
            logger.severeException(ex.getMessage(), ex);
        }
        timer.stopAndLogTiming("");
        return filteredTaskActions;
    }