0001: /*
0002: The contents of this file are subject to the Common Public Attribution License
0003: Version 1.0 (the "License"); you may not use this file except in compliance with
0004: the License. You may obtain a copy of the License at
0005: http://www.projity.com/license . The License is based on the Mozilla Public
0006: License Version 1.1 but Sections 14 and 15 have been added to cover use of
0007: software over a computer network and provide for limited attribution for the
0008: Original Developer. In addition, Exhibit A has been modified to be consistent
0009: with Exhibit B.
0010:
0011: Software distributed under the License is distributed on an "AS IS" basis,
0012: WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the
0013: specific language governing rights and limitations under the License. The
0014: Original Code is OpenProj. The Original Developer is the Initial Developer and
0015: is Projity, Inc. All portions of the code written by Projity are Copyright (c)
0016: 2006, 2007. All Rights Reserved. Contributors Projity, Inc.
0017:
0018: Alternatively, the contents of this file may be used under the terms of the
0019: Projity End-User License Agreeement (the Projity License), in which case the
0020: provisions of the Projity License are applicable instead of those above. If you
0021: wish to allow use of your version of this file only under the terms of the
0022: Projity License and not to allow others to use your version of this file under
0023: the CPAL, indicate your decision by deleting the provisions above and replace
0024: them with the notice and other provisions required by the Projity License. If
0025: you do not delete the provisions above, a recipient may use your version of this
0026: file under either the CPAL or the Projity License.
0027:
0028: [NOTE: The text of this license may differ slightly from the text of the notices
0029: in Exhibits A and B of the license at http://www.projity.com/license. You should
0030: use the latest text at http://www.projity.com/license for your modifications.
0031: You may not remove this license text from the source files.]
0032:
0033: Attribution Information: Attribution Copyright Notice: Copyright � 2006, 2007
0034: Projity, Inc. Attribution Phrase (not exceeding 10 words): Powered by OpenProj,
0035: an open source solution from Projity. Attribution URL: http://www.projity.com
0036: Graphic Image as provided in the Covered Code as file: openproj_logo.png with
0037: alternatives listed on http://www.projity.com/logo
0038:
0039: Display of Attribution Information is required in Larger Works which are defined
0040: in the CPAL as a work which combines Covered Code or portions thereof with code
0041: not governed by the terms of the CPAL. However, in addition to the other notice
0042: obligations, all copies of the Covered Code in Executable and Source Code form
0043: distributed must, as a form of attribution of the original author, include on
0044: each user interface screen the "OpenProj" logo visible to all users. The
0045: OpenProj logo should be located horizontally aligned with the menu bar and left
0046: justified on the top left of the screen adjacent to the File menu. The logo
0047: must be at least 100 x 25 pixels. When users click on the "OpenProj" logo it
0048: must direct them back to http://www.projity.com.
0049: */
0050:
0051: package com.projity.pm.assignment;
0052:
0053: import java.io.IOException;
0054: import java.io.ObjectInputStream;
0055: import java.io.ObjectOutputStream;
0056: import java.util.Collection;
0057: import java.util.Comparator;
0058: import java.util.Date;
0059:
0060: import org.apache.commons.collections.Closure;
0061: import org.apache.commons.collections.Predicate;
0062:
0063: import com.projity.algorithm.CollectionIntervalGenerator;
0064: import com.projity.algorithm.DoubleValue;
0065: import com.projity.algorithm.InstantIntervalGenerator;
0066: import com.projity.algorithm.IntervalGeneratorSet;
0067: import com.projity.algorithm.Merge;
0068: import com.projity.algorithm.Query;
0069: import com.projity.algorithm.RangeIntervalGenerator;
0070: import com.projity.algorithm.ReverseQuery;
0071: import com.projity.algorithm.SelectFrom;
0072: import com.projity.algorithm.TimeIteratorGenerator;
0073: import com.projity.algorithm.buffer.CalculatedValues;
0074: import com.projity.association.Association;
0075: import com.projity.association.InvalidAssociationException;
0076: import com.projity.configuration.Configuration;
0077: import com.projity.datatype.CanSupplyRateUnit;
0078: import com.projity.datatype.Duration;
0079: import com.projity.datatype.DurationFormat;
0080: import com.projity.datatype.ImageLink;
0081: import com.projity.datatype.Rate;
0082: import com.projity.datatype.RateFormat;
0083: import com.projity.datatype.TimeUnit;
0084: import com.projity.document.Document;
0085: import com.projity.field.Field;
0086: import com.projity.field.FieldContext;
0087: import com.projity.functor.IntervalConsumer;
0088: import com.projity.functor.ObjectVisitor;
0089: import com.projity.options.AdvancedOption;
0090: import com.projity.pm.assignment.contour.AbstractContour;
0091: import com.projity.pm.assignment.contour.AbstractContourBucket;
0092: import com.projity.pm.assignment.contour.ContourBucketIntervalGenerator;
0093: import com.projity.pm.assignment.contour.ContourFactory;
0094: import com.projity.pm.assignment.contour.PersonalContour;
0095: import com.projity.pm.assignment.functor.AssignmentFieldClosureCollection;
0096: import com.projity.pm.assignment.functor.AssignmentFieldFunctor;
0097: import com.projity.pm.assignment.functor.CalculatedValuesFunctor;
0098: import com.projity.pm.assignment.functor.CostFunctor;
0099: import com.projity.pm.assignment.functor.DateAtValueFunctor;
0100: import com.projity.pm.assignment.functor.FixedCostFunctor;
0101: import com.projity.pm.assignment.functor.PercentAllocFunctor;
0102: import com.projity.pm.assignment.functor.PersonalContourMaker;
0103: import com.projity.pm.assignment.functor.PrintValueFunctor;
0104: import com.projity.pm.assignment.functor.ResourceAvailabilityFunctor;
0105: import com.projity.pm.assignment.functor.ValueAtInstant;
0106: import com.projity.pm.assignment.functor.WorkComparator;
0107: import com.projity.pm.assignment.functor.WorkFunctor;
0108: import com.projity.pm.assignment.functor.ZeroFunctor;
0109: import com.projity.pm.assignment.timesheet.AssignmentWorkflowState;
0110: import com.projity.pm.assignment.timesheet.TimesheetHelper;
0111: import com.projity.pm.assignment.timesheet.TimesheetStatus;
0112: import com.projity.pm.assignment.timesheet.UpdatesFromTimesheet;
0113: import com.projity.pm.calendar.WorkCalendar;
0114: import com.projity.pm.costing.Accrual;
0115: import com.projity.pm.costing.EarnedValueCalculator;
0116: import com.projity.pm.costing.EarnedValueFields;
0117: import com.projity.pm.costing.EarnedValueValues;
0118: import com.projity.pm.costing.HasCostRateIndex;
0119: import com.projity.pm.criticalpath.TaskSchedule;
0120: import com.projity.pm.key.HasKey;
0121: import com.projity.pm.key.HasKeyImpl;
0122: import com.projity.pm.resource.Resource;
0123: import com.projity.pm.resource.ResourceImpl;
0124: import com.projity.pm.scheduling.BarClosure;
0125: import com.projity.pm.scheduling.ConstraintType;
0126: import com.projity.pm.scheduling.Delayable;
0127: import com.projity.pm.scheduling.Schedule;
0128: import com.projity.pm.scheduling.ScheduleInterval;
0129: import com.projity.pm.scheduling.ScheduleUtil;
0130: import com.projity.pm.scheduling.SchedulingType;
0131: import com.projity.pm.snapshot.Snapshottable;
0132: import com.projity.pm.task.BelongsToDocument;
0133: import com.projity.pm.task.NormalTask;
0134: import com.projity.pm.task.Project;
0135: import com.projity.pm.task.Task;
0136: import com.projity.pm.time.HasStartAndEnd;
0137: import com.projity.pm.time.ImmutableInterval;
0138: import com.projity.pm.time.Interval;
0139: import com.projity.pm.time.MutableInterval;
0140: import com.projity.server.data.DataObject;
0141: import com.projity.strings.Messages;
0142: import com.projity.util.Alert;
0143: import com.projity.util.Environment;
0144:
0145: /**
0146: * Class representing resource assignments
0147: * @stereotype thing
0148: */
0149: public final class Assignment implements Schedule, Association,
0150: Allocation, Delayable, HasTimeDistributedData,
0151: TimeDistributedFields, EarnedValueValues, EarnedValueFields,
0152: AssignmentSpecificFields, HasKey, BelongsToDocument,
0153: HasRequestDemandType, UpdatesFromTimesheet, CanSupplyRateUnit,
0154: HasCostRateIndex, Cloneable, DataObject {
0155: static final long serialVersionUID = 56779404923241L;
0156: AssignmentDetail detail = null;
0157: private transient HasKeyImpl hasKey = new HasKeyImpl(true, this ); //local ids only for assignments
0158:
0159: private static Field unitsFieldInstance = null;
0160:
0161: private transient Date cachedStart = null;
0162: private transient Date cachedEnd = null;
0163: private transient int timesheetStatus = TimesheetStatus.NO_DATA;
0164: private transient long lastTimesheetUpdate = 0;
0165: private transient int workflowState = AssignmentWorkflowState.NEW;
0166: private transient boolean timesheetAssignment = false;
0167:
0168: public static Field getUnitsField() {
0169: if (unitsFieldInstance == null)
0170: unitsFieldInstance = Configuration
0171: .getFieldFromId("Field.assignmentUnits");
0172: return unitsFieldInstance;
0173: }
0174:
0175: private static Field rateFieldInstance = null;
0176:
0177: public static Field getRateField() {
0178: if (rateFieldInstance == null)
0179: rateFieldInstance = Configuration
0180: .getFieldFromId("Field.rate");
0181: return rateFieldInstance;
0182: }
0183:
0184: private static Field requestDemandTypeInstance = null;
0185:
0186: public static Field getRequestDemandTypeField() {
0187: if (requestDemandTypeInstance == null)
0188: requestDemandTypeInstance = Configuration
0189: .getFieldFromId("Field.requestDemandType");
0190: return requestDemandTypeInstance;
0191: }
0192:
0193: public static Assignment getInstance(Task task, Resource resource,
0194: double units, int requestDemandType) {
0195: return new Assignment(task, resource, units, requestDemandType);
0196: }
0197:
0198: public static Assignment getInstance(Task task, Resource resource,
0199: double units, long delay) {
0200: return new Assignment(task, resource, units, delay);
0201: }
0202:
0203: /**
0204: * Construct an assignment. The arguments are those that are presented in the assign resource dialog
0205: * @param task
0206: * @param resource
0207: * @param units
0208: * @param requestDemandType -Normally empty
0209: */
0210: private Assignment(Task task, Resource resource, double units,
0211: int requestDemandType) {
0212: detail = new AssignmentDetail(task, resource, units,
0213: requestDemandType, 0);
0214: }
0215:
0216: public Assignment(Assignment from) {
0217: detail = from.detail;
0218: }
0219:
0220: public Assignment(AssignmentDetail detail) {
0221: this .detail = detail;
0222: }
0223:
0224: public boolean isReadOnly() {
0225: return getTask().isReadOnly();
0226: }
0227:
0228: public boolean isExternal() {
0229: return getTask().isExternal();
0230: }
0231:
0232: public static Predicate instanceof Predicate() {
0233: return new Predicate() {
0234: public boolean evaluate(Object arg0) {
0235: return arg0 instanceof Assignment;
0236: }
0237: };
0238: }
0239:
0240: /**
0241: * Copy the properies from another assignment. This also includes the contour. However, it does NOT
0242: * include the resource or the units. This is called in the context of replacing an assignment.
0243: * @param from Assignment to copy from
0244: */
0245: public void usePropertiesOf(Assignment from) {
0246: boolean compatibleTypes = isLabor() == from.isLabor();
0247: double units = getUnits();
0248: Rate r = getRate();
0249: Resource resource = getResource();
0250: detail = (AssignmentDetail) from.detail.clone();
0251: detail.replaceResourceAndUnits(units, resource);
0252:
0253: if (!compatibleTypes) // don't want to set units if not compatible
0254: detail.setRate(r);
0255: }
0256:
0257: public void setStart(long start) {
0258: detail.setStart(start);
0259: }
0260:
0261: public int getRequestDemandType() {
0262: return detail.getRequestDemandType();
0263: }
0264:
0265: public void setRequestDemandType(int requestDemandType) {
0266: newDetail().setRequestDemandType(requestDemandType);
0267: }
0268:
0269: public AbstractContourBucket[] getContour(Object type) {
0270: return detail.getContour(type);
0271: }
0272:
0273: /**
0274: * Constructor adapted to mpx
0275: * @param task
0276: * @param resource
0277: * @param units
0278: * @param delay
0279: */
0280: private Assignment(Task task, Resource resource, double units,
0281: long delay) {
0282: detail = new AssignmentDetail(task, resource, units,
0283: RequestDemandType.NONE, delay);
0284: }
0285:
0286: /**
0287: * Accessor for units (value of work)
0288: * @return units
0289: */
0290: public final double getUnits() {
0291: return detail.getUnits();
0292: }
0293:
0294: public final double getLaborUnits() {
0295: if (isLabor())
0296: return getUnits();
0297: return 0.0D;
0298: }
0299:
0300: public void setOvertimeWork(long overtimeWork) {
0301: newDetail().setOvertimeWork(overtimeWork);
0302: }
0303:
0304: /**
0305: * For use in sharing with external systems such as salesforce. Uniquely identifies object
0306: * @return
0307: */
0308: public String toExternalId() {
0309: return getResourceId() + "." + getTaskId();
0310: }
0311:
0312: public String toString() {
0313: return getTask() + "/" + getResource(); //super.toString();
0314: // return "[start] " + new java.util.Date(getStart())
0315: // + "\n[end] " + new java.util.Date(getEnd())
0316: // +"\n[units] " + getUnits()// in hours
0317: // +"\n[work] " + getWork() / (1000*60*60); // in hours
0318: }
0319:
0320: /**
0321: * @return Returns the contour.
0322: */
0323: public AbstractContour getWorkContour() {
0324: return detail.getWorkContour();
0325: }
0326:
0327: /**
0328: * @param contour The contour to set.
0329: * TODO get rid of this
0330: */
0331: public void debugSetWorkContour(AbstractContour contour) {
0332: newDetail().setWorkContour(contour);
0333: }
0334:
0335: /**
0336: * Accessor for the assignment's delay
0337: * @return delay
0338: */
0339: public long getDelay() {
0340: return detail.getDelay();
0341: }
0342:
0343: public void setDelay(long delay) {
0344: newDetail().setDelay(delay);
0345: }
0346:
0347: public long getLevelingDelay() {
0348: return detail.getLevelingDelay();
0349: }
0350:
0351: public void setLevelingDelay(long levelingDelay) {
0352: newDetail().setLevelingDelay(levelingDelay);
0353: }
0354:
0355: /**
0356: * @return Returns the duration.
0357: */
0358: public long getDurationMillis() {
0359: return detail.getDuration();
0360: }
0361:
0362: private void setDurationMillis(long durationMillis) {
0363: newDetail().setDuration(durationMillis);
0364: }
0365:
0366: public void setDuration(long duration) {
0367: adjustRemainingDuration(duration - getActualDuration(), false);
0368: }
0369:
0370: /**
0371: * @param duration The duration to set.
0372: */
0373: public void adjustRemainingDuration(long newRemainingDuration,
0374: boolean doChildren) {
0375: long old = getRemainingDuration();
0376: newRemainingDuration = Duration.millis(newRemainingDuration); // don't use time unit
0377: if (getUnits() == 0) // take care of degenerate case
0378: newRemainingDuration = 0;
0379: newDetail().adjustRemainingDuration(newRemainingDuration);
0380:
0381: //TODO commented out may 8 2007
0382: // if (getTaskSchedulingType() == SchedulingType.FIXED_WORK && newRemainingDuration != 0) {
0383: // double multiplier = ((double)old) / newRemainingDuration;
0384: // if (multiplier > 0) {
0385: // detail.setWorkContour(detail.getWorkContour().adjustUnits(multiplier, getActualDuration()));
0386: // detail.setUnits(getUnits() * multiplier);
0387: // }
0388: // }
0389:
0390: }
0391:
0392: public void adjustRemainingDurationIfWorkingAtTaskEnd(
0393: long newRemainingDuration) {
0394: if (getEnd() >= getTask().getEnd() || !isInitialized())
0395: adjustRemainingDuration(newRemainingDuration, false);
0396: }
0397:
0398: public void adjustRemainingWork(double multiplier,
0399: boolean doChildren) {
0400: if (getPercentComplete() > 0.0D) // if actuals, then remaining may have different contour
0401: makeContourPersonal(); //fix?
0402: newDetail().adjustRemainingWork(multiplier);
0403: }
0404:
0405: /**
0406: * @param units The units to set.
0407: */
0408: public void adjustRemainingUnits(double newRemainingUnits,
0409: double oldRemainingUnits, boolean doChildren,
0410: boolean conserveTotalUnits) {
0411: if (!isTemporal()) // for absolute quantities, don't change
0412: return;
0413: oldRemainingUnits = getRemainingUnits();
0414: NormalTask task = (NormalTask) getTask();
0415: // task.adjustUnitsDelta(newRemainingUnits - oldRemainingUnits);
0416: if (doChildren) {
0417: task.getSchedulingRule().adjustRemainingUnits(this ,
0418: newRemainingUnits, oldRemainingUnits, false, false);
0419: } else { // just treat the assignment
0420: if (getPercentComplete() > 0.0D) // if actuals, then remaining may have different contour
0421: makeContourPersonal();
0422:
0423: newDetail().adjustRemainingUnits(newRemainingUnits);
0424: }
0425: }
0426:
0427: public void setWork(long work, FieldContext context) {
0428: work = Duration.millis(work);
0429: if (isLabor() && work < 60000) {
0430: work *= Duration.timeUnitFactor(TimeUnit.HOURS);
0431: System.out
0432: .println("modifying invalid work to make it hours");
0433: }
0434: long remainingWork = work - getActualWork(null);
0435: WorkCalendar cal = getEffectiveWorkCalendar();
0436: if (!FieldContext.hasInterval(context)) {
0437: long currentRemainingWork = Duration
0438: .millis(getWork(context))
0439: - Duration.millis(getActualWork(context));
0440: if (currentRemainingWork == 0 && remainingWork == 0)
0441: adjustRemainingUnits(0, 0, true, false); //TODO is this ok?
0442: else {
0443: if (getTaskSchedulingType() == SchedulingType.FIXED_UNITS)
0444: adjustRemainingDuration(remainingWork, false);
0445: else if (getTaskSchedulingType() == SchedulingType.FIXED_DURATION
0446: && currentRemainingWork > 0)
0447: adjustRemainingWork(((double) remainingWork)
0448: / currentRemainingWork, true);
0449: // newDetail().setWorkContour(getWorkContour().adjustUnits((((double)remainingWork) / currentRemainingWork), getActualDuration()));
0450:
0451: else
0452: newDetail().adjustRemainingDuration(remainingWork); // just set duration
0453:
0454: }
0455: } else {
0456: long start = cal.adjustInsideCalendar(context.getStart(),
0457: false);
0458: long end = cal.adjustInsideCalendar(context.getEnd(), true);
0459: if (end > start)
0460: setWorkInterval(start, end, work);
0461: if (getRate().getValue() == 0.0D) {// special case if units was 0
0462: double rate = ((double) work)
0463: / cal.compare(end, start, false); // use rate for the period as the assignment's rate
0464: this .forceUnits(rate);
0465: }
0466: }
0467: }
0468:
0469: private boolean addWorkingTimeIfRequired(long work,
0470: FieldContext context, boolean actual) {
0471: WorkCalendar cal = getEffectiveWorkCalendar();
0472: // adjust start and end inside the calendar
0473: long start = cal
0474: .adjustInsideCalendar(context.getStart(), false);
0475: long end = cal.adjustInsideCalendar(context.getEnd(), true);
0476: if (work != 0 && !verifyBounds(start, end))
0477: return false;
0478:
0479: // if adding during non calendar time
0480: if (end <= start && work != 0) {
0481: start = context.getStart();
0482: end = context.getEnd();
0483: // example 10 day task - MTWTFSSMTWTF
0484: // initial bar: ===== =====
0485: // calendar time add ====== ==== here I make a saturday working, so the task finishes sooner
0486: // shift the saturday ===== ===== shifting the saturday puts the task back as it was
0487: // set the interval ====== ===== now I set the saturday value
0488:
0489: // System.out.println("before adding" + getWorkContour());
0490: addCalendarTime(start, end);
0491: cal = getEffectiveWorkCalendar();
0492: if (actual && getTask().getActualStart() == 0) {
0493: long taskStart = cal.adjustInsideCalendar(start, false);
0494: ((NormalTask) getTask())
0495: .setActualStartNoEvent(taskStart);
0496: }
0497: long addedDuration = cal.compare(end, start, false);
0498: // System.out.println("before shift" + getWorkContour());
0499: shift(start, end, addedDuration); // shift the remaining contour to the right by the duration of the inserted time
0500: // System.out.println("after shift" + getWorkContour());
0501:
0502: getTask().recalculateLater(this ); // task needs to be recalculated
0503:
0504: // SwingUtilities.invokeLater(new Runnable());
0505:
0506: }
0507: return true;
0508: }
0509:
0510: public void makeFlatIfPossible() {
0511: if (getWorkContour().isPersonal()) {
0512: detail.recalculateDuration(); // set duration from contour
0513: detail.setWorkContour(((PersonalContour) getWorkContour())
0514: .convertToFlatIfPossible());
0515: }
0516:
0517: }
0518:
0519: public void setActualWork(long actualWork, FieldContext fieldContext) {
0520: long workValue = Duration.millis(actualWork);
0521:
0522: if (FieldContext.hasInterval(fieldContext)) {
0523:
0524: boolean isComplete = getPercentComplete() == 1.0D;
0525: long oldWork = work();
0526:
0527: if (!addWorkingTimeIfRequired(actualWork, fieldContext,
0528: true))
0529: return;
0530:
0531: //if the task isn't yet started and setting work, move to start date
0532: if (getTask().getActualStart() == 0 && actualWork != 0) {
0533: long s = getEffectiveWorkCalendar()
0534: .adjustInsideCalendar(fieldContext.getStart(),
0535: false);
0536: getTask().setActualStart(s); //TODO handle if setting non calendar time
0537: }
0538:
0539: long stop = getStop();
0540:
0541: setWork(actualWork, fieldContext); //when entering time phased actuals, first thing is adjust the work contour
0542:
0543: // if (!isComplete)
0544: // ((NormalTask)getTask()).getSchedulingRule().adjustRemainingWork(this, newRemainingWork, remainingWork, false);
0545:
0546: // need to 0 out an time between stop and the intervals start, if any
0547: if (fieldContext.getStart() > stop) {
0548: FieldContext empty = new FieldContext();
0549: empty.setInterval(new ImmutableInterval(
0550: stop == 0 ? getStart() : stop, fieldContext
0551: .getStart()));
0552: setWork(0L, empty);
0553: }
0554: if (workValue > 0) {
0555:
0556: if (fieldContext.getEnd() > stop) // if will set stop later because new work is after stop
0557: setStop(fieldContext.getEnd());
0558: else
0559: setStop(stop); // use current stop - for case when setting actuals before current stop
0560: } else {
0561: if (fieldContext.getStart() < stop
0562: && fieldContext.getEnd() >= stop) {//if overlapping current stop
0563: setStop(fieldContext.getStart());
0564: } else {
0565: if (stop != 0)
0566: setStop(stop); // make sure stop stays where it is
0567: }
0568: }
0569: // if (!isComplete)
0570: // ((NormalTask)getTask()).getSchedulingRule().adjustRemainingWork(this, newRemainingWork, remainingWork, false);
0571:
0572: // adjust remaining work
0573: long remainingWork = oldWork - getActualWork(null);
0574:
0575: // adjustRemainingWork( ((double)remainingWork) / currentRemainingWork, true);
0576:
0577: if (!isComplete) {
0578: ((NormalTask) getTask())
0579: .getSchedulingRule()
0580: .adjustRemainingWork(this , remainingWork, false);
0581: }
0582: getTask().recalculateLater(this ); // task needs to be recalculated
0583: } else {
0584: if (workValue == 0) {
0585: setPercentComplete(0);
0586: return;
0587: }
0588: long date = ReverseQuery.getDateAtValue(WORK, this ,
0589: workValue, false);
0590: setStop(date);
0591: }
0592:
0593: }
0594:
0595: public boolean isInitialized() {
0596: return getProject().isInitialized();
0597: }
0598:
0599: /**
0600: * Allow setting of working duration. MSProject displays working duration (excludes non-work intervals) except when
0601: * task type is fixed duration, in which case it shows duration with non-work intervals
0602: * @param newWorkingDuration
0603: */
0604: public void adjustWorkingDuration(long newWorkingDuration) {
0605: newDetail().adjustWorkingDuration(newWorkingDuration);
0606: }
0607:
0608: public WorkCalendar getEffectiveWorkCalendar() {
0609: return detail.getEffectiveWorkCalendar();
0610: }
0611:
0612: /**
0613: * @return Returns the task.
0614: */
0615: public Task getTask() {
0616: return detail.getTask();
0617: }
0618:
0619: public String getTaskName() {
0620: return getTask().getName();
0621: }
0622:
0623: public String getTaskId() {
0624: return "" + getTask().getId();
0625: }
0626:
0627: public String getResourceName() {
0628: return getResource().getName();
0629: }
0630:
0631: public String getResourceId() {
0632: return "" + getResource().getId();
0633: }
0634:
0635: public long getStart() {
0636: return detail.getStart();
0637: }
0638:
0639: public long getFinish() {
0640: return detail.getFinish();
0641: }
0642:
0643: /**
0644: * Offset a date by its duration or remaining duration
0645: * @param date
0646: * @param ahead - true if we want to add duraiton, false otherwise
0647: * @param remainingOnly - true if use remaining duration, otherwise all duration
0648: * @param useSooner - Used for end/beginning of day issues
0649: * @return new date
0650: */
0651:
0652: private long computeStart(long startDate, long dependencyDate) {
0653: long delay = this .calcTotalDelay();
0654: long s = startDate;
0655: if (getPercentComplete() == 0) { // if no completion, use task dependency date if needed
0656: if (dependencyDate > s)
0657: s = dependencyDate;
0658: }
0659: if (delay > 0)
0660: s = getEffectiveWorkCalendar().add(s, delay, true);
0661: //TODO check if above should use task calendar or assignment calendar
0662: return s;
0663: }
0664:
0665: public long calcOffsetFrom(long startDate, long dependencyDate,
0666: boolean ahead, boolean remainingOnly, boolean useSooner) {
0667: long start;
0668: if (ahead)
0669: start = computeStart(startDate, dependencyDate);
0670: else
0671: start = startDate;
0672: if (getPercentComplete() > 0)
0673: start = getEffectiveWorkCalendar().add(start,
0674: getActualDuration(), useSooner);
0675:
0676: long duration = remainingOnly ? detail.getRemainingDuration()
0677: : detail.getDuration();// + this.detail.getSplitDuration();
0678: //TODO integrate split - still needed?
0679:
0680: long amount = (ahead ? duration : -duration);
0681: return getEffectiveWorkCalendar().add(start, amount, useSooner);
0682: }
0683:
0684: boolean isInRange(long start, long finish) {
0685: long s = getStart();
0686: return (finish > s && start < getEffectiveWorkCalendar().add(s,
0687: detail.getDuration(), true));
0688: }
0689:
0690: /**
0691: * @return Returns the resource.
0692: */
0693: public Resource getResource() {
0694: return detail.getResource();
0695: }
0696:
0697: public Closure forResource(Closure visitor) {
0698: return new ObjectVisitor(visitor) {
0699: protected final Object getObject(Object caller) {
0700: return ((Assignment) caller).getResource();
0701: }
0702: };
0703: }
0704:
0705: public Closure forTask(Closure visitor) {
0706: return new ObjectVisitor(visitor) {
0707: protected final Object getObject(Object caller) {
0708: return ((Assignment) caller).getTask();
0709: }
0710: };
0711: }
0712:
0713: /**
0714: * @return
0715: */
0716: public long calcWork() {
0717: if (!isLabor())
0718: return 0;
0719: return detail.calcWork();
0720: }
0721:
0722: /**
0723: * @return
0724: */
0725: public long calcTotalDelay() {
0726: return detail.calcTotalDelay();
0727: }
0728:
0729: /**
0730: * @return Returns the detail.
0731: */
0732: public AssignmentDetail getDetail() {
0733: return detail;
0734: }
0735:
0736: /**
0737: * Gets an instance of a generator that acts on the cost contour
0738: * @return instance of generator
0739: */
0740: public ContourBucketIntervalGenerator contourGeneratorInstance(
0741: Object type) {
0742: return contourGeneratorInstance(type, getStart());
0743:
0744: }
0745:
0746: /**
0747: * Gets an instance of a generator that acts on the cost contour
0748: * @return instance of generator
0749: */
0750: public ContourBucketIntervalGenerator contourGeneratorInstance(
0751: Object type, long start) {
0752: return ContourBucketIntervalGenerator.getInstance(this , type);//getEffectiveWorkCalendar(), detail.getContour(type), detail.getDurationMillis(), getStart());
0753: //TODO actual cost treatment if entered manually
0754: }
0755:
0756: /**
0757: * Fills in a SelectFrom clause with "select work from contour"
0758: * @param clause to fill in
0759: * @return work field functor
0760: */
0761: AssignmentFieldFunctor work(SelectFrom clause) {
0762: ContourBucketIntervalGenerator contour = contourGeneratorInstance(WORK);
0763: WorkFunctor workF = WorkFunctor
0764: .getInstance(this , contour.getWorkCalendar(), contour,
0765: detail.calcOvertimeUnits());
0766: clause.select(workF).from(contour);
0767: return workF;
0768: }
0769:
0770: // public AssignmentFieldFunctor work(SelectFrom clause, AssignmentFieldFunctor using) {
0771: // ContourBucketIntervalGenerator contour = using.getContourBucketIntervalGenerator();
0772: // WorkFunctor workF = WorkFunctor.getInstance(this, contour.getWorkCalendar(), contour, detail.calcOvertimeUnits());
0773: // clause.select(workF);
0774: // return workF;
0775: // }
0776:
0777: private AssignmentFieldFunctor percentAlloc(SelectFrom clause,
0778: boolean threshold) {
0779: ContourBucketIntervalGenerator contour = contourGeneratorInstance(WORK);
0780: PercentAllocFunctor functor = PercentAllocFunctor.getInstance(
0781: this , contour.getWorkCalendar(), contour, threshold);
0782: clause.select(functor).from(contour);
0783: return functor;
0784: }
0785:
0786: private AssignmentFieldFunctor fixedCost(SelectFrom clause) {
0787: ContourBucketIntervalGenerator contour = contourGeneratorInstance(COST);
0788: FixedCostFunctor functor = FixedCostFunctor.getInstance(this );
0789: clause.select(functor).from(contour);
0790: return functor;
0791: }
0792:
0793: private AssignmentFieldFunctor availability(SelectFrom clause) {
0794: //TODO this is untested, as histogram is currently broken 8/12/04 hk
0795: ResourceAvailabilityFunctor functor = ResourceAvailabilityFunctor
0796: .getInstance(this );
0797: CollectionIntervalGenerator availability = CollectionIntervalGenerator
0798: .getInstance(((ResourceImpl) detail.getResource())
0799: .getAvailabilityTable().getList());
0800: clause.select(functor).from(availability); //use range?
0801: return functor;
0802: }
0803:
0804: /** This version does not depend on the assignment
0805: *
0806: * @param clause
0807: * @param resource
0808: * @return
0809: */
0810: private static AssignmentFieldFunctor resourceAvailability(
0811: SelectFrom clause, Resource resource) {
0812: //TODO this is untested, as histogram is currently broken 8/12/04 hk
0813: ResourceAvailabilityFunctor functor = ResourceAvailabilityFunctor
0814: .getInstance(resource);
0815: CollectionIntervalGenerator availability = CollectionIntervalGenerator
0816: .getInstance(resource.getAvailabilityTable().getList());
0817: clause.select(functor).from(availability); //use range?
0818: return functor;
0819: }
0820:
0821: // PercentAllocFunctor(Assignment assignment, WorkCalendar workCalendar, ContourBucketIntervalGenerator contourBucketIntervalGenerator) {
0822: /**
0823: * Convenience function to fill in the SelectFrom clause and return the cost functor.
0824: * One particularity of this function is that if the cost has been saved in the cost contour, then the cost contour is used.
0825: * This is the usual case in baselines.
0826: * If the cost contour is not personal, then the cost is calculated from costRate and work; however, the resource accrual settings
0827: * determine how the data is grouped: If accrual is not PRORATED, then the entire cost is calculated, and the result is then put
0828: * in a special ConstantCost functor which will return the value if and only if the current group by range encloses the value.
0829: * @param clause: SelectFrom clause to populate
0830: * @return The functor to use to calculate cost
0831: */
0832: private AssignmentFieldFunctor cost(SelectFrom clause, boolean all) {
0833: if (detail.getCostContour().isPersonal()) { // in the case where a cost contour has already been saved, use it
0834: ContourBucketIntervalGenerator costContour = contourGeneratorInstance(COST);
0835: WorkFunctor workF = WorkFunctor.getInstance(this ,
0836: getEffectiveWorkCalendar(), costContour, detail
0837: .calcOvertimeUnits()); // note that is is a work contour
0838: clause.select(workF).from(costContour);
0839: return workF;
0840: } else {
0841: boolean prorated = isProratedCost();
0842: // if prorated, or if calculating a total, then treat as prorated
0843: if (all || prorated) {
0844: ContourBucketIntervalGenerator workContour = contourGeneratorInstance(WORK);
0845: CollectionIntervalGenerator costRate = CollectionIntervalGenerator
0846: .getInstance(detail.getResource()
0847: .getCostRateTable(
0848: detail.getCostRateIndex())
0849: .getList());
0850: clause.from(costRate).from(workContour);
0851: // Note that the getStart() parameter implies cost per use is applied at start
0852: CostFunctor costF = CostFunctor.getInstance(this ,
0853: getEffectiveWorkCalendar(), workContour, detail
0854: .calcOvertimeUnits(), costRate,
0855: getStart(), prorated);
0856: clause.select(costF);
0857: return costF;
0858: } else { // accrue start or end
0859: long triggerDate = (detail.getResource().getAccrueAt() == Accrual.START) ? getStart()
0860: : getFinish();// use start or end
0861: AssignmentFieldFunctor constantCost = ValueAtInstant
0862: .getInstance(triggerDate, calcAll(COST));
0863: clause.select(constantCost).from(
0864: InstantIntervalGenerator
0865: .getInstance(triggerDate)); // just one instant
0866: return constantCost;
0867: }
0868: }
0869: }
0870:
0871: public boolean isProratedCost() {
0872: return detail.getResource().getAccrueAt() == Accrual.PRORATED;
0873: }
0874:
0875: public AssignmentFieldFunctor getDataSelect(Object type,
0876: SelectFrom clause, boolean all) {
0877: if (type == PERCENT_ALLOC) {
0878: return percentAlloc(clause, false);
0879: } else if (type == AVAILABILITY) {
0880: return availability(clause);
0881: } else if (type == COST) {
0882: return cost(clause, all);
0883: } else if (type == OVERALLOCATED) {
0884: return work(clause);
0885: } else if (type == WORK || type == THIS_PROJECT) {
0886: return work(clause);
0887: } else if (type == ACTUAL_WORK) {
0888: clause.whereInRange(detail.getStart(), getStop());
0889: return work(clause);
0890: } else if (type == REMAINING_WORK) {
0891: clause.whereInRange(getResume(), detail.getFinish());
0892: return work(clause);
0893: } else if (type == ACTUAL_COST) {
0894: clause.whereInRange(detail.getStart(), getStop());
0895: return cost(clause, all);
0896: } else if (type == FIXED_COST) {
0897: clause.whereInRange(detail.getStart(), getEnd());
0898: return fixedCost(clause);
0899: } else if (type == ACTUAL_FIXED_COST) {
0900: clause.whereInRange(detail.getStart(), getStop());
0901: return fixedCost(clause);
0902: } else if (type == REMAINING_COST) {
0903: clause.whereInRange(getResume(), detail.getFinish());
0904: return cost(clause, all);
0905: } else if (type == BASELINE_COST) {
0906: return baselineData(COST, clause);
0907: } else if (type == BASELINE_WORK) {
0908: return baselineData(WORK, clause);
0909: } else if (type == ACWP) {
0910: clause.whereInRange(detail.effectiveBaselineStart(),
0911: getCompletedOrStatusDate());
0912: return cost(clause, all);
0913: } else if (type == BCWP) {
0914: clause.whereInRange(detail.getStart(), getStatusDate());
0915: AssignmentFieldFunctor costF = cost(clause, all);
0916: costF.setMultiplier(efficiency());
0917: return costF;
0918: } else if (type == BCWS) {
0919: clause.whereInRange(detail.effectiveBaselineStart(),
0920: getStatusDate());
0921: return baselineData(COST, clause);
0922: } else if (type instanceof Field) { // treat all baselines
0923: Field field = (Field) type;
0924: if (field.isIndexed()) {
0925: Integer index = new Integer(field.getIndex());
0926: if (field.isWork())
0927: return baselineData(WORK, clause, index);
0928: else
0929: return baselineData(COST, clause, index);
0930: }
0931: }
0932:
0933: return null;
0934: }
0935:
0936: public void calcDataBetween(Object type, long start, long end) {
0937: SelectFrom clause = SelectFrom.getInstance().whereInRange(
0938: start, end); // automatically also adds a generator to limit range
0939: AssignmentFieldFunctor dataFunctor = getDataSelect(type,
0940: clause, false);
0941:
0942: RangeIntervalGenerator dailyInRange = RangeIntervalGenerator
0943: .getInstance(start, end, 1000 * 60 * 60 * 24);
0944: PrintValueFunctor print = PrintValueFunctor
0945: .getInstance(dataFunctor);
0946: Query.getInstance().selectFrom(clause).groupBy(dailyInRange)
0947: .action(print).execute();
0948: }
0949:
0950: /**
0951: * Calculate the total cost
0952: * @return
0953: */
0954: public double calcAll(Object type) {
0955: SelectFrom clause = SelectFrom.getInstance();
0956: AssignmentFieldFunctor dataFunctor = getDataSelect(type,
0957: clause, true);
0958: Query.getInstance().selectFrom(clause).execute();
0959: return dataFunctor.getValue();
0960: }
0961:
0962: // public void forEach(Object type, Closure actionVisitor) {
0963: // SelectFrom clause = SelectFrom.getInstance();
0964: // AssignmentFieldFunctor dataFunctor = getDataSelect(type,clause,false);
0965: // Query.getInstance().selectFrom(clause)
0966: // .action(actionVisitor)
0967: // .execute();
0968: // }
0969:
0970: public void buildReverseQuery(ReverseQuery reverseQuery) {
0971: SelectFrom clause = SelectFrom.getInstance();
0972: reverseQuery.addField(getDataSelect(reverseQuery.getType(),
0973: clause, false));
0974: reverseQuery.addGroupBy(IntervalGeneratorSet
0975: .extractUnshared(clause.getFromIntervalGenerators()));
0976: reverseQuery.addSelectFrom(clause);
0977: }
0978:
0979: /**
0980: * calls back on for all "gantt bar" intervals having matching rates. Matching is determined by a comparator.
0981: * @param visitor : callback
0982: * @param mergeWorking : if true, then matching occurs if the periods hava non-zero work, otherwise the work
0983: * must be an exact match.
0984: */
0985: public void forEachWorkingInterval(Closure visitor,
0986: boolean mergeWorking, WorkCalendar workCalendar) {
0987: Comparator comparator = (mergeWorking ? null : WorkComparator
0988: .getInstance()); // if a value of null is used , then the bars will be grouped based on time only
0989: Merge merge = Merge.getInstance(visitor, comparator);
0990: Query.getInstance().groupBy(contourGeneratorInstance(WORK))
0991: .action(merge).execute();
0992: }
0993:
0994: private transient static BarClosure barClosureInstance = new BarClosure();
0995:
0996: public void consumeIntervals(IntervalConsumer consumer) {
0997: barClosureInstance.initialize(consumer, this );
0998: boolean inProgress = getPercentComplete() > 0.0D;
0999: MutableInterval bounds = null;
1000: if (inProgress) {
1001: bounds = new MutableInterval(getStart(), getStop());
1002: barClosureInstance.setBounds(bounds);
1003: }
1004: forEachWorkingInterval(barClosureInstance, true,
1005: getEffectiveWorkCalendar());
1006: if (inProgress) {
1007: // barClosureInstance.initCount();
1008: bounds.setStart(bounds.getEnd()); // shift for second half
1009: bounds.setEnd(getEnd());
1010:
1011: forEachWorkingInterval(barClosureInstance, true,
1012: getEffectiveWorkCalendar());
1013: }
1014: barClosureInstance.setBounds(null);
1015: }
1016:
1017: public void moveInterval(Object eventSource, long start, long end,
1018: ScheduleInterval oldInterval, boolean isChild) {
1019: long startShiftDuration = getEffectiveWorkCalendar().compare(
1020: start, oldInterval.getStart(), false);
1021: long endShiftDuration = getEffectiveWorkCalendar().compare(end,
1022: oldInterval.getEnd(), false);
1023: boolean shifting = startShiftDuration != 0L
1024: && endShiftDuration != 0L;
1025: long stop = getStop(); // Store old completion
1026: if (shifting) {
1027: shift(oldInterval.getStart(), oldInterval.getEnd(),
1028: startShiftDuration);
1029: } else {
1030: if (endShiftDuration != 0)
1031: extend(oldInterval.getStart(), oldInterval.getEnd(),
1032: endShiftDuration);
1033: else
1034: extendBefore(oldInterval.getStart(), oldInterval
1035: .getEnd(), startShiftDuration);
1036: }
1037:
1038: setStop(stop); // put back old completion
1039: //Need to update schedule if the assignment bar was moved
1040: if (!isChild) {
1041: getTask().updateCachedDuration();
1042: getTask().recalculate(eventSource);
1043: }
1044:
1045: // //Undo
1046: // UndoableEditSupport undoableEditSupport=getProject().getUndoController().getEditSupport();
1047: // if (undoableEditSupport!=null&&!(eventSource instanceof UndoableEdit)){
1048: // undoableEditSupport.postEdit(new ScheduleEdit(this,new ScheduleInterval(start,end),oldInterval,isChild,eventSource));
1049: // }
1050:
1051: }
1052:
1053: /**
1054: * This function will calculate the exact date/time when the value is achieved
1055: * @param type
1056: * @param value
1057: * @return
1058: */
1059: public long getDateAtValue(Object type, double value) {
1060: SelectFrom clause = SelectFrom.getInstance();
1061: AssignmentFieldFunctor dataFunctor = (type == COST) ? cost(
1062: clause, true) : work(clause);
1063:
1064: DateAtValueFunctor dateAtValue = DateAtValueFunctor
1065: .getInstance(value, AssignmentFieldClosureCollection
1066: .getInstance(dataFunctor));
1067: clause.select(dateAtValue); // override existing select
1068:
1069: Query.getInstance().selectFrom(clause).execute();
1070: return dateAtValue.getDate();
1071: }
1072:
1073: public long getDateAtWorkFraction(double workFraction) {
1074: // System.out.println("all work is " + calcAll(WORK) / (1000*60*60*8));
1075: return getDateAtValue(WORK, calcAll(WORK) * workFraction);
1076: }
1077:
1078: private AssignmentFieldFunctor baselineData(Object type,
1079: SelectFrom baselineSelectFrom) {
1080: Assignment baselineAssignment = detail.getBaselineAssignment();
1081: AssignmentFieldFunctor baselineFunctor;
1082: if (baselineAssignment == null) {
1083: baselineFunctor = ZeroFunctor.getInstance();
1084: baselineSelectFrom.select(baselineFunctor).from(
1085: RangeIntervalGenerator.empty());
1086: } else {
1087: baselineFunctor = baselineAssignment.getDataSelect(type,
1088: baselineSelectFrom, false);
1089: }
1090: return baselineFunctor;
1091: }
1092:
1093: public Assignment getBaselineAssignment(Object baseline,
1094: boolean createIfDoesntExist) {
1095: return detail.getBaselineAssignment(baseline,
1096: createIfDoesntExist);
1097: }
1098:
1099: private AssignmentFieldFunctor baselineData(Object type,
1100: SelectFrom baselineSelectFrom, Object baseline) {
1101: Assignment baselineAssignment = detail.getBaselineAssignment(
1102: baseline, false);
1103: AssignmentFieldFunctor baselineFunctor;
1104: if (baselineAssignment == null) {
1105: baselineFunctor = ZeroFunctor.getInstance();
1106: baselineSelectFrom.select(baselineFunctor).from(
1107: RangeIntervalGenerator.empty());
1108: } else {
1109: baselineFunctor = baselineAssignment.getDataSelect(type,
1110: baselineSelectFrom, false);
1111: }
1112: return baselineFunctor;
1113: }
1114:
1115: private long getStatusDate() {
1116: return getProject().getStatusDate();
1117: }
1118:
1119: private long getCompletedOrStatusDate() {
1120: return Math.min(getStatusDate(), detail.getStop());
1121: }
1122:
1123: public double getCost() {
1124: return calcAll(COST);
1125: }
1126:
1127: /**
1128: * Set the total delay for the assignment. Priority is given to changing the assignment delay over the
1129: * leveling delay.
1130: * @param newDelay
1131: */
1132: void setTotalDelay(long newDelay) {
1133:
1134: /*
1135: * DDDDDDDLLLLLL
1136: * NNNNNNNNNN
1137: */
1138:
1139: // long delay = getDelay();
1140: // long levelingDelay = getLevelingDelay();
1141: // long oldTotalDelay = calcTotalDelay();
1142: // if (newDelay == oldTotalDelay)
1143: // return;
1144: //
1145: // if (newDelay <= delay) {
1146: // delay = newDelay;
1147: // levelingDelay = 0;
1148: // }
1149: // else {
1150: // delay = 0;
1151: // levelingDelay =
1152: // }
1153: // if (newDelay < oldTotalDelay) {
1154: // delay -= newDelay;
1155: // if (delay < 0) {
1156: // levelingDelay += delay;
1157: // delay = 0;
1158: // }
1159: // } else {
1160: // delay += (newDelay - oldTotalDelay);
1161: // }
1162: // setDelay(delay);
1163: // setLevelingDelay(levelingDelay);
1164: setDelay(newDelay);
1165: setLevelingDelay(0);
1166: }
1167:
1168: /**
1169: * This treats just a single interval to merge in
1170: * @param type - WORK or COST. can also be a time distributed field
1171: * @param start - start of interval
1172: * @param end - end of interval
1173: * @param value - value of interval
1174: */
1175: public void setInterval(Object type, long start, long end,
1176: double value) {
1177: //TODO treat cost values - they are currently ignored
1178: if (type == ACTUAL_WORK || type == WORK
1179: || type == REMAINING_WORK) {
1180: if (value != 0 && !verifyBounds(start, end))
1181: return;
1182: if (Environment.isImporting()) {
1183: WorkCalendar cal = getEffectiveWorkCalendar();
1184: start = cal.adjustInsideCalendar(start, false);
1185: end = cal.adjustInsideCalendar(end, true);
1186: }
1187:
1188: setWorkInterval(start, end, value);
1189: if (type == ACTUAL_WORK && end > getStop() && value > 0.0D) // bug fix - actuals were lost
1190: setStop(end);
1191: } else {
1192: if (TimeDistributedHelper.isWork(type)) {
1193: Object baseline = TimeDistributedHelper
1194: .baselineForData(type);
1195: //System.out.println("baseline " + baseline + " " + new Date(start) + " " + new Date(end) + " " + value);
1196:
1197: Assignment baselineAssignment = getBaselineAssignment(
1198: baseline, true); // get or create baseline assignment
1199:
1200: // update the task schedule too
1201: TaskSchedule schedule = baselineAssignment
1202: .getTaskScheduleOfAssignment();
1203: long baselineStart = schedule.getStart();
1204: long baselineFinish = schedule.getFinish();
1205: if (baselineStart == 0 || baselineStart > start)
1206: schedule.setStart(start);
1207: if (baselineFinish == 0 || baselineFinish < end)
1208: schedule.setEnd(end);
1209:
1210: baselineAssignment.setWorkInterval(start, end, value);
1211: }
1212: }
1213: }
1214:
1215: TaskSchedule getTaskScheduleOfAssignment() {
1216: return detail.getTaskSchedule();
1217: }
1218:
1219: public boolean verifyBounds(long start, long end) {
1220: if (Environment.isImporting()) // always accept time distributed edits when importing
1221: return true;
1222: if (getProject().getStart() > start) // if setting before project start
1223: return Alert
1224: .okCancel(Messages
1225: .getString("Message.allowDistrbutedStartBeforeProjectStart"));
1226: else if (start < getTask().getStart())
1227: return Alert
1228: .okCancel(Messages
1229: .getString("Message.allowDistrbutedStartBeforeTaskStart"));
1230: else
1231: return true;
1232:
1233: }
1234:
1235: /**
1236: * This treats just a single interval to merge in
1237: * @param type - WORK or COST
1238: * @param start - start of interval
1239: * @param end - end of interval
1240: * @param value - value of interval (an wmount of work (duration), not units)
1241: */
1242: private void setWorkInterval(long start, long end, double value) {
1243: moveDelayToContour();
1244: long assignmentStart = getStart(); // the start including any delay
1245: WorkCalendar cal = getEffectiveWorkCalendar();
1246:
1247: if (end > getFinish()) { // add end if necessary
1248: if (value == 0) {
1249: this .setEnd(start);
1250: return; // don't extend if value is 0
1251: }
1252: makeContourPersonal();
1253: long extraDuration = cal.compare(end, getFinish(), false);
1254: setDurationMillis(getDurationMillis() + extraDuration);
1255: AbstractContour contour = PersonalContour.addEmptyBucket(
1256: getWorkContour(), extraDuration, true);
1257: newDetail().setWorkContour(contour);
1258:
1259: // adjust task end too
1260: if (end > getTask().getEnd())
1261: getTask().setEnd(end);
1262: } else if (start < assignmentStart) { // if before assignment start
1263: // if (value == 0)
1264: // return; // don't extend if value is 0
1265: long taskStart = getTask().getStart();
1266: if (start < taskStart) {
1267: long shiftAmount = cal.compare(taskStart, start, false);
1268: setDurationMillis(getDurationMillis() + shiftAmount);
1269: if (!getWorkContour().isPersonal())
1270: makeContourPersonal();
1271: newDetail().setWorkContour(
1272: PersonalContour.addEmptyBucket(
1273: getWorkContour(), shiftAmount, false)); // add empty space before
1274: getTask().setScheduleConstraintAndUpdate(
1275: ConstraintType.SNLT, start); // this can set start before project duration.
1276: taskStart = getTask().getStart();
1277: }
1278: assignmentStart = taskStart;
1279: }
1280:
1281: long startDuration = cal.compare(start, assignmentStart, false);
1282: long endDuration = cal.compare(end, assignmentStart, false);
1283: if (startDuration == endDuration)
1284: return;
1285:
1286: if (!getWorkContour().isPersonal())
1287: makeContourPersonal();
1288:
1289: value /= (endDuration - startDuration);
1290: PersonalContour newContour = ((PersonalContour) getWorkContour())
1291: .setInterval(startDuration, endDuration, value);
1292:
1293: extractDelayFromContour(newContour); // move delay back into delay field from contour
1294: newDetail().setWorkContour(newContour);
1295: newDetail().recalculateDuration();
1296: getTask().updateCachedDuration();
1297: }
1298:
1299: /**
1300: * Move remaining work to the date given. It will either be moved later or sooner. Also, if at the date, there
1301: * is a gap in the work, the gap is reduced so that the work begins on the date
1302: * @param date
1303: */
1304: public void moveRemainingToDate(long date) {
1305: long resume = getResume();
1306: long end = getEnd();
1307: long shiftDuration = getEffectiveWorkCalendar().compare(date,
1308: resume, false); // get shift time
1309: shift(resume, end, shiftDuration);
1310:
1311: long duration = getEffectiveWorkCalendar().compare(date,
1312: getStart(), false); // get offset from start
1313: if (duration > 0)
1314: newDetail().removeEmptyBucketAtDuration(duration);
1315: }
1316:
1317: public void shift(long start, long end, long shiftDuration) {
1318: //get bounds.
1319: WorkCalendar cal = getEffectiveWorkCalendar();
1320:
1321: if (getTask().inProgress()
1322: && cal.compare(getStop(), start, false) > 0) { // if stop greater than start, can't change it
1323: // System.out.println("Not shifting - stop = " + new Date(getStop()) + "start " + new Date(start));
1324: return;
1325: }
1326: long taskStart = getTask().getStart();
1327: start = Math.max(start, getStop()); //only shift remaining
1328: start = Math.max(start, taskStart); // dont shift before task start of course
1329: long startOffset = cal.compare(start, taskStart, false);
1330: long endOffset = cal.compare(end, taskStart, false);
1331:
1332: if (getResume() != 0 && start >= getResume()) {
1333: long splitDuration = detail.getSplitDuration();
1334: startOffset -= splitDuration;
1335: endOffset -= splitDuration;
1336: }
1337:
1338: if (startOffset >= endOffset) // this prevents moving a completed interval
1339: return;
1340: boolean firstBar = (cal.compare(getStart(), start, false) == 0);
1341: MutableInterval range = getRangeThatIntervalCanBeMoved(start,
1342: startOffset, endOffset);
1343: if (firstBar)
1344: range.setStart(range.getStart() - calcTotalDelay());
1345: if (shiftDuration > 0)
1346: shiftDuration = Math.min(shiftDuration, range.getEnd()
1347: - endOffset); // don't allow to shift more than possible
1348: else {
1349: shiftDuration = Math.max(shiftDuration, range.getStart() /*+ (firstBar ? -calcTotalDelay() : 0)*/
1350: - startOffset); // don't allow to shift more than possible
1351: }
1352: if (firstBar) {
1353: setTotalDelay(calcTotalDelay() + shiftDuration);
1354: } else {
1355: newDetail().shift(startOffset, endOffset, shiftDuration);
1356: }
1357: }
1358:
1359: private MutableInterval getRangeThatIntervalCanBeMoved(long start,
1360: long startOffset, long endOffset) {
1361: MutableInterval range = getRangeThatIntervalCanBeMoved(
1362: startOffset, endOffset);
1363: if (getEffectiveWorkCalendar()
1364: .compare(getStart(), start, false) == 0)
1365: range.setStart(range.getStart() - calcTotalDelay());
1366: return range;
1367:
1368: }
1369:
1370: public MutableInterval getRangeThatIntervalCanBeMoved(
1371: long startOffset, long endOffset) {
1372: return detail.getRangeThatIntervalCanBeMoved(startOffset,
1373: endOffset);
1374: }
1375:
1376: public void extend(long start, long end, long extendDuration) {
1377: if (end < getStop())
1378: return;
1379: WorkCalendar cal = getEffectiveWorkCalendar();
1380: long startOffset = cal.compare(start, getStart(), false);
1381: long endOffset = cal.compare(end, getStart(), false);
1382:
1383: // if (start >= getResume()) {
1384: // long splitDuration = detail.getSplitDuration();
1385: // startOffset -= splitDuration;
1386: // endOffset -= splitDuration;
1387: // }
1388:
1389: Interval range = getRangeThatIntervalCanBeMoved(start,
1390: startOffset, endOffset);
1391: if (extendDuration > 0)
1392: extendDuration = Math.min(extendDuration, range.getEnd()
1393: - endOffset); // don't allow to shift more than possible
1394: else
1395: extendDuration = Math.max(extendDuration, startOffset
1396: - endOffset);
1397: newDetail().extend(endOffset, extendDuration);
1398: }
1399:
1400: /* (non-Javadoc)
1401: * @see com.projity.pm.scheduling.Schedule#split(java.lang.Object, long, long)
1402: */
1403: public void split(Object eventSource, long from, long to) {
1404: from = Math.max(from, getResume());
1405: if (from >= to)
1406: return;
1407: long duration = getEffectiveWorkCalendar().compare(to, from,
1408: false); // calculate shift duration
1409: shift(from, getEnd(), duration); // shift the remaining contour to the right by the duration
1410: }
1411:
1412: public void extendBefore(long start, long end, long extendDuration) {
1413: if (start < getStop())
1414: return;
1415: WorkCalendar cal = getEffectiveWorkCalendar();
1416:
1417: boolean firstBar = (cal.compare(getStart(), start, false) == 0);
1418: long startOffset = cal.compare(start, getStart(), false);
1419: long endOffset = cal.compare(end, getStart(), false);
1420: Interval range = getRangeThatIntervalCanBeMoved(start,
1421: startOffset, startOffset);
1422:
1423: if (extendDuration > 0)
1424: extendDuration = Math.min(extendDuration, endOffset
1425: - startOffset);
1426: else
1427: extendDuration = Math.max(extendDuration, range.getStart()
1428: - startOffset); // don't allow to shift more than possible
1429:
1430: if (firstBar)
1431: setTotalDelay(calcTotalDelay() + extendDuration);
1432: newDetail().extendBefore(startOffset, extendDuration);
1433:
1434: }
1435:
1436: public void addCalendarTime(long start, long end) {
1437: newDetail().addCalendarTime(start, end);
1438: }
1439:
1440: /* (non-Javadoc)
1441: * @see com.projity.pm.assignment.AssignmentSpecificFields#getWorkContourId()
1442: */
1443: public int getWorkContourType() {
1444: return getWorkContour().getType();
1445: }
1446:
1447: /* (non-Javadoc)
1448: * @see com.projity.pm.assignment.AssignmentSpecificFields#setWorkContourId(int)
1449: */
1450: public void setWorkContourType(int workContourType) {
1451: newDetail().setWorkContour(
1452: ContourFactory.getInstance(workContourType));
1453: // TODO need to actually transform contour correctly when changing
1454:
1455: }
1456:
1457: public final double getRemainingUnits() {
1458: if (!isLabor())
1459: return 1.0D;
1460: long duration = getRemainingDuration();
1461: if (duration == 0.0)
1462: return getWorkContour().getLastBucketUnits();
1463: // return 1.0D; // degeneratate case
1464: long work = getRemainingWork();
1465: if (work == 0) // degenerate case with no work yet
1466: return 1.0;
1467: return ((double) work) / duration;
1468:
1469: }
1470:
1471: public final double getRemainingLaborUnits() {
1472: if (isLabor())
1473: return getRemainingUnits();
1474: return 0.0D;
1475: }
1476:
1477: /* (non-Javadoc)
1478: * @see com.projity.util.Association#getLeft()
1479: */
1480: public Object getLeft() {
1481: return getTask();
1482: }
1483:
1484: /* (non-Javadoc)
1485: * @see com.projity.util.Association#getRight()
1486: */
1487: public Object getRight() {
1488: return getResource();
1489: }
1490:
1491: /* (non-Javadoc)
1492: * @see com.projity.util.Association#testValid()
1493: */
1494: public void testValid(boolean allowDuplicate)
1495: throws InvalidAssociationException {
1496: }
1497:
1498: /* (non-Javadoc)
1499: * @see com.projity.util.Association#copyPrincipalFieldsFrom(com.projity.util.Association)
1500: */
1501: public void copyPrincipalFieldsFrom(Association from) {
1502: Assignment fromAssignment = (Assignment) from;
1503: double units = fromAssignment.getUnits();
1504: if (fromAssignment.isLabor()) {
1505: adjustRemainingUnits(fromAssignment.getUnits(), 0, true,
1506: false);
1507: detail.setUnits(units);
1508: } else {
1509: detail.rate = fromAssignment.detail.getRate();
1510: detail.setDuration(getDuration()); // reset duration
1511: }
1512:
1513: getTask().updateCachedDuration(); // needed for checking if milestone
1514:
1515: }
1516:
1517: public void forceUnits(double units) {
1518: newDetail().setUnits(units);
1519: }
1520:
1521: public void doAddService(Object eventSource) {
1522: AssignmentService.getInstance().connect(this , eventSource);
1523: }
1524:
1525: public void doRemoveService(Object eventSource) {
1526: AssignmentService.getInstance().remove(this , eventSource, true);
1527: }
1528:
1529: /* (non-Javadoc)
1530: * @see com.projity.association.Association#doUpdateService(java.lang.Object)
1531: */
1532: public void doUpdateService(Object eventSource) {
1533: getUnitsField().fireEvent(this , this , null); // need to update
1534:
1535: }
1536:
1537: public boolean isDefault() {
1538: return getTask() == NormalTask.getUnassignedInstance()
1539: || getResource() == ResourceImpl
1540: .getUnassignedInstance();
1541: }
1542:
1543: public boolean isReadOnlyUnits(FieldContext fieldContext) {
1544: return false;
1545: }
1546:
1547: //Costs and earned value
1548: public double acwp(long start, long end) {
1549: if (!isInRange(start, end))
1550: return NO_VALUE_DOUBLE;
1551: Query query = Query.getInstance();
1552: SelectFrom clause = SelectFrom.getInstance().whereInRange(
1553: Math.max(start, detail.effectiveBaselineStart()),
1554: Math.min(end, getCompletedOrStatusDate()));
1555: query.selectFrom(clause).action(cost(clause, false)).execute();
1556: return ((DoubleValue) query.getActionVisitor()).getValue();
1557: }
1558:
1559: public double bcws(long start, long end) {
1560: if (!isInRange(start, end))
1561: return NO_VALUE_DOUBLE;
1562:
1563: if (AdvancedOption.getInstance()
1564: .isEarnedValueFieldsCumulative())
1565: start = getStart(); // start from the beginning of the task and ignore the range start
1566:
1567: Query query = Query.getInstance();
1568: SelectFrom clause = SelectFrom.getInstance().whereInRange(
1569: Math.max(start, detail.effectiveBaselineStart()),
1570: Math.min(end, getStatusDate()));
1571: query.selectFrom(clause).action(baselineData(COST, clause))
1572: .execute();
1573: return ((DoubleValue) query.getActionVisitor()).getValue();
1574: }
1575:
1576: public double efficiency() {
1577: Assignment baselineAssignment = detail.getBaselineAssignment();
1578: if (baselineAssignment == null)
1579: return 0.0D;
1580:
1581: long baselineWork = baselineAssignment.work();
1582: if (baselineWork == 0)
1583: return 0.0;
1584: long work = work();
1585: if (work == 0.0D)
1586: return 1.0D;
1587: return ((double) baselineWork) / work;
1588:
1589: }
1590:
1591: //[(Actual % of completion / Expected % of completion) of an activity for a given period] * Actual cost of activity
1592: public double bcwp(long start, long end) {
1593: if (!isInRange(start, end))
1594: return NO_VALUE_DOUBLE;
1595: end = Math.min(end, getStatusDate());
1596: if (end == 0)
1597: return 0.0D;
1598: if (AdvancedOption.getInstance()
1599: .isEarnedValueFieldsCumulative())
1600: start = getStart(); // start from the beginning of the task and ignore the range start
1601: double cost = actualCost(start, end);
1602: if (cost == 0)
1603: return 0;
1604: return efficiency() * cost;
1605: //
1606: // Query query = Query.getInstance();
1607: // long boundaryStart = detail.getStart(); // always use assignment start and never start
1608: // long boundaryEnd = Math.min(getStatusDate(),baselineAssignment.getEffectiveWorkCalendar().add(boundaryStart, (long) (baselineAssignment.getDuration() * getPercentComplete()),false));
1609: // SelectFrom clause = SelectFrom.getInstance().whereInRange(boundaryStart,Math.min(end,boundaryEnd));
1610: // query.selectFrom(clause)
1611: // .action(baselineData(COST,clause))
1612: // .execute();
1613: // return ((DoubleValue)query.getActionVisitor()).getValue();
1614: }
1615:
1616: public double bac(long start, long end) {
1617: if (!isInRange(start, end))
1618: return NO_VALUE_DOUBLE;
1619: Query query = Query.getInstance();
1620: SelectFrom clause = SelectFrom.getInstance().whereInRange(
1621: start, end);
1622: query.selectFrom(clause).action(baselineData(COST, clause))
1623: .execute();
1624: return ((DoubleValue) query.getActionVisitor()).getValue();
1625: }
1626:
1627: public double cost(long start, long end) {
1628: if (!isInRange(start, end))
1629: return NO_VALUE_DOUBLE;
1630: Query query = Query.getInstance();
1631: SelectFrom clause = SelectFrom.getInstance().whereInRange(
1632: start, end);
1633: query.selectFrom(clause).action(cost(clause, false)).execute();
1634: return ((DoubleValue) query.getActionVisitor()).getValue();
1635: }
1636:
1637: public long work(long start, long end) {
1638: if (!isInRange(start, end))
1639: return NO_VALUE_LONG;
1640: // if (!isLabor()) // TODO this right?
1641: // return 0;
1642: Query query = Query.getInstance();
1643: SelectFrom clause = SelectFrom.getInstance().whereInRange(
1644: start, end);
1645: query.selectFrom(clause).action(work(clause)).execute();
1646: return (long) ((DoubleValue) query.getActionVisitor())
1647: .getValue();
1648:
1649: }
1650:
1651: public double actualCost(long start, long end) {
1652: if (!isInRange(start, end))
1653: return NO_VALUE_DOUBLE;
1654:
1655: Query query = Query.getInstance();
1656: SelectFrom clause = SelectFrom.getInstance().whereInRange(
1657: Math.max(start, detail.getStart()),
1658: Math.min(end, getStop()));
1659: query.selectFrom(clause).action(cost(clause, false)).execute();
1660: return ((DoubleValue) query.getActionVisitor()).getValue();
1661: }
1662:
1663: public long actualWork(long start, long end) {
1664: if (!isInRange(start, end))
1665: return NO_VALUE_LONG;
1666: if (!isLabor()) // TODO this right?
1667: return 0;
1668: Query query = Query.getInstance();
1669: SelectFrom clause = SelectFrom.getInstance().whereInRange(
1670: Math.max(start, detail.getStart()),
1671: Math.min(end, getStop()));
1672: query.selectFrom(clause).action(work(clause)).execute();
1673: return (long) ((DoubleValue) query.getActionVisitor())
1674: .getValue();
1675: }
1676:
1677: public long remainingWork(long start, long end) {
1678: if (!isInRange(start, end))
1679: return NO_VALUE_LONG;
1680: if (!isLabor()) // TODO this right?
1681: return 0;
1682: Query query = Query.getInstance();
1683: SelectFrom clause = SelectFrom.getInstance().whereInRange(
1684: Math.max(start, detail.getStop()),
1685: Math.min(end, getEnd()));
1686: query.selectFrom(clause).action(work(clause)).execute();
1687: return (long) ((DoubleValue) query.getActionVisitor())
1688: .getValue();
1689: }
1690:
1691: public double baselineCost(long start, long end) {
1692: // if (!isInRange(start,end))
1693: // return NO_VALUE_DOUBLE;
1694:
1695: Query query = Query.getInstance();
1696: SelectFrom clause = SelectFrom.getInstance().whereInRange(
1697: start, end);
1698: query.selectFrom(clause).action(baselineData(COST, clause))
1699: .execute();
1700: return ((DoubleValue) query.getActionVisitor()).getValue();
1701: }
1702:
1703: public long baselineWork(long start, long end) {
1704: // if (!isInRange(start,end))
1705: // return NO_VALUE_LONG;
1706: if (!isLabor()) // TODO this right?
1707: return 0;
1708: Query query = Query.getInstance();
1709: SelectFrom clause = SelectFrom.getInstance().whereInRange(
1710: start, end);
1711: query.selectFrom(clause).action(baselineData(WORK, clause))
1712: .execute();
1713: return (long) ((DoubleValue) query.getActionVisitor())
1714: .getValue();
1715: }
1716:
1717: private boolean isFieldHidden(FieldContext fieldContext) {
1718: return fieldContext != null
1719: && !isInRange(fieldContext.getStart(), fieldContext
1720: .getEnd());
1721: }
1722:
1723: private boolean isEarnedValueFieldHidden(FieldContext fieldContext) {
1724: if (isFieldHidden(fieldContext))
1725: return true;
1726: return getStatusDate() < fieldContext.getStart();
1727: }
1728:
1729: private boolean isBaselineFieldHidden(int numBaseline,
1730: FieldContext fieldContext) {
1731: Assignment baselineAssignment = getBaselineAssignment(
1732: new Integer(numBaseline), false);
1733: if (baselineAssignment == null)
1734: return true;
1735:
1736: if (fieldContext == null) // the baseline exists, but no time range
1737: return false;
1738: return (fieldContext.getStart() >= baselineAssignment
1739: .getFinish() || fieldContext.getEnd() <= baselineAssignment
1740: .getStart());
1741: }
1742:
1743: /***************************************************************************************
1744: * Time Distributed Fields
1745: **************************************************************************************/
1746:
1747: public boolean fieldHideCost(FieldContext fieldContext) {
1748: return isFieldHidden(fieldContext);
1749: }
1750:
1751: public boolean fieldHideWork(FieldContext fieldContext) {
1752: return isFieldHidden(fieldContext);
1753: }
1754:
1755: public boolean fieldHideActualCost(FieldContext fieldContext) {
1756: return isFieldHidden(fieldContext);
1757: }
1758:
1759: public boolean fieldHideActualWork(FieldContext fieldContext) {
1760: return fieldHideWork(fieldContext);
1761: }
1762:
1763: public boolean fieldHideBaselineCost(int numBaseline,
1764: FieldContext fieldContext) {
1765: return isBaselineFieldHidden(numBaseline, fieldContext);
1766: }
1767:
1768: public boolean fieldHideBaselineWork(int numBaseline,
1769: FieldContext fieldContext) {
1770: return isBaselineFieldHidden(numBaseline, fieldContext);
1771: }
1772:
1773: public boolean fieldHideAcwp(FieldContext fieldContext) {
1774: return isFieldHidden(fieldContext);
1775: }
1776:
1777: public boolean fieldHideBac(FieldContext fieldContext) {
1778: return isFieldHidden(fieldContext);
1779: }
1780:
1781: public boolean fieldHideBcwp(FieldContext fieldContext) {
1782: return isEarnedValueFieldHidden(fieldContext);
1783: }
1784:
1785: public boolean fieldHideBcws(FieldContext fieldContext) {
1786: return isEarnedValueFieldHidden(fieldContext);
1787: }
1788:
1789: public boolean fieldHideCv(FieldContext fieldContext) {
1790: return isFieldHidden(fieldContext);
1791: }
1792:
1793: public boolean fieldHideSv(FieldContext fieldContext) {
1794: return isFieldHidden(fieldContext);
1795: }
1796:
1797: public boolean fieldHideEac(FieldContext fieldContext) {
1798: return isFieldHidden(fieldContext);
1799: }
1800:
1801: public boolean fieldHideVac(FieldContext fieldContext) {
1802: return isFieldHidden(fieldContext);
1803: }
1804:
1805: public boolean fieldHideCpi(FieldContext fieldContext) {
1806: return isFieldHidden(fieldContext);
1807: }
1808:
1809: public boolean fieldHideSpi(FieldContext fieldContext) {
1810: return isFieldHidden(fieldContext);
1811: }
1812:
1813: public boolean fieldHideCvPercent(FieldContext fieldContext) {
1814: return isFieldHidden(fieldContext);
1815: }
1816:
1817: public boolean fieldHideSvPercent(FieldContext fieldContext) {
1818: return isFieldHidden(fieldContext);
1819: }
1820:
1821: public boolean fieldHideTcpi(FieldContext fieldContext) {
1822: return isFieldHidden(fieldContext);
1823: }
1824:
1825: public double getCost(FieldContext fieldContext) {
1826: return cost(FieldContext.start(fieldContext), FieldContext
1827: .end(fieldContext));
1828: }
1829:
1830: public long work() {
1831: return work(FieldContext.defaultStart, FieldContext.defaultEnd);
1832: }
1833:
1834: public long getWork(FieldContext fieldContext) {
1835: long w = work(FieldContext.start(fieldContext), FieldContext
1836: .end(fieldContext));
1837: if (!isLabor()) {
1838: // System.out.println("work before setting non temporal" + DurationFormat.format(w));
1839: w = Duration.setAsNonTemporal(w);
1840: }
1841: return w;
1842: }
1843:
1844: public double getActualCost(FieldContext fieldContext) {
1845: return actualCost(FieldContext.start(fieldContext),
1846: FieldContext.end(fieldContext));
1847: }
1848:
1849: public long getActualWork(FieldContext fieldContext) {
1850: return actualWork(FieldContext.start(fieldContext),
1851: FieldContext.end(fieldContext));
1852: }
1853:
1854: public long getRemainingWork(FieldContext fieldContext) {
1855: return remainingWork(FieldContext.start(fieldContext),
1856: FieldContext.end(fieldContext));
1857: }
1858:
1859: public long getRemainingWork() {
1860: return getRemainingWork(null);
1861: }
1862:
1863: /* (non-Javadoc)
1864: * @see com.projity.pm.assignment.TimeDistributedFields#getBaselineCost(int, com.projity.field.FieldContext)
1865: */
1866: public double getBaselineCost(int numBaseline,
1867: FieldContext fieldContext) {
1868: return baselineCost(FieldContext.start(fieldContext),
1869: FieldContext.end(fieldContext));
1870: }
1871:
1872: /* (non-Javadoc)
1873: * @see com.projity.pm.assignment.TimeDistributedFields#getBaselineWork(int, com.projity.field.FieldContext)
1874: */
1875: public long getBaselineWork(int numBaseline,
1876: FieldContext fieldContext) {
1877: return baselineWork(FieldContext.start(fieldContext),
1878: FieldContext.end(fieldContext));
1879: }
1880:
1881: /***************************************************************************************
1882: * Earned Value Fields
1883: **************************************************************************************/
1884: public double getAcwp(FieldContext fieldContext) {
1885: return acwp(FieldContext.start(fieldContext), FieldContext
1886: .end(fieldContext));
1887: }
1888:
1889: public double getBac(FieldContext fieldContext) {
1890: return bac(FieldContext.start(fieldContext), FieldContext
1891: .end(fieldContext));
1892: }
1893:
1894: public double getBcwp(FieldContext fieldContext) {
1895: return bcwp(FieldContext.start(fieldContext), FieldContext
1896: .end(fieldContext));
1897: }
1898:
1899: public double getBcws(FieldContext fieldContext) {
1900: return bcws(FieldContext.start(fieldContext), FieldContext
1901: .end(fieldContext));
1902: }
1903:
1904: public double getCv(FieldContext fieldContext) {
1905: return EarnedValueCalculator.getInstance().cv(this ,
1906: FieldContext.start(fieldContext),
1907: FieldContext.end(fieldContext));
1908: }
1909:
1910: public double getSv(FieldContext fieldContext) {
1911: return EarnedValueCalculator.getInstance().sv(this ,
1912: FieldContext.start(fieldContext),
1913: FieldContext.end(fieldContext));
1914: }
1915:
1916: public double getEac(FieldContext fieldContext) {
1917: return EarnedValueCalculator.getInstance().eac(this ,
1918: FieldContext.start(fieldContext),
1919: FieldContext.end(fieldContext));
1920: }
1921:
1922: public double getVac(FieldContext fieldContext) {
1923: return EarnedValueCalculator.getInstance().vac(this ,
1924: FieldContext.start(fieldContext),
1925: FieldContext.end(fieldContext));
1926: }
1927:
1928: public double getCpi(FieldContext fieldContext) {
1929: return EarnedValueCalculator.getInstance().cpi(this ,
1930: FieldContext.start(fieldContext),
1931: FieldContext.end(fieldContext));
1932: }
1933:
1934: public double getSpi(FieldContext fieldContext) {
1935: return EarnedValueCalculator.getInstance().spi(this ,
1936: FieldContext.start(fieldContext),
1937: FieldContext.end(fieldContext));
1938: }
1939:
1940: public double getCsi(FieldContext fieldContext) {
1941: return EarnedValueCalculator.getInstance().csi(this ,
1942: FieldContext.start(fieldContext),
1943: FieldContext.end(fieldContext));
1944: }
1945:
1946: public double getCvPercent(FieldContext fieldContext) {
1947: return EarnedValueCalculator.getInstance().cvPercent(this ,
1948: FieldContext.start(fieldContext),
1949: FieldContext.end(fieldContext));
1950: }
1951:
1952: public double getSvPercent(FieldContext fieldContext) {
1953: return EarnedValueCalculator.getInstance().svPercent(this ,
1954: FieldContext.start(fieldContext),
1955: FieldContext.end(fieldContext));
1956: }
1957:
1958: public double getTcpi(FieldContext fieldContext) {
1959: return EarnedValueCalculator.getInstance().tcpi(this ,
1960: FieldContext.start(fieldContext),
1961: FieldContext.end(fieldContext));
1962: }
1963:
1964: /**
1965: * @return
1966: */
1967: public Date getCreated() {
1968: return hasKey.getCreated();
1969: }
1970:
1971: /**
1972: * @return
1973: */
1974: public long getId() {
1975: return hasKey.getId();
1976: }
1977:
1978: /**
1979: * @return
1980: */
1981: public String getName() {
1982: return "" + getLeft() + " " + getRight();
1983: }
1984:
1985: /**
1986: * @param context
1987: * @return
1988: */
1989: public String getName(FieldContext context) {
1990: if (context == null)
1991: return "???"; //fix
1992: if (context.isLeftAssociation())
1993: return getRight().toString();
1994: else
1995: return getLeft().toString();
1996: }
1997:
1998: /**
1999: * @return
2000: */
2001: public long getUniqueId() {
2002: return hasKey.getUniqueId();
2003: }
2004:
2005: // public void setNew(boolean isNew) {
2006: // hasKey.setNew(isNew);
2007: // }
2008: /**
2009: * @param created
2010: */
2011: public void setCreated(Date created) {
2012: hasKey.setCreated(created);
2013: }
2014:
2015: /**
2016: * @param id
2017: */
2018: public void setId(long id) {
2019: hasKey.setId(id);
2020: }
2021:
2022: /**
2023: * @param name
2024: */
2025: public void setName(String name) {
2026: hasKey.setName(name);
2027: }
2028:
2029: /**
2030: * @param id
2031: */
2032: public void setUniqueId(long id) {
2033: hasKey.setUniqueId(id);
2034: }
2035:
2036: public Document getDocument() {
2037: return getProject();
2038: }
2039:
2040: public Document getDocument(boolean leftObject) {
2041: return (leftObject) ? getTask().getDocument() : getResource()
2042: .getDocument();
2043: }
2044:
2045: public Query workQuery() {
2046: Query query = Query.getInstance();
2047: SelectFrom clause = SelectFrom.getInstance();
2048: query.selectFrom(clause).action(work(clause));
2049: return query;
2050: }
2051:
2052: public void calcDataBetween(Object type, HasStartAndEnd generator,
2053: CalculatedValues values) {
2054: SelectFrom clause = SelectFrom.getInstance();
2055: AssignmentFieldFunctor dataFunctor = getDataSelect(type,
2056: clause, false);
2057: calcDataBetween(dataFunctor, clause, generator, values);
2058: }
2059:
2060: public static void calcResourceAvailabilityBetween(
2061: Resource resource, HasStartAndEnd generator,
2062: CalculatedValues values) {
2063: SelectFrom clause = SelectFrom.getInstance();
2064: AssignmentFieldFunctor dataFunctor = resourceAvailability(
2065: clause, resource);
2066: calcDataBetween(dataFunctor, clause, generator, values);
2067: }
2068:
2069: public static void calcDataBetween(
2070: AssignmentFieldFunctor dataFunctor, SelectFrom clause,
2071: HasStartAndEnd generator, CalculatedValues values) {
2072: if (generator != null)
2073: clause.whereInRange(generator.getStart(), generator
2074: .getEnd()); // automatically also adds a generator to limit range
2075:
2076: CalculatedValuesFunctor visitor = CalculatedValuesFunctor
2077: .getInstance(dataFunctor, values,
2078: (TimeIteratorGenerator) generator);
2079:
2080: Query query = Query.getInstance();
2081: query.selectFrom(clause);
2082: if (generator != null
2083: && generator instanceof TimeIteratorGenerator) {
2084: query.groupBy((TimeIteratorGenerator) generator).action(
2085: visitor);
2086: } else {
2087: clause.select(visitor); // replaces other one
2088: }
2089: query.execute();
2090: }
2091:
2092: public long getResourceAvailability() {
2093: return detail.getResourceAvailability();
2094: }
2095:
2096: public Collection childrenToRollup() {
2097: return null;
2098: }
2099:
2100: /* (non-Javadoc)
2101: * @see com.projity.pm.assignment.Allocation#getMostLoadedAssignmentUnits()
2102: */
2103: public double getMostLoadedAssignmentUnits() {
2104: return getUnits();
2105: }
2106:
2107: /**
2108: * @return
2109: */
2110:
2111: public void makeContourPersonal() {
2112: // if (getWorkContour().isPersonal())
2113: // return;
2114: Object type = WORK;
2115: ContourBucketIntervalGenerator contourGenerator = contourGeneratorInstance(type); //contour
2116: PersonalContourMaker contourBuilder = PersonalContourMaker
2117: .getInstance(this , contourGenerator);
2118: Query.getInstance().selectFrom(
2119: SelectFrom.getInstance().select(contourBuilder).from(
2120: contourGenerator).all()).execute();
2121:
2122: newDetail().setContour(type, contourBuilder.getList());
2123: detail.recalculateDuration();
2124: }
2125:
2126: /**
2127: * Clone the detail and set it
2128: * @return cloned assignment detail
2129: */
2130: private AssignmentDetail newDetail() {
2131: //TODO store off detail for undo
2132: //System.out.println("before clone " + new Date(detail.getStop()));
2133: detail = (AssignmentDetail) detail.clone();
2134: if (getTask() != null)
2135: getTask().setDirty(true);
2136: setDirty(true);
2137: //System.out.println("after clone " + new Date(detail.getStop()));
2138: return detail;
2139: }
2140:
2141: public long getElapsedDuration() {
2142: return detail.getElapsedDuration();
2143: }
2144:
2145: public long getDuration() {
2146: return detail.getDuration();
2147: }
2148:
2149: public double getPercentComplete() {
2150: return detail.getPercentComplete();
2151: }
2152:
2153: public void setPercentComplete(double percentComplete) {
2154: newDetail().setPercentComplete(percentComplete);
2155: }
2156:
2157: public long getEnd() {
2158: return detail.getEnd();
2159: }
2160:
2161: public void setEnd(long end) {
2162: detail.setEnd(end);
2163: }
2164:
2165: public long getActualStart() {
2166: return detail.getActualStart();
2167: }
2168:
2169: public void setActualStart(long actualStart) {
2170: newDetail().setActualStart(actualStart);
2171: }
2172:
2173: public void setRemainingDuration(long remainingDuration) {
2174: newDetail().setRemainingDuration(remainingDuration);
2175: }
2176:
2177: public long getActualFinish() {
2178: return detail.getActualFinish();
2179: }
2180:
2181: public void setActualFinish(long actualFinish) {
2182: newDetail().setActualFinish(actualFinish);
2183: }
2184:
2185: public long getResume() {
2186: return detail.getResume();
2187: }
2188:
2189: public long getActualDuration() {
2190: return detail.getActualDuration();
2191: }
2192:
2193: public void setActualDuration(long actualDuration) {
2194: newDetail().setActualDuration(actualDuration);
2195: }
2196:
2197: public long getRemainingDuration() {
2198: return detail.getRemainingDuration();
2199: }
2200:
2201: public void setResume(long resume) {
2202: newDetail().setResume(resume);
2203: }
2204:
2205: public long getStop() {
2206: return detail.getStop();
2207: }
2208:
2209: public void setStop(long stop) {
2210: if (stop < getStart()) // make sure in assignment's range
2211: stop = getStart();
2212: else if (stop > getEnd())
2213: stop = getEnd();
2214: long currentStop = getStop();
2215: if (currentStop == stop)
2216: return;
2217: if (stop < currentStop) // if uncompleting
2218: newDetail().removeFillerAfter(stop);
2219: if (currentStop > 0 && getDependencyStart() > currentStop
2220: && getDependencyStart() < stop) {// if setting stop incorporates split due to dependency
2221: makeContourPersonal();
2222:
2223: }
2224: newDetail().setStop(stop);
2225: }
2226:
2227: public void clearDuration() {
2228: newDetail().clearDuration();
2229: }
2230:
2231: public long getDependencyStart() {
2232: return detail.getDependencyStart();
2233: }
2234:
2235: public void setDependencyStart(long dependencyStart) {
2236: detail.setDependencyStart(dependencyStart); //TODO is it ok to modify schedule directly? Should be if it is transient
2237: }
2238:
2239: //for gantt bar formula
2240: //probably to add in a common interface with task
2241: public boolean isNormal() {
2242: return false;
2243: }
2244:
2245: public boolean isCritical() {
2246: return false;
2247: }
2248:
2249: public boolean isSummary() {
2250: return false;
2251: }
2252:
2253: public boolean isMilestone() {
2254: return false;
2255: }
2256:
2257: public boolean isAssignment() {
2258: return true;
2259: }
2260:
2261: public void setTaskSchedule(TaskSchedule taskSchedule) {
2262: newDetail().setTaskSchedule(taskSchedule);
2263: }
2264:
2265: public boolean inProgress() {
2266: double percentComplete = getPercentComplete();
2267: return (percentComplete > 0.0D && percentComplete < 1.0D);
2268: }
2269:
2270: public boolean isComplete() {
2271: return getPercentComplete() == 1.0D;
2272: }
2273:
2274: public boolean isUnstarted() {
2275: return getPercentComplete() == 0.0D;
2276: }
2277:
2278: public boolean isMine() {
2279: return getResource().isMe();
2280: }
2281:
2282: private void writeObject(ObjectOutputStream s) throws IOException {
2283: s.defaultWriteObject();
2284: hasKey.serialize(s);
2285: }
2286:
2287: private void readObject(ObjectInputStream s) throws IOException,
2288: ClassNotFoundException {
2289: s.defaultReadObject();
2290: hasKey = HasKeyImpl.deserialize(s, this );
2291: // barClosureInstance = new BarClosure();
2292: }
2293:
2294: public Object clone() {
2295: try {
2296: Assignment a = (Assignment) super .clone();
2297: a.hasKey = new HasKeyImpl(true, a);
2298: a.setName(getName());
2299: // barClosureInstance = new BarClosure();
2300: a.detail = (AssignmentDetail) detail.clone();
2301: return a;
2302: } catch (CloneNotSupportedException e) {
2303: throw new InternalError();
2304: }
2305: }
2306:
2307: public Object cloneWithTask(Task task) {
2308: Assignment a = (Assignment) clone();
2309: a.detail.setTask(task);
2310: return a;
2311: }
2312:
2313: public Object cloneWithResource(Resource resource) {
2314: Assignment a = (Assignment) clone();
2315: a.detail.setResource(resource);
2316: return a;
2317: }
2318:
2319: public Object cloneWithResourceAndTask(Resource resource, Task task) {
2320: Assignment a = (Assignment) clone();
2321: a.detail.setResource(resource);
2322: a.detail.setTask(task);
2323: return a;
2324: }
2325:
2326: public void convertToBaselineAssignment(boolean useDefaultCalendar) {
2327: detail.convertToBaselineAssignment(useDefaultCalendar);
2328: }
2329:
2330: /**
2331: * See if assignment duration is empty
2332: * @return
2333: */
2334: public boolean hasDuration() {
2335: return detail.hasDuration();
2336: }
2337:
2338: public void setRemainingWork(long remainingWork,
2339: FieldContext fieldContext) {
2340: setActualWork(getWork(fieldContext)
2341: - Duration.millis(remainingWork), fieldContext);
2342: }
2343:
2344: public boolean isReadOnlyWork(FieldContext fieldContext) {
2345:
2346: if (fieldContext == null) {
2347: return isMaterial(); //// material resource assignments cannot have their total work modified
2348: }
2349: // see if there is some calendar time
2350: return !isActiveBetween(fieldContext.getStart(), fieldContext
2351: .getEnd());
2352: }
2353:
2354: public boolean isActiveBetween(long start, long end) {
2355: return getEffectiveWorkCalendar().compare(end, start, false) > 0;
2356:
2357: }
2358:
2359: public boolean isReadOnlyActualWork(FieldContext fieldContext) {
2360: return false;
2361: }
2362:
2363: public boolean isReadOnlyRemainingWork(FieldContext fieldContext) {
2364: return isReadOnlyWork(fieldContext);
2365: }
2366:
2367: public double getFixedCost(FieldContext fieldContext) {
2368: return 0;
2369: }
2370:
2371: public double getActualFixedCost(FieldContext fieldContext) {
2372: return 0;
2373: }
2374:
2375: public boolean fieldHideActualFixedCost(FieldContext fieldContext) {
2376: return true;
2377: }
2378:
2379: public double fixedCost(long start, long end) {
2380: return ((Task) getTask()).fixedCost(start, end);
2381: }
2382:
2383: public double actualFixedCost(long start, long end) {
2384: return ((Task) getTask()).actualFixedCost(start, end);
2385: }
2386:
2387: /* (non-Javadoc)
2388: * @see com.projity.pm.assignment.TimeDistributedFields#setFixedCost(double, com.projity.field.FieldContext)
2389: */
2390: public void setFixedCost(double fixedCost, FieldContext fieldContext) {
2391: }
2392:
2393: public boolean isReadOnlyFixedCost(FieldContext fieldContext) {
2394: return true;
2395: }
2396:
2397: public boolean isLabor() {
2398: return getResource().isLabor();
2399: }
2400:
2401: public boolean isTemporal() {
2402: return detail.isTemporal();
2403: }
2404:
2405: /**
2406: * @param timeUnit
2407: */
2408: public void setRateUnit(int timeUnit) {
2409: detail.setRateUnit(timeUnit);
2410: getTask().updateCachedDuration(); // needed for checking if milestone - happens when changing units in dialog
2411: }
2412:
2413: public final Rate getRate() {
2414: return detail.getRate();
2415: }
2416:
2417: private int getTaskSchedulingType() {
2418: return ((NormalTask) getTask()).getSchedulingType();
2419: }
2420:
2421: public String valuesString() {
2422: return "Duration=" + DurationFormat.format(getDuration())
2423: + " Work=" + DurationFormat.format(getWork(null))
2424: + " Units=" + getUnits() + " contour="
2425: + getWorkContour().toString(getDuration());
2426:
2427: }
2428:
2429: public final void setRate(Rate rate) {
2430:
2431: if (rate.isNonTemporal() == getRate().isNonTemporal()) { // normal case. If a material resource is modified from temporal to non (or vice versa), just set the rate
2432: double old = getRate().getValue();
2433: if (rate.getValue() == old) // if no change
2434: return;
2435: long oldRemaining = getRemainingDuration();
2436: newDetail();
2437: double multiplier = rate.getValue() / old;
2438: if (getPercentComplete() > 0)
2439: makeContourPersonal();
2440: detail.adjustRemainingUnits(rate.getValue());
2441: if (getTaskSchedulingType() != SchedulingType.FIXED_DURATION)
2442: detail
2443: .adjustRemainingDuration((long) (oldRemaining / multiplier));
2444: }
2445: detail.setRate(rate);
2446:
2447: getTask().updateCachedDuration(); // needed for checking if milestone
2448:
2449: }
2450:
2451: // public boolean isNew() {
2452: // return hasKey.isNew();
2453: // }
2454: public double getRemainingCost(FieldContext fieldContext) {
2455: return getCost(fieldContext) - getActualCost(fieldContext);
2456: }
2457:
2458: public void invalidateAssignmentCalendar() {
2459: detail.invalidateAssignmentCalendar();
2460: }
2461:
2462: public boolean isSubproject() {
2463: return false;
2464: }
2465:
2466: public boolean isJustModified() {
2467: Task task = getTask();
2468: if (task == null)
2469: return false;
2470: else
2471: return task.isJustModified();
2472: }
2473:
2474: public boolean isInvalidIntersectionCalendar() {
2475: return detail.isInvalidIntersectionCalendar();
2476: }
2477:
2478: private void moveDelayToContour() {
2479: // Remove delay and add at beginning of contour
2480: long oldDelay = calcTotalDelay();
2481: if (oldDelay == 0)
2482: return;
2483: makeContourPersonal();
2484: setTotalDelay(0);
2485: setDurationMillis(getDurationMillis() + oldDelay);
2486: AbstractContour contour = PersonalContour.addEmptyBucket(
2487: getWorkContour(), oldDelay, false); // add empty space before
2488: newDetail().setWorkContour(contour);
2489: }
2490:
2491: private void extractDelayFromContour(PersonalContour newContour) {
2492: long delay = newContour.extractDelay(); // the case when adding 0 units at start
2493: if (delay > 0) {
2494: newDetail();
2495: detail.setDelay(delay);
2496: // detail.recalculateDuration();
2497: }
2498: }
2499:
2500: public void setComplete(boolean complete) {
2501: ScheduleUtil.setComplete(this , complete);
2502: }
2503:
2504: public String getProjectName() {
2505: return getOwningProject().getName();
2506: }
2507:
2508: public Project getProject() {
2509: return getTask().getProject();
2510: }
2511:
2512: public Project getOwningProject() {
2513: Project p = getTask().getOwningProject();
2514: if (p == null)
2515: p = getProject();
2516: return p;
2517: }
2518:
2519: public final int getTimesheetStatus() {
2520: Assignment ts = getTimesheetAssignment();
2521: if (ts != null)
2522: return ts.timesheetStatus;
2523: return timesheetStatus;
2524: }
2525:
2526: public final void setTimesheetStatus(int timesheetStatus) {
2527: this .timesheetStatus = timesheetStatus;
2528: }
2529:
2530: public final long getLastTimesheetUpdate() {
2531: return lastTimesheetUpdate;
2532: }
2533:
2534: public final void setLastTimesheetUpdate(long lastTimesheetUpdate) {
2535: this .lastTimesheetUpdate = lastTimesheetUpdate;
2536: ;
2537: }
2538:
2539: public boolean isPendingTimesheetUpdate() {
2540: return (getTimesheetStatus() == TimesheetStatus.VALIDATED);
2541: }
2542:
2543: public String getTimesheetStatusName() {
2544: return TimesheetHelper
2545: .getTimesheetStatusName(getTimesheetStatus());
2546: }
2547:
2548: public final boolean isTimesheetAssignment() {
2549: return timesheetAssignment;
2550: }
2551:
2552: public final void setTimesheetAssignment(boolean timesheetAssignment) {
2553: this .timesheetAssignment = timesheetAssignment;
2554: }
2555:
2556: public Assignment getTimesheetAssignment() {
2557: if (isTimesheetAssignment())
2558: return this ;
2559: return detail.getBaselineAssignment(Snapshottable.TIMESHEET,
2560: false);
2561: }
2562:
2563: public long getTimesheetStart() {
2564: return getCachedStart().getTime();
2565: }
2566:
2567: public long getTimesheetFinish() {
2568: return getCachedEnd().getTime();
2569: }
2570:
2571: public boolean isTimesheetEditable() {
2572: return getTimesheetStatus() != TimesheetStatus.INTEGRATED;
2573: }
2574:
2575: public boolean isTimesheetEntered() {
2576: return getTimesheetStatus() != TimesheetStatus.ENTERED;
2577: }
2578:
2579: public boolean isTimesheetValidated() {
2580: return getTimesheetStatus() != TimesheetStatus.VALIDATED;
2581: }
2582:
2583: public boolean isTimesheetRejected() {
2584: return getTimesheetStatus() != TimesheetStatus.REJECTED;
2585: }
2586:
2587: public boolean copyFieldsFromTimesheet(Collection fieldArray) {
2588: Assignment ts = getTimesheetAssignment();
2589: if (ts == null)
2590: return false;
2591: if (ts.getTimesheetStatus() != TimesheetStatus.VALIDATED) // only incorporate validated data
2592: return false;
2593: Field.copyData(fieldArray, this , ts);
2594: return true;
2595: }
2596:
2597: public boolean applyTimesheet(Collection fieldArray,
2598: long timesheetUpdateDate) {
2599: boolean updated = copyFieldsFromTimesheet(fieldArray);
2600: if (updated) {
2601: setTimesheetStatus(TimesheetStatus.INTEGRATED);
2602: this .lastTimesheetUpdate = timesheetUpdateDate;
2603: Assignment ts = getTimesheetAssignment();
2604: ts.setTimesheetStatus(TimesheetStatus.INTEGRATED);
2605: ts.lastTimesheetUpdate = timesheetUpdateDate;
2606:
2607: }
2608: return updated;
2609: }
2610:
2611: public String getTimesheetStatusStyle() { // used for display style in web
2612: return TimesheetHelper
2613: .getTimesheetStatusStyle(getTimesheetStatus());
2614: }
2615:
2616: private transient boolean dirty = true;
2617:
2618: public boolean isDirty() {
2619: return dirty;
2620: }
2621:
2622: public void setDirty(boolean dirty) {
2623: this .dirty = dirty;
2624: }
2625:
2626: public final Date getCachedEnd() {
2627: return cachedEnd;
2628: }
2629:
2630: public final void setCachedEnd(Date savedEnd) {
2631: this .cachedEnd = savedEnd;
2632: }
2633:
2634: public final Date getCachedStart() {
2635: return cachedStart;
2636: }
2637:
2638: public final void setCachedStart(Date savedStart) {
2639: this .cachedStart = savedStart;
2640: }
2641:
2642: public final int getWorkflowState() {
2643: return workflowState;
2644: }
2645:
2646: public final void setWorkflowState(int workflowState) {
2647: this .workflowState = workflowState;
2648: }
2649:
2650: public void setTaskAndResource(Task task, Resource resource) {
2651: detail.setTask(task);
2652: detail.setResource(resource);
2653: }
2654:
2655: public long getDeadline() { // needed for indicators
2656: return 0;
2657: }
2658:
2659: public final long getEarliestStop() {
2660: return detail.getEarliestStop();
2661: }
2662:
2663: public final long getCompletedThrough() {
2664: return detail.getCompletedThrough();
2665: }
2666:
2667: public void setCompletedThrough(long completedThrough) {
2668: setStop(completedThrough);
2669: }
2670:
2671: public void replace(Object newOne, boolean leftObject) {
2672: if (leftObject)
2673: newDetail().setTask((Task) newOne);
2674: else
2675: newDetail().setResource((Resource) newOne);
2676: }
2677:
2678: public long getFinishOffset() {
2679: return EarnedValueCalculator.getInstance()
2680: .getFinishOffset(this );
2681: }
2682:
2683: public long getStartOffset() {
2684: return EarnedValueCalculator.getInstance().getStartOffset(this );
2685: }
2686:
2687: public String getTimeUnitLabel() {
2688: return getResource().getTimeUnitLabel();
2689: }
2690:
2691: public boolean isMaterial() {
2692: return getResource().isMaterial();
2693: }
2694:
2695: public RateFormat getRateFormat() {
2696: return getResource().getRateFormat();
2697: }
2698:
2699: public ImageLink getBudgetStatusIndicator() {
2700: return EarnedValueCalculator.getInstance()
2701: .getBudgetStatusIndicator(getCpi(null));
2702: }
2703:
2704: public ImageLink getScheduleStatusIndicator() {
2705: return EarnedValueCalculator.getInstance()
2706: .getBudgetStatusIndicator(getSpi(null));
2707: }
2708:
2709: public Object backupDetail() {
2710: return detail.backupDetail();
2711: }
2712:
2713: //use when updating only this assignment
2714: public void restoreDetail(Object source, Object detail,
2715: boolean isChild) {
2716: restoreDetail(detail);
2717: getTask().recalculate(source);
2718: getTask().updateCachedDuration();
2719: }
2720:
2721: public void restoreDetail(Object detail) {
2722: this .detail = (AssignmentDetail) detail;
2723: }
2724:
2725: public String getDelegatedToName() {
2726: return getTask().getDelegatedToName();
2727: }
2728:
2729: public boolean isLocal() {
2730: return true;
2731: }
2732:
2733: public void setLocal(boolean local) {
2734: }
2735:
2736: public void renumber() {
2737: hasKey.renumber();
2738: }
2739:
2740: public int getCostRateIndex() {
2741: return detail.getCostRateIndex();
2742: }
2743:
2744: public void setCostRateIndex(int val) {
2745: newDetail().setCostRateIndex(val);
2746: }
2747:
2748: public String getUniqueIdString() {
2749: return getTask().getUniqueId() + "."
2750: + getResource().getUniqueId();
2751: }
2752:
2753: }
|