blob: 16ebd181a204045302f3b307dc4bb5d671a90197 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.ofbiz.manufacturing.techdata;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.apache.ofbiz.base.util.Debug;
import org.apache.ofbiz.base.util.UtilDateTime;
import org.apache.ofbiz.base.util.UtilMisc;
import org.apache.ofbiz.base.util.UtilProperties;
import org.apache.ofbiz.base.util.UtilValidate;
import org.apache.ofbiz.entity.Delegator;
import org.apache.ofbiz.entity.GenericEntityException;
import org.apache.ofbiz.entity.GenericValue;
import org.apache.ofbiz.entity.condition.EntityCondition;
import org.apache.ofbiz.entity.condition.EntityExpr;
import org.apache.ofbiz.entity.condition.EntityOperator;
import org.apache.ofbiz.entity.util.EntityQuery;
import org.apache.ofbiz.entity.util.EntityUtil;
import org.apache.ofbiz.service.DispatchContext;
import org.apache.ofbiz.service.ServiceUtil;
import com.ibm.icu.util.Calendar;
/**
* TechDataServices - TechData related Services
*
*/
public class TechDataServices {
public static final String module = TechDataServices.class.getName();
public static final String resource = "ManufacturingUiLabels";
/**
*
* Used to retrieve some RoutingTasks (WorkEffort) selected by Name or MachineGroup ordered by Name
*
* @param ctx the dispatch context
* @param context a map containing workEffortName (routingTaskName) and fixedAssetId (MachineGroup or ANY)
* @return result a map containing lookupResult (list of RoutingTask <=> workEffortId with currentStatusId = "ROU_ACTIVE" and workEffortTypeId = "ROU_TASK"
*/
public static Map<String, Object> lookupRoutingTask(DispatchContext ctx, Map<String, ? extends Object> context) {
Delegator delegator = ctx.getDelegator();
Map<String, Object> result = new HashMap<String, Object>();
Locale locale = (Locale) context.get("locale");
String workEffortName = (String) context.get("workEffortName");
String fixedAssetId = (String) context.get("fixedAssetId");
List<GenericValue> listRoutingTask = null;
List<EntityExpr> constraints = new LinkedList<EntityExpr>();
if (UtilValidate.isNotEmpty(workEffortName)) {
constraints.add(EntityCondition.makeCondition("workEffortName", EntityOperator.GREATER_THAN_EQUAL_TO, workEffortName));
}
if (UtilValidate.isNotEmpty(fixedAssetId) && ! "ANY".equals(fixedAssetId)) {
constraints.add(EntityCondition.makeCondition("fixedAssetId", EntityOperator.EQUALS, fixedAssetId));
}
constraints.add(EntityCondition.makeCondition("currentStatusId", EntityOperator.EQUALS, "ROU_ACTIVE"));
constraints.add(EntityCondition.makeCondition("workEffortTypeId", EntityOperator.EQUALS, "ROU_TASK"));
try {
listRoutingTask = EntityQuery.use(delegator).from("WorkEffort")
.where(constraints)
.orderBy("workEffortName")
.queryList();
} catch (GenericEntityException e) {
Debug.logWarning(e, module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource, "ManufacturingTechDataWorkEffortNotExist", UtilMisc.toMap("errorString", e.toString()), locale));
}
if (listRoutingTask == null) {
listRoutingTask = new LinkedList<GenericValue>();
}
if (listRoutingTask.size() == 0) {
//FIXME is it correct ?
// listRoutingTask.add(UtilMisc.toMap("label","no Match","value","NO_MATCH"));
}
result.put("lookupResult", listRoutingTask);
return result;
}
/**
*
* Used to check if there is not two routing task with the same SeqId valid at the same period
*
* @param ctx The DispatchContext that this service is operating in.
* @param context a map containing workEffortIdFrom (routing) and SeqId, fromDate thruDate
* @return result a map containing sequenceNumNotOk which is equal to "Y" if it's not Ok
*/
public static Map<String, Object> checkRoutingTaskAssoc(DispatchContext ctx, Map<String, ? extends Object> context) {
Delegator delegator = ctx.getDelegator();
Map<String, Object> result = new HashMap<String, Object>();
String sequenceNumNotOk = "N";
Locale locale = (Locale) context.get("locale");
String workEffortIdFrom = (String) context.get("workEffortIdFrom");
String workEffortIdTo = (String) context.get("workEffortIdTo");
String workEffortAssocTypeId = (String) context.get("workEffortAssocTypeId");
Long sequenceNum = (Long) context.get("sequenceNum");
Timestamp fromDate = (Timestamp) context.get("fromDate");
Timestamp thruDate = (Timestamp) context.get("thruDate");
String create = (String) context.get("create");
boolean createProcess = (create !=null && create.equals("Y")) ? true : false;
List<GenericValue> listRoutingTaskAssoc = null;
try {
listRoutingTaskAssoc = EntityQuery.use(delegator).from("WorkEffortAssoc")
.where("workEffortIdFrom", workEffortIdFrom,"sequenceNum",sequenceNum)
.orderBy("fromDate")
.queryList();
} catch (GenericEntityException e) {
Debug.logWarning(e, module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource, "ManufacturingTechDataWorkEffortAssocNotExist", UtilMisc.toMap("errorString", e.toString()), locale));
}
if (listRoutingTaskAssoc != null) {
for (GenericValue routingTaskAssoc : listRoutingTaskAssoc) {
if (! workEffortIdFrom.equals(routingTaskAssoc.getString("workEffortIdFrom")) ||
! workEffortIdTo.equals(routingTaskAssoc.getString("workEffortIdTo")) ||
! workEffortAssocTypeId.equals(routingTaskAssoc.getString("workEffortAssocTypeId")) ||
! sequenceNum.equals(routingTaskAssoc.getLong("sequenceNum"))
) {
if (routingTaskAssoc.getTimestamp("thruDate") == null && routingTaskAssoc.getTimestamp("fromDate") == null) sequenceNumNotOk = "Y";
else if (routingTaskAssoc.getTimestamp("thruDate") == null) {
if (thruDate == null) sequenceNumNotOk = "Y";
else if (thruDate.after(routingTaskAssoc.getTimestamp("fromDate"))) sequenceNumNotOk = "Y";
}
else if (routingTaskAssoc.getTimestamp("fromDate") == null) {
if (fromDate == null) sequenceNumNotOk = "Y";
else if (fromDate.before(routingTaskAssoc.getTimestamp("thruDate"))) sequenceNumNotOk = "Y";
}
else if (fromDate == null && thruDate == null) sequenceNumNotOk = "Y";
else if (thruDate == null) {
if (fromDate.before(routingTaskAssoc.getTimestamp("thruDate"))) sequenceNumNotOk = "Y";
}
else if (fromDate == null) {
if (thruDate.after(routingTaskAssoc.getTimestamp("fromDate"))) sequenceNumNotOk = "Y";
}
else if (routingTaskAssoc.getTimestamp("fromDate").before(thruDate) && fromDate.before(routingTaskAssoc.getTimestamp("thruDate"))) sequenceNumNotOk = "Y";
} else if (createProcess) sequenceNumNotOk = "Y";
}
}
result.put("sequenceNumNotOk", sequenceNumNotOk);
return result;
}
/**
* Used to get the techDataCalendar for a routingTask, if there is a entity exception
* or routingTask associated with no MachineGroup the DEFAULT TechDataCalendar is return.
*
* @param routingTask the routingTask for which we are looking for
* @return the techDataCalendar associated
*/
public static GenericValue getTechDataCalendar(GenericValue routingTask) {
GenericValue machineGroup = null, techDataCalendar = null;
try {
machineGroup = routingTask.getRelatedOne("FixedAsset", true);
} catch (GenericEntityException e) {
Debug.logError("Pb reading FixedAsset associated with routingTask"+e.getMessage(), module);
}
if (machineGroup != null) {
if (machineGroup.getString("calendarId") != null) {
try {
techDataCalendar = machineGroup.getRelatedOne("TechDataCalendar", true);
} catch (GenericEntityException e) {
Debug.logError("Pb reading TechDataCalendar associated with machineGroup"+e.getMessage(), module);
}
} else {
try {
List<GenericValue> machines = machineGroup.getRelated("ChildFixedAsset", null, null, true);
if (machines != null && machines.size()>0) {
GenericValue machine = EntityUtil.getFirst(machines);
techDataCalendar = machine.getRelatedOne("TechDataCalendar", true);
}
} catch (GenericEntityException e) {
Debug.logError("Pb reading machine child from machineGroup"+e.getMessage(), module);
}
}
}
if (techDataCalendar == null) {
try {
Delegator delegator = routingTask.getDelegator();
techDataCalendar = EntityQuery.use(delegator).from("TechDataCalendar").where("calendarId", "DEFAULT").queryOne();
} catch (GenericEntityException e) {
Debug.logError("Pb reading TechDataCalendar DEFAULT"+e.getMessage(), module);
}
}
return techDataCalendar;
}
/** Used to find the fisrt day in the TechDataCalendarWeek where capacity != 0, beginning at dayStart, dayStart included.
*
* @param techDataCalendarWeek The TechDataCalendarWeek cover
* @param dayStart
* @return a map with the capacity (Double) available and moveDay (int): the number of day it's necessary to move to have capacity available
*/
public static Map<String, Object> dayStartCapacityAvailable(GenericValue techDataCalendarWeek, int dayStart) {
Map<String, Object> result = new HashMap<String, Object>();
int moveDay = 0;
Double capacity = null;
Time startTime = null;
while (capacity == null || capacity.doubleValue()==0) {
switch (dayStart) {
case Calendar.MONDAY:
capacity = techDataCalendarWeek.getDouble("mondayCapacity");
startTime = techDataCalendarWeek.getTime("mondayStartTime");
break;
case Calendar.TUESDAY:
capacity = techDataCalendarWeek.getDouble("tuesdayCapacity");
startTime = techDataCalendarWeek.getTime("tuesdayStartTime");
break;
case Calendar.WEDNESDAY:
capacity = techDataCalendarWeek.getDouble("wednesdayCapacity");
startTime = techDataCalendarWeek.getTime("wednesdayStartTime");
break;
case Calendar.THURSDAY:
capacity = techDataCalendarWeek.getDouble("thursdayCapacity");
startTime = techDataCalendarWeek.getTime("thursdayStartTime");
break;
case Calendar.FRIDAY:
capacity = techDataCalendarWeek.getDouble("fridayCapacity");
startTime = techDataCalendarWeek.getTime("fridayStartTime");
break;
case Calendar.SATURDAY:
capacity = techDataCalendarWeek.getDouble("saturdayCapacity");
startTime = techDataCalendarWeek.getTime("saturdayStartTime");
break;
case Calendar.SUNDAY:
capacity = techDataCalendarWeek.getDouble("sundayCapacity");
startTime = techDataCalendarWeek.getTime("sundayStartTime");
break;
}
if (capacity == null || capacity.doubleValue() == 0) {
moveDay +=1;
dayStart = (dayStart==7) ? 1 : dayStart +1;
}
}
result.put("capacity",capacity);
result.put("startTime",startTime);
result.put("moveDay",Integer.valueOf(moveDay));
return result;
}
/** Used to to request the remain capacity available for dateFrom in a TechDataCalenda,
* If the dateFrom (param in) is not in an available TechDataCalendar period, the return value is zero.
*
* @param techDataCalendar The TechDataCalendar cover
* @param dateFrom the date
* @return long capacityRemaining
*/
public static long capacityRemaining(GenericValue techDataCalendar, Timestamp dateFrom) {
GenericValue techDataCalendarWeek = null;
// TODO read TechDataCalendarExcWeek to manage execption week (maybe it's needed to refactor the entity definition
try {
techDataCalendarWeek = techDataCalendar.getRelatedOne("TechDataCalendarWeek", true);
} catch (GenericEntityException e) {
Debug.logError("Pb reading Calendar Week associated with calendar"+e.getMessage(), module);
return 0;
}
// TODO read TechDataCalendarExcDay to manage execption day
Calendar cDateTrav = Calendar.getInstance();
cDateTrav.setTime(dateFrom);
Map<String, Object> position = dayStartCapacityAvailable(techDataCalendarWeek, cDateTrav.get(Calendar.DAY_OF_WEEK));
int moveDay = ((Integer) position.get("moveDay")).intValue();
if (moveDay != 0) return 0;
Time startTime = (Time) position.get("startTime");
Double capacity = (Double) position.get("capacity");
Timestamp startAvailablePeriod = new Timestamp(UtilDateTime.getDayStart(dateFrom).getTime() + startTime.getTime() + cDateTrav.get(Calendar.ZONE_OFFSET) + cDateTrav.get(Calendar.DST_OFFSET));
if (dateFrom.before(startAvailablePeriod)) return 0;
Timestamp endAvailablePeriod = new Timestamp(startAvailablePeriod.getTime()+capacity.longValue());
if (dateFrom.after(endAvailablePeriod)) return 0;
return endAvailablePeriod.getTime() - dateFrom.getTime();
}
/** Used to move in a TechDataCalenda, produce the Timestamp for the begining of the next day available and its associated capacity.
* If the dateFrom (param in) is not in an available TechDataCalendar period, the return value is the next day available
*
* @param techDataCalendar The TechDataCalendar cover
* @param dateFrom the date
* @return a map with Timestamp dateTo, Double nextCapacity
*/
public static Map<String, Object> startNextDay(GenericValue techDataCalendar, Timestamp dateFrom) {
Map<String, Object> result = new HashMap<String, Object>();
Timestamp dateTo = null;
GenericValue techDataCalendarWeek = null;
// TODO read TechDataCalendarExcWeek to manage execption week (maybe it's needed to refactor the entity definition
try {
techDataCalendarWeek = techDataCalendar.getRelatedOne("TechDataCalendarWeek", true);
} catch (GenericEntityException e) {
Debug.logError("Pb reading Calendar Week associated with calendar"+e.getMessage(), module);
return ServiceUtil.returnError("Pb reading Calendar Week associated with calendar");
}
// TODO read TechDataCalendarExcDay to manage execption day
Calendar cDateTrav = Calendar.getInstance();
cDateTrav.setTime(dateFrom);
Map<String, Object> position = dayStartCapacityAvailable(techDataCalendarWeek, cDateTrav.get(Calendar.DAY_OF_WEEK));
Time startTime = (Time) position.get("startTime");
int moveDay = ((Integer) position.get("moveDay")).intValue();
dateTo = (moveDay == 0) ? dateFrom : UtilDateTime.getDayStart(dateFrom,moveDay);
Timestamp startAvailablePeriod = new Timestamp(UtilDateTime.getDayStart(dateTo).getTime() + startTime.getTime() + cDateTrav.get(Calendar.ZONE_OFFSET) + cDateTrav.get(Calendar.DST_OFFSET));
if (dateTo.before(startAvailablePeriod)) {
dateTo = startAvailablePeriod;
}
else {
dateTo = UtilDateTime.getNextDayStart(dateTo);
cDateTrav.setTime(dateTo);
position = dayStartCapacityAvailable(techDataCalendarWeek, cDateTrav.get(Calendar.DAY_OF_WEEK));
startTime = (Time) position.get("startTime");
moveDay = ((Integer) position.get("moveDay")).intValue();
if (moveDay != 0) dateTo = UtilDateTime.getDayStart(dateTo,moveDay);
dateTo.setTime(dateTo.getTime() + startTime.getTime() + cDateTrav.get(Calendar.ZONE_OFFSET) + cDateTrav.get(Calendar.DST_OFFSET));
}
result.put("dateTo",dateTo);
result.put("nextCapacity",position.get("capacity"));
return result;
}
/** Used to move forward in a TechDataCalenda, start from the dateFrom and move forward only on available period.
* If the dateFrom (param in) is not a available TechDataCalendar period, the startDate is the begining of the next day available
*
* @param techDataCalendar The TechDataCalendar cover
* @param dateFrom the start date
* @param amount the amount of millisecond to move forward
* @return the dateTo
*/
public static Timestamp addForward(GenericValue techDataCalendar, Timestamp dateFrom, long amount) {
Timestamp dateTo = (Timestamp) dateFrom.clone();
long nextCapacity = capacityRemaining(techDataCalendar, dateFrom);
if (amount <= nextCapacity) {
dateTo.setTime(dateTo.getTime()+amount);
amount = 0;
} else amount -= nextCapacity;
Map<String, Object> result = new HashMap<String, Object>();
while (amount > 0) {
result = startNextDay(techDataCalendar, dateTo);
dateTo = (Timestamp) result.get("dateTo");
nextCapacity = ((Double) result.get("nextCapacity")).longValue();
if (amount <= nextCapacity) {
dateTo.setTime(dateTo.getTime()+amount);
amount = 0;
} else amount -= nextCapacity;
}
return dateTo;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/** Used to find the last day in the TechDataCalendarWeek where capacity != 0, ending at dayEnd, dayEnd included.
*
* @param techDataCalendarWeek The TechDataCalendarWeek cover
* @param dayEnd
* @return a map with the capacity (Double) available, the startTime and moveDay (int): the number of day it's necessary to move to have capacity available
*/
public static Map<String, Object> dayEndCapacityAvailable(GenericValue techDataCalendarWeek, int dayEnd) {
Map<String, Object> result = new HashMap<String, Object>();
int moveDay = 0;
Double capacity = null;
Time startTime = null;
while (capacity == null || capacity.doubleValue() == 0) {
switch (dayEnd) {
case Calendar.MONDAY:
capacity = techDataCalendarWeek.getDouble("mondayCapacity");
startTime = techDataCalendarWeek.getTime("mondayStartTime");
break;
case Calendar.TUESDAY:
capacity = techDataCalendarWeek.getDouble("tuesdayCapacity");
startTime = techDataCalendarWeek.getTime("tuesdayStartTime");
break;
case Calendar.WEDNESDAY:
capacity = techDataCalendarWeek.getDouble("wednesdayCapacity");
startTime = techDataCalendarWeek.getTime("wednesdayStartTime");
break;
case Calendar.THURSDAY:
capacity = techDataCalendarWeek.getDouble("thursdayCapacity");
startTime = techDataCalendarWeek.getTime("thursdayStartTime");
break;
case Calendar.FRIDAY:
capacity = techDataCalendarWeek.getDouble("fridayCapacity");
startTime = techDataCalendarWeek.getTime("fridayStartTime");
break;
case Calendar.SATURDAY:
capacity = techDataCalendarWeek.getDouble("saturdayCapacity");
startTime = techDataCalendarWeek.getTime("saturdayStartTime");
break;
case Calendar.SUNDAY:
capacity = techDataCalendarWeek.getDouble("sundayCapacity");
startTime = techDataCalendarWeek.getTime("sundayStartTime");
break;
}
if (capacity == null || capacity.doubleValue() == 0) {
moveDay -=1;
dayEnd = (dayEnd==1) ? 7 : dayEnd - 1;
}
}
result.put("capacity",capacity);
result.put("startTime",startTime);
result.put("moveDay",Integer.valueOf(moveDay));
return result;
}
/** Used to request the remaining capacity available for dateFrom in a TechDataCalenda,
* If the dateFrom (param in) is not in an available TechDataCalendar period, the return value is zero.
*
* @param techDataCalendar The TechDataCalendar cover
* @param dateFrom the date
* @return long capacityRemaining
*/
public static long capacityRemainingBackward(GenericValue techDataCalendar, Timestamp dateFrom) {
GenericValue techDataCalendarWeek = null;
// TODO read TechDataCalendarExcWeek to manage exception week (maybe it's needed to refactor the entity definition
try {
techDataCalendarWeek = techDataCalendar.getRelatedOne("TechDataCalendarWeek", true);
} catch (GenericEntityException e) {
Debug.logError("Pb reading Calendar Week associated with calendar"+e.getMessage(), module);
return 0;
}
// TODO read TechDataCalendarExcDay to manage execption day
Calendar cDateTrav = Calendar.getInstance();
cDateTrav.setTime(dateFrom);
Map<String, Object> position = dayEndCapacityAvailable(techDataCalendarWeek, cDateTrav.get(Calendar.DAY_OF_WEEK));
int moveDay = ((Integer) position.get("moveDay")).intValue();
if (moveDay != 0) return 0;
Time startTime = (Time) position.get("startTime");
Double capacity = (Double) position.get("capacity");
Timestamp startAvailablePeriod = new Timestamp(UtilDateTime.getDayStart(dateFrom).getTime() + startTime.getTime() + cDateTrav.get(Calendar.ZONE_OFFSET) + cDateTrav.get(Calendar.DST_OFFSET));
if (dateFrom.before(startAvailablePeriod)) return 0;
Timestamp endAvailablePeriod = new Timestamp(startAvailablePeriod.getTime()+capacity.longValue());
if (dateFrom.after(endAvailablePeriod)) return 0;
return dateFrom.getTime() - startAvailablePeriod.getTime();
}
/** Used to move in a TechDataCalenda, produce the Timestamp for the end of the previous day available and its associated capacity.
* If the dateFrom (param in) is not in an available TechDataCalendar period, the return value is the previous day available
*
* @param techDataCalendar The TechDataCalendar cover
* @param dateFrom the date
* @return a map with Timestamp dateTo, Double previousCapacity
*/
public static Map<String, Object> endPreviousDay(GenericValue techDataCalendar, Timestamp dateFrom) {
Map<String, Object> result = new HashMap<String, Object>();
Timestamp dateTo = null;
GenericValue techDataCalendarWeek = null;
// TODO read TechDataCalendarExcWeek to manage exception week (maybe it's needed to refactor the entity definition
try {
techDataCalendarWeek = techDataCalendar.getRelatedOne("TechDataCalendarWeek", true);
} catch (GenericEntityException e) {
Debug.logError("Pb reading Calendar Week associated with calendar"+e.getMessage(), module);
return ServiceUtil.returnError("Pb reading Calendar Week associated with calendar");
}
// TODO read TechDataCalendarExcDay to manage execption day
Calendar cDateTrav = Calendar.getInstance();
cDateTrav.setTime(dateFrom);
Map<String, Object> position = dayEndCapacityAvailable(techDataCalendarWeek, cDateTrav.get(Calendar.DAY_OF_WEEK));
Time startTime = (Time) position.get("startTime");
int moveDay = ((Integer) position.get("moveDay")).intValue();
Double capacity = (Double) position.get("capacity");
dateTo = (moveDay == 0) ? dateFrom : UtilDateTime.getDayEnd(dateFrom, Long.valueOf(moveDay));
Timestamp endAvailablePeriod = new Timestamp(UtilDateTime.getDayStart(dateTo).getTime() + startTime.getTime() + capacity.longValue() + cDateTrav.get(Calendar.ZONE_OFFSET) + cDateTrav.get(Calendar.DST_OFFSET));
if (dateTo.after(endAvailablePeriod)) {
dateTo = endAvailablePeriod;
}
else {
dateTo = UtilDateTime.getDayStart(dateTo, -1);
cDateTrav.setTime(dateTo);
position = dayEndCapacityAvailable(techDataCalendarWeek, cDateTrav.get(Calendar.DAY_OF_WEEK));
startTime = (Time) position.get("startTime");
moveDay = ((Integer) position.get("moveDay")).intValue();
capacity = (Double) position.get("capacity");
if (moveDay != 0) dateTo = UtilDateTime.getDayStart(dateTo,moveDay);
dateTo.setTime(dateTo.getTime() + startTime.getTime() + capacity.longValue() + cDateTrav.get(Calendar.ZONE_OFFSET) + cDateTrav.get(Calendar.DST_OFFSET));
}
result.put("dateTo",dateTo);
result.put("previousCapacity",position.get("capacity"));
return result;
}
/** Used to move backward in a TechDataCalendar, start from the dateFrom and move backward only on available period.
* If the dateFrom (param in) is not a available TechDataCalendar period, the startDate is the end of the previous day available
*
* @param techDataCalendar The TechDataCalendar cover
* @param dateFrom the start date
* @param amount the amount of millisecond to move backward
* @return the dateTo
*/
public static Timestamp addBackward(GenericValue techDataCalendar, Timestamp dateFrom, long amount) {
Timestamp dateTo = (Timestamp) dateFrom.clone();
long previousCapacity = capacityRemainingBackward(techDataCalendar, dateFrom);
if (amount <= previousCapacity) {
dateTo.setTime(dateTo.getTime()-amount);
amount = 0;
} else amount -= previousCapacity;
Map<String, Object> result = new HashMap<String, Object>();
while (amount > 0) {
result = endPreviousDay(techDataCalendar, dateTo);
dateTo = (Timestamp) result.get("dateTo");
previousCapacity = ((Double) result.get("previousCapacity")).longValue();
if (amount <= previousCapacity) {
dateTo.setTime(dateTo.getTime()-amount);
amount = 0;
} else amount -= previousCapacity;
}
return dateTo;
}
}