diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/util/Emitter.java b/ganttzk/src/main/java/org/zkoss/ganttz/util/Emitter.java index ffbda8aff..b671502f2 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/util/Emitter.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/util/Emitter.java @@ -23,7 +23,7 @@ import java.util.ArrayList; import java.util.List; /** - * + * * @author Óscar González Fernández */ public class Emitter { diff --git a/libreplan-business/src/main/java/org/libreplan/business/externalcompanies/daos/CustomerCommunicationDAO.java b/libreplan-business/src/main/java/org/libreplan/business/externalcompanies/daos/CustomerCommunicationDAO.java new file mode 100644 index 000000000..ed35d33bc --- /dev/null +++ b/libreplan-business/src/main/java/org/libreplan/business/externalcompanies/daos/CustomerCommunicationDAO.java @@ -0,0 +1,54 @@ +/* + * This file is part of LibrePlan + * + * Copyright (C) 2010-2011 WirelessGalicia, S.L. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.libreplan.business.externalcompanies.daos; + +import java.util.List; + +import org.hibernate.Criteria; +import org.hibernate.criterion.Restrictions; +import org.libreplan.business.common.daos.GenericDAOHibernate; +import org.libreplan.business.externalcompanies.entities.CustomerCommunication; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Repository; + +/** + * Hibernate DAO for {@link CustomerCommunication} + * + * @author Susana Montes Pedreira + */ +@Repository +@Scope(BeanDefinition.SCOPE_SINGLETON) +public class CustomerCommunicationDAO extends GenericDAOHibernate +implements ICustomerCommunicationDAO { + + @Override + public List getAll(){ + return list(CustomerCommunication.class); + } + + @Override + public List getAllNotReviewed(){ + Criteria c = getSession().createCriteria(CustomerCommunication.class); + c.add(Restrictions.eq("reviewed", false)); + return c.list(); + } + +} diff --git a/libreplan-business/src/main/java/org/libreplan/business/externalcompanies/daos/ICustomerCommunicationDAO.java b/libreplan-business/src/main/java/org/libreplan/business/externalcompanies/daos/ICustomerCommunicationDAO.java new file mode 100644 index 000000000..e855b9e4c --- /dev/null +++ b/libreplan-business/src/main/java/org/libreplan/business/externalcompanies/daos/ICustomerCommunicationDAO.java @@ -0,0 +1,39 @@ +/* + * This file is part of LibrePlan + * + * Copyright (C) 2011 WirelessGalicia, S.L. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.libreplan.business.externalcompanies.daos; + +import java.util.List; + +import org.libreplan.business.common.daos.IGenericDAO; +import org.libreplan.business.externalcompanies.entities.CustomerCommunication; + + +/** + * Interface of the DAO for {@link CustomerCommunication} + * + * @author Susana Montes Pedreira + */ +public interface ICustomerCommunicationDAO extends IGenericDAO { + + List getAll(); + + List getAllNotReviewed(); + +} \ No newline at end of file diff --git a/libreplan-business/src/main/java/org/libreplan/business/externalcompanies/entities/CommunicationType.java b/libreplan-business/src/main/java/org/libreplan/business/externalcompanies/entities/CommunicationType.java new file mode 100644 index 000000000..48ce55856 --- /dev/null +++ b/libreplan-business/src/main/java/org/libreplan/business/externalcompanies/entities/CommunicationType.java @@ -0,0 +1,47 @@ +/* + * This file is part of LibrePlan + * + * Copyright (C) 2011 WirelessGalicia, S.L. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.libreplan.business.externalcompanies.entities; + +import static org.libreplan.business.i18n.I18nHelper._; + +/** + * Enum for specified the type of {@link CustomerCommunication} + * + * @author Susana Montes Pedreira + */ +public enum CommunicationType { + + NEW_PROJECT(_("New project")), PROGRESS_UPDATE(_("Progress Update")), UPDATE_DELIVERING_DATE( + _("Update Delivering Date")), END_DATE_UPDATE(_("End date update")); + + private String description; + + private CommunicationType(String description) { + this.description = description; + } + + public String toString() { + return this.description; + } + + public static CommunicationType getDefault() { + return NEW_PROJECT; + } +} diff --git a/libreplan-business/src/main/java/org/libreplan/business/externalcompanies/entities/CustomerCommunication.java b/libreplan-business/src/main/java/org/libreplan/business/externalcompanies/entities/CustomerCommunication.java new file mode 100644 index 000000000..4e6f4ed7b --- /dev/null +++ b/libreplan-business/src/main/java/org/libreplan/business/externalcompanies/entities/CustomerCommunication.java @@ -0,0 +1,136 @@ +/* + * This file is part of LibrePlan + * + * Copyright (C) 2011 WirelessGalicia, S.L. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.libreplan.business.externalcompanies.entities; + +import java.util.Date; + +import org.hibernate.validator.NotEmpty; +import org.hibernate.validator.NotNull; +import org.libreplan.business.common.BaseEntity; +import org.libreplan.business.orders.entities.Order; + +/** + * Entity CustomerCommunication + * + * @author Susana Montes Pedreira + */ +public class CustomerCommunication extends BaseEntity { + + private Date deadline; + + private CommunicationType communicationType; + + private Date communicationDate; + + private Boolean reviewed = false; + + private Order order; + + protected CustomerCommunication() { + this.setCommunicationDate(new Date()); + } + + private CustomerCommunication(Date deadline) { + this.setDeadline(deadline); + this.setCommunicationDate(new Date()); + } + + public static CustomerCommunication create() { + return create(new CustomerCommunication()); + } + + public static CustomerCommunication createToday(Date deadline) { + return create(new CustomerCommunication(deadline)); + } + + public static CustomerCommunication createTodayNewProject(Date deadline) { + return create(new CustomerCommunication(deadline, new Date(), + CommunicationType.NEW_PROJECT)); + } + + protected CustomerCommunication(Date deadline, Date communicationDate, + CommunicationType communicationType) { + this.setDeadline(deadline); + this.setCommunicationDate(communicationDate); + this.setCommunicationType(communicationType); + } + + protected CustomerCommunication(Date deadline, Date communicationDate, + CommunicationType type, Order order) { + this.setDeadline(deadline); + this.setCommunicationDate(communicationDate); + this.setCommunicationType(type); + this.setOrder(order); + } + + public static CustomerCommunication create(Date deadline, + Date communicationDate, CommunicationType communicationType) { + return (CustomerCommunication) create(new CustomerCommunication( + deadline, communicationDate, communicationType)); + } + + public static CustomerCommunication create(Date deadline, + Date communicationDate, CommunicationType type, + Order order) { + return (CustomerCommunication) create(new CustomerCommunication( + deadline, communicationDate, type, order)); + } + + public void setDeadline(Date deadline) { + this.deadline = deadline; + } + + public Date getDeadline() { + return deadline; + } + + public void setCommunicationType(CommunicationType type) { + this.communicationType = type; + } + + public CommunicationType getCommunicationType() { + return this.communicationType; + } + + public void setCommunicationDate(Date communicationDate) { + this.communicationDate = communicationDate; + } + + public Date getCommunicationDate() { + return communicationDate; + } + + public void setReviewed(Boolean reviewed) { + this.reviewed = reviewed; + } + + public Boolean getReviewed() { + return reviewed; + } + + public void setOrder(Order order) { + this.order = order; + } + + @NotNull(message = "order not specified") + public Order getOrder() { + return order; + } +} diff --git a/libreplan-business/src/main/java/org/libreplan/business/externalcompanies/entities/DeadlineCommunication.java b/libreplan-business/src/main/java/org/libreplan/business/externalcompanies/entities/DeadlineCommunication.java new file mode 100644 index 000000000..8b8b39a67 --- /dev/null +++ b/libreplan-business/src/main/java/org/libreplan/business/externalcompanies/entities/DeadlineCommunication.java @@ -0,0 +1,72 @@ +/* + * This file is part of LibrePlan + * + * Copyright (C) 2011 WirelessGalicia, S.L. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.libreplan.business.externalcompanies.entities; + +import java.util.Date; +import org.hibernate.validator.NotEmpty; +import org.hibernate.validator.NotNull; +import org.libreplan.business.common.BaseEntity; + +/** + * Entity DeadlineCommunication + * + * @author Susana Montes Pedreira + */ +public class DeadlineCommunication extends BaseEntity implements DeliverDate{ + + private Date saveDate; + + private Date deliverDate; + + protected DeadlineCommunication(){ + + } + + private DeadlineCommunication(Date saveDate, Date deliverDate){ + this.setSaveDate(saveDate); + this.setDeliverDate(deliverDate); + } + + public static DeadlineCommunication create(Date saveDate, Date deliverDate){ + return create(new DeadlineCommunication(saveDate, deliverDate)); + } + + public static DeadlineCommunication create() { + return create(new DeadlineCommunication()); + } + + public void setSaveDate(Date saveDate) { + this.saveDate = saveDate; + } + + @NotNull + public Date getSaveDate() { + return saveDate; + } + + public void setDeliverDate(Date deliverDate) { + this.deliverDate = deliverDate; + } + + public Date getDeliverDate() { + return deliverDate; + } + +} diff --git a/libreplan-business/src/main/java/org/libreplan/business/externalcompanies/entities/DeliverDate.java b/libreplan-business/src/main/java/org/libreplan/business/externalcompanies/entities/DeliverDate.java new file mode 100644 index 000000000..2ee7b3abb --- /dev/null +++ b/libreplan-business/src/main/java/org/libreplan/business/externalcompanies/entities/DeliverDate.java @@ -0,0 +1,32 @@ +/* + * This file is part of LibrePlan + * + * Copyright (C) 2011 WirelessGalicia, S.L. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.libreplan.business.externalcompanies.entities; + +import java.util.Date; + +import org.libreplan.business.planner.entities.SubcontractorCommunication; + +/** + * Interface for {@link SubcontractorDeliverDate} and {@link DeadlineCommunication} + * + * @author Susana Montes Pedreira + */ +public interface DeliverDate { + public Date getSaveDate(); +} diff --git a/libreplan-business/src/main/java/org/libreplan/business/externalcompanies/entities/DeliverDateComparator.java b/libreplan-business/src/main/java/org/libreplan/business/externalcompanies/entities/DeliverDateComparator.java new file mode 100644 index 000000000..2d5b4b2d7 --- /dev/null +++ b/libreplan-business/src/main/java/org/libreplan/business/externalcompanies/entities/DeliverDateComparator.java @@ -0,0 +1,50 @@ +/* + * This file is part of LibrePlan + * + * Copyright (C) 2011 WirelessGalicia, S.L. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.libreplan.business.externalcompanies.entities; + +import java.util.Comparator; + +import org.libreplan.business.externalcompanies.entities.DeadlineCommunication; + +/** +* Comparator to {@link DeliverDate} interface +* +* @author Susana Montes Pedreira +*/ + +public class DeliverDateComparator implements Comparator { + + public DeliverDateComparator(){ + } + + @Override + public int compare(DeliverDate arg0, DeliverDate arg1) { + if (arg0.getSaveDate() == arg1.getSaveDate()) { + return 0; + } + if (arg0.getSaveDate() == null) { + return -1; + } + if (arg1.getSaveDate() == null) { + return 1; + } + return arg1.getSaveDate().compareTo(arg0.getSaveDate()); + } + } diff --git a/libreplan-business/src/main/java/org/libreplan/business/externalcompanies/entities/EndDateCommunication.java b/libreplan-business/src/main/java/org/libreplan/business/externalcompanies/entities/EndDateCommunication.java new file mode 100644 index 000000000..45d0bb506 --- /dev/null +++ b/libreplan-business/src/main/java/org/libreplan/business/externalcompanies/entities/EndDateCommunication.java @@ -0,0 +1,93 @@ +/* + * This file is part of LibrePlan + * + * Copyright (C) 2012 WirelessGalicia, S.L. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.libreplan.business.externalcompanies.entities; + +import java.util.Date; + +import org.libreplan.business.common.BaseEntity; + +/** + * Entity EndDateCommunication + * + * @author Susana Montes Pedreira + */ + +public class EndDateCommunication extends BaseEntity { + + private Date saveDate; + + private Date endDate; + + private Date communicationDate; + + protected EndDateCommunication() { + this.setSaveDate(new Date()); + } + + protected EndDateCommunication(Date endDate) { + this.setEndDate(endDate); + this.setSaveDate(new Date()); + } + + protected EndDateCommunication(Date saveDate, Date endDate, + Date communicationDate) { + this.setSaveDate(saveDate); + this.setEndDate(endDate); + this.setCommunicationDate(communicationDate); + } + + public static EndDateCommunication create() { + return create(new EndDateCommunication()); + } + + public static EndDateCommunication create(Date endDate) { + return create(new EndDateCommunication(endDate)); + } + + public static EndDateCommunication create(Date saveDate, Date endDate, + Date communicationDate) { + return create(new EndDateCommunication(saveDate, endDate, communicationDate)); + } + + public void setSaveDate(Date saveDate) { + this.saveDate = saveDate; + } + + public Date getSaveDate() { + return saveDate; + } + + public void setEndDate(Date endDate) { + this.endDate = endDate; + } + + public Date getEndDate() { + return endDate; + } + + public void setCommunicationDate(Date communicationDate) { + this.communicationDate = communicationDate; + } + + public Date getCommunicationDate() { + return communicationDate; + } + +} diff --git a/libreplan-business/src/main/java/org/libreplan/business/externalcompanies/entities/EndDateCommunicationComparator.java b/libreplan-business/src/main/java/org/libreplan/business/externalcompanies/entities/EndDateCommunicationComparator.java new file mode 100644 index 000000000..7e06a65a7 --- /dev/null +++ b/libreplan-business/src/main/java/org/libreplan/business/externalcompanies/entities/EndDateCommunicationComparator.java @@ -0,0 +1,45 @@ +/* + * This file is part of LibrePlan + * + * Copyright (C) 2012 WirelessGalicia, S.L. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.libreplan.business.externalcompanies.entities; + +import java.util.Comparator; + + +public class EndDateCommunicationComparator implements + Comparator { + + public EndDateCommunicationComparator() { + } + + @Override + public int compare(EndDateCommunication arg0, EndDateCommunication arg1) { + if (arg0.getSaveDate() == arg1.getSaveDate()) { + return 0; + } + if (arg0.getSaveDate() == null) { + return -1; + } + if (arg1.getSaveDate() == null) { + return 1; + } + return arg1.getSaveDate().compareTo(arg0.getSaveDate()); + } + +} diff --git a/libreplan-business/src/main/java/org/libreplan/business/orders/daos/IOrderElementDAO.java b/libreplan-business/src/main/java/org/libreplan/business/orders/daos/IOrderElementDAO.java index dcddd9778..edbfa904c 100644 --- a/libreplan-business/src/main/java/org/libreplan/business/orders/daos/IOrderElementDAO.java +++ b/libreplan-business/src/main/java/org/libreplan/business/orders/daos/IOrderElementDAO.java @@ -128,4 +128,6 @@ public interface IOrderElementDAO extends IIntegrationEntityDAO { boolean hasImputedExpenseSheet(Long id) throws InstanceNotFoundException; + OrderElement findByExternalCode(String code) throws InstanceNotFoundException; + } diff --git a/libreplan-business/src/main/java/org/libreplan/business/orders/daos/OrderElementDAO.java b/libreplan-business/src/main/java/org/libreplan/business/orders/daos/OrderElementDAO.java index 29dab5380..c0060e9c6 100644 --- a/libreplan-business/src/main/java/org/libreplan/business/orders/daos/OrderElementDAO.java +++ b/libreplan-business/src/main/java/org/libreplan/business/orders/daos/OrderElementDAO.java @@ -281,6 +281,29 @@ public class OrderElementDAO extends IntegrationEntityDAO return c.list(); } + + @SuppressWarnings("unchecked") + @Override + public OrderElement findByExternalCode(String code) throws InstanceNotFoundException { + + if (StringUtils.isBlank(code)) { + throw new InstanceNotFoundException(null, getEntityClass() + .getName()); + } + + Criteria c = getSession().createCriteria(OrderElement.class); + c.add(Restrictions.eq("externalCode", code.trim()).ignoreCase()); + OrderElement entity = (OrderElement) c.uniqueResult(); + + if (entity == null) { + throw new InstanceNotFoundException(code, getEntityClass() + .getName()); + } else { + return entity; + } + + } + /** * Methods to calculate estatistics with the estimated hours and worked * hours of a set of order elements. diff --git a/libreplan-business/src/main/java/org/libreplan/business/orders/entities/Order.java b/libreplan-business/src/main/java/org/libreplan/business/orders/entities/Order.java index 9cbb49c7a..249351ccc 100644 --- a/libreplan-business/src/main/java/org/libreplan/business/orders/entities/Order.java +++ b/libreplan-business/src/main/java/org/libreplan/business/orders/entities/Order.java @@ -24,16 +24,20 @@ package org.libreplan.business.orders.entities; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collections; +import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.Validate; import org.hibernate.validator.AssertTrue; import org.hibernate.validator.NotNull; +import org.hibernate.validator.Valid; import org.libreplan.business.advance.bootstrap.PredefinedAdvancedTypes; import org.libreplan.business.advance.entities.AdvanceType; import org.libreplan.business.advance.entities.DirectAdvanceAssignment; @@ -41,6 +45,11 @@ import org.libreplan.business.calendars.entities.BaseCalendar; import org.libreplan.business.common.Registry; import org.libreplan.business.common.entities.EntitySequence; import org.libreplan.business.common.exceptions.InstanceNotFoundException; +import org.libreplan.business.externalcompanies.entities.CustomerCommunication; +import org.libreplan.business.externalcompanies.entities.DeadlineCommunication; +import org.libreplan.business.externalcompanies.entities.DeliverDateComparator; +import org.libreplan.business.externalcompanies.entities.EndDateCommunication; +import org.libreplan.business.externalcompanies.entities.EndDateCommunicationComparator; import org.libreplan.business.externalcompanies.entities.ExternalCompany; import org.libreplan.business.orders.daos.IOrderDAO; import org.libreplan.business.planner.entities.DayAssignment; @@ -109,6 +118,16 @@ public class Order extends OrderLineGroup implements Comparable { private CurrentVersionInfo currentVersionInfo; + private Set customerCommunications = new HashSet(); + + @Valid + private SortedSet deliveringDates = new TreeSet( + new DeliverDateComparator()); + + @Valid + private SortedSet endDateCommunicationToCustomer = new TreeSet( + new EndDateCommunicationComparator()); + public enum SchedulingMode { FORWARD, BACKWARDS; } @@ -578,6 +597,63 @@ public class Order extends OrderLineGroup implements Comparable { return this.getName().compareToIgnoreCase(((Order) o).getName()); } + public void setCustomerCommunications(Set customerCommunications) { + this.customerCommunications = customerCommunications; + } + + public Set getCustomerCommunications() { + return customerCommunications; + } + + public void setDeliveringDates(SortedSet deliveringDates) { + this.deliveringDates = deliveringDates; + } + + public SortedSet getDeliveringDates() { + return deliveringDates; + } + + public void setEndDateCommunicationToCustomer( + SortedSet endDateCommunicationToCustomer) { + this.endDateCommunicationToCustomer.clear(); + this.endDateCommunicationToCustomer.addAll(endDateCommunicationToCustomer); + } + + public SortedSet getEndDateCommunicationToCustomer() { + return Collections.unmodifiableSortedSet(this.endDateCommunicationToCustomer); + } + + + public void updateFirstAskedEndDate(Date communicationDate) { + if (this.endDateCommunicationToCustomer != null && !this.endDateCommunicationToCustomer.isEmpty()) { + this.endDateCommunicationToCustomer.first().setCommunicationDate(communicationDate); + } + } + + public Date getLastAskedEndDate() { + if (this.endDateCommunicationToCustomer != null + && !this.endDateCommunicationToCustomer.isEmpty()) { + return this.endDateCommunicationToCustomer.first().getEndDate(); + } + return null; + } + + public EndDateCommunication getLastEndDateCommunicationToCustomer() { + if (this.endDateCommunicationToCustomer != null + && !this.endDateCommunicationToCustomer.isEmpty()) { + return this.endDateCommunicationToCustomer.first(); + } + return null; + } + + public void removeAskedEndDate(EndDateCommunication endDate) { + this.endDateCommunicationToCustomer.remove(endDate); + } + + public void addAskedEndDate(EndDateCommunication endDate) { + this.endDateCommunicationToCustomer.add(endDate); + } + public void markAsNeededToRecalculateSumChargedEfforts() { neededToRecalculateSumChargedEfforts = true; } diff --git a/libreplan-business/src/main/java/org/libreplan/business/orders/entities/OrderElement.java b/libreplan-business/src/main/java/org/libreplan/business/orders/entities/OrderElement.java index 6d6a93a6d..ba21c7f15 100644 --- a/libreplan-business/src/main/java/org/libreplan/business/orders/entities/OrderElement.java +++ b/libreplan-business/src/main/java/org/libreplan/business/orders/entities/OrderElement.java @@ -299,6 +299,16 @@ public abstract class OrderElement extends IntegrationEntity implements //we have to remove the TaskSource which contains a TaskGroup instead of TaskElement removeTaskSource(result); } + if(currentTaskSourceIsNotTheSame()) { + //this element was unscheduled and then scheduled again. Its TaskSource has + //been recreated but we have to remove the old one. + if(!getParent().currentTaskSourceIsNotTheSame()) { + //we only remove the TaskSource if the parent is not in the same situation. + //In case the parent is in the same situation, it will remove the related + //TaskSources in children tasks. + removeTaskSource(result); + } + } result .addAll(synchronizationForSchedulingPoint(schedulingDataForVersion)); } else if (isSuperElementPartialOrCompletelyScheduled()) { @@ -306,6 +316,15 @@ public abstract class OrderElement extends IntegrationEntity implements if (wasASchedulingPoint()) { result.add(taskSourceRemoval()); } + if(currentTaskSourceIsNotTheSame()) { + //all the children of this element were unscheduled and then scheduled again, + //its TaskSource has been recreated but we have to remove the old one. + if(getParent() == null || !getParent().currentTaskSourceIsNotTheSame()) { + //if it's a container node inside another container we could have the + //same problem than in the case of leaf tasks. + result.add(taskSourceRemoval()); + } + } result .add(synchronizationForSuperelement(schedulingDataForVersion)); } else if (schedulingState.isNoScheduled()) { @@ -333,6 +352,10 @@ public abstract class OrderElement extends IntegrationEntity implements .getSchedulingStateType(); } + protected boolean currentTaskSourceIsNotTheSame() { + return getOnDBTaskSource() != getTaskSource(); + } + private List childrenSynchronizations() { List childrenOfGroup = new ArrayList(); for (OrderElement orderElement : getSomewhatScheduledOrderElements()) { @@ -742,7 +765,7 @@ public abstract class OrderElement extends IntegrationEntity implements * @param newAdvanceAssignment * @throws DuplicateAdvanceAssignmentForOrderElementException */ - private void checkAncestorsNoOtherAssignmentWithSameAdvanceType( + public void checkAncestorsNoOtherAssignmentWithSameAdvanceType( OrderElement orderElement, DirectAdvanceAssignment newAdvanceAssignment) throws DuplicateAdvanceAssignmentForOrderElementException { diff --git a/libreplan-business/src/main/java/org/libreplan/business/planner/daos/ISubcontractedTaskDataDAO.java b/libreplan-business/src/main/java/org/libreplan/business/planner/daos/ISubcontractedTaskDataDAO.java index 265d77030..df7bd86de 100644 --- a/libreplan-business/src/main/java/org/libreplan/business/planner/daos/ISubcontractedTaskDataDAO.java +++ b/libreplan-business/src/main/java/org/libreplan/business/planner/daos/ISubcontractedTaskDataDAO.java @@ -5,6 +5,8 @@ * Desenvolvemento Tecnolóxico de Galicia * Copyright (C) 2010-2011 Igalia, S.L. * + * Copyright (C) 2011 WirelessGalicia, S.L. + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or @@ -24,6 +26,8 @@ package org.libreplan.business.planner.daos; import java.util.List; import org.libreplan.business.common.daos.IGenericDAO; +import org.libreplan.business.common.exceptions.InstanceNotFoundException; +import org.libreplan.business.orders.entities.OrderElement; import org.libreplan.business.planner.entities.SubcontractedTaskData; /** @@ -42,4 +46,6 @@ public interface ISubcontractedTaskDataDAO extends List getAllForMasterScenario(); + SubcontractedTaskData getSubcontratedTaskDataByOrderElement( + OrderElement orderElement) throws InstanceNotFoundException; } \ No newline at end of file diff --git a/libreplan-business/src/main/java/org/libreplan/business/planner/daos/ISubcontractorCommunicationDAO.java b/libreplan-business/src/main/java/org/libreplan/business/planner/daos/ISubcontractorCommunicationDAO.java new file mode 100644 index 000000000..a8f91e681 --- /dev/null +++ b/libreplan-business/src/main/java/org/libreplan/business/planner/daos/ISubcontractorCommunicationDAO.java @@ -0,0 +1,32 @@ +/* + * This file is part of LibrePlan + * + * Copyright (C) 2011 WirelessGalicia, S.L. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.libreplan.business.planner.daos; + +import java.util.List; + +import org.libreplan.business.common.daos.IGenericDAO; +import org.libreplan.business.planner.entities.SubcontractorCommunication; + +public interface ISubcontractorCommunicationDAO extends IGenericDAO { + + List getAll(); + + List getAllNotReviewed(); +} diff --git a/libreplan-business/src/main/java/org/libreplan/business/planner/daos/SubcontractedTaskDataDAO.java b/libreplan-business/src/main/java/org/libreplan/business/planner/daos/SubcontractedTaskDataDAO.java index 757960f71..da6d3c839 100644 --- a/libreplan-business/src/main/java/org/libreplan/business/planner/daos/SubcontractedTaskDataDAO.java +++ b/libreplan-business/src/main/java/org/libreplan/business/planner/daos/SubcontractedTaskDataDAO.java @@ -5,6 +5,8 @@ * Desenvolvemento Tecnolóxico de Galicia * Copyright (C) 2010-2011 Igalia, S.L. * + * Copyright (C) 2011 WirelessGalicia, S.L. + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or @@ -24,10 +26,13 @@ package org.libreplan.business.planner.daos; import java.util.ArrayList; import java.util.List; +import org.hibernate.Criteria; +import org.hibernate.criterion.Restrictions; import org.libreplan.business.common.daos.GenericDAOHibernate; import org.libreplan.business.common.exceptions.InstanceNotFoundException; import org.libreplan.business.orders.entities.Order; import org.libreplan.business.orders.entities.OrderElement; +import org.libreplan.business.orders.entities.TaskSource; import org.libreplan.business.planner.entities.SubcontractedTaskData; import org.libreplan.business.planner.entities.Task; import org.libreplan.business.planner.entities.TaskElement; @@ -99,4 +104,18 @@ public class SubcontractedTaskDataDAO extends return result; } + @Override + @Transactional(readOnly = true) + public SubcontractedTaskData getSubcontratedTaskDataByOrderElement( + OrderElement orderElement) throws InstanceNotFoundException { + Criteria c = getSession().createCriteria(TaskElement.class) + .createCriteria("taskSource","ts") + .createCriteria("schedulingData","data") + .add(Restrictions.eq("data.orderElement",orderElement)); + + TaskElement taskElement = (TaskElement) c.uniqueResult(); + return (taskElement != null && taskElement.isSubcontracted()) ? ((Task) taskElement) + .getSubcontractedTaskData() : null; + } + } \ No newline at end of file diff --git a/libreplan-business/src/main/java/org/libreplan/business/planner/daos/SubcontractorCommunicationDAO.java b/libreplan-business/src/main/java/org/libreplan/business/planner/daos/SubcontractorCommunicationDAO.java new file mode 100644 index 000000000..0ef904ddf --- /dev/null +++ b/libreplan-business/src/main/java/org/libreplan/business/planner/daos/SubcontractorCommunicationDAO.java @@ -0,0 +1,53 @@ +/* + * This file is part of LibrePlan + * + * Copyright (C) 2011 WirelessGalicia, S.L. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.libreplan.business.planner.daos; + +import java.util.List; + +import org.hibernate.Criteria; +import org.hibernate.criterion.Restrictions; +import org.libreplan.business.common.daos.GenericDAOHibernate; +import org.libreplan.business.planner.entities.SubcontractorCommunication; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Repository; + +/** + * DAO for {@link SubcontractorCommunication} + * @author Susana Montes Pedreira + */ + +@Repository +@Scope(BeanDefinition.SCOPE_SINGLETON) +public class SubcontractorCommunicationDAO extends GenericDAOHibernate + implements ISubcontractorCommunicationDAO { + + @Override + public List getAll() { + return list(SubcontractorCommunication.class); + } + + @Override + public List getAllNotReviewed(){ + Criteria c = getSession().createCriteria(SubcontractorCommunication.class); + c.add(Restrictions.eq("reviewed", false)); + return c.list(); + } +} diff --git a/libreplan-business/src/main/java/org/libreplan/business/planner/entities/CompanyEarnedValueCalculator.java b/libreplan-business/src/main/java/org/libreplan/business/planner/entities/CompanyEarnedValueCalculator.java new file mode 100644 index 000000000..c9420ba1b --- /dev/null +++ b/libreplan-business/src/main/java/org/libreplan/business/planner/entities/CompanyEarnedValueCalculator.java @@ -0,0 +1,159 @@ +/* + * This file is part of LibrePlan + * + * Copyright (C) 2012 Igalia, S.L. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.libreplan.business.planner.entities; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; + +import org.joda.time.LocalDate; +import org.libreplan.business.calendars.entities.AvailabilityTimeLine; +import org.libreplan.business.calendars.entities.AvailabilityTimeLine.Interval; +import org.libreplan.business.hibernate.notification.PredefinedDatabaseSnapshots; +import org.libreplan.business.workreports.entities.WorkReportLine; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +/** + * @author Diego Pino García + */ +@Component +@Scope(BeanDefinition.SCOPE_SINGLETON) +public class CompanyEarnedValueCalculator extends EarnedValueCalculator implements ICompanyEarnedValueCalculator { + + @Autowired + private PredefinedDatabaseSnapshots databaseSnapshots; + + @Override + @Transactional(readOnly = true) + public SortedMap calculateBudgetedCostWorkScheduled(AvailabilityTimeLine.Interval interval) { + Map> estimatedCostPerTask = databaseSnapshots + .snapshotEstimatedCostPerTask(); + Collection list = filterTasksByDate( + estimatedCostPerTask.keySet(), interval); + SortedMap estimatedCost = new TreeMap(); + + for (TaskElement each : list) { + addCost(estimatedCost, estimatedCostPerTask.get(each)); + } + return accumulateResult(estimatedCost); + } + + private List filterTasksByDate( + Collection tasks, + AvailabilityTimeLine.Interval interval) { + List result = new ArrayList(); + for(TaskElement task : tasks) { + if (interval.includes(task.getStartAsLocalDate()) + || interval.includes(task.getEndAsLocalDate())) { + result.add(task); + } + } + return result; + } + + private List filterWorkReportLinesByDate( + Collection lines, + AvailabilityTimeLine.Interval interval) { + List result = new ArrayList(); + for(WorkReportLine line: lines) { + if (interval.includes(line.getLocalDate())) { + result.add(line); + } + } + return result; + } + + private void addCost(SortedMap currentCost, + SortedMap additionalCost) { + for (LocalDate day : additionalCost.keySet()) { + if (!currentCost.containsKey(day)) { + currentCost.put(day, BigDecimal.ZERO); + } + currentCost.put(day, currentCost.get(day).add( + additionalCost.get(day))); + } + } + + private SortedMap accumulateResult( + SortedMap map) { + SortedMap result = new TreeMap(); + if (map.isEmpty()) { + return result; + } + + BigDecimal accumulatedResult = BigDecimal.ZERO; + for (LocalDate day : map.keySet()) { + BigDecimal value = map.get(day); + accumulatedResult = accumulatedResult.add(value); + result.put(day, accumulatedResult); + } + + return result; + } + + @Override + public SortedMap calculateActualCostWorkPerformed( + Interval interval) { + SortedMap result = new TreeMap(); + Collection workReportLines = filterWorkReportLinesByDate( + databaseSnapshots.snapshotWorkReportLines(), + interval); + + if (workReportLines.isEmpty()) { + return result; + } + + for (WorkReportLine workReportLine : workReportLines) { + LocalDate day = new LocalDate(workReportLine.getDate()); + BigDecimal cost = workReportLine.getEffort() + .toHoursAsDecimalWithScale(2); + + if (!result.containsKey(day)) { + result.put(day, BigDecimal.ZERO); + } + result.put(day, result.get(day).add(cost)); + } + return accumulateResult(result); + } + + @Override + public SortedMap calculateBudgetedCostWorkPerformed( + Interval interval) { + Map> advanceCostPerTask = databaseSnapshots + .snapshotAdvanceCostPerTask(); + Collection tasks = filterTasksByDate( + advanceCostPerTask.keySet(), interval); + + SortedMap result = new TreeMap(); + for (TaskElement each : tasks) { + addCost(result, advanceCostPerTask.get(each)); + } + return result; + } + +} diff --git a/libreplan-business/src/main/java/org/libreplan/business/planner/entities/EarnedValueCalculator.java b/libreplan-business/src/main/java/org/libreplan/business/planner/entities/EarnedValueCalculator.java new file mode 100644 index 000000000..98bd28741 --- /dev/null +++ b/libreplan-business/src/main/java/org/libreplan/business/planner/entities/EarnedValueCalculator.java @@ -0,0 +1,252 @@ +/* + * This file is part of LibrePlan + * + * Copyright (C) 2012 Igalia, S.L. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.libreplan.business.planner.entities; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Collections; +import java.util.Map; +import java.util.Map.Entry; +import java.util.SortedMap; +import java.util.TreeMap; + +import org.joda.time.LocalDate; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +/** + * @author Manuel Rego Casasnovas + * @author Diego Pino García + * + * Calculates generic 'Earned Value' indicators (those calculated out of + * BCWP, ACWP and BCWS + */ +@Component +@Scope(BeanDefinition.SCOPE_SINGLETON) +public class EarnedValueCalculator implements IEarnedValueCalculator { + + @Override + public SortedMap calculateCostVariance( + SortedMap bcwp, + SortedMap acwp) { + return substract(bcwp, acwp); + } + + @Override + public SortedMap calculateScheduleVariance( + SortedMap bcwp, + SortedMap bcws) { + return substract(bcwp, bcws); + } + + @Override + public SortedMap calculateBudgetAtCompletion( + SortedMap bcws) { + SortedMap result = new TreeMap(); + BigDecimal value = Collections.max(bcws.values()); + for (LocalDate day : bcws.keySet()) { + result.put(day, value); + } + return result; + } + + @Override + public SortedMap calculateEstimateAtCompletion( + SortedMap acwp, + SortedMap bcwp, + SortedMap bac) { + return multiply(divide(acwp, bcwp, + BigDecimal.ZERO), bac); + } + + @Override + public SortedMap calculateVarianceAtCompletion( + SortedMap bac, + SortedMap eac) { + return substract(bac, eac); + } + + @Override + public SortedMap calculateEstimatedToComplete( + SortedMap eac, + SortedMap acwp) { + return substract(eac, acwp); + } + + @Override + public SortedMap calculateCostPerformanceIndex( + SortedMap bcwp, + SortedMap acwp) { + return divide(bcwp, acwp, BigDecimal.ZERO); + } + + @Override + public SortedMap calculateSchedulePerformanceIndex( + SortedMap bcwp, + SortedMap bcws) { + return divide(bcwp, bcws, BigDecimal.ZERO); + } + + private SortedMap substract( + SortedMap minuend, + SortedMap subtrahend) { + final SortedMap result = new TreeMap(); + forValuesAtSameKey(minuend, subtrahend, substractionOperation(result)); + return result; + + } + + public static void forValuesAtSameKey(Map a, Map b, + IOperation onSameKey) { + for (Entry each : a.entrySet()) { + V aValue = each.getValue(); + V bValue = b.get(each.getKey()); + onSameKey.operate(each.getKey(), aValue, bValue); + } + } + + private static IOperation substractionOperation( + final SortedMap result) { + return notNullOperands(new IOperation() { + + @Override + public void operate(LocalDate key, BigDecimal minuedValue, + BigDecimal subtrahendValue) { + result.put(key, minuedValue.subtract(subtrahendValue)); + } + + @Override + public void undefinedFor(LocalDate key) { + } + }); + } + + public static IOperation notNullOperands( + final IOperation operation) { + return new PreconditionChecker(operation) { + @Override + protected boolean isOperationDefinedFor(K key, V a, V b) { + return a != null && b != null; + } + }; + } + + public interface IOperation { + + public void operate(K key, V a, V b); + + public void undefinedFor(K key); + } + + protected static abstract class PreconditionChecker implements + IOperation { + + private final IOperation decorated; + + protected PreconditionChecker(IOperation decorated) { + this.decorated = decorated; + } + + @Override + public void operate(K key, V a, V b) { + if (isOperationDefinedFor(key, a, b)) { + decorated.operate(key, a, b); + } else { + decorated.undefinedFor(key); + } + } + + protected abstract boolean isOperationDefinedFor(K key, V a, V b); + + @Override + public void undefinedFor(K key) { + decorated.undefinedFor(key); + } + + } + + private static SortedMap multiply( + Map firstFactor, + Map secondFactor) { + final SortedMap result = new TreeMap(); + forValuesAtSameKey(firstFactor, secondFactor, + multiplicationOperation(result)); + return result; + } + + private static IOperation multiplicationOperation( + final SortedMap result) { + return notNullOperands(new IOperation() { + + @Override + public void operate(LocalDate key, BigDecimal a, + BigDecimal b) { + result.put(key, a.multiply(b)); + } + + @Override + public void undefinedFor(LocalDate key) { + result.put(key, BigDecimal.ZERO); + } + }); + } + + private static SortedMap divide( + Map dividend, + Map divisor, + final BigDecimal defaultIfNotComputable) { + final TreeMap result = new TreeMap(); + forValuesAtSameKey(dividend, divisor, + divisionOperation(result, defaultIfNotComputable)); + return result; + } + + private static IOperation divisionOperation( + final TreeMap result, + final BigDecimal defaultIfNotComputable) { + return notNullOperands(secondOperandNotZero(new IOperation() { + + @Override + public void operate(LocalDate key, BigDecimal dividendValue, + BigDecimal divisorValue) { + result.put(key, + dividendValue.divide(divisorValue, RoundingMode.DOWN)); + } + + @Override + public void undefinedFor(LocalDate key) { + result.put(key, defaultIfNotComputable); + } + })); + } + + public static IOperation secondOperandNotZero( + final IOperation operation) { + return new PreconditionChecker(operation) { + @Override + protected boolean isOperationDefinedFor(K key, BigDecimal a, + BigDecimal b) { + return b.signum() != 0; + } + }; + } + +} \ No newline at end of file diff --git a/libreplan-business/src/main/java/org/libreplan/business/planner/entities/ICompanyEarnedValueCalculator.java b/libreplan-business/src/main/java/org/libreplan/business/planner/entities/ICompanyEarnedValueCalculator.java new file mode 100644 index 000000000..4f9e0d781 --- /dev/null +++ b/libreplan-business/src/main/java/org/libreplan/business/planner/entities/ICompanyEarnedValueCalculator.java @@ -0,0 +1,44 @@ +/* + * This file is part of LibrePlan + * + * Copyright (C) 2012 Igalia, S.L. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.libreplan.business.planner.entities; + +import java.math.BigDecimal; +import java.util.SortedMap; + +import org.joda.time.LocalDate; +import org.libreplan.business.calendars.entities.AvailabilityTimeLine; + +/** + * @author Diego Pino García + * + * Utility class for calculating all 'Earned Value' indicators + */ +public interface ICompanyEarnedValueCalculator extends IEarnedValueCalculator { + + SortedMap calculateBudgetedCostWorkScheduled( + AvailabilityTimeLine.Interval interval); + + SortedMap calculateActualCostWorkPerformed( + AvailabilityTimeLine.Interval interval); + + SortedMap calculateBudgetedCostWorkPerformed( + AvailabilityTimeLine.Interval interval); + +} diff --git a/libreplan-business/src/main/java/org/libreplan/business/planner/entities/IEarnedValueCalculator.java b/libreplan-business/src/main/java/org/libreplan/business/planner/entities/IEarnedValueCalculator.java new file mode 100644 index 000000000..13776a1e1 --- /dev/null +++ b/libreplan-business/src/main/java/org/libreplan/business/planner/entities/IEarnedValueCalculator.java @@ -0,0 +1,74 @@ +/* + * This file is part of LibrePlan + * + * Copyright (C) 2012 Igalia, S.L. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.libreplan.business.planner.entities; + +import java.math.BigDecimal; +import java.util.SortedMap; + +import org.joda.time.LocalDate; + +/** + * @author Diego Pino García + * + * Utility class for calculating all 'Earned Value' indicators + */ +public interface IEarnedValueCalculator { + + // CV = BCWP - ACWP + SortedMap calculateCostVariance( + SortedMap bcwp, + SortedMap acwp); + + // SV = BCWP - BCWS + SortedMap calculateScheduleVariance( + SortedMap bcwp, + SortedMap bcws); + + // BAC = max (BCWS) + SortedMap calculateBudgetAtCompletion( + SortedMap bcws); + + // EAC = (ACWP/BCWP) * BAC + SortedMap calculateEstimateAtCompletion( + SortedMap acwp, + SortedMap bcwp, + SortedMap bac); + + // VAC = BAC - EAC + SortedMap calculateVarianceAtCompletion( + SortedMap bac, + SortedMap eac); + + // ETC = EAC - ACWP + SortedMap calculateEstimatedToComplete( + SortedMap eac, + SortedMap acwp); + + // SPI = BCWP / BCWS + SortedMap calculateSchedulePerformanceIndex( + SortedMap bcwp, + SortedMap bcws); + + // CPI = BCWP / ACWP + SortedMap calculateCostPerformanceIndex( + SortedMap bcwp, + SortedMap acwp); + +} diff --git a/libreplan-business/src/main/java/org/libreplan/business/planner/entities/IOrderEarnedValueCalculator.java b/libreplan-business/src/main/java/org/libreplan/business/planner/entities/IOrderEarnedValueCalculator.java new file mode 100644 index 000000000..232f3ee0d --- /dev/null +++ b/libreplan-business/src/main/java/org/libreplan/business/planner/entities/IOrderEarnedValueCalculator.java @@ -0,0 +1,66 @@ +/* + * This file is part of LibrePlan + * + * Copyright (C) 2012 Igalia, S.L. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.libreplan.business.planner.entities; + +import java.math.BigDecimal; +import java.util.SortedMap; + +import org.joda.time.LocalDate; +import org.libreplan.business.orders.entities.Order; + +/** + * @author Diego Pino García + * + * Utility class for calculating all 'Earned Value' indicators + */ +public interface IOrderEarnedValueCalculator extends IEarnedValueCalculator { + + // ACWP (Actual Cost Work Performed) + SortedMap calculateActualCostWorkPerformed( + Order order); + + // BCWP (Budgeted Cost Work Performed) + SortedMap calculateBudgetedCostWorkPerformed( + Order order); + + // BCWS (Budgeted Cost Work Scheduled) + SortedMap calculateBudgetedCostWorkScheduled(Order order); + + // ACWP at date + BigDecimal getActualCostWorkPerformedAt(Order order, LocalDate date); + + // BAC (Budget at Completion) + BigDecimal getBudgetAtCompletion(Order order); + + // BCWP at date + BigDecimal getBudgetedCostWorkPerformedAt(Order order, LocalDate date); + + // CPI (Cost Performance Index) + BigDecimal getCostPerformanceIndex(BigDecimal budgetedCost, + BigDecimal actualCost); + + // CV (Cost Variance) + BigDecimal getCostVariance(BigDecimal budgetedCost, BigDecimal actualCost); + + // EAC (Estimate At Completion) + BigDecimal getEstimateAtCompletion(BigDecimal budgetAtCompletion, + BigDecimal costPerformanceIndex); + +} \ No newline at end of file diff --git a/libreplan-business/src/main/java/org/libreplan/business/planner/entities/OrderEarnedValueCalculator.java b/libreplan-business/src/main/java/org/libreplan/business/planner/entities/OrderEarnedValueCalculator.java new file mode 100644 index 000000000..788229667 --- /dev/null +++ b/libreplan-business/src/main/java/org/libreplan/business/planner/entities/OrderEarnedValueCalculator.java @@ -0,0 +1,181 @@ +/* + * This file is part of LibrePlan + * + * Copyright (C) 2012 Igalia, S.L. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.libreplan.business.planner.entities; + +import java.math.BigDecimal; +import java.util.List; +import java.util.SortedMap; +import java.util.TreeMap; + +import org.joda.time.LocalDate; +import org.libreplan.business.orders.entities.Order; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +/** + * @author Diego Pino García + */ +@Component +@Scope(BeanDefinition.SCOPE_SINGLETON) +public class OrderEarnedValueCalculator extends EarnedValueCalculator implements IOrderEarnedValueCalculator { + + @Autowired + private ICostCalculator hoursCostCalculator; + + @Transactional(readOnly = true) + @Override + public BigDecimal getActualCostWorkPerformedAt(Order order, LocalDate date) { + SortedMap actualCost = calculateActualCostWorkPerformed(order); + BigDecimal result = actualCost.get(date); + return (result != null) ? result : BigDecimal.ZERO; + } + + @Transactional(readOnly = true) + @Override + public SortedMap calculateActualCostWorkPerformed( + Order order) { + SortedMap result = new TreeMap(); + for (TaskElement taskElement : getAllTaskElements(order)) { + if (taskElement instanceof Task) { + addCost(result, getWorkReportCost((Task) taskElement)); + } + } + return accumulateResult(result); + } + + private SortedMap accumulateResult( + SortedMap map) { + SortedMap result = new TreeMap(); + if (map.isEmpty()) { + return result; + } + + BigDecimal accumulatedResult = BigDecimal.ZERO; + for (LocalDate day : map.keySet()) { + BigDecimal value = map.get(day); + accumulatedResult = accumulatedResult.add(value); + result.put(day, accumulatedResult); + } + return result; + } + + private void addCost(SortedMap currentCost, + SortedMap additionalCost) { + for (LocalDate day : additionalCost.keySet()) { + if (!currentCost.containsKey(day)) { + currentCost.put(day, BigDecimal.ZERO); + } + currentCost.put(day, + currentCost.get(day).add(additionalCost.get(day))); + } + } + + private List getAllTaskElements(Order order) { + List result = order.getAllChildrenAssociatedTaskElements(); + result.add(order.getAssociatedTaskElement()); + return result; + } + + private SortedMap getWorkReportCost(Task taskElement) { + return hoursCostCalculator.getWorkReportCost(taskElement); + } + + @Override + @Transactional(readOnly = true) + public BigDecimal getBudgetAtCompletion(Order order) { + SortedMap budgedtedCost = calculateBudgetedCostWorkScheduled(order); + LocalDate lastKey = budgedtedCost.lastKey(); + return (lastKey) != null ? budgedtedCost.get(lastKey) : BigDecimal.ZERO; + } + + @Override + @Transactional(readOnly = true) + public SortedMap calculateBudgetedCostWorkScheduled( + Order order) { + SortedMap result = new TreeMap(); + for (TaskElement taskElement : getAllTaskElements(order)) { + if (taskElement instanceof Task) { + addCost(result, getEstimatedCost((Task) taskElement)); + } + } + return accumulateResult(result); + } + + private SortedMap getEstimatedCost(Task task) { + return hoursCostCalculator.getEstimatedCost(task); + } + + @Override + @Transactional(readOnly = true) + public BigDecimal getBudgetedCostWorkPerformedAt(Order order, LocalDate date) { + SortedMap budgetedCost = calculateBudgetedCostWorkPerformed(order); + BigDecimal result = budgetedCost.get(date); + return (result != null) ? result : BigDecimal.ZERO; + } + + @Override + @Transactional(readOnly = true) + public SortedMap calculateBudgetedCostWorkPerformed( + Order order) { + SortedMap estimatedCost = new TreeMap(); + for (TaskElement taskElement : getAllTaskElements(order)) { + if (taskElement instanceof Task) { + addCost(estimatedCost, getAdvanceCost((Task) taskElement)); + } + } + return accumulateResult(estimatedCost); + } + + private SortedMap getAdvanceCost(Task task) { + return hoursCostCalculator.getAdvanceCost(task); + } + + @Override + public BigDecimal getCostPerformanceIndex(BigDecimal budgetedCost, + BigDecimal actualCost) { + if (BigDecimal.ZERO.compareTo(actualCost) == 0) { + return BigDecimal.ZERO; + } + return asPercentage(budgetedCost.divide(actualCost)); + } + + private BigDecimal asPercentage(BigDecimal value) { + return value.multiply(BigDecimal.valueOf(100)).setScale(2); + } + + @Override + public BigDecimal getCostVariance(BigDecimal budgetedCost, + BigDecimal actualCost) { + return budgetedCost.subtract(actualCost); + } + + @Override + public BigDecimal getEstimateAtCompletion(BigDecimal budgetAtCompletion, + BigDecimal costPerformanceIndex) { + if (BigDecimal.ZERO.compareTo(costPerformanceIndex) == 0) { + return BigDecimal.ZERO; + } + return asPercentage(budgetAtCompletion.divide(costPerformanceIndex)); + } + +} diff --git a/libreplan-business/src/main/java/org/libreplan/business/planner/entities/SubcontractState.java b/libreplan-business/src/main/java/org/libreplan/business/planner/entities/SubcontractState.java index 0ab374f5d..dfaaa740b 100644 --- a/libreplan-business/src/main/java/org/libreplan/business/planner/entities/SubcontractState.java +++ b/libreplan-business/src/main/java/org/libreplan/business/planner/entities/SubcontractState.java @@ -29,7 +29,9 @@ import static org.libreplan.business.i18n.I18nHelper._; * @author Manuel Rego Casasnovas */ public enum SubcontractState { - PENDING(_("Pending"), true), FAILED_SENT(_("Failed sent"), true), SUCCESS_SENT( + PENDING_INITIAL_SEND(_("Pending initial send"), true), PENDING_UPDATE_DELIVERING_DATE( + _("Pending update delivering date"), true), FAILED_SENT( + _("Failed sent"), true), FAILED_UPDATE(_("Failed update"), true), SUCCESS_SENT( _("Success sent"), false); private String name; diff --git a/libreplan-business/src/main/java/org/libreplan/business/planner/entities/SubcontractedTaskData.java b/libreplan-business/src/main/java/org/libreplan/business/planner/entities/SubcontractedTaskData.java index afa5892f6..526413fce 100644 --- a/libreplan-business/src/main/java/org/libreplan/business/planner/entities/SubcontractedTaskData.java +++ b/libreplan-business/src/main/java/org/libreplan/business/planner/entities/SubcontractedTaskData.java @@ -22,11 +22,19 @@ package org.libreplan.business.planner.entities; import java.math.BigDecimal; +import java.util.Collections; import java.util.Date; +import java.util.HashSet; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; import org.hibernate.validator.AssertTrue; import org.hibernate.validator.NotNull; import org.libreplan.business.common.BaseEntity; +import org.libreplan.business.externalcompanies.entities.DeliverDateComparator; +import org.libreplan.business.externalcompanies.entities.EndDateCommunication; +import org.libreplan.business.externalcompanies.entities.EndDateCommunicationComparator; import org.libreplan.business.externalcompanies.entities.ExternalCompany; import org.libreplan.business.util.deepcopy.OnCopy; import org.libreplan.business.util.deepcopy.Strategy; @@ -62,7 +70,10 @@ public class SubcontractedTaskData extends BaseEntity { result.labelsExported = subcontractedTaskData.labelsExported; result.materialAssignmentsExported = subcontractedTaskData.materialAssignmentsExported; result.hoursGroupsExported = subcontractedTaskData.hoursGroupsExported; - + result.setState(subcontractedTaskData.getState()); + result.setRequiredDeliveringDates(subcontractedTaskData.getRequiredDeliveringDates()); + result.setEndDatesCommunicatedFromSubcontractor(subcontractedTaskData + .getEndDatesCommunicatedFromSubcontractor()); return create(result); } @@ -86,7 +97,15 @@ public class SubcontractedTaskData extends BaseEntity { private Boolean materialAssignmentsExported; private Boolean hoursGroupsExported; - private SubcontractState state = SubcontractState.PENDING; + private SubcontractState state = SubcontractState.PENDING_INITIAL_SEND; + + private final SortedSet requiredDeliveringDates = new TreeSet( + new DeliverDateComparator()); + + private Set subcontractorCommunications = new HashSet(); + + private SortedSet endDatesCommunicatedFromSubcontractor = new TreeSet( + new EndDateCommunicationComparator()); /** * Constructor for hibernate. Do not use! @@ -219,6 +238,10 @@ public class SubcontractedTaskData extends BaseEntity { this.labelsExported = subcontratedTask.labelsExported; this.materialAssignmentsExported = subcontratedTask.materialAssignmentsExported; this.hoursGroupsExported = subcontratedTask.hoursGroupsExported; + this.state = subcontratedTask.getState(); + this.setRequiredDeliveringDates(subcontratedTask.getRequiredDeliveringDates()); + this.setEndDatesCommunicatedFromSubcontractor(subcontratedTask + .getEndDatesCommunicatedFromSubcontractor()); } @AssertTrue(message = "external company should be subcontractor") @@ -248,4 +271,54 @@ public class SubcontractedTaskData extends BaseEntity { && externalCompany.getInteractsWithApplications(); } -} \ No newline at end of file + public void setRequiredDeliveringDates( + SortedSet requiredDeliveringDates) { + this.requiredDeliveringDates.clear(); + this.requiredDeliveringDates.addAll(requiredDeliveringDates); + } + + public SortedSet getRequiredDeliveringDates() { + return Collections.unmodifiableSortedSet(this.requiredDeliveringDates); + } + + public void addRequiredDeliveringDates( + SubcontractorDeliverDate subDeliverDate) { + this.requiredDeliveringDates.add(subDeliverDate); + } + + public void removeRequiredDeliveringDates( + SubcontractorDeliverDate subcontractorDeliverDate) { + this.requiredDeliveringDates.remove(subcontractorDeliverDate); + } + + public void updateFirstRequiredDeliverDate(Date subcontractCommunicationDate){ + if(this.requiredDeliveringDates != null && !this.requiredDeliveringDates.isEmpty()){ + this.requiredDeliveringDates.first().setCommunicationDate(subcontractCommunicationDate); + } + } + + public Date getLastRequiredDeliverDate() { + if (this.requiredDeliveringDates != null + && !this.requiredDeliveringDates.isEmpty()) { + return this.requiredDeliveringDates.first() + .getSubcontractorDeliverDate(); + } + return null; + } + + public void setEndDatesCommunicatedFromSubcontractor( + SortedSet endDatesCommunicatedFromSubcontractor) { + this.endDatesCommunicatedFromSubcontractor = endDatesCommunicatedFromSubcontractor; + } + + public SortedSet getEndDatesCommunicatedFromSubcontractor() { + return endDatesCommunicatedFromSubcontractor; + } + + public EndDateCommunication getLastEndDatesCommunicatedFromSubcontractor() { + if (getEndDatesCommunicatedFromSubcontractor() != null) { + return getEndDatesCommunicatedFromSubcontractor().first(); + } + return null; + } +} diff --git a/libreplan-business/src/main/java/org/libreplan/business/planner/entities/SubcontractorCommunication.java b/libreplan-business/src/main/java/org/libreplan/business/planner/entities/SubcontractorCommunication.java new file mode 100644 index 000000000..f0943d55d --- /dev/null +++ b/libreplan-business/src/main/java/org/libreplan/business/planner/entities/SubcontractorCommunication.java @@ -0,0 +1,125 @@ +/* + * This file is part of LibrePlan + * + * Copyright (C) 2011 WirelessGalicia, S.L. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.libreplan.business.planner.entities; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import org.hibernate.validator.NotNull; +import org.libreplan.business.common.BaseEntity; +import org.libreplan.business.externalcompanies.entities.CommunicationType; +import org.libreplan.business.qualityforms.entities.QualityFormItem; + +/** + * Entity {@link SubcontractorCommunication}. + * + * @author Susana Montes Pedreira + */ +public class SubcontractorCommunication extends BaseEntity { + + private SubcontractedTaskData subcontractedTaskData; + + private CommunicationType communicationType; + + private Date communicationDate; + + private Boolean reviewed = false; + + private List subcontractorCommunicationValues = new ArrayList(); + + // Default constructor, needed by Hibernate + protected SubcontractorCommunication() { + + } + + private SubcontractorCommunication ( SubcontractedTaskData subcontractedTaskData, CommunicationType communicationType, Date communicationDate, Boolean reviewed){ + this.setSubcontractedTaskData(subcontractedTaskData); + this.setCommunicationType(communicationType); + this.setCommunicationDate(communicationDate); + this.setReviewed(reviewed); + } + + public static SubcontractorCommunication create( + SubcontractedTaskData subcontractedTaskData, + CommunicationType communicationType, Date communicationDate, + Boolean reviewed) { + return create(new SubcontractorCommunication(subcontractedTaskData, + communicationType, communicationDate, reviewed)); + } + + public static SubcontractorCommunication create() { + return create(new SubcontractorCommunication()); + } + + public void setSubcontractedTaskData(SubcontractedTaskData subcontractedTaskData) { + this.subcontractedTaskData = subcontractedTaskData; + } + + @NotNull(message="subcontrated task data not specified") + public SubcontractedTaskData getSubcontractedTaskData() { + return subcontractedTaskData; + } + + public void setCommunicationType(CommunicationType communicationType) { + this.communicationType = communicationType; + } + + public CommunicationType getCommunicationType() { + return communicationType; + } + + public void setCommunicationDate(Date communicationDate) { + this.communicationDate = communicationDate; + } + + public Date getCommunicationDate() { + return communicationDate; + } + + public void setReviewed(Boolean reviewed) { + this.reviewed = reviewed; + } + + public Boolean getReviewed() { + return reviewed; + } + + public void setSubcontractorCommunicationValues( + List subcontractorCommunicationValues) { + this.subcontractorCommunicationValues = subcontractorCommunicationValues; + } + + public List getSubcontractorCommunicationValues() { + return subcontractorCommunicationValues; + } + + public SubcontractorCommunicationValue getLastSubcontractorCommunicationValues(){ + if (subcontractorCommunicationValues.isEmpty()){ + return null; + } + return subcontractorCommunicationValues.get(subcontractorCommunicationValues.size()-1); + } + + public Date getLastSubcontractorCommunicationValueDate(){ + SubcontractorCommunicationValue value = getLastSubcontractorCommunicationValues(); + return (value == null) ? null : value.getDate(); + } +} \ No newline at end of file diff --git a/libreplan-business/src/main/java/org/libreplan/business/planner/entities/SubcontractorCommunicationValue.java b/libreplan-business/src/main/java/org/libreplan/business/planner/entities/SubcontractorCommunicationValue.java new file mode 100644 index 000000000..ff7364c50 --- /dev/null +++ b/libreplan-business/src/main/java/org/libreplan/business/planner/entities/SubcontractorCommunicationValue.java @@ -0,0 +1,106 @@ +/* + * This file is part of LibrePlan + * + * Copyright (C) 2011 WirelessGalicia, S.L. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.libreplan.business.planner.entities; + +import java.math.BigDecimal; +import java.text.SimpleDateFormat; +import java.util.Date; + +import org.hibernate.validator.AssertTrue; +import org.libreplan.business.INewObject; + +/** + * Entity to represent each {@SubcontractorCommunicationValue}. + * + * @author Susana Montes Pedreira + */ +public class SubcontractorCommunicationValue implements INewObject { + + public static SubcontractorCommunicationValue create() { + SubcontractorCommunicationValue subcontractorCommunicationValue = new SubcontractorCommunicationValue(); + subcontractorCommunicationValue.setNewObject(true); + return subcontractorCommunicationValue; + } + + public static SubcontractorCommunicationValue create(Date date, + BigDecimal progress) { + SubcontractorCommunicationValue subcontractorCommunicationValue = new SubcontractorCommunicationValue( + date, progress); + subcontractorCommunicationValue.setNewObject(true); + return subcontractorCommunicationValue; + } + + protected SubcontractorCommunicationValue() { + + } + + private SubcontractorCommunicationValue(Date date, BigDecimal progress) { + this.setDate(date); + this.setProgress(progress); + } + + private boolean newObject = false; + + private Date date; + + private BigDecimal progress; + + public boolean isNewObject() { + return newObject; + } + + private void setNewObject(boolean newObject) { + this.newObject = newObject; + } + + @SuppressWarnings("unused") + @AssertTrue(message = "progress should be greater than 0% and less than 100%") + public boolean checkConstraintQualityFormItemPercentage() { + if (getProgress() == null) { + return true; + } + return ((getProgress().compareTo(new BigDecimal(100).setScale(2)) <= 0) && (getProgress() + .compareTo(new BigDecimal(0).setScale(2)) > 0)); + } + + public void setDate(Date date) { + this.date = date; + } + + public Date getDate() { + return date; + } + + public void setProgress(BigDecimal progress) { + this.progress = progress; + } + + public BigDecimal getProgress() { + return progress; + } + + @Override + public String toString() { + String datetime = (date == null) ? "" : new SimpleDateFormat( + "dd/MM/yyyy").format(date); + String progress_reported = progress != null ? progress.toString() + "% - " : ""; + return progress_reported + datetime; + } +} diff --git a/libreplan-business/src/main/java/org/libreplan/business/planner/entities/SubcontractorDeliverDate.java b/libreplan-business/src/main/java/org/libreplan/business/planner/entities/SubcontractorDeliverDate.java new file mode 100644 index 000000000..98b2da25f --- /dev/null +++ b/libreplan-business/src/main/java/org/libreplan/business/planner/entities/SubcontractorDeliverDate.java @@ -0,0 +1,82 @@ +/* + * This file is part of LibrePlan + * + * Copyright (C) 2011 WirelessGalicia, S.L. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.libreplan.business.planner.entities; + +import java.util.Date; + +import org.libreplan.business.common.BaseEntity; +import org.libreplan.business.externalcompanies.entities.DeliverDate; + +/** + * Entity {@link SubcontractorDeliverDate}. + * + * @author Susana Montes Pedreira + */ +public class SubcontractorDeliverDate extends BaseEntity implements DeliverDate{ + + private Date saveDate; + + private Date subcontractorDeliverDate; + + private Date communicationDate; + + protected SubcontractorDeliverDate(){ + + } + + private SubcontractorDeliverDate(Date saveDate, Date subcontractorDeliverDate, Date communicationDate){ + this.setSaveDate(saveDate); + this.setSubcontractorDeliverDate(subcontractorDeliverDate); + this.setCommunicationDate(communicationDate); + } + + public static SubcontractorDeliverDate create(){ + return create(new SubcontractorDeliverDate()); + } + + public static SubcontractorDeliverDate create(Date saveDate, Date subcontractorDeliverDate, Date communicationDate){ + return create(new SubcontractorDeliverDate(saveDate, subcontractorDeliverDate, communicationDate)); + } + + public void setSaveDate(Date saveDate) { + this.saveDate = saveDate; + } + + public Date getSaveDate() { + return saveDate; + } + + public void setSubcontractorDeliverDate(Date subcontractorDeliverDate) { + this.subcontractorDeliverDate = subcontractorDeliverDate; + } + + public Date getSubcontractorDeliverDate() { + return subcontractorDeliverDate; + } + + public void setCommunicationDate(Date communicationDate) { + this.communicationDate = communicationDate; + } + + public Date getCommunicationDate() { + return communicationDate; + } + +} diff --git a/libreplan-business/src/main/java/org/libreplan/business/planner/entities/Task.java b/libreplan-business/src/main/java/org/libreplan/business/planner/entities/Task.java index 15a24867b..4947700ab 100644 --- a/libreplan-business/src/main/java/org/libreplan/business/planner/entities/Task.java +++ b/libreplan-business/src/main/java/org/libreplan/business/planner/entities/Task.java @@ -887,7 +887,7 @@ public class Task extends TaskElement implements ITaskPositionConstrained { public boolean isSubcontractedAndWasAlreadySent() { return (subcontractedTaskData != null) && (!subcontractedTaskData.getState() - .equals(SubcontractState.PENDING)); + .equals(SubcontractState.PENDING_INITIAL_SEND)); } public boolean hasSomeSatisfiedAllocation() { diff --git a/libreplan-business/src/main/java/org/libreplan/business/qualityforms/entities/QualityForm.java b/libreplan-business/src/main/java/org/libreplan/business/qualityforms/entities/QualityForm.java index 235e0f5db..e0d401b01 100644 --- a/libreplan-business/src/main/java/org/libreplan/business/qualityforms/entities/QualityForm.java +++ b/libreplan-business/src/main/java/org/libreplan/business/qualityforms/entities/QualityForm.java @@ -4,6 +4,7 @@ * Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e * Desenvolvemento Tecnolóxico de Galicia * Copyright (C) 2010-2011 Igalia, S.L. + * Copyright (C) 2010-2011 WirelessGalicia, S.L. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by diff --git a/libreplan-business/src/main/resources/db.changelog-1.2.xml b/libreplan-business/src/main/resources/db.changelog-1.2.xml index f4fb366f0..4444d9dbf 100644 --- a/libreplan-business/src/main/resources/db.changelog-1.2.xml +++ b/libreplan-business/src/main/resources/db.changelog-1.2.xml @@ -4,6 +4,134 @@ xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-2.0.xsd"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Rename table customer_comunication to customer_communication + + + + + Rename column comunication_type to communication_type + + + + + Rename column comunication_date to communication_date + + + + + Rename table subcontractor_comunication to subcontractor_communication + + + + + Rename column comunication_type to communication_type + + + + + Rename column comunication_date to communication_date + + + + + Rename table subcontractor_comunication_values to subcontractor_communication_values + + + + + Rename column subcontractor_comunication_id + + + + + + + + + + + + + + + + + + Add new delivering date column to order + + + + + + + + + + + + + + + + + + + + + Add the column subcontracted_task_id to maintain the relation + + + + + taskEndDate attribute in class ConsolidatedValue has been changed to IntraDayDate. @@ -70,6 +198,33 @@ + + + + + + + + + + + + + + + + + Add subcontracted date id column to end date communication to customer + + + + + + + Rename table to end_date_communication + + + diff --git a/libreplan-business/src/main/resources/db.changelog.xml b/libreplan-business/src/main/resources/db.changelog.xml index 0ace5ccf8..a4937a422 100644 --- a/libreplan-business/src/main/resources/db.changelog.xml +++ b/libreplan-business/src/main/resources/db.changelog.xml @@ -10,5 +10,4 @@ - diff --git a/libreplan-business/src/main/resources/libreplan-business-spring-config.xml b/libreplan-business/src/main/resources/libreplan-business-spring-config.xml index 8ccd44195..10dda88a4 100644 --- a/libreplan-business/src/main/resources/libreplan-business-spring-config.xml +++ b/libreplan-business/src/main/resources/libreplan-business-spring-config.xml @@ -81,6 +81,9 @@ org/libreplan/business/planner/entities/AdvanceConsolidations.hbm.xml + + org/libreplan/business/planner/entities/SubcontractorCommunication.hbm.xml + org/libreplan/business/scenarios/entities/Scenarios.hbm.xml diff --git a/libreplan-business/src/main/resources/org/libreplan/business/externalcompanies/entities/ExternalCompanies.hbm.xml b/libreplan-business/src/main/resources/org/libreplan/business/externalcompanies/entities/ExternalCompanies.hbm.xml index bf8cf5a86..618413ca8 100644 --- a/libreplan-business/src/main/resources/org/libreplan/business/externalcompanies/entities/ExternalCompanies.hbm.xml +++ b/libreplan-business/src/main/resources/org/libreplan/business/externalcompanies/entities/ExternalCompanies.hbm.xml @@ -33,4 +33,51 @@ column="company_user" /> + + + + + 100 + + + + + + + + + org.libreplan.business.externalcompanies.entities.CommunicationType + + + + + + + + + + + + + 100 + + + + + + + + + + + + + 100 + + + + + + + diff --git a/libreplan-business/src/main/resources/org/libreplan/business/orders/entities/Orders.hbm.xml b/libreplan-business/src/main/resources/org/libreplan/business/orders/entities/Orders.hbm.xml index 80486666d..3a75a59f4 100644 --- a/libreplan-business/src/main/resources/org/libreplan/business/orders/entities/Orders.hbm.xml +++ b/libreplan-business/src/main/resources/org/libreplan/business/orders/entities/Orders.hbm.xml @@ -144,6 +144,23 @@ + + + + + + + + + + + + + + + diff --git a/libreplan-business/src/main/resources/org/libreplan/business/planner/entities/SubcontractorCommunication.hbm.xml b/libreplan-business/src/main/resources/org/libreplan/business/planner/entities/SubcontractorCommunication.hbm.xml new file mode 100644 index 000000000..8ae7336de --- /dev/null +++ b/libreplan-business/src/main/resources/org/libreplan/business/planner/entities/SubcontractorCommunication.hbm.xml @@ -0,0 +1,52 @@ + + + + + + + + + 100 + + + + + + + org.libreplan.business.externalcompanies.entities.CommunicationType + + + + + + + + + + + + + + + + + + + + + 100 + + + + + + + + + diff --git a/libreplan-business/src/main/resources/org/libreplan/business/planner/entities/Tasks.hbm.xml b/libreplan-business/src/main/resources/org/libreplan/business/planner/entities/Tasks.hbm.xml index 8c4912e11..860945e05 100644 --- a/libreplan-business/src/main/resources/org/libreplan/business/planner/entities/Tasks.hbm.xml +++ b/libreplan-business/src/main/resources/org/libreplan/business/planner/entities/Tasks.hbm.xml @@ -213,6 +213,22 @@ + + + + + + + + + + + + + + diff --git a/libreplan-business/src/test/java/org/libreplan/business/test/externalcompanies/daos/CustomerCommunicationDAOTest.java b/libreplan-business/src/test/java/org/libreplan/business/test/externalcompanies/daos/CustomerCommunicationDAOTest.java new file mode 100644 index 000000000..5b891cf57 --- /dev/null +++ b/libreplan-business/src/test/java/org/libreplan/business/test/externalcompanies/daos/CustomerCommunicationDAOTest.java @@ -0,0 +1,143 @@ +/* + * This file is part of LibrePlan + * + * Copyright (C) 2011 WirelessGalicia, S.L. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.libreplan.business.test.externalcompanies.daos; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertNotNull; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.fail; +import static org.libreplan.business.BusinessGlobalNames.BUSINESS_SPRING_CONFIG_FILE; +import static org.libreplan.business.test.BusinessGlobalNames.BUSINESS_SPRING_CONFIG_TEST_FILE; + +import java.util.Date; +import java.util.List; +import java.util.UUID; + +import org.joda.time.LocalDate; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.libreplan.business.calendars.daos.IBaseCalendarDAO; +import org.libreplan.business.calendars.entities.BaseCalendar; +import org.libreplan.business.common.IAdHocTransactionService; +import org.libreplan.business.common.IOnTransaction; +import org.libreplan.business.common.exceptions.InstanceNotFoundException; +import org.libreplan.business.common.exceptions.ValidationException; +import org.libreplan.business.externalcompanies.daos.ICustomerCommunicationDAO; +import org.libreplan.business.externalcompanies.entities.CustomerCommunication; +import org.libreplan.business.externalcompanies.entities.ExternalCompany; +import org.libreplan.business.orders.daos.IOrderDAO; +import org.libreplan.business.orders.entities.Order; +import org.libreplan.business.scenarios.IScenarioManager; +import org.libreplan.business.test.calendars.entities.BaseCalendarTest; +import org.libreplan.business.workreports.entities.WorkReportLine; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.transaction.annotation.Transactional; +import static org.junit.Assert.assertTrue; + +/** + * Tests for {@link CustomerCommunication} + * + * @author Susana Montes Pedreira + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(locations = { BUSINESS_SPRING_CONFIG_FILE, + BUSINESS_SPRING_CONFIG_TEST_FILE }) +@Transactional +public class CustomerCommunicationDAOTest { + + @Autowired + ICustomerCommunicationDAO customerCommunicationDAO; + + @Autowired + IOrderDAO orderDAO; + + @Autowired + private IBaseCalendarDAO calendarDAO; + + public Order createValidOrder(String name) { + Order order = Order.create(); + order.setName(name); + order.setCode(UUID.randomUUID().toString()); + order.setInitDate(new Date()); + BaseCalendar basicCalendar = BaseCalendarTest.createBasicCalendar(); + calendarDAO.save(basicCalendar); + order.setCalendar(basicCalendar); + orderDAO.save(order); + return order; + } + + private Date givenDeadLine(int months) { + LocalDate date = new LocalDate(); + date.plusMonths(months); + return date.toDateTimeAtStartOfDay().toDate(); + } + + public CustomerCommunication createValidCustomerCommunication() { + Order order = createValidOrder("Order A"); + CustomerCommunication customerCommunication = CustomerCommunication + .createTodayNewProject(givenDeadLine(2)); + customerCommunication.setOrder(order); + return customerCommunication; + } + + @Test + public void testOrderDAOInSpringContainer() { + assertNotNull(orderDAO); + } + + @Test + public void testCustomerCommunicationDAOInSpringContainer() { + assertNotNull(customerCommunicationDAO); + } + + @Test + public void testSaveCustomerCommunication() { + CustomerCommunication customerCommunication = createValidCustomerCommunication(); + customerCommunicationDAO.save(customerCommunication); + assertTrue(customerCommunication.getId() != null); + } + + @Test + public void testRemoveCustomerCommunication() + throws InstanceNotFoundException { + CustomerCommunication customerCommunication = createValidCustomerCommunication(); + customerCommunicationDAO.save(customerCommunication); + assertTrue(customerCommunication.getId() != null); + customerCommunicationDAO.remove(customerCommunication.getId()); + assertFalse(customerCommunicationDAO + .exists(customerCommunication.getId())); + } + + @Test + public void testSaveCustomerCommunicationWithoutOrder() + throws InstanceNotFoundException { + CustomerCommunication customerCommunication = createValidCustomerCommunication(); + customerCommunication.setOrder(null); + try { + customerCommunicationDAO.save(customerCommunication); + fail("It should throw an exception"); + } catch (ValidationException e) { + // Ok + } + } + +} diff --git a/libreplan-business/src/test/java/org/libreplan/business/test/orders/daos/OrderDAOTest.java b/libreplan-business/src/test/java/org/libreplan/business/test/orders/daos/OrderDAOTest.java index 347dea5c7..44118161a 100644 --- a/libreplan-business/src/test/java/org/libreplan/business/test/orders/daos/OrderDAOTest.java +++ b/libreplan-business/src/test/java/org/libreplan/business/test/orders/daos/OrderDAOTest.java @@ -20,12 +20,17 @@ package org.libreplan.business.test.orders.daos; import static junit.framework.Assert.assertNotNull; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; import static org.libreplan.business.BusinessGlobalNames.BUSINESS_SPRING_CONFIG_FILE; import static org.libreplan.business.test.BusinessGlobalNames.BUSINESS_SPRING_CONFIG_TEST_FILE; import java.util.Date; import java.util.UUID; +import org.joda.time.LocalDate; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -34,6 +39,7 @@ import org.libreplan.business.calendars.entities.BaseCalendar; import org.libreplan.business.common.IAdHocTransactionService; import org.libreplan.business.common.IOnTransaction; import org.libreplan.business.common.exceptions.ValidationException; +import org.libreplan.business.externalcompanies.entities.DeadlineCommunication; import org.libreplan.business.orders.daos.IOrderDAO; import org.libreplan.business.orders.entities.Order; import org.libreplan.business.scenarios.IScenarioManager; @@ -102,6 +108,56 @@ public class OrderDAOTest { return order; } + private Order createValidOrderWithDeadlineCommunications(String name) { + Order order = createValidOrder(name); + + //create two deadline communications + Date date1 = (new Date()); + Date date2 = (new LocalDate(date1).plusDays(3)).toDateTimeAtStartOfDay().toDate(); + + DeadlineCommunication deadlineCommunication1 = DeadlineCommunication.create(date1, null); + DeadlineCommunication deadlineCommunication2 = DeadlineCommunication.create(date2, null); + + order.getDeliveringDates().add(deadlineCommunication1); + order.getDeliveringDates().add(deadlineCommunication2); + + return order; + } + + @Test + public void testSaveOrdersWithDeliveringDates() { + Order order = createValidOrderWithDeadlineCommunications("test"); + orderDAO.save(order); + orderDAO.flush(); + + assertThat(order.getDeliveringDates().size(), equalTo(2)); + + DeadlineCommunication dcFirst = order.getDeliveringDates().first(); + DeadlineCommunication dcLast = order.getDeliveringDates().last(); + + assertTrue(dcFirst.getSaveDate().after(dcLast.getSaveDate())); + + + //A new DeadlineCommunication is placed between the existing communications. + Date date = (new LocalDate(dcLast.getSaveDate()).plusDays(2)).toDateTimeAtStartOfDay().toDate(); + DeadlineCommunication deadlineCommunication = DeadlineCommunication.create(date, null); + order.getDeliveringDates().add(deadlineCommunication); + + orderDAO.save(order); + orderDAO.flush(); + + assertThat(order.getDeliveringDates().size(), equalTo(3)); + + dcFirst = order.getDeliveringDates().first(); + dcLast = order.getDeliveringDates().last(); + DeadlineCommunication new_dc = (DeadlineCommunication) order.getDeliveringDates().toArray()[1]; + + assertTrue(dcFirst.getSaveDate().after(dcLast.getSaveDate())); + assertTrue(dcFirst.getSaveDate().after(new_dc.getSaveDate())); + assertFalse(dcLast.equals(new_dc)); + assertTrue(dcLast.getSaveDate().before(new_dc.getSaveDate())); + } + @Test public void testSaveTwoOrdersWithDifferentNames() { transactionService.runOnAnotherTransaction(new IOnTransaction() { diff --git a/libreplan-business/src/test/java/org/libreplan/business/test/planner/daos/SubcontractorCommunicationDAOTest.java b/libreplan-business/src/test/java/org/libreplan/business/test/planner/daos/SubcontractorCommunicationDAOTest.java new file mode 100644 index 000000000..0d8e1e93b --- /dev/null +++ b/libreplan-business/src/test/java/org/libreplan/business/test/planner/daos/SubcontractorCommunicationDAOTest.java @@ -0,0 +1,258 @@ +/* + * This file is part of LibrePlan + * + * Copyright (C) 2011 WirelessGalicia, S.L. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.libreplan.business.test.planner.daos; + +import static junit.framework.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.libreplan.business.BusinessGlobalNames.BUSINESS_SPRING_CONFIG_FILE; +import static org.libreplan.business.test.BusinessGlobalNames.BUSINESS_SPRING_CONFIG_TEST_FILE; + +import java.util.Arrays; +import java.util.Date; +import java.util.UUID; + +import org.hibernate.SessionFactory; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.libreplan.business.calendars.daos.IBaseCalendarDAO; +import org.libreplan.business.calendars.entities.BaseCalendar; +import org.libreplan.business.common.daos.IConfigurationDAO; +import org.libreplan.business.common.exceptions.InstanceNotFoundException; +import org.libreplan.business.common.exceptions.ValidationException; +import org.libreplan.business.externalcompanies.daos.IExternalCompanyDAO; +import org.libreplan.business.externalcompanies.entities.CommunicationType; +import org.libreplan.business.externalcompanies.entities.ExternalCompany; +import org.libreplan.business.orders.daos.IOrderDAO; +import org.libreplan.business.orders.entities.HoursGroup; +import org.libreplan.business.orders.entities.Order; +import org.libreplan.business.orders.entities.OrderLine; +import org.libreplan.business.orders.entities.SchedulingDataForVersion; +import org.libreplan.business.orders.entities.TaskSource; +import org.libreplan.business.orders.entities.TaskSource.TaskSourceSynchronization; +import org.libreplan.business.planner.daos.ISubcontractedTaskDataDAO; +import org.libreplan.business.planner.daos.ISubcontractorCommunicationDAO; +import org.libreplan.business.planner.daos.ITaskElementDAO; +import org.libreplan.business.planner.daos.ITaskSourceDAO; +import org.libreplan.business.planner.entities.SubcontractedTaskData; +import org.libreplan.business.planner.entities.SubcontractorCommunication; +import org.libreplan.business.planner.entities.Task; +import org.libreplan.business.scenarios.IScenarioManager; +import org.libreplan.business.scenarios.bootstrap.IScenariosBootstrap; +import org.libreplan.business.scenarios.entities.OrderVersion; +import org.libreplan.business.test.calendars.entities.BaseCalendarTest; +import org.libreplan.business.test.externalcompanies.daos.ExternalCompanyDAOTest; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.transaction.annotation.Transactional; + +/** + * Tests for {@link SubcontractorCommunication} + * + * @author Susana Montes Pedreira + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(locations = { BUSINESS_SPRING_CONFIG_FILE, + BUSINESS_SPRING_CONFIG_TEST_FILE }) +@Transactional +public class SubcontractorCommunicationDAOTest { + + @Autowired + ISubcontractorCommunicationDAO subcontractorCommunicationDAO; + + @Autowired + ISubcontractedTaskDataDAO subcontractedTaskDataDAO; + + @Autowired + IExternalCompanyDAO externalCompanyDAO; + + @Autowired + private ITaskElementDAO taskElementDAO; + + @Autowired + private IOrderDAO orderDAO; + + @Autowired + private ITaskSourceDAO taskSourceDAO; + + @Autowired + private SessionFactory sessionFactory; + + @Autowired + private IConfigurationDAO configurationDAO; + + @Autowired + private IScenarioManager scenarioManager; + + @Autowired + private IBaseCalendarDAO calendarDAO; + + @Autowired + private IScenariosBootstrap scenariosBootstrap; + + @Before + public void loadRequiredData() { + scenariosBootstrap.loadRequiredData(); + } + + private HoursGroup associatedHoursGroup; + + private ExternalCompany getSubcontractorExternalCompanySaved() { + ExternalCompany externalCompany = ExternalCompanyDAOTest + .createValidExternalCompany(); + externalCompany.setSubcontractor(true); + + externalCompanyDAO.save(externalCompany); + externalCompanyDAO.flush(); + sessionFactory.getCurrentSession().evict(externalCompany); + + externalCompany.dontPoseAsTransientObjectAnymore(); + + return externalCompany; + } + + private OrderLine createOrderLine() { + OrderLine orderLine = OrderLine.create(); + orderLine.setName("bla"); + orderLine.setCode("code-" + UUID.randomUUID()); + HoursGroup hoursGroup = new HoursGroup(); + hoursGroup.setCode("hours-group-code-" + UUID.randomUUID()); + orderLine.addHoursGroup(hoursGroup); + Order order = Order.create(); + OrderVersion orderVersion = ResourceAllocationDAOTest + .setupVersionUsing(scenarioManager, order); + order.setName("bla-" + UUID.randomUUID()); + order.setInitDate(new Date()); + order.setCode("code-" + UUID.randomUUID()); + order.useSchedulingDataFor(orderVersion); + order.add(orderLine); + + //add a basic calendar + BaseCalendar basicCalendar = BaseCalendarTest.createBasicCalendar(); + calendarDAO.save(basicCalendar); + order.setCalendar(basicCalendar); + + try { + orderDAO.save(order); + sessionFactory.getCurrentSession().flush(); + } catch (ValidationException e) { + throw new RuntimeException(e); + } + return orderLine; + } + + private Task createValidTask() { + associatedHoursGroup = new HoursGroup(); + associatedHoursGroup.setCode("hours-group-code-" + UUID.randomUUID()); + OrderLine orderLine = createOrderLine(); + orderLine.addHoursGroup(associatedHoursGroup); + OrderVersion orderVersion = ResourceAllocationDAOTest + .setupVersionUsing(scenarioManager, + orderLine.getOrder()); + orderLine.useSchedulingDataFor(orderVersion); + SchedulingDataForVersion schedulingDataForVersion = orderLine + .getCurrentSchedulingDataForVersion(); + TaskSource taskSource = TaskSource.create(schedulingDataForVersion, + Arrays.asList(associatedHoursGroup)); + TaskSourceSynchronization mustAdd = TaskSource.mustAdd(taskSource); + mustAdd.apply(TaskSource.persistTaskSources(taskSourceDAO)); + Task task = (Task) taskSource.getTask(); + return task; + } + + public SubcontractedTaskData createValidSubcontractedTaskData(String name) { + Task task = createValidTask(); + SubcontractedTaskData subcontractedTaskData = SubcontractedTaskData + .create(task); + subcontractedTaskData.setExternalCompany(getSubcontractorExternalCompanySaved()); + + task.setSubcontractedTaskData(subcontractedTaskData); + taskElementDAO.save(task); + taskElementDAO.flush(); + sessionFactory.getCurrentSession().evict(task); + sessionFactory.getCurrentSession().evict(subcontractedTaskData); + + subcontractedTaskDataDAO.save(subcontractedTaskData); + return subcontractedTaskData; + } + + public SubcontractorCommunication createValidSubcontractorCommunication(){ + SubcontractedTaskData subcontractedTaskData = createValidSubcontractedTaskData("Task A"); + Date communicationDate = new Date(); + SubcontractorCommunication subcontractorCommunication = SubcontractorCommunication + .create(subcontractedTaskData, CommunicationType.NEW_PROJECT, + communicationDate, false); + return subcontractorCommunication; + } + + @Test + public void testSubcontractorCommunicationDAOInSpringContainer() { + assertNotNull(subcontractorCommunicationDAO); + } + + @Test + public void testSaveSubcontractorCommunication() { + SubcontractorCommunication subcontractorCommunication = createValidSubcontractorCommunication(); + subcontractorCommunicationDAO.save(subcontractorCommunication); + assertTrue(subcontractorCommunication.getId() != null); + } + + @Test + public void testRemoveSubcontractorCommunication() + throws InstanceNotFoundException { + SubcontractorCommunication subcontractorCommunication = createValidSubcontractorCommunication(); + subcontractorCommunicationDAO.save(subcontractorCommunication); + + assertTrue(subcontractorCommunication.getId() != null); + Long idSubcontratecTaskData = subcontractorCommunication + .getSubcontractedTaskData().getId(); + Long idCommunication = subcontractorCommunication.getId(); + + subcontractorCommunicationDAO + .remove(subcontractorCommunication.getId()); + try{ + subcontractorCommunicationDAO.findExistingEntity(idCommunication); + fail("error"); + }catch(RuntimeException e){ + //ok + } + try{ + subcontractedTaskDataDAO.findExistingEntity(idSubcontratecTaskData); + }catch(RuntimeException e){ + fail("error"); + } + } + + @Test + public void testSaveSubcontractorCommunicationWithoutSubcontratedTaskData() + throws InstanceNotFoundException { + SubcontractorCommunication subcontractorCommunication = createValidSubcontractorCommunication(); + subcontractorCommunication.setSubcontractedTaskData(null); + try { + subcontractorCommunicationDAO.save(subcontractorCommunication); + fail("It should throw an exception"); + } catch (ValidationException e) { + // Ok + } + } + +} \ No newline at end of file diff --git a/libreplan-business/src/test/resources/libreplan-business-spring-config-test.xml b/libreplan-business/src/test/resources/libreplan-business-spring-config-test.xml index 5b24722eb..0c89eb0b7 100644 --- a/libreplan-business/src/test/resources/libreplan-business-spring-config-test.xml +++ b/libreplan-business/src/test/resources/libreplan-business-spring-config-test.xml @@ -87,6 +87,9 @@ org/libreplan/business/planner/entities/AdvanceConsolidations.hbm.xml + + org/libreplan/business/planner/entities/SubcontractorCommunication.hbm.xml + org/libreplan/business/scenarios/entities/Scenarios.hbm.xml diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/common/CustomMenuController.java b/libreplan-webapp/src/main/java/org/libreplan/web/common/CustomMenuController.java index d5f88880b..d195646d5 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/common/CustomMenuController.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/common/CustomMenuController.java @@ -5,6 +5,8 @@ * Desenvolvemento Tecnolóxico de Galicia * Copyright (C) 2010-2011 Igalia, S.L. * + * Copyright (C) 2011 WirelessGalicia, S.L. + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or @@ -307,9 +309,11 @@ public class CustomMenuController extends Div implements IMenuItemsRegister { if (SecurityUtils.isUserInRole(UserRole.ROLE_ADMINISTRATION)) { resourcesItems.add(subItem(_("Companies"), "/externalcompanies/externalcompanies.zul","")); } - resourcesItems.add(subItem(_("Subcontracting"), "/subcontract/subcontractedTasks.zul", "", - subItem(_("Subcontracted Tasks"), "/subcontract/subcontractedTasks.zul", ""), - subItem(_("Report Progress"), "/subcontract/reportAdvances.zul", ""))); + resourcesItems.add(subItem(_("Communications"), "/subcontract/subcontractedTasks.zul", "", + subItem(_("Send to subcontractors"), "/subcontract/subcontractedTasks.zul", ""), + subItem(_("Received from subcontractors"), "/subcontract/subcontractorCommunications.zul",""), + subItem(_("Send to customers"), "/subcontract/reportAdvances.zul", ""), + subItem(_("Received from customers"), "/subcontract/customerCommunications.zul",""))); topItem(_("Resources"), "/resources/worker/worker.zul", "", resourcesItems); if (isScenariosVisible()) { diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/common/TemplateModel.java b/libreplan-webapp/src/main/java/org/libreplan/web/common/TemplateModel.java index 09bf5de3a..9f7f7765e 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/common/TemplateModel.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/common/TemplateModel.java @@ -482,7 +482,11 @@ public class TemplateModel implements ITemplateModel { @Override @Transactional(readOnly = true) public boolean isUserAdmin() { - return UserUtil.getUserFromSession().isAdministrator(); + User user = UserUtil.getUserFromSession(); + if(user == null) { + return false; + } + return user.isAdministrator(); } @Override diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/common/Util.java b/libreplan-webapp/src/main/java/org/libreplan/web/common/Util.java index 9cf2d05b5..b03f150de 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/common/Util.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/common/Util.java @@ -661,6 +661,7 @@ public class Util { * {@link Configuration} object. */ public static String addCurrencySymbol(BigDecimal value) { + value = (value == null ? BigDecimal.ZERO : value); DecimalFormat decimalFormat = (DecimalFormat) DecimalFormat .getInstance(); decimalFormat.applyPattern(getMoneyFormat()); diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/common/converters/TaskConverter.java b/libreplan-webapp/src/main/java/org/libreplan/web/common/converters/TaskConverter.java new file mode 100644 index 000000000..045043bbd --- /dev/null +++ b/libreplan-webapp/src/main/java/org/libreplan/web/common/converters/TaskConverter.java @@ -0,0 +1,67 @@ +/* + * This file is part of LibrePlan + * + * Copyright (C) 2011 WirelessGalicia, S.L. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.libreplan.web.common.converters; + +import org.libreplan.business.common.exceptions.InstanceNotFoundException; +import org.libreplan.business.planner.daos.ITaskElementDAO; +import org.libreplan.business.planner.entities.Task; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +/** + * A {@link IConverter} for {@link Task}
+ * @author Susana Montes Pedreira + */ +@Component +@Scope(BeanDefinition.SCOPE_SINGLETON) +public class TaskConverter implements IConverter { + + @Autowired + private ITaskElementDAO taskDAO; + + @Override + @Transactional(readOnly = true) + public Task asObject(String stringRepresentation) { + try { + return (Task) taskDAO.find(Long.parseLong(stringRepresentation)); + } catch (InstanceNotFoundException e) { + throw new RuntimeException(e); + } + } + + @Override + public String asString(Task entity) { + return entity.getId() + ""; + } + + @Override + public String asStringUngeneric(Object entity) { + return asString((Task) entity); + } + + @Override + public Class getType() { + return Task.class; + } + +} diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/common/converters/TaskElementConverter.java b/libreplan-webapp/src/main/java/org/libreplan/web/common/converters/TaskElementConverter.java new file mode 100644 index 000000000..d80475111 --- /dev/null +++ b/libreplan-webapp/src/main/java/org/libreplan/web/common/converters/TaskElementConverter.java @@ -0,0 +1,68 @@ +/* + * This file is part of LibrePlan + * + * Copyright (C) 2011 Wireless Galicia, S.L. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.libreplan.web.common.converters; + +import org.libreplan.business.common.exceptions.InstanceNotFoundException; +import org.libreplan.business.planner.daos.ITaskElementDAO; +import org.libreplan.business.planner.entities.TaskElement; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +/** + * A {@link IConverter} for {@link TaskElement}
+ * @author Susana Montes Pedreira + */ + +@Component +@Scope(BeanDefinition.SCOPE_SINGLETON) +public class TaskElementConverter implements IConverter { + + @Autowired + private ITaskElementDAO taskElementDAO; + + @Override + public String asString(TaskElement entity) { + return entity.getId() + ""; + } + + @Override + public Class getType() { + return TaskElement.class; + } + + @Override + public String asStringUngeneric(Object entity) { + return asString((TaskElement) entity); + } + + @Override + @Transactional(readOnly = true) + public TaskElement asObject(String stringRepresentation) { + long id = Long.parseLong(stringRepresentation); + try { + return taskElementDAO.find(id); + } catch (InstanceNotFoundException e) { + throw new RuntimeException(e); + } + } + } \ No newline at end of file diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/common/typeconverters/DateTimeConverter.java b/libreplan-webapp/src/main/java/org/libreplan/web/common/typeconverters/DateTimeConverter.java new file mode 100644 index 000000000..8e23c4241 --- /dev/null +++ b/libreplan-webapp/src/main/java/org/libreplan/web/common/typeconverters/DateTimeConverter.java @@ -0,0 +1,47 @@ +/* + * This file is part of LibrePlan + * + * Copyright (C) 2012 WirelessGalicia, S.L. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.libreplan.web.common.typeconverters; + +import java.text.SimpleDateFormat; +import java.util.Date; + +import org.zkoss.zk.ui.Component; +import org.zkoss.zkplus.databind.TypeConverter; + +/** + * Converter for the type java.util.Date with an hour minute precision + * + * @author Susana Montes Pedreira + * + */ +public class DateTimeConverter implements TypeConverter { + + @Override + public Object coerceToBean(Object arg0, Component arg1) { + return null; + } + + @Override + public Object coerceToUi(Object object, Component component) { + return object != null ? (new SimpleDateFormat("dd/MM/yyyy HH:mm")) + .format((Date) object) : new String(""); + } + +} diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/dashboard/CostStatusModel.java b/libreplan-webapp/src/main/java/org/libreplan/web/dashboard/CostStatusModel.java index 1198c5443..838cf8629 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/dashboard/CostStatusModel.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/dashboard/CostStatusModel.java @@ -20,15 +20,10 @@ package org.libreplan.web.dashboard; import java.math.BigDecimal; -import java.util.List; -import java.util.SortedMap; -import java.util.TreeMap; import org.joda.time.LocalDate; import org.libreplan.business.orders.entities.Order; -import org.libreplan.business.planner.entities.ICostCalculator; -import org.libreplan.business.planner.entities.Task; -import org.libreplan.business.planner.entities.TaskElement; +import org.libreplan.business.planner.entities.IOrderEarnedValueCalculator; import org.libreplan.web.planner.order.OrderPlanningModel; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanDefinition; @@ -53,7 +48,7 @@ import org.springframework.transaction.annotation.Transactional; public class CostStatusModel implements ICostStatusModel { @Autowired - private ICostCalculator hoursCostCalculator; + private IOrderEarnedValueCalculator earnedValueCalculator; private Order order; @@ -64,128 +59,27 @@ public class CostStatusModel implements ICostStatusModel { @Override @Transactional(readOnly = true) public BigDecimal getActualCostWorkPerformedAt(LocalDate date) { - SortedMap actualCost = calculateActualCostWorkPerformed(); - BigDecimal result = actualCost.get(date); - return (result != null) ? result : BigDecimal.ZERO; - } - - private SortedMap calculateActualCostWorkPerformed() { - SortedMap result = new TreeMap(); - for (TaskElement taskElement : getAllTaskElements(order)) { - if (taskElement instanceof Task) { - addCost(result, getWorkReportCost((Task) taskElement)); - } - } - return accumulateResult(result); - } - - private SortedMap accumulateResult( - SortedMap map) { - SortedMap result = new TreeMap(); - if (map.isEmpty()) { - return result; - } - - BigDecimal accumulatedResult = BigDecimal.ZERO; - for (LocalDate day : map.keySet()) { - BigDecimal value = map.get(day); - accumulatedResult = accumulatedResult.add(value); - result.put(day, accumulatedResult); - } - return result; - } - - private void addCost(SortedMap currentCost, - SortedMap additionalCost) { - for (LocalDate day : additionalCost.keySet()) { - if (!currentCost.containsKey(day)) { - currentCost.put(day, BigDecimal.ZERO); - } - currentCost.put(day, - currentCost.get(day).add(additionalCost.get(day))); - } - } - - private List getAllTaskElements(Order order) { - List result = order.getAllChildrenAssociatedTaskElements(); - result.add(order.getAssociatedTaskElement()); - return result; - } - - private SortedMap getWorkReportCost(Task taskElement) { - return hoursCostCalculator.getWorkReportCost(taskElement); - } - - @Override - @Transactional(readOnly = true) - public BigDecimal getBudgetAtCompletion() { - SortedMap budgedtedCost = calculateBudgetedCostWorkScheduled(); - LocalDate lastKey = budgedtedCost.lastKey(); - return (lastKey) != null ? budgedtedCost.get(lastKey) : BigDecimal.ZERO; - } - - private SortedMap calculateBudgetedCostWorkScheduled() { - SortedMap result = new TreeMap(); - for (TaskElement taskElement : getAllTaskElements(order)) { - if (taskElement instanceof Task) { - addCost(result, getEstimatedCost((Task) taskElement)); - } - } - return accumulateResult(result); - } - - private SortedMap getEstimatedCost(Task task) { - return hoursCostCalculator.getEstimatedCost(task); - } - - @Override - @Transactional(readOnly = true) - public BigDecimal getBudgetedCostWorkPerformedAt(LocalDate date) { - SortedMap budgetedCost = calculateBudgetedCostWorkPerformed(); - BigDecimal result = budgetedCost.get(date); - return (result != null) ? result : BigDecimal.ZERO; - } - - private SortedMap calculateBudgetedCostWorkPerformed() { - SortedMap estimatedCost = new TreeMap(); - for (TaskElement taskElement : getAllTaskElements(order)) { - if (taskElement instanceof Task) { - addCost(estimatedCost, getAdvanceCost((Task) taskElement)); - } - } - return accumulateResult(estimatedCost); - } - - private SortedMap getAdvanceCost(Task task) { - return hoursCostCalculator.getAdvanceCost(task); + return earnedValueCalculator.getActualCostWorkPerformedAt(order, date); } @Override public BigDecimal getCostPerformanceIndex(BigDecimal budgetedCost, BigDecimal actualCost) { - if (BigDecimal.ZERO.compareTo(actualCost) == 0) { - return BigDecimal.ZERO; - } - return asPercentage(budgetedCost.divide(actualCost)); - } - - private BigDecimal asPercentage(BigDecimal value) { - return value.multiply(BigDecimal.valueOf(100)).setScale(2); + return earnedValueCalculator.getCostPerformanceIndex(budgetedCost, + actualCost); } @Override public BigDecimal getCostVariance(BigDecimal budgetedCost, BigDecimal actualCost) { - return budgetedCost.subtract(actualCost); + return earnedValueCalculator.getCostVariance(budgetedCost, actualCost); } @Override public BigDecimal getEstimateAtCompletion(BigDecimal budgetAtCompletion, BigDecimal costPerformanceIndex) { - if (BigDecimal.ZERO.compareTo(costPerformanceIndex) == 0) { - return BigDecimal.ZERO; - } - return asPercentage(budgetAtCompletion.divide(costPerformanceIndex)); + return earnedValueCalculator.getEstimateAtCompletion( + budgetAtCompletion, costPerformanceIndex); } @Override @@ -199,4 +93,15 @@ public class CostStatusModel implements ICostStatusModel { this.order = order; } + @Override + public BigDecimal getBudgetAtCompletion() { + return earnedValueCalculator.getBudgetAtCompletion(order); + } + + @Override + public BigDecimal getBudgetedCostWorkPerformedAt(LocalDate date) { + return earnedValueCalculator + .getBudgetedCostWorkPerformedAt(order, date); + } + } \ No newline at end of file diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/dashboard/DashboardController.java b/libreplan-webapp/src/main/java/org/libreplan/web/dashboard/DashboardController.java index 0702dbacc..4ddbdf57b 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/dashboard/DashboardController.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/dashboard/DashboardController.java @@ -32,7 +32,6 @@ import java.util.Set; import org.apache.commons.lang.StringUtils; import org.libreplan.business.orders.entities.Order; import org.libreplan.business.planner.entities.TaskStatusEnum; -import org.libreplan.web.common.Util; import org.libreplan.web.dashboard.DashboardModel.Interval; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Scope; @@ -42,7 +41,6 @@ import org.zkoss.zk.ui.util.GenericForwardComposer; import org.zkoss.zul.Div; import org.zkoss.zul.Grid; import org.zkoss.zul.Label; -import org.zkoss.zul.Window; import br.com.digilabs.jqplot.Chart; import br.com.digilabs.jqplot.JqPlotUtils; @@ -62,8 +60,6 @@ public class DashboardController extends GenericForwardComposer { private IDashboardModel dashboardModel; - private Window dashboardWindow; - private Grid gridTasksSummary; private Grid gridMarginWithDeadline; @@ -78,28 +74,25 @@ public class DashboardController extends GenericForwardComposer { @Override public void doAfterCompose(org.zkoss.zk.ui.Component comp) throws Exception { super.doAfterCompose(comp); - this.dashboardWindow = (Window) comp; - self.setAttribute("controller", this); - Util.createBindingsFor(this.dashboardWindow); } public void setCurrentOrder(Order order) { dashboardModel.setCurrentOrder(order); if (dashboardModel.tasksAvailable()) { + if (self != null) { + renderGlobalProgress(); + renderTaskStatus(); + renderTaskCompletationLag(); + renderTasksSummary(); + renderDeadlineViolation(); + renderMarginWithDeadline(); + renderEstimationAccuracy(); + renderCostStatus(order); + } showCharts(); } else { hideCharts(); } - if (this.dashboardWindow != null) { - renderGlobalProgress(); - renderTaskStatus(); - renderTaskCompletationLag(); - renderTasksSummary(); - renderDeadlineViolation(); - renderMarginWithDeadline(); - renderEstimationAccuracy(); - renderCostStatus(order); - } } private void renderCostStatus(Order order) { @@ -423,7 +416,7 @@ public class DashboardController extends GenericForwardComposer { private Map getData() { if (taskCompletationData == null) { taskCompletationData = dashboardModel - .calculateTaskCompletation(); + .calculateTaskCompletion(); } return taskCompletationData; } diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/dashboard/DashboardModel.java b/libreplan-webapp/src/main/java/org/libreplan/web/dashboard/DashboardModel.java index 22d5ea423..5bfc81a12 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/dashboard/DashboardModel.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/dashboard/DashboardModel.java @@ -141,65 +141,64 @@ public class DashboardModel implements IDashboardModel { /* Progress KPI: "Global Progress of the Project" */ @Override public BigDecimal getAdvancePercentageByHours() { - TaskGroup rootAsTaskGroup = (TaskGroup) getRootTask(); - if (this.getRootTask() == null) { + TaskGroup rootTask = (TaskGroup) getRootTask(); + if (rootTask == null) { throw new RuntimeException("Root task is null"); } - BigDecimal ratio = rootAsTaskGroup.getProgressAllByNumHours(); - return ratio.multiply(BigDecimal.TEN).multiply(BigDecimal.TEN); + return asPercentage(rootTask.getProgressAllByNumHours()); + } + + private BigDecimal asPercentage(BigDecimal value) { + return value != null ? value.multiply(BigDecimal.valueOf(100)) + : BigDecimal.ZERO; } @Override public BigDecimal getExpectedAdvancePercentageByHours() { - TaskGroup rootAsTaskGroup = (TaskGroup) getRootTask(); - if (this.getRootTask() == null) { + TaskGroup rootTask = (TaskGroup) getRootTask(); + if (rootTask == null) { throw new RuntimeException("Root task is null"); } - BigDecimal ratio = rootAsTaskGroup - .getTheoreticalProgressByNumHoursForAllTasksUntilNow(); - return ratio.multiply(BigDecimal.TEN).multiply(BigDecimal.TEN); + return asPercentage(rootTask + .getTheoreticalProgressByNumHoursForAllTasksUntilNow()); } @Override public BigDecimal getCriticalPathProgressByNumHours() { - TaskGroup rootAsTaskGroup = (TaskGroup) getRootTask(); - if (this.getRootTask() == null) { + TaskGroup rootTask = (TaskGroup) getRootTask(); + if (rootTask == null) { throw new RuntimeException("Root task is null"); } - BigDecimal ratio = rootAsTaskGroup.getCriticalPathProgressByNumHours(); - return ratio.multiply(BigDecimal.TEN).multiply(BigDecimal.TEN); + return asPercentage(rootTask.getCriticalPathProgressByNumHours()); } @Override public BigDecimal getExpectedCriticalPathProgressByNumHours() { - TaskGroup rootAsTaskGroup = (TaskGroup) getRootTask(); - if (this.getRootTask() == null) { + TaskGroup rootTask = (TaskGroup) getRootTask(); + if (rootTask == null) { throw new RuntimeException("Root task is null"); } - BigDecimal ratio = rootAsTaskGroup - .getTheoreticalProgressByNumHoursForCriticalPathUntilNow(); - return ratio.multiply(BigDecimal.TEN).multiply(BigDecimal.TEN); + return asPercentage(rootTask + .getTheoreticalProgressByNumHoursForCriticalPathUntilNow()); } @Override public BigDecimal getCriticalPathProgressByDuration() { - TaskGroup rootAsTaskGroup = (TaskGroup) getRootTask(); - if (this.getRootTask() == null) { + TaskGroup rootTask = (TaskGroup) getRootTask(); + if (rootTask == null) { throw new RuntimeException("Root task is null"); } - BigDecimal ratio = rootAsTaskGroup.getCriticalPathProgressByDuration(); - return ratio.multiply(BigDecimal.TEN).multiply(BigDecimal.TEN); + return asPercentage(rootTask.getCriticalPathProgressByDuration()); } @Override public BigDecimal getExpectedCriticalPathProgressByDuration() { - TaskGroup rootAsTaskGroup = (TaskGroup) getRootTask(); - if (this.getRootTask() == null) { + TaskGroup rootTask = (TaskGroup) getRootTask(); + if (rootTask == null) { throw new RuntimeException("Root task is null"); } - BigDecimal ratio = rootAsTaskGroup - .getTheoreticalProgressByDurationForCriticalPathUntilNow(); - return ratio.multiply(BigDecimal.TEN).multiply(BigDecimal.TEN); + return asPercentage(rootTask + .getTheoreticalProgressByDurationForCriticalPathUntilNow()); } /* Time KPI: Margin with deadline */ @@ -353,16 +352,16 @@ public class DashboardModel implements IDashboardModel { * @return */ @Override - public Map calculateTaskCompletation() { + public Map calculateTaskCompletion() { Map result = new LinkedHashMap(); Double max, min; // Get deviations of finished tasks, calculate max, min and delta List deviations = getTaskLagDeviations(); if (deviations.isEmpty()) { - return result; - } - if (deviations.size() == 1) { + max = Double.valueOf(3); + min = Double.valueOf(-2); + } else if (deviations.size() == 1) { max = deviations.get(0).doubleValue() + 3; min = deviations.get(0).doubleValue() - 2; } else { @@ -421,9 +420,9 @@ public class DashboardModel implements IDashboardModel { // Get deviations of finished tasks, calculate max, min and delta List deviations = getEstimationAccuracyDeviations(); if (deviations.isEmpty()) { - return result; - } - if (deviations.size() == 1) { + max = Double.valueOf(30); + min = Double.valueOf(-20); + } else if (deviations.size() == 1) { max = deviations.get(0).doubleValue() + 30; min = deviations.get(0).doubleValue() - 20; } else { diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/dashboard/ICostStatusModel.java b/libreplan-webapp/src/main/java/org/libreplan/web/dashboard/ICostStatusModel.java index ae75c0891..a150ab2da 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/dashboard/ICostStatusModel.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/dashboard/ICostStatusModel.java @@ -31,12 +31,14 @@ import org.libreplan.business.orders.entities.Order; */ interface ICostStatusModel { - BigDecimal getActualCostWorkPerformedAt(LocalDate day); + // Actual Cost Work Performed (ACWP) + BigDecimal getActualCostWorkPerformedAt(LocalDate date); - // Budget at Completion (BAC) + // Budget At Completion (BAC) BigDecimal getBudgetAtCompletion(); - BigDecimal getBudgetedCostWorkPerformedAt(LocalDate day); + // Budgeted Cost Work Performed (BCWP) + BigDecimal getBudgetedCostWorkPerformedAt(LocalDate date); // Cost Performance Index (CPI) BigDecimal getCostPerformanceIndex(BigDecimal budgetedCost, diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/dashboard/IDashboardModel.java b/libreplan-webapp/src/main/java/org/libreplan/web/dashboard/IDashboardModel.java index c8241560f..0411ad222 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/dashboard/IDashboardModel.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/dashboard/IDashboardModel.java @@ -75,7 +75,7 @@ interface IDashboardModel { Map calculateTaskStatus(); - Map calculateTaskCompletation(); + Map calculateTaskCompletion(); Map calculateEstimationAccuracy(); diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/orders/IManageOrderElementAdvancesModel.java b/libreplan-webapp/src/main/java/org/libreplan/web/orders/IManageOrderElementAdvancesModel.java index d57a3ac19..4cd1b9b83 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/orders/IManageOrderElementAdvancesModel.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/orders/IManageOrderElementAdvancesModel.java @@ -121,4 +121,17 @@ public interface IManageOrderElementAdvancesModel { boolean hasAnyConsolidatedAdvanceCurrentOrderElement(); + boolean hasAnySubcontratedTaskOnChildren(); + + boolean isSubcontratedAdvanceType(AdvanceAssignment advance); + + boolean isSubcontratedAdvanceTypeAndSubcontratedTask( + AdvanceAssignment advance); + + Boolean isAlreadyReportedProgress(AdvanceMeasurement measure); + + boolean hasReportedProgress(AdvanceAssignment advance); + + Boolean isAlreadyReportedProgressWith(LocalDate date); + } diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/orders/IOrderModel.java b/libreplan-webapp/src/main/java/org/libreplan/web/orders/IOrderModel.java index c81a1d4c1..21eee591b 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/orders/IOrderModel.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/orders/IOrderModel.java @@ -21,10 +21,13 @@ package org.libreplan.web.orders; +import java.util.Date; import java.util.List; import java.util.Map; +import java.util.SortedSet; import org.libreplan.business.calendars.entities.BaseCalendar; +import org.libreplan.business.externalcompanies.entities.EndDateCommunication; import org.libreplan.business.externalcompanies.entities.ExternalCompany; import org.libreplan.business.labels.entities.Label; import org.libreplan.business.orders.entities.Order; @@ -132,6 +135,14 @@ public interface IOrderModel extends IIntegrationEntityModel { boolean hasImputedExpenseSheets(OrderElement order); + void removeAskedEndDate(EndDateCommunication endDate); + + SortedSet getEndDates(); + + void addAskedEndDate(Date value); + + boolean alreadyExistsRepeatedEndDate(Date value); + boolean isAnyTaskWithConstraint(PositionConstraintType type); } diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/orders/ManageOrderElementAdvancesController.java b/libreplan-webapp/src/main/java/org/libreplan/web/orders/ManageOrderElementAdvancesController.java index f6ce1f1b5..e79f2d17a 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/orders/ManageOrderElementAdvancesController.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/orders/ManageOrderElementAdvancesController.java @@ -224,6 +224,7 @@ public class ManageOrderElementAdvancesController extends AdvanceAssignment advance = (AdvanceAssignment) selectedItem .getValue(); indexSelectedItem = editAdvances.getIndexOfItem(selectedItem); + showInfoAbout(advance); prepareEditAdvanceMeasurements(advance); reloadAdvances(); } @@ -244,6 +245,7 @@ public class ManageOrderElementAdvancesController extends .getSpreadAdvance(); if (advance != null) { indexSelectedItem = getAdvanceAssignments().indexOf(advance); + showInfoAbout(advance); prepareEditAdvanceMeasurements(advance); } else { selectAdvanceLine(getAdvanceAssignments().size() - 1); @@ -251,6 +253,13 @@ public class ManageOrderElementAdvancesController extends reloadAdvances(); } + private void showInfoAbout(AdvanceAssignment advance) { + if (manageOrderElementAdvancesModel + .isSubcontratedAdvanceTypeAndSubcontratedTask(advance)) { + showErrorMessage(_("Subcontractor values are read only because they were reported by the subcontractor company.")); + } + } + public void prepareEditAdvanceMeasurements(AdvanceAssignment advance) { if (advance != null && advance.getAdvanceType() != null) { manageOrderElementAdvancesModel @@ -351,10 +360,16 @@ public class ManageOrderElementAdvancesController extends final AdvanceAssignment advance = (AdvanceAssignment) data; listItem.setValue(advance); + Boolean readOnlyAdvance = false; boolean isQualityForm = false; + if (advance.getAdvanceType() != null) { isQualityForm = manageOrderElementAdvancesModel .isQualityForm(advance); + if (manageOrderElementAdvancesModel + .isSubcontratedAdvanceTypeAndSubcontratedTask(advance)) { + readOnlyAdvance = true; + } } if ((advance instanceof DirectAdvanceAssignment) @@ -372,7 +387,7 @@ public class ManageOrderElementAdvancesController extends appendRadioSpread(listItem); appendCalculatedCheckbox(listItem); appendChartCheckbox(listItem); - appendOperations(listItem); + appendOperations(listItem, readOnlyAdvance); } } @@ -661,17 +676,18 @@ public class ManageOrderElementAdvancesController extends listItem.appendChild(listCell); } - private void appendOperations(final Listitem listItem) { + private void appendOperations(final Listitem listItem, Boolean readOnly) { Hbox hbox = new Hbox(); - appendAddMeasurement(hbox, listItem); - appendRemoveButton(hbox, listItem); + appendAddMeasurement(hbox, listItem, readOnly); + appendRemoveButton(hbox, listItem, readOnly); Listcell listCell = new Listcell(); listCell.appendChild(hbox); listItem.appendChild(listCell); } - private void appendAddMeasurement(final Hbox hbox, final Listitem listItem) { + private void appendAddMeasurement(final Hbox hbox, final Listitem listItem, + Boolean readOnly) { final AdvanceAssignment advance = (AdvanceAssignment) listItem .getValue(); final Button addMeasurementButton = createAddMeasurementButton(); @@ -696,13 +712,17 @@ public class ManageOrderElementAdvancesController extends addMeasurementButton.setDisabled(true); addMeasurementButton .setTooltiptext(_("Calculated progress can not be modified")); + } else if (readOnly) { + addMeasurementButton.setDisabled(true); + addMeasurementButton + .setTooltiptext(_("Subcontractor values are read only because they were reported by the subcontractor company.")); } - hbox.appendChild(addMeasurementButton); } - private void appendRemoveButton(final Hbox hbox, final Listitem listItem) { + private void appendRemoveButton(final Hbox hbox, final Listitem listItem, + Boolean readOnly) { final AdvanceAssignment advance = (AdvanceAssignment) listItem .getValue(); final Button removeButton = createRemoveButton(); @@ -728,6 +748,14 @@ public class ManageOrderElementAdvancesController extends removeButton.setDisabled(true); removeButton .setTooltiptext(_("Consolidated progress can not be removed")); + } else if (readOnly) { + removeButton.setDisabled(true); + removeButton + .setTooltiptext(_("Subcontractor values are read only because they were reported by the subcontractor company.")); + } else if (manageOrderElementAdvancesModel.hasReportedProgress(advance)) { + removeButton.setDisabled(true); + removeButton + .setTooltiptext(_("Advance assignment can not be removed because, it has advance measures that have already been reported to customer.")); } hbox.appendChild(removeButton); @@ -1033,7 +1061,6 @@ public class ManageOrderElementAdvancesController extends @Override public void render(Listitem item, Object data) { AdvanceMeasurement advanceMeasurement = (AdvanceMeasurement) data; - item.setValue(advanceMeasurement); appendDecimalBoxValue(item); @@ -1053,7 +1080,9 @@ public class ManageOrderElementAdvancesController extends value.setScale(calculateScale(advanceMeasurement)); value.setDisabled(isReadOnlyAdvanceMeasurements() || manageOrderElementAdvancesModel - .hasConsolidatedAdvances(advanceMeasurement)); + .hasConsolidatedAdvances(advanceMeasurement) + || manageOrderElementAdvancesModel.isAlreadyReportedProgress(advanceMeasurement)); + value.addEventListener(Events.ON_CHANGE, new EventListener() { @Override @@ -1114,7 +1143,9 @@ public class ManageOrderElementAdvancesController extends date.setDisabled(isReadOnlyAdvanceMeasurements() || manageOrderElementAdvancesModel - .hasConsolidatedAdvances(advanceMeasurement)); + .hasConsolidatedAdvances(advanceMeasurement) + || manageOrderElementAdvancesModel.isAlreadyReportedProgress(advanceMeasurement)); + date.addEventListener(Events.ON_CHANGE, new EventListener() { @Override @@ -1197,6 +1228,14 @@ public class ManageOrderElementAdvancesController extends removeButton.setDisabled(true); removeButton .setTooltiptext(_("Consolidated progress measurement can not be removed")); + } else if (manageOrderElementAdvancesModel.isAlreadyReportedProgress(measure)) { + removeButton.setDisabled(true); + removeButton + .setTooltiptext(_("Values are read only because they have already been sent to the customer company.")); + } else if (isReadOnlyAdvanceMeasurements()) { + removeButton.setDisabled(isReadOnlyAdvanceMeasurements()); + removeButton + .setTooltiptext(_("Subcontractor values are read only because they were reported by the subcontractor company.")); } removeButton.addEventListener(Events.ON_CLICK, new EventListener() { @@ -1321,6 +1360,9 @@ public class ManageOrderElementAdvancesController extends return _("Date is not valid, it must be greater than the last progress consolidation"); } } + if (manageOrderElementAdvancesModel.isAlreadyReportedProgressWith(value)) { + return _("Date is not valid, it must be greater than the last progress reported to the customer"); + } } } diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/orders/ManageOrderElementAdvancesModel.java b/libreplan-webapp/src/main/java/org/libreplan/web/orders/ManageOrderElementAdvancesModel.java index c19de2eaa..6f177e6ec 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/orders/ManageOrderElementAdvancesModel.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/orders/ManageOrderElementAdvancesModel.java @@ -5,6 +5,8 @@ * Desenvolvemento Tecnolóxico de Galicia * Copyright (C) 2010-2011 Igalia, S.L. * + * Copyright (C) 2011-2012 WirelessGalicia, S.L. + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or @@ -54,7 +56,9 @@ import org.libreplan.business.advance.exceptions.DuplicateAdvanceAssignmentForOr import org.libreplan.business.advance.exceptions.DuplicateValueTrueReportGlobalAdvanceException; import org.libreplan.business.common.exceptions.InstanceNotFoundException; import org.libreplan.business.orders.daos.IOrderElementDAO; +import org.libreplan.business.orders.entities.Order; import org.libreplan.business.orders.entities.OrderElement; +import org.libreplan.business.planner.entities.TaskElement; import org.libreplan.business.planner.entities.consolidations.CalculatedConsolidatedValue; import org.libreplan.business.planner.entities.consolidations.CalculatedConsolidation; import org.libreplan.business.planner.entities.consolidations.Consolidation; @@ -344,6 +348,14 @@ public class ManageOrderElementAdvancesModel implements continue; } } + // if the associated task of the order elemement or of some its + // children is subcontrated the advance type subcontractor is not + // permitted + if (advanceType.getUnitName().equals( + PredefinedAdvancedTypes.SUBCONTRACTOR.getTypeName()) + && hasAnySubcontratedTaskOnChildren()) { + continue; + } advanceTypes.add(advanceType); } return getSpecificOrder(advanceTypes); @@ -402,7 +414,30 @@ public class ManageOrderElementAdvancesModel implements } } - return this.isIndirectAdvanceAssignment; + if(isIndirectAdvanceAssignment){ + return true; + } + + return isSubcontratedAdvanceTypeAndSubcontratedTask(advanceAssignment); + } + + @Override + public boolean isSubcontratedAdvanceTypeAndSubcontratedTask( + AdvanceAssignment advance) { + return (isSubcontratedAdvanceType(advance) && hasSubcontractedAssociatedTask(advance + .getOrderElement())); + } + + @Override + public boolean isSubcontratedAdvanceType(AdvanceAssignment advance) { + AdvanceType advanceType = advance.getAdvanceType(); + if (advanceType != null) { + if (advanceType.getUnitName().equals( + PredefinedAdvancedTypes.SUBCONTRACTOR.getTypeName())) { + return true; + } + } + return false; } @Override @@ -697,7 +732,7 @@ public class ManageOrderElementAdvancesModel implements @Transactional(readOnly = true) public boolean canRemoveOrChange(AdvanceMeasurement advanceMeasurement) { - return (!hasConsolidatedAdvances(advanceMeasurement)); + return (!hasConsolidatedAdvances(advanceMeasurement) && !isAlreadyReportedProgress(advanceMeasurement)); } @Transactional(readOnly = true) @@ -838,4 +873,88 @@ public class ManageOrderElementAdvancesModel implements return orderElement.hasAnyConsolidatedAdvance(); } + @Override + public boolean hasAnySubcontratedTaskOnChildren() { + List list = new ArrayList(); + list.add(orderElement); + list.addAll(orderElement.getAllChildren()); + for (OrderElement child : list) { + if (hasSubcontractedAssociatedTask(child)) { + return true; + } + } + return false; + } + + public boolean hasSubcontractedAssociatedTask(OrderElement orderElement) { + for (TaskElement task : orderElement.getTaskElements()) { + if (task.isSubcontracted()) { + return true; + } + } + return false; + } + + @Override + public Boolean isAlreadyReportedProgress(AdvanceMeasurement measure) { + if (measure != null && !measure.isNewObject()) { + return isAlreadyReportedProgressWith(measure.getDate()); + } + return false; + } + + @Override + public Boolean isAlreadyReportedProgressWith(LocalDate date) { + if (isSubcontractedByCustomer() && isSubcontratedAdvanceType(advanceAssignment)) { + LocalDate lastReported = getLastReportedProgressToCustomer(); + return (date != null && lastReported != null && date.compareTo(lastReported) <= 0); + } + return false; + } + + private LocalDate getLastReportedProgressToCustomer() { + return getLastReportedProgressToCustomerOf(advanceAssignment); + } + + private LocalDate getLastReportedProgressToCustomerOf(DirectAdvanceAssignment advanceAssignment) { + if (advanceAssignment != null && isSubcontractedByCustomer()) { + AdvanceMeasurement lastAdvanceMeasurementReported = null; + for (AdvanceMeasurement advanceMeasurement : advanceAssignment.getAdvanceMeasurements()) { + if (advanceMeasurement.getCommunicationDate() != null) { + if (lastAdvanceMeasurementReported == null) { + lastAdvanceMeasurementReported = advanceMeasurement; + } else { + if (advanceMeasurement.getCommunicationDate().compareTo( + lastAdvanceMeasurementReported.getCommunicationDate()) > 0) { + lastAdvanceMeasurementReported = advanceMeasurement; + } + } + } + } + return (lastAdvanceMeasurementReported != null) ? lastAdvanceMeasurementReported + .getDate() : null; + } + return null; + } + + private Boolean isSubcontractedByCustomer() { + if (orderElement != null) { + Order order = orderElement.getOrder(); + if (order != null && order.getExternalCode() != null) { + return true; + } + } + return false; + } + + @Override + public boolean hasReportedProgress(AdvanceAssignment advance) { + if (advance instanceof DirectAdvanceAssignment) { + return isSubcontractedByCustomer() + && isSubcontratedAdvanceType(advance) + && (getLastReportedProgressToCustomerOf((DirectAdvanceAssignment) advance) != null); + } + return false; + } + } diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/orders/OrderCRUDController.java b/libreplan-webapp/src/main/java/org/libreplan/web/orders/OrderCRUDController.java index db3ab3bf5..c66668252 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/orders/OrderCRUDController.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/orders/OrderCRUDController.java @@ -27,8 +27,11 @@ import java.text.SimpleDateFormat; import java.util.ConcurrentModificationException; import java.util.Date; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.SortedSet; +import java.util.TreeSet; import javax.annotation.Resource; @@ -38,6 +41,9 @@ import org.hibernate.validator.InvalidValue; import org.libreplan.business.calendars.entities.BaseCalendar; import org.libreplan.business.common.exceptions.InstanceNotFoundException; import org.libreplan.business.common.exceptions.ValidationException; +import org.libreplan.business.externalcompanies.entities.DeadlineCommunication; +import org.libreplan.business.externalcompanies.entities.DeliverDateComparator; +import org.libreplan.business.externalcompanies.entities.EndDateCommunication; import org.libreplan.business.externalcompanies.entities.ExternalCompany; import org.libreplan.business.orders.daos.IOrderDAO; import org.libreplan.business.orders.entities.HoursGroup; @@ -70,6 +76,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Scope; import org.zkoss.ganttz.util.LongOperationFeedback; +import org.zkoss.util.Locales; import org.zkoss.zk.ui.Component; import org.zkoss.zk.ui.Desktop; import org.zkoss.zk.ui.Executions; @@ -87,13 +94,13 @@ import org.zkoss.zul.Comboitem; import org.zkoss.zul.ComboitemRenderer; import org.zkoss.zul.Constraint; import org.zkoss.zul.Datebox; -import org.zkoss.zul.Decimalbox; import org.zkoss.zul.Grid; import org.zkoss.zul.Hbox; import org.zkoss.zul.Label; import org.zkoss.zul.Messagebox; import org.zkoss.zul.Row; import org.zkoss.zul.RowRenderer; +import org.zkoss.zul.Rows; import org.zkoss.zul.SimpleListModel; import org.zkoss.zul.Tab; import org.zkoss.zul.Tabbox; @@ -225,6 +232,10 @@ public class OrderCRUDController extends GenericForwardComposer { @Autowired private IOrderDAO orderDAO; + private Grid gridAskedEndDates; + + private EndDatesRenderer endDatesRenderer = new EndDatesRenderer(); + @Override public void doAfterCompose(Component comp) throws Exception { super.doAfterCompose(comp); @@ -247,7 +258,6 @@ public class OrderCRUDController extends GenericForwardComposer { checkCreationPermissions(); setupGlobalButtons(); - } private void setupGlobalButtons() { @@ -1049,6 +1059,8 @@ public class OrderCRUDController extends GenericForwardComposer { } }); + gridAskedEndDates = (Grid) editWindow.getFellow("gridAskedEndDates"); + } public void setupOrderDetails() { @@ -1340,8 +1352,8 @@ public class OrderCRUDController extends GenericForwardComposer { return orderModel.gettooltipText(order); } - public void reloadTotalBudget(Decimalbox decimalboxTotalBudget) { - Util.reloadBindings(decimalboxTotalBudget); + public void reloadTotalBudget(Label labelTotalBudget) { + Util.reloadBindings(labelTotalBudget); } /** @@ -1528,6 +1540,13 @@ public class OrderCRUDController extends GenericForwardComposer { } } + public SortedSet getDeliverDates() { + if(getOrder() != null){ + return getOrder().getDeliveringDates(); + } + return new TreeSet(new DeliverDateComparator()); + } + public Constraint chekValidProjectName() { return new Constraint() { @Override @@ -1576,8 +1595,147 @@ public class OrderCRUDController extends GenericForwardComposer { }; } + public boolean isSubcontractedProject() { + return (getOrder() != null) ? (getOrder().getExternalCode() != null) + : false; + } + + public String getProjectType() { + return (isSubcontractedProject()) ? "Subcontracted by client" + : "Regular project"; + } + + public void setCurrentDeliveryDate(Grid listDeliveryDates) { + if (getOrder() != null && getOrder().getDeliveringDates() != null + && !getOrder().getDeliveringDates().isEmpty()) { + DeadlineCommunication lastDeliveryDate = getOrder() + .getDeliveringDates().first(); + if (listDeliveryDates != null) { + listDeliveryDates.renderAll(); + final Rows rows = listDeliveryDates.getRows(); + for (Iterator i = rows.getChildren().iterator(); i.hasNext();) { + final Row row = (Row) i.next(); + final DeadlineCommunication deliveryDate = (DeadlineCommunication) row + .getValue(); + if (deliveryDate.equals(lastDeliveryDate)) { + row.setSclass("current-delivery-date"); + return; + } + } + } + } + } + + public SortedSet getEndDates() { + return orderModel.getEndDates(); + } + + public void addAskedEndDate(Datebox newEndDate) { + if (newEndDate == null || newEndDate.getValue() == null) { + messagesForUser.showMessage(Level.ERROR, _("You must select a valid date. ")); + return; + } + if (thereIsSomeCommunicationDateEmpty()) { + messagesForUser + .showMessage( + Level.ERROR, + _("It will only be possible to add a end date if all the exiting ones in the table have already been sent to customer..")); + return; + } + if (orderModel.alreadyExistsRepeatedEndDate(newEndDate.getValue())) { + messagesForUser.showMessage(Level.ERROR, + _("It already exists a end date with the same date. ")); + return; + } + orderModel.addAskedEndDate(newEndDate.getValue()); + reloadGridAskedEndDates(); + } + + private void reloadGridAskedEndDates() { + Util.reloadBindings(gridAskedEndDates); + } + + private boolean thereIsSomeCommunicationDateEmpty() { + for (EndDateCommunication endDate : orderModel.getEndDates()) { + if (endDate.getCommunicationDate() == null) { + return true; + } + } + return false; + } + + public EndDatesRenderer getEndDatesRenderer() { + return this.endDatesRenderer; + } + + private class EndDatesRenderer implements RowRenderer { + + @Override + public void render(Row row, Object data) throws Exception { + EndDateCommunication endDate = (EndDateCommunication) data; + row.setValue(endDate); + + appendLabel(row, toString(endDate.getSaveDate(), "dd/MM/yyyy HH:mm")); + appendLabel(row, toString(endDate.getEndDate(), "dd/MM/yyyy")); + appendLabel(row, toString(endDate.getCommunicationDate(), "dd/MM/yyyy HH:mm")); + appendOperations(row, endDate); + } + + private String toString(Date date, String precision) { + if (date == null) { + return ""; + } + return new SimpleDateFormat(precision, Locales.getCurrent()).format(date); + } + + private void appendLabel(Row row, String label) { + row.appendChild(new Label(label)); + } + + private void appendOperations(Row row, EndDateCommunication endDate) { + Hbox hbox = new Hbox(); + hbox.appendChild(getDeleteButton(endDate)); + row.appendChild(hbox); + } + + private Button getDeleteButton(final EndDateCommunication endDate) { + + Button deleteButton = new Button(); + deleteButton.setDisabled(isNotUpdate(endDate)); + deleteButton.setSclass("icono"); + deleteButton.setImage("/common/img/ico_borrar1.png"); + deleteButton.setHoverImage("/common/img/ico_borrar.png"); + deleteButton.setTooltiptext(_("Delete")); + deleteButton.addEventListener(Events.ON_CLICK, new EventListener() { + @Override + public void onEvent(Event event) { + removeAskedEndDate(endDate); + } + }); + + return deleteButton; + } + + private boolean isNotUpdate(final EndDateCommunication endDate) { + EndDateCommunication lastAskedEndDate = getOrder() + .getEndDateCommunicationToCustomer().first(); + if ((lastAskedEndDate != null) && (lastAskedEndDate.equals(endDate))) { + return (lastAskedEndDate.getCommunicationDate() != null); + } + return true; + } + } + + public void removeAskedEndDate(EndDateCommunication endDate) { + orderModel.removeAskedEndDate(endDate); + reloadGridAskedEndDates(); + } + public String getMoneyFormat() { return Util.getMoneyFormat(); } + public String getCurrencySymbol() { + return Util.getCurrencySymbol(); + } } diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/orders/OrderModel.java b/libreplan-webapp/src/main/java/org/libreplan/web/orders/OrderModel.java index ad091a66c..22ce8448e 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/orders/OrderModel.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/orders/OrderModel.java @@ -33,6 +33,8 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; import org.apache.commons.lang.Validate; import org.libreplan.business.advance.entities.AdvanceMeasurement; @@ -46,6 +48,7 @@ import org.libreplan.business.common.entities.Configuration; import org.libreplan.business.common.entities.EntityNameEnum; import org.libreplan.business.common.exceptions.InstanceNotFoundException; import org.libreplan.business.externalcompanies.daos.IExternalCompanyDAO; +import org.libreplan.business.externalcompanies.entities.EndDateCommunication; import org.libreplan.business.externalcompanies.entities.ExternalCompany; import org.libreplan.business.labels.daos.ILabelDAO; import org.libreplan.business.labels.entities.Label; @@ -282,12 +285,24 @@ public class OrderModel extends IntegrationEntityModel implements IOrderModel { forceLoadCriterionRequirements(order); forceLoadCalendar(this.getCalendar()); forceLoadCustomer(order.getCustomer()); + forceLoadDeliveringDates(order); forceLoadLabels(order); forceLoadMaterialAssignments(order); forceLoadTaskQualityForms(order); + forceLoadEndDateCommunicationToCustomer(order); initOldCodes(); } + private void forceLoadEndDateCommunicationToCustomer(Order order) { + if (order != null) { + order.getEndDateCommunicationToCustomer().size(); + } + } + + private void forceLoadDeliveringDates(Order order){ + order.getDeliveringDates().size(); + } + private void forceLoadLabels(OrderElement orderElement) { orderElement.getLabels().size(); for (OrderElement each : orderElement.getChildren()) { @@ -864,6 +879,49 @@ public class OrderModel extends IntegrationEntityModel implements IOrderModel { } } + public void removeAskedEndDate(EndDateCommunication endDate) { + Order order = (Order) getOrder(); + if (getOrder() != null && endDate != null) { + order.removeAskedEndDate(endDate); + } + } + + @Override + public SortedSet getEndDates() { + Order order = (Order) getOrder(); + if (getOrder() != null) { + return order.getEndDateCommunicationToCustomer(); + } + return new TreeSet(); + } + + @Override + public void addAskedEndDate(Date value) { + if (getOrder() != null) { + Order order = (Order) getOrder(); + + EndDateCommunication askedEndDate = EndDateCommunication.create( + new Date(), value, null); + order.addAskedEndDate(askedEndDate); + } + } + + @Override + public boolean alreadyExistsRepeatedEndDate(Date value) { + if(getOrder() != null){ + Order order = (Order) getOrder(); + if (order.getEndDateCommunicationToCustomer().isEmpty()) { + return false; + } + + EndDateCommunication endDateCommunicationToCustomer = order + .getEndDateCommunicationToCustomer().first(); + Date currentEndDate = endDateCommunicationToCustomer.getEndDate(); + return (currentEndDate.compareTo(value) == 0); + } + return false; + } + public boolean isAnyTaskWithConstraint(PositionConstraintType type) { if ((planningState == null) || (planningState.getRootTask() == null)) { return false; diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/orders/TreeElementOperationsController.java b/libreplan-webapp/src/main/java/org/libreplan/web/orders/TreeElementOperationsController.java index 971e1b734..63a17ef2a 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/orders/TreeElementOperationsController.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/orders/TreeElementOperationsController.java @@ -4,7 +4,7 @@ * Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e * Desenvolvemento Tecnolóxico de Galicia * - * Copyright (C) 2011 Igalia S.L + * Copyright (C) 2011-2012 Igalia S.L * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by @@ -92,7 +92,11 @@ public abstract class TreeElementOperationsController { public void indentSelectedElement() { if (tree.getSelectedCount() == 1) { + int page = tree.getActivePage(); indent(getSelectedElement()); + if (tree.getPageCount() > page) { + tree.setActivePage(page); + } } else { showSelectAnElementError(); } diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/planner/advances/AdvanceAssignmentPlanningController.java b/libreplan-webapp/src/main/java/org/libreplan/web/planner/advances/AdvanceAssignmentPlanningController.java index 2fb9ebffa..dde9c205b 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/planner/advances/AdvanceAssignmentPlanningController.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/planner/advances/AdvanceAssignmentPlanningController.java @@ -5,6 +5,8 @@ * Desenvolvemento Tecnolóxico de Galicia * Copyright (C) 2010-2011 Igalia, S.L. * + * Copyright (C) 2010-2011 WirelessGalicia, S.L. + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or @@ -77,7 +79,6 @@ public class AdvanceAssignmentPlanningController extends GenericForwardComposer public void showWindow(IContextWithPlannerTask context, TaskElement task, PlanningState planningState) { - this.context = context; advanceAssignmentPlanningModel.initAdvancesFor(task, context, planningState); @@ -86,7 +87,7 @@ public class AdvanceAssignmentPlanningController extends GenericForwardComposer try { window.setTitle(getTitle()); Util.reloadBindings(window); - this.window.doModal(); + this.window.setMode("modal"); } catch (SuspendNotAllowedException e) { throw new RuntimeException(e); } catch (InterruptedException e) { diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/planner/chart/ChartFiller.java b/libreplan-webapp/src/main/java/org/libreplan/web/planner/chart/ChartFiller.java index eaac867bf..bd25935b7 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/planner/chart/ChartFiller.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/planner/chart/ChartFiller.java @@ -508,6 +508,12 @@ public abstract class ChartFiller implements IChartFiller { return result; } + protected SortedMap calculatedValueForEveryDay( + SortedMap values, Interval interval) { + return calculatedValueForEveryDay(values, interval.getStart(), + interval.getFinish()); + } + protected SortedMap calculatedValueForEveryDay( SortedMap map, Date start, Date finish) { return calculatedValueForEveryDay(map, new LocalDate(start), diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/planner/chart/EarnedValueChartFiller.java b/libreplan-webapp/src/main/java/org/libreplan/web/planner/chart/EarnedValueChartFiller.java index b4dffbd2d..d8a8c7b2f 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/planner/chart/EarnedValueChartFiller.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/planner/chart/EarnedValueChartFiller.java @@ -3,7 +3,7 @@ * * Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e * Desenvolvemento Tecnolóxico de Galicia - * Copyright (C) 2010-2011 Igalia, S.L. + * Copyright (C) 2010-2012 Igalia, S.L. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by @@ -22,18 +22,13 @@ package org.libreplan.web.planner.chart; import java.math.BigDecimal; -import java.math.RoundingMode; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import java.util.Set; import java.util.SortedMap; -import java.util.TreeMap; -import org.apache.commons.lang.Validate; import org.joda.time.LocalDate; import org.libreplan.web.I18nHelper; import org.zkforge.timeplot.Plotinfo; @@ -44,130 +39,24 @@ import org.zkoss.ganttz.util.Interval; /** - * Abstract class with the common functionality for the earned value chart. * * @author Manuel Rego Casasnovas + * @author Diego Pino García + * + * Abstract class with the common functionality for the earned value + * chart. */ public abstract class EarnedValueChartFiller extends ChartFiller { - public static void forValuesAtSameKey(Map a, Map b, - IOperation onSameKey) { - for (Entry each : a.entrySet()) { - V aValue = each.getValue(); - V bValue = b.get(each.getKey()); - onSameKey.operate(each.getKey(), aValue, bValue); - } - } - public interface IOperation { - - public void operate(K key, V a, V b); - - public void undefinedFor(K key); - } - - protected static abstract class PreconditionChecker implements - IOperation { - - private final IOperation decorated; - - protected PreconditionChecker(IOperation decorated) { - this.decorated = decorated; - } - - @Override - public void operate(K key, V a, V b) { - if (isOperationDefinedFor(key, a, b)) { - decorated.operate(key, a, b); - } else { - decorated.undefinedFor(key); - } - } - - protected abstract boolean isOperationDefinedFor(K key, V a, V b); - - @Override - public void undefinedFor(K key) { - decorated.undefinedFor(key); - } - - } - - public static IOperation notNullOperands( - final IOperation operation) { - return new PreconditionChecker(operation) { - @Override - protected boolean isOperationDefinedFor(K key, V a, V b) { - return a != null && b != null; - } - }; - } - - public static IOperation secondOperandNotZero( - final IOperation operation) { - return new PreconditionChecker(operation) { - @Override - protected boolean isOperationDefinedFor(K key, BigDecimal a, BigDecimal b) { - return b.signum() != 0; - } - }; - } - public static boolean includes(Interval interval, LocalDate date) { LocalDate start = interval.getStart(); LocalDate end = interval.getFinish(); return start.compareTo(date) <= 0 && date.compareTo(end) < 0; } - public enum EarnedValueType { - - BCWS(_("BCWS"), _("Budgeted Cost Work Scheduled"), "#0000FF"), ACWP( - _("ACWP"), _("Actual Cost Work Performed"), "#FF0000"), BCWP( - _("BCWP"), _("Budgeted Cost Work Performed"), "#00FF00"), CV( - _("CV"), _("Cost Variance"), "#FF8800"), SV(_("SV"), - _("Schedule Variance"), "#00FFFF"), BAC(_("BAC"), - _("Budget At Completion"), "#FF00FF"), EAC(_("EAC"), - _("Estimate At Completion"), "#880000"), VAC(_("VAC"), - _("Variance At Completion"), "#000088"), ETC(_("ETC"), - _("Estimate To Complete"), "#008800"), CPI(_("CPI"), - _("Cost Performance Index"), "#888800"), SPI(_("SPI"), - _("Schedule Performance Index"), "#008888") - ; - - /** - * Forces to mark the string as needing translation - */ - private static String _(String string) { - return string; - } - - private String acronym; - private String name; - private String color; - - private EarnedValueType(String acronym, String name, String color) { - this.acronym = acronym; - this.name = name; - this.color = color; - } - - public String getAcronym() { - return I18nHelper._(acronym); - } - - public String getName() { - return I18nHelper._(name); - } - - public String getColor() { - return color; - } - } protected Map> indicators = new HashMap>(); - private Interval indicatorsInterval; - protected abstract void calculateBudgetedCostWorkScheduled(Interval interval); - protected abstract void calculateActualCostWorkPerformed(Interval interval); - protected abstract void calculateBudgetedCostWorkPerformed(Interval interval); + private Interval indicatorsInterval; protected Plotinfo createPlotInfo(SortedMap map, Interval interval, String lineColor) { @@ -203,167 +92,55 @@ public abstract class EarnedValueChartFiller extends ChartFiller { calculateSchedulePerformanceIndex(); } + protected abstract void calculateBudgetedCostWorkScheduled(Interval interval); + + protected abstract void calculateActualCostWorkPerformed(Interval interval); + + protected abstract void calculateBudgetedCostWorkPerformed(Interval interval); + + protected abstract void calculateCostVariance(); + + protected abstract void calculateScheduleVariance(); + + protected abstract void calculateBudgetAtCompletion(); + + protected abstract void calculateEstimateAtCompletion(); + + protected abstract void calculateVarianceAtCompletion(); + + protected abstract void calculateEstimatedToComplete(); + + protected abstract void calculateSchedulePerformanceIndex(); + + protected abstract void calculateCostPerformanceIndex(); + + protected abstract Set getSelectedIndicators(); + + public SortedMap getIndicator(EarnedValueType indicator) { + return indicators.get(indicator); + } + public BigDecimal getIndicator(EarnedValueType indicator, LocalDate date) { return indicators.get(indicator).get(date); } - private void calculateCostVariance() { - // CV = BCWP - ACWP - indicators.put(EarnedValueType.CV, - substract(EarnedValueType.BCWP, EarnedValueType.ACWP)); + public void setIndicator(EarnedValueType type, SortedMap values) { + indicators.put(type, values); } - private void calculateScheduleVariance() { - // SV = BCWP - BCWS - - indicators.put(EarnedValueType.SV, - substract(EarnedValueType.BCWP, EarnedValueType.BCWS)); + public void setIndicatorInInterval(EarnedValueType type, + Interval interval, SortedMap values) { + addZeroBeforeTheFirstValue(values); + indicators.put(type, calculatedValueForEveryDay(values, interval)); } - private void calculateBudgetAtCompletion() { - // BAC = max (BCWS) - SortedMap bac = new TreeMap(); - SortedMap bcws = indicators - .get(EarnedValueType.BCWS); - - BigDecimal value = Collections.max(bcws.values()); - for (LocalDate day : bcws.keySet()) { - bac.put(day, value); + protected void addZeroBeforeTheFirstValue( + SortedMap map) { + if (!map.isEmpty()) { + map.put(map.firstKey().minusDays(1), BigDecimal.ZERO); } - - indicators.put(EarnedValueType.BAC, bac); } - private void calculateEstimateAtCompletion() { - // EAC = (ACWP/BCWP) * BAC - SortedMap dividend = divide( - EarnedValueType.ACWP, EarnedValueType.BCWP, - BigDecimal.ZERO); - SortedMap bac = indicators - .get(EarnedValueType.BAC); - indicators.put(EarnedValueType.EAC, multiply(dividend, bac)); - } - - private static SortedMap multiply( - Map firstFactor, - Map secondFactor) { - final SortedMap result = new TreeMap(); - forValuesAtSameKey(firstFactor, secondFactor, - multiplicationOperation(result)); - return result; - } - - private static IOperation multiplicationOperation( - final SortedMap result) { - return notNullOperands(new IOperation() { - - @Override - public void operate(LocalDate key, BigDecimal a, - BigDecimal b) { - result.put(key, a.multiply(b)); - } - - @Override - public void undefinedFor(LocalDate key) { - result.put(key, BigDecimal.ZERO); - } - }); - } - - private void calculateVarianceAtCompletion() { - indicators.put(EarnedValueType.VAC, - substract(EarnedValueType.BAC, EarnedValueType.EAC)); - } - - private void calculateEstimatedToComplete() { - // ETC = EAC - ACWP - indicators.put(EarnedValueType.ETC, - substract(EarnedValueType.EAC, EarnedValueType.ACWP)); - } - - private SortedMap substract(EarnedValueType minuend, - EarnedValueType subtrahend) { - return substract(indicators.get(minuend), indicators.get(subtrahend)); - } - - private static SortedMap substract( - Map minuend, - Map subtrahend) { - final SortedMap result = new TreeMap(); - forValuesAtSameKey(minuend, subtrahend, substractionOperation(result)); - return result; - } - - private static IOperation substractionOperation( - final SortedMap result) { - return notNullOperands(new IOperation() { - - @Override - public void operate(LocalDate key, BigDecimal minuedValue, - BigDecimal subtrahendValue) { - result.put(key, - minuedValue.subtract(subtrahendValue)); - } - - @Override - public void undefinedFor(LocalDate key) { - } - }); - } - - private void calculateCostPerformanceIndex() { - // CPI = BCWP / ACWP - indicators.put(EarnedValueType.CPI, - divide(EarnedValueType.BCWP, EarnedValueType.ACWP, - BigDecimal.ZERO)); - } - - private void calculateSchedulePerformanceIndex() { - // SPI = BCWP / BCWS - indicators.put(EarnedValueType.SPI, - divide(EarnedValueType.BCWP, EarnedValueType.BCWS, - BigDecimal.ZERO)); - } - - private SortedMap divide(EarnedValueType dividend, - EarnedValueType divisor, BigDecimal defaultIfNotComputable) { - Validate.notNull(indicators.get(dividend)); - Validate.notNull(indicators.get(divisor)); - return divide(indicators.get(dividend), indicators.get(divisor), - defaultIfNotComputable); - } - - private static SortedMap divide( - Map dividend, - Map divisor, - final BigDecimal defaultIfNotComputable) { - final TreeMap result = new TreeMap(); - forValuesAtSameKey(dividend, divisor, - divisionOperation(result, defaultIfNotComputable)); - return result; - } - - private static IOperation divisionOperation( - final TreeMap result, - final BigDecimal defaultIfNotComputable) { - return notNullOperands(secondOperandNotZero(new IOperation() { - - @Override - public void operate(LocalDate key, BigDecimal dividendValue, - BigDecimal divisorValue) { - result.put(key, dividendValue.divide(divisorValue, - RoundingMode.DOWN)); - } - - @Override - public void undefinedFor(LocalDate key) { - result.put(key, defaultIfNotComputable); - } - })); - } - - protected abstract Set getSelectedIndicators(); - @Override public void fillChart(Timeplot chart, Interval interval, Integer size) { chart.getChildren().clear(); @@ -411,11 +188,55 @@ public abstract class EarnedValueChartFiller extends ChartFiller { return includes(chartInterval, today) ? today : chartInterval .getFinish().minusDays(1); } - protected void addZeroBeforeTheFirstValue( - SortedMap map) { - if (!map.isEmpty()) { - map.put(map.firstKey().minusDays(1), BigDecimal.ZERO); + + /** + * + * @author Manuel Rego Casasnovas + * + */ + public enum EarnedValueType { + + BCWS(_("BCWS"), _("Budgeted Cost Work Scheduled"), "#0000FF"), ACWP( + _("ACWP"), _("Actual Cost Work Performed"), "#FF0000"), BCWP( + _("BCWP"), _("Budgeted Cost Work Performed"), "#00FF00"), CV( + _("CV"), _("Cost Variance"), "#FF8800"), SV(_("SV"), + _("Schedule Variance"), "#00FFFF"), BAC(_("BAC"), + _("Budget At Completion"), "#FF00FF"), EAC(_("EAC"), + _("Estimate At Completion"), "#880000"), VAC(_("VAC"), + _("Variance At Completion"), "#000088"), ETC(_("ETC"), + _("Estimate To Complete"), "#008800"), CPI(_("CPI"), + _("Cost Performance Index"), "#888800"), SPI(_("SPI"), + _("Schedule Performance Index"), "#008888") + ; + + /** + * Forces to mark the string as needing translation + */ + private static String _(String string) { + return string; + } + + private String acronym; + private String name; + private String color; + + private EarnedValueType(String acronym, String name, String color) { + this.acronym = acronym; + this.name = name; + this.color = color; + } + + public String getAcronym() { + return I18nHelper._(acronym); + } + + public String getName() { + return I18nHelper._(name); + } + + public String getColor() { + return color; } } -} +} \ No newline at end of file diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/planner/company/CompanyPlanningModel.java b/libreplan-webapp/src/main/java/org/libreplan/web/planner/company/CompanyPlanningModel.java index eebdf859b..b5c52f78e 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/planner/company/CompanyPlanningModel.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/planner/company/CompanyPlanningModel.java @@ -38,8 +38,6 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.SortedMap; -import java.util.TreeMap; import org.joda.time.LocalDate; import org.libreplan.business.calendars.entities.AvailabilityTimeLine; @@ -55,6 +53,7 @@ import org.libreplan.business.orders.entities.Order; import org.libreplan.business.orders.entities.OrderStatusEnum; import org.libreplan.business.planner.chart.ILoadChartData; import org.libreplan.business.planner.chart.ResourceLoadChartData; +import org.libreplan.business.planner.entities.ICompanyEarnedValueCalculator; import org.libreplan.business.planner.entities.TaskElement; import org.libreplan.business.planner.entities.TaskGroup; import org.libreplan.business.planner.entities.TaskMilestone; @@ -62,7 +61,6 @@ import org.libreplan.business.scenarios.IScenarioManager; import org.libreplan.business.scenarios.entities.Scenario; import org.libreplan.business.users.daos.IUserDAO; import org.libreplan.business.users.entities.User; -import org.libreplan.business.workreports.entities.WorkReportLine; import org.libreplan.web.planner.TaskElementAdapter; import org.libreplan.web.planner.TaskGroupPredicate; import org.libreplan.web.planner.chart.Chart; @@ -132,6 +130,9 @@ public class CompanyPlanningModel implements ICompanyPlanningModel { @Autowired private IAdHocTransactionService transactionService; + @Autowired + private ICompanyEarnedValueCalculator earnedValueCalculator; + private List keepAliveZoomListeners = new ArrayList(); private List earnedValueChartConfigurationCheckboxes = new ArrayList(); @@ -275,6 +276,7 @@ public class CompanyPlanningModel implements ICompanyPlanningModel { setupChart(chartLoadTimeplot, new CompanyLoadChartFiller(), planner); chartComponent.getTabs().getLastChild().addEventListener(Events.ON_SELECT, new EventListener() { + @Override public void onEvent(Event event) throws Exception { createOnDemandEarnedValueTimePlot(chartComponent, planner); event.getTarget().removeEventListener(Events.ON_SELECT, this); @@ -783,75 +785,101 @@ public class CompanyPlanningModel implements ICompanyPlanningModel { } + /** + * + * @author Manuel Rego Casasnovas + * @author Diego Pino García + * + * Calculates 'Earned Value' indicators and set them in the Company + * 'Earned Valued' chart + * + */ private class CompanyEarnedValueChartFiller extends EarnedValueChartFiller { + @Override protected void calculateBudgetedCostWorkScheduled(Interval interval) { - Map> estimatedCostPerTask = - databaseSnapshots.snapshotEstimatedCostPerTask(); - Collection list = filterTasksByDate( - estimatedCostPerTask.keySet(), getFilterInterval()); - - SortedMap estimatedCost = new TreeMap(); - - for (TaskElement taskElement : list) { - addCost(estimatedCost, estimatedCostPerTask.get(taskElement)); - } - - estimatedCost = accumulateResult(estimatedCost); - addZeroBeforeTheFirstValue(estimatedCost); - indicators.put(EarnedValueType.BCWS, calculatedValueForEveryDay( - estimatedCost, interval.getStart(), interval.getFinish())); + setIndicatorInInterval(EarnedValueType.BCWS, interval, + earnedValueCalculator + .calculateBudgetedCostWorkScheduled(getFilterInterval())); } + @Override protected void calculateActualCostWorkPerformed(Interval interval) { - SortedMap workReportCost = getWorkReportCost(); - - workReportCost = accumulateResult(workReportCost); - addZeroBeforeTheFirstValue(workReportCost); - indicators.put(EarnedValueType.ACWP, calculatedValueForEveryDay( - workReportCost, interval.getStart(), interval.getFinish())); - } - - private SortedMap getWorkReportCost() { - SortedMap result = new TreeMap(); - - Collection workReportLines = filterWorkReportLinesByDate( - databaseSnapshots.snapshotWorkReportLines(), - getFilterInterval()); - - if (workReportLines.isEmpty()) { - return result; - } - - for (WorkReportLine workReportLine : workReportLines) { - LocalDate day = new LocalDate(workReportLine.getDate()); - BigDecimal cost = workReportLine.getEffort() - .toHoursAsDecimalWithScale(2); - - if (!result.containsKey(day)) { - result.put(day, BigDecimal.ZERO); - } - result.put(day, result.get(day).add(cost)); - } - - return result; + setIndicatorInInterval(EarnedValueType.ACWP, interval, + earnedValueCalculator + .calculateActualCostWorkPerformed(getFilterInterval())); } + @Override protected void calculateBudgetedCostWorkPerformed(Interval interval) { - Map> advanceCostPerTask = - databaseSnapshots.snapshotAdvanceCostPerTask(); - Collection list = filterTasksByDate( - advanceCostPerTask.keySet(), getFilterInterval()); + setIndicatorInInterval(EarnedValueType.BCWP, interval, + earnedValueCalculator + .calculateBudgetedCostWorkPerformed(getFilterInterval())); + } - SortedMap advanceCost = new TreeMap(); + @Override + protected void calculateCostVariance() { + setIndicator(EarnedValueType.CV, + earnedValueCalculator.calculateCostVariance( + getIndicator(EarnedValueType.BCWP), + getIndicator(EarnedValueType.ACWP))); + } - for (TaskElement taskElement : list) { - addCost(advanceCost, advanceCostPerTask.get(taskElement)); - } + @Override + protected void calculateScheduleVariance() { + setIndicator(EarnedValueType.SV, + earnedValueCalculator.calculateScheduleVariance( + getIndicator(EarnedValueType.BCWP), + getIndicator(EarnedValueType.BCWS))); + } - addZeroBeforeTheFirstValue(advanceCost); - indicators.put(EarnedValueType.BCWP, calculatedValueForEveryDay( - advanceCost, interval.getStart(), interval.getFinish())); + @Override + protected void calculateSchedulePerformanceIndex() { + setIndicator(EarnedValueType.SPI, + earnedValueCalculator.calculateSchedulePerformanceIndex( + getIndicator(EarnedValueType.BCWP), + getIndicator(EarnedValueType.BCWS))); + } + + @Override + protected void calculateBudgetAtCompletion() { + setIndicator( + EarnedValueType.BAC, + earnedValueCalculator + .calculateBudgetAtCompletion(getIndicator(EarnedValueType.BCWS))); + } + + @Override + protected void calculateEstimateAtCompletion() { + setIndicator(EarnedValueType.EAC, + earnedValueCalculator.calculateEstimateAtCompletion( + getIndicator(EarnedValueType.ACWP), + getIndicator(EarnedValueType.BCWP), + getIndicator(EarnedValueType.BAC))); + } + + @Override + protected void calculateVarianceAtCompletion() { + setIndicator(EarnedValueType.VAC, + earnedValueCalculator.calculateVarianceAtCompletion( + getIndicator(EarnedValueType.BAC), + getIndicator(EarnedValueType.EAC))); + } + + @Override + protected void calculateEstimatedToComplete() { + setIndicator(EarnedValueType.ETC, + earnedValueCalculator.calculateEstimatedToComplete( + getIndicator(EarnedValueType.EAC), + getIndicator(EarnedValueType.ACWP))); + } + + @Override + protected void calculateCostPerformanceIndex() { + setIndicator(EarnedValueType.CPI, + earnedValueCalculator.calculateCostPerformanceIndex( + getIndicator(EarnedValueType.BCWP), + getIndicator(EarnedValueType.ACWP))); } @Override @@ -859,33 +887,9 @@ public class CompanyPlanningModel implements ICompanyPlanningModel { return getEarnedValueSelectedIndicators(); } - private List filterTasksByDate( - Collection tasks, - AvailabilityTimeLine.Interval interval) { - List result = new ArrayList(); - for(TaskElement task : tasks) { - if (interval.includes(task.getStartAsLocalDate()) - || interval.includes(task.getEndAsLocalDate())) { - result.add(task); - } - } - return result; - } - - - private List filterWorkReportLinesByDate( - Collection lines, - AvailabilityTimeLine.Interval interval) { - List result = new ArrayList(); - for(WorkReportLine line: lines) { - if (interval.includes(line.getLocalDate())) { - result.add(line); - } - } - return result; - } } + @Override @Transactional(readOnly=true) public ProgressType getProgressTypeFromConfiguration() { return configurationDAO.getConfiguration().getProgressType(); diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/planner/order/ISubcontractModel.java b/libreplan-webapp/src/main/java/org/libreplan/web/planner/order/ISubcontractModel.java index 0cd881d13..4cbee88d6 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/planner/order/ISubcontractModel.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/planner/order/ISubcontractModel.java @@ -23,10 +23,13 @@ package org.libreplan.web.planner.order; import java.util.Date; import java.util.List; +import java.util.SortedSet; import org.libreplan.business.common.exceptions.ValidationException; +import org.libreplan.business.externalcompanies.entities.EndDateCommunication; import org.libreplan.business.externalcompanies.entities.ExternalCompany; import org.libreplan.business.planner.entities.SubcontractedTaskData; +import org.libreplan.business.planner.entities.SubcontractorDeliverDate; import org.libreplan.business.planner.entities.Task; /** @@ -65,4 +68,15 @@ public interface ISubcontractModel { void confirm() throws ValidationException; void cancel(); + SortedSet getDeliverDates(); + + void addDeliverDate(Date subDeliverDate); + + void removeRequiredDeliverDate( + SubcontractorDeliverDate subcontractorDeliverDate); + + boolean alreadyExistsRepeatedDeliverDate(Date newDeliverDate); + + SortedSet getAskedEndDates(); + } \ No newline at end of file diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/planner/order/OrderPlanningController.java b/libreplan-webapp/src/main/java/org/libreplan/web/planner/order/OrderPlanningController.java index adacac38e..7fb6b16c9 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/planner/order/OrderPlanningController.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/planner/order/OrderPlanningController.java @@ -4,6 +4,7 @@ * Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e * Desenvolvemento Tecnolóxico de Galicia * Copyright (C) 2010-2011 Igalia, S.L. + * Copyright (C) 2010-2011 WirelessGalicia S.L. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by @@ -63,6 +64,7 @@ import org.zkoss.ganttz.util.ProfilingLogFactory; import org.zkoss.zk.ui.Executions; import org.zkoss.zk.ui.WrongValueException; import org.zkoss.zk.ui.util.Composer; +import org.zkoss.zk.ui.util.GenericForwardComposer; import org.zkoss.zul.Checkbox; import org.zkoss.zul.Constraint; import org.zkoss.zul.Datebox; @@ -72,6 +74,7 @@ import org.zkoss.zul.Vbox; /** * @author Óscar González Fernández + * @author Susana Montes Pedreira */ @Component @Scope(BeanDefinition.SCOPE_PROTOTYPE) @@ -108,6 +111,8 @@ public class OrderPlanningController implements Composer { @Autowired private OrderCRUDController orderCRUDController; + private GenericForwardComposer currentControllerToShow; + private Order order; private TaskElement task; @@ -216,6 +221,7 @@ public class OrderPlanningController implements Composer { + (System.currentTimeMillis() - time) + " ms"); planner.updateSelectedZoomLevel(); showResorceAllocationIfIsNeeded(); + } } @@ -363,9 +369,17 @@ public class OrderPlanningController implements Composer { if ((foundTask != null) && (foundTaskElement != null)) { IContextWithPlannerTask contextTask = ContextWithPlannerTask .create(context, foundTask); - this.editTaskController - .showEditFormResourceAllocation(contextTask, - foundTaskElement, model.getPlanningState()); + if (this.getCurrentControllerToShow().equals( + getEditTaskController())) { + this.editTaskController.showEditFormResourceAllocation( + contextTask, foundTaskElement, + model.getPlanningState()); + } else if (this.getCurrentControllerToShow().equals( + this.getAdvanceAssignmentPlanningController())) { + getAdvanceAssignmentPlanningController().showWindow( + contextTask, foundTaskElement, + model.getPlanningState()); + } } } } @@ -378,4 +392,12 @@ public class OrderPlanningController implements Composer { return advanceAssignmentPlanningController; } + public void setCurrentControllerToShow(GenericForwardComposer currentControllerToShow) { + this.currentControllerToShow = currentControllerToShow; + } + + public GenericForwardComposer getCurrentControllerToShow() { + return currentControllerToShow; + } + } diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/planner/order/OrderPlanningModel.java b/libreplan-webapp/src/main/java/org/libreplan/web/planner/order/OrderPlanningModel.java index 8d058fa1c..9c04f7c78 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/planner/order/OrderPlanningModel.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/planner/order/OrderPlanningModel.java @@ -38,8 +38,6 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.SortedMap; -import java.util.TreeMap; import org.apache.commons.lang.Validate; import org.apache.commons.logging.Log; @@ -51,7 +49,6 @@ import org.libreplan.business.common.AdHocTransactionService; import org.libreplan.business.common.IAdHocTransactionService; import org.libreplan.business.common.IOnTransaction; import org.libreplan.business.common.Registry; -import org.libreplan.business.common.entities.ProgressType; import org.libreplan.business.common.exceptions.InstanceNotFoundException; import org.libreplan.business.orders.daos.IOrderDAO; import org.libreplan.business.orders.entities.HoursGroup; @@ -64,7 +61,7 @@ import org.libreplan.business.planner.chart.ResourceLoadChartData; import org.libreplan.business.planner.entities.DayAssignment; import org.libreplan.business.planner.entities.DayAssignment.FilterType; import org.libreplan.business.planner.entities.ICostCalculator; -import org.libreplan.business.planner.entities.Task; +import org.libreplan.business.planner.entities.IOrderEarnedValueCalculator; import org.libreplan.business.planner.entities.TaskElement; import org.libreplan.business.planner.entities.TaskGroup; import org.libreplan.business.resources.entities.CriterionSatisfaction; @@ -140,7 +137,6 @@ import org.zkoss.zul.Div; import org.zkoss.zul.Hbox; import org.zkoss.zul.Label; import org.zkoss.zul.Messagebox; -import org.zkoss.zul.Progressmeter; import org.zkoss.zul.Tab; import org.zkoss.zul.Tabbox; import org.zkoss.zul.Tabpanel; @@ -152,6 +148,7 @@ import org.zkoss.zul.Vbox; * @author Óscar González Fernández * @author Manuel Rego Casasnovas * @author Lorenzo Tilve Álvaro + * @author Diego Pino García */ @Component @Scope(BeanDefinition.SCOPE_PROTOTYPE) @@ -262,14 +259,15 @@ public class OrderPlanningModel implements IOrderPlanningModel { @Autowired private ICostCalculator hoursCostCalculator; + @Autowired + private IOrderEarnedValueCalculator earnedValueCalculator; + private List earnedValueChartConfigurationCheckboxes = new ArrayList(); private List keepAliveChartVisibilityListeners = new ArrayList(); private Planner planner; - private OverAllProgressContent overallProgressContent; - private String tabSelected = "load_tab"; private static class NullSeparatorCommandOnTask implements @@ -387,11 +385,6 @@ public class OrderPlanningModel implements IOrderPlanningModel { this.earnedValueChartFiller = createOrderEarnedValueChartFiller(planner.getTimeTracker()); chartTabpanels.appendChild(createEarnedValueTab(chartEarnedValueTimeplot, earnedValueChartFiller)); - // Create 'Overall progress' tab - Hbox chartOverallProgressTimeplot = new Hbox(); - Tabpanel overallProgressTab = createOverallProgressTab(chartOverallProgressTimeplot); - chartTabpanels.appendChild(overallProgressTab); - // Append tab panels chartComponent.appendChild(chartTabpanels); ChangeHooker changeHooker = new ChangeHooker(configuration, saveCommand); @@ -399,45 +392,38 @@ public class OrderPlanningModel implements IOrderPlanningModel { setupLoadChart(chartLoadTimeplot, planner, changeHooker); setupEarnedValueChart(chartEarnedValueTimeplot, earnedValueChartFiller, planner, changeHooker); - setupOverallProgress(planner, changeHooker); setupAdvanceAssignmentPlanningController(planner, advanceAssignmentPlanningController); PROFILING_LOG .info("preparing charts and miscellaneous took: " + (System.currentTimeMillis() - preparingChartsAndMisc) + " ms"); + // Calculate critical path progress, needed for 'Project global progress' chart in Dashboard view planner.addGraphChangeListenersFromConfiguration(configuration); + updateCriticalPathProgress(); long overalProgressContentTime = System.currentTimeMillis(); - overallProgressContent = new OverAllProgressContent(overallProgressTab); - overallProgressContent.updateAndRefresh(); PROFILING_LOG.info("overalProgressContent took: " + (System.currentTimeMillis() - overalProgressContentTime)); } + /** + * First time a project is loaded, it's needed to calculate the theoretical + * critical path progress and real critical path progress. These values are + * later updated whenever the project is saved + */ + private void updateCriticalPathProgress() { + if (planningState.isEmpty() || planner == null) { + return; + } + TaskGroup rootTask = planningState.getRootTask(); + rootTask.updateCriticalPathProgress(planner.getCriticalPath()); + } + private OrderEarnedValueChartFiller earnedValueChartFiller; private void setupAdvanceAssignmentPlanningController(final Planner planner, AdvanceAssignmentPlanningController advanceAssignmentPlanningController) { - advanceAssignmentPlanningController.reloadOverallProgressListener(new IReloadChartListener() { - - @Override - public void reloadChart() { - Registry.getTransactionService().runOnReadOnlyTransaction(new IOnTransaction() { - - @Override - public Void execute() { - if (isExecutingOutsideZKExecution()) { - return null; - } - if (planner.isVisibleChart()) { - overallProgressContent.updateAndRefresh(); - } - return null; - } - }); - } - }); advanceAssignmentPlanningController.setReloadEarnedValueListener(new IReloadChartListener() { @Override @@ -462,15 +448,6 @@ public class OrderPlanningModel implements IOrderPlanningModel { }); } - private Tabpanel createOverallProgressTab( - Hbox chartOverallProgressTimeplot) { - Tabpanel result = new Tabpanel(); - org.zkoss.zk.ui.Component component = Executions.createComponents( - "/planner/_tabPanelOverallProgress.zul", result, null); - component.setParent(result); - return result; - } - private Timeplot createEmptyTimeplot() { Timeplot timeplot = new Timeplot(); timeplot.appendChild(new Plotinfo()); @@ -576,24 +553,6 @@ public class OrderPlanningModel implements IOrderPlanningModel { setEventListenerConfigurationCheckboxes(earnedValueChart); } - private void setupOverallProgress(final Planner planner, - ChangeHooker changeHooker) { - - changeHooker.withReadOnlyTransactionWraping().hookInto( - EnumSet.allOf(ChangeTypes.class), new IReloadChartListener() { - - @Override - public void reloadChart() { - if (isExecutingOutsideZKExecution()) { - return; - } - if (planner.isVisibleChart()) { - overallProgressContent.updateAndRefresh(); - } - } - }); - } - enum ChangeTypes { ON_SAVE, ON_RELOAD_CHART_REQUESTED, ON_GRAPH_CHANGED; } @@ -763,8 +722,6 @@ public class OrderPlanningModel implements IOrderPlanningModel { Tabs chartTabs = new Tabs(); chartTabs.appendChild(createTab(_("Load"), "load_tab")); chartTabs.appendChild(createTab(_("Earned value"), "earned_value_tab")); - chartTabs.appendChild(createTab(_("Overall progress"), - "overall_progress_tab")); chartComponent.appendChild(chartTabs); chartTabs.setSclass("charts-tabbox"); @@ -1130,6 +1087,7 @@ public class OrderPlanningModel implements IOrderPlanningModel { | Messagebox.CANCEL, Messagebox.QUESTION, new org.zkoss.zk.ui.event.EventListener() { + @Override public void onEvent(Event evt) throws InterruptedException { if (evt.getName().equals("onOK")) { @@ -1386,6 +1344,15 @@ public class OrderPlanningModel implements IOrderPlanningModel { } + /** + * + * @author Manuel Rego Casasnovas + * @author Diego Pino García + * + * Calculates 'Earned Value' indicators and set them in the Order + * 'Earned Valued' chart + * + */ class OrderEarnedValueChartFiller extends EarnedValueChartFiller { private Order order; @@ -1394,63 +1361,90 @@ public class OrderPlanningModel implements IOrderPlanningModel { this.order = orderReloaded; } + @Override protected void calculateBudgetedCostWorkScheduled(Interval interval) { - List list = order - .getAllChildrenAssociatedTaskElements(); - list.add(order.getAssociatedTaskElement()); - - SortedMap estimatedCost = new TreeMap(); - - for (TaskElement taskElement : list) { - if (taskElement instanceof Task) { - addCost(estimatedCost, hoursCostCalculator - .getEstimatedCost((Task) taskElement)); - } - } - - estimatedCost = accumulateResult(estimatedCost); - addZeroBeforeTheFirstValue(estimatedCost); - indicators.put(EarnedValueType.BCWS, calculatedValueForEveryDay( - estimatedCost, interval.getStart(), interval.getFinish())); + setIndicatorInInterval(EarnedValueType.BCWS, interval, + earnedValueCalculator + .calculateBudgetedCostWorkScheduled(order)); } + @Override protected void calculateActualCostWorkPerformed(Interval interval) { - List list = order - .getAllChildrenAssociatedTaskElements(); - list.add(order.getAssociatedTaskElement()); - - SortedMap workReportCost = new TreeMap(); - - for (TaskElement taskElement : list) { - if (taskElement instanceof Task) { - addCost(workReportCost, hoursCostCalculator - .getWorkReportCost((Task) taskElement)); - } - } - - workReportCost = accumulateResult(workReportCost); - addZeroBeforeTheFirstValue(workReportCost); - indicators.put(EarnedValueType.ACWP, calculatedValueForEveryDay( - workReportCost, interval.getStart(), interval.getFinish())); + setIndicatorInInterval(EarnedValueType.ACWP, interval, + earnedValueCalculator + .calculateActualCostWorkPerformed(order)); } + @Override protected void calculateBudgetedCostWorkPerformed(Interval interval) { - List list = order - .getAllChildrenAssociatedTaskElements(); - list.add(order.getAssociatedTaskElement()); + setIndicatorInInterval(EarnedValueType.BCWP, interval, + earnedValueCalculator + .calculateBudgetedCostWorkPerformed(order)); + } - SortedMap advanceCost = new TreeMap(); + @Override + protected void calculateCostVariance() { + setIndicator(EarnedValueType.CV, + earnedValueCalculator.calculateCostVariance( + getIndicator(EarnedValueType.BCWP), + getIndicator(EarnedValueType.ACWP))); + } - for (TaskElement taskElement : list) { - if (taskElement instanceof Task) { - addCost(advanceCost, hoursCostCalculator - .getAdvanceCost((Task) taskElement)); - } - } + @Override + protected void calculateScheduleVariance() { + setIndicator(EarnedValueType.SV, + earnedValueCalculator.calculateScheduleVariance( + getIndicator(EarnedValueType.BCWP), + getIndicator(EarnedValueType.BCWS))); + } - addZeroBeforeTheFirstValue(advanceCost); - indicators.put(EarnedValueType.BCWP, calculatedValueForEveryDay( - advanceCost, interval.getStart(), interval.getFinish())); + @Override + protected void calculateBudgetAtCompletion() { + setIndicator( + EarnedValueType.BAC, + earnedValueCalculator + .calculateBudgetAtCompletion(getIndicator(EarnedValueType.BCWS))); + } + + @Override + protected void calculateEstimateAtCompletion() { + setIndicator(EarnedValueType.EAC, + earnedValueCalculator.calculateEstimateAtCompletion( + getIndicator(EarnedValueType.ACWP), + getIndicator(EarnedValueType.BCWP), + getIndicator(EarnedValueType.BAC))); + } + + @Override + protected void calculateVarianceAtCompletion() { + setIndicator(EarnedValueType.VAC, + earnedValueCalculator.calculateVarianceAtCompletion( + getIndicator(EarnedValueType.BAC), + getIndicator(EarnedValueType.EAC))); + } + + @Override + protected void calculateEstimatedToComplete() { + setIndicator(EarnedValueType.ETC, + earnedValueCalculator.calculateEstimatedToComplete( + getIndicator(EarnedValueType.EAC), + getIndicator(EarnedValueType.ACWP))); + } + + @Override + protected void calculateCostPerformanceIndex() { + setIndicator(EarnedValueType.CPI, + earnedValueCalculator.calculateCostPerformanceIndex( + getIndicator(EarnedValueType.BCWP), + getIndicator(EarnedValueType.ACWP))); + } + + @Override + protected void calculateSchedulePerformanceIndex() { + setIndicator(EarnedValueType.SPI, + earnedValueCalculator.calculateSchedulePerformanceIndex( + getIndicator(EarnedValueType.BCWP), + getIndicator(EarnedValueType.BCWS))); } @Override @@ -1466,6 +1460,7 @@ public class OrderPlanningModel implements IOrderPlanningModel { return subcontractCommand; } + @Override public Order getOrder() { return planningState.getOrder(); } @@ -1505,149 +1500,4 @@ public class OrderPlanningModel implements IOrderPlanningModel { } } - /** - * - * @author Diego Pino García - * - * Helper class to show the content of a OverallProgress panel - * - */ - private class OverAllProgressContent { - - private Progressmeter progressCriticalPathByDuration; - - private Label lbCriticalPathByDuration; - - private Progressmeter progressCriticalPathByNumHours; - - private Label lbCriticalPathByNumHours; - - private Progressmeter progressSpread; - - private Label lbProgressSpread; - - private Progressmeter progressAllByNumHours; - - private Label lbProgressAllByNumHours; - - public OverAllProgressContent(Tabpanel tabpanel) { - initializeProgressCriticalPathByDuration(tabpanel); - initializeProgressCriticalPathByNumHours(tabpanel); - initializeProgressSpread(tabpanel); - initializeProgressAllByNumHours(tabpanel); - - tabpanel.setVariable("overall_progress_content", this, true); - } - - private void initializeProgressCriticalPathByNumHours(Tabpanel tabpanel) { - progressCriticalPathByNumHours = (Progressmeter) tabpanel - .getFellow("progressCriticalPathByNumHours"); - lbCriticalPathByNumHours = (Label) tabpanel - .getFellow("lbCriticalPathByNumHours"); - ((Label) tabpanel.getFellow("textCriticalPathByNumHours")) - .setValue(_(ProgressType.CRITICAL_PATH_NUMHOURS.toString())); - } - - private void initializeProgressCriticalPathByDuration(Tabpanel tabpanel) { - progressCriticalPathByDuration = (Progressmeter) tabpanel - .getFellow("progressCriticalPathByDuration"); - lbCriticalPathByDuration = (Label) tabpanel - .getFellow("lbCriticalPathByDuration"); - ((Label) tabpanel.getFellow("textCriticalPathByDuration")) - .setValue(_(ProgressType.CRITICAL_PATH_DURATION.toString())); - } - - public void initializeProgressSpread(Tabpanel tabpanel) { - progressSpread = (Progressmeter) tabpanel - .getFellow("progressSpread"); - lbProgressSpread = (Label) tabpanel.getFellow("lbProgressSpread"); - ((Label) tabpanel.getFellow("textProgressSpread")) - .setValue(_(ProgressType.SPREAD_PROGRESS.toString())); - } - - public void initializeProgressAllByNumHours(Tabpanel tabpanel) { - progressAllByNumHours = (Progressmeter) tabpanel - .getFellow("progressAllByNumHours"); - lbProgressAllByNumHours = (Label) tabpanel - .getFellow("lbProgressAllByNumHours"); - ((Label) tabpanel.getFellow("textProgressAllByNumHours")) - .setValue(_(ProgressType.ALL_NUMHOURS.toString())); - } - - public void refresh() { - if (planningState.isEmpty()) { - return; - } - TaskGroup rootTask = planningState.getRootTask(); - - setProgressSpread(rootTask.getAdvancePercentage()); - setProgressAllByNumHours(rootTask.getProgressAllByNumHours()); - setCriticalPathByDuration(rootTask - .getCriticalPathProgressByDuration()); - setCriticalPathByNumHours(rootTask - .getCriticalPathProgressByNumHours()); - } - - private void updateAndRefresh() { - if (planningState.isEmpty()) { - return; - } - update(); - refresh(); - } - - private void update() { - TaskGroup rootTask = planningState.getRootTask(); - updateCriticalPathProgress(rootTask); - } - - private void updateCriticalPathProgress(TaskGroup rootTask) { - if (planner != null) { - rootTask.updateCriticalPathProgress((List) planner - .getCriticalPath()); - } - } - - private void setProgressSpread(BigDecimal value) { - if (value == null) { - value = BigDecimal.ZERO; - } - value = value.multiply(BigDecimal.valueOf(100)); - value = value.setScale(2, BigDecimal.ROUND_HALF_EVEN); - lbProgressSpread.setValue(value.toString() + " %"); - progressSpread.setValue(value.intValue()); - } - - private void setProgressAllByNumHours(BigDecimal value) { - if (value == null) { - value = BigDecimal.ZERO; - } - value = value.multiply(BigDecimal.valueOf(100)); - value = value.setScale(2, BigDecimal.ROUND_HALF_EVEN); - lbProgressAllByNumHours.setValue(value.toString() + " %"); - progressAllByNumHours.setValue(value.intValue()); - } - - public void setCriticalPathByDuration(BigDecimal value) { - if (value == null) { - value = BigDecimal.ZERO; - } - value = value.multiply(BigDecimal.valueOf(100)); - value = value.setScale(2, BigDecimal.ROUND_HALF_EVEN); - lbCriticalPathByDuration.setValue(value.toString() + " %"); - progressCriticalPathByDuration.setValue(value.intValue()); - } - - public void setCriticalPathByNumHours(BigDecimal value) { - if (value == null) { - value = BigDecimal.ZERO; - } - value = value.multiply(BigDecimal.valueOf(100)); - value = value.setScale(2, BigDecimal.ROUND_HALF_EVEN); - lbCriticalPathByNumHours.setValue(value.toString() + " %"); - progressCriticalPathByNumHours.setValue(value.intValue()); - } - - } - } diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/planner/order/PlanningStateCreator.java b/libreplan-webapp/src/main/java/org/libreplan/web/planner/order/PlanningStateCreator.java index 576088f7c..8e81d8dce 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/planner/order/PlanningStateCreator.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/planner/order/PlanningStateCreator.java @@ -61,6 +61,7 @@ import org.libreplan.business.planner.entities.ResourceAllocation; import org.libreplan.business.planner.entities.ResourceAllocation.IVisitor; import org.libreplan.business.planner.entities.SpecificResourceAllocation; import org.libreplan.business.planner.entities.StretchesFunction; +import org.libreplan.business.planner.entities.SubcontractorDeliverDate; import org.libreplan.business.planner.entities.Task; import org.libreplan.business.planner.entities.TaskElement; import org.libreplan.business.planner.entities.TaskGroup; @@ -333,6 +334,7 @@ public class PlanningStateCreator { private void forceLoadOfDataAssociatedTo(TaskElement each) { forceLoadOfResourceAllocationsResourcesAndAssignmentFunction(each); forceLoadOfCriterions(each); + forceLoadOfSubcontractedTaskData(each); BaseCalendar calendar = each.getOwnCalendar(); if (calendar == null) { @@ -392,6 +394,18 @@ public class PlanningStateCreator { } } + private static void forceLoadOfSubcontractedTaskData(TaskElement taskElement){ + if(taskElement.isLeaf()){ + if(((Task)taskElement).getSubcontractedTaskData() != null){ + for (SubcontractorDeliverDate subDeliverDate : ((Task) taskElement) + .getSubcontractedTaskData() + .getRequiredDeliveringDates()) { + subDeliverDate.getSaveDate(); + } + } + } + } + private void findChildrenWithQueryToAvoidProxies(TaskGroup group) { for (TaskElement eachTask : taskDAO.findChildrenOf(group)) { Hibernate.initialize(eachTask); diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/planner/order/SaveCommandBuilder.java b/libreplan-webapp/src/main/java/org/libreplan/web/planner/order/SaveCommandBuilder.java index ce6cee755..1734de2b9 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/planner/order/SaveCommandBuilder.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/planner/order/SaveCommandBuilder.java @@ -69,6 +69,8 @@ import org.libreplan.business.planner.entities.DerivedAllocation; import org.libreplan.business.planner.entities.DerivedDayAssignment; import org.libreplan.business.planner.entities.DerivedDayAssignmentsContainer; import org.libreplan.business.planner.entities.ResourceAllocation; +import org.libreplan.business.planner.entities.SubcontractedTaskData; +import org.libreplan.business.planner.entities.SubcontractorDeliverDate; import org.libreplan.business.planner.entities.Task; import org.libreplan.business.planner.entities.TaskElement; import org.libreplan.business.planner.entities.TaskGroup; @@ -120,6 +122,7 @@ import org.zkoss.zul.Messagebox; @Scope(BeanDefinition.SCOPE_SINGLETON) public class SaveCommandBuilder { + private static final Log LOG = LogFactory.getLog(SaveCommandBuilder.class); public ISaveCommand build(PlanningState planningState, @@ -291,6 +294,8 @@ public class SaveCommandBuilder { } }); dontPoseAsTransientObjectAnymore(state.getOrder()); + dontPoseAsTransientObjectAnymore(state.getOrder() + .getEndDateCommunicationToCustomer()); state.getScenarioInfo().afterCommit(); if (state.getOrder() @@ -359,6 +364,7 @@ public class SaveCommandBuilder { state.synchronizeTrees(); TaskGroup rootTask = state.getRootTask(); + if (rootTask != null) { // This reattachment is needed to ensure that the root task in // the state is the one associated to the transaction's session. @@ -375,6 +381,7 @@ public class SaveCommandBuilder { updateTasksRelatedData(); removeTasksToRemove(); + loadDataAccessedWithNotPosedAsTransientInOrder(state.getOrder()); loadDataAccessedWithNotPosedAsTransient(state.getOrder()); if (state.getRootTask() != null) { loadDependenciesCollectionsForTaskRoot(state.getRootTask()); @@ -812,6 +819,10 @@ public class SaveCommandBuilder { } } + private void loadDataAccessedWithNotPosedAsTransientInOrder(Order order) { + order.getEndDateCommunicationToCustomer().size(); + } + private void loadDataAccessedWithNotPosedAsTransient( OrderElement orderElement) { orderElement.getDirectAdvanceAssignments().size(); @@ -986,6 +997,7 @@ public class SaveCommandBuilder { } if (taskElement instanceof Task) { dontPoseAsTransient(((Task) taskElement).getConsolidation()); + dontPoseAsTransient(((Task) taskElement).getSubcontractedTaskData()); } if (taskElement instanceof TaskGroup) { ((TaskGroup) taskElement).dontPoseAsTransientPlanningData(); @@ -1005,6 +1017,19 @@ public class SaveCommandBuilder { } } + private void dontPoseAsTransient(SubcontractedTaskData subcontractedTaskData) { + if (subcontractedTaskData != null) { + //dontPoseAsTransient - subcontratedTaskData + subcontractedTaskData.dontPoseAsTransientObjectAnymore(); + + for (SubcontractorDeliverDate subDeliverDate : subcontractedTaskData + .getRequiredDeliveringDates()) { + //dontPoseAsTransient - DeliverDate + subDeliverDate.dontPoseAsTransientObjectAnymore(); + } + } + } + private void dontPoseAsTransient( SortedSet values) { for (ConsolidatedValue value : values) { diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/planner/order/SubcontractController.java b/libreplan-webapp/src/main/java/org/libreplan/web/planner/order/SubcontractController.java index 619d9ab37..c57c529b8 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/planner/order/SubcontractController.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/planner/order/SubcontractController.java @@ -21,21 +21,42 @@ package org.libreplan.web.planner.order; +import static org.libreplan.web.I18nHelper._; + +import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; +import java.util.SortedSet; import org.libreplan.business.common.exceptions.ValidationException; +import org.libreplan.business.externalcompanies.entities.EndDateCommunication; import org.libreplan.business.externalcompanies.entities.ExternalCompany; +import org.libreplan.business.planner.entities.SubcontractState; import org.libreplan.business.planner.entities.SubcontractedTaskData; +import org.libreplan.business.planner.entities.SubcontractorDeliverDate; import org.libreplan.business.planner.entities.Task; import org.libreplan.business.planner.entities.TaskElement; +import org.libreplan.web.common.IMessagesForUser; +import org.libreplan.web.common.Level; +import org.libreplan.web.common.MessagesForUser; import org.libreplan.web.common.Util; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Scope; +import org.zkoss.ganttz.TaskEditFormComposer; import org.zkoss.ganttz.extensions.IContextWithPlannerTask; import org.zkoss.zk.ui.Component; +import org.zkoss.zk.ui.event.Event; +import org.zkoss.zk.ui.event.EventListener; +import org.zkoss.zk.ui.event.Events; import org.zkoss.zk.ui.util.GenericForwardComposer; +import org.zkoss.zul.Button; import org.zkoss.zul.Comboitem; +import org.zkoss.zul.Grid; +import org.zkoss.zul.Hbox; +import org.zkoss.zul.Label; +import org.zkoss.zul.Row; +import org.zkoss.zul.RowRenderer; +import org.zkoss.zul.api.Datebox; import org.zkoss.zul.api.Tabpanel; /** @@ -51,15 +72,32 @@ public class SubcontractController extends GenericForwardComposer { private ISubcontractModel subcontractModel; + private Grid gridDeliverDate; + + private DeliverDatesRenderer deliverDatesRenderer = new DeliverDatesRenderer(); + + protected IMessagesForUser messagesForUser; + + private Component messagesContainer; + + private IContextWithPlannerTask currentContext; + + private Grid gridEndDates; + + private TaskEditFormComposer taskEditFormComposer = new TaskEditFormComposer(); + @Override public void doAfterCompose(Component comp) throws Exception { super.doAfterCompose(comp); tabpanel = (Tabpanel) comp; + messagesForUser = new MessagesForUser(messagesContainer); } - public void init(Task task, - IContextWithPlannerTask context) { + public void init(Task task, IContextWithPlannerTask context, + TaskEditFormComposer taskEditFormComposer) { + this.currentContext = context; subcontractModel.init(task, context.getTask()); + this.taskEditFormComposer = taskEditFormComposer; Util.reloadBindings(tabpanel); } @@ -101,8 +139,216 @@ public class SubcontractController extends GenericForwardComposer { subcontractModel.removeSubcontractedTaskData(); } + public SortedSet getDeliverDates() { + return subcontractModel.getDeliverDates(); + } + + public void addDeliverDate(Datebox newDeliverDate){ + if (newDeliverDate == null || newDeliverDate.getValue() == null) { + messagesForUser.showMessage(Level.ERROR, + _("You must select a valid date. ")); + return; + } + if (thereIsSomeCommunicationDateEmpty()) { + messagesForUser + .showMessage( + Level.ERROR, + _("It will only be possible to add a Deliver Date if all the deliver date exiting in the table have a CommunicationDate not empty. ")); + return; + } + if(subcontractModel.alreadyExistsRepeatedDeliverDate(newDeliverDate.getValue())){ + messagesForUser + .showMessage( + Level.ERROR, + _("It already exists a deliver date with the same date. ")); + return; + } + subcontractModel.addDeliverDate(newDeliverDate.getValue()); + Util.reloadBindings(gridDeliverDate); + gridDeliverDate.invalidate(); + } + + private boolean thereIsSomeCommunicationDateEmpty(){ + for(SubcontractorDeliverDate subDeliverDate : subcontractModel.getDeliverDates()){ + if(subDeliverDate.getCommunicationDate() == null){ + return true; + } + } + return false; + } + + public DeliverDatesRenderer getDeliverDatesRenderer(){ + return new DeliverDatesRenderer(); + } + + private class DeliverDatesRenderer implements RowRenderer{ + + @Override + public void render(Row row, Object data) throws Exception { + SubcontractorDeliverDate subcontractorDeliverDate = (SubcontractorDeliverDate) data; + row.setValue(subcontractorDeliverDate); + + appendLabel(row, SubcontractController.this.toString( + subcontractorDeliverDate.getSaveDate(), "dd/MM/yyyy HH:mm")); + appendLabel( + row, + SubcontractController.this.toString( + subcontractorDeliverDate.getSubcontractorDeliverDate(), "dd/MM/yyyy")); + appendLabel( + row, + SubcontractController.this.toString( + subcontractorDeliverDate.getCommunicationDate(), "dd/MM/yyyy HH:mm")); + appendOperations(row, subcontractorDeliverDate); + } + + private void appendLabel(Row row, String label) { + row.appendChild(new Label(label)); + } + + private void appendOperations(Row row, + SubcontractorDeliverDate subcontractorDeliverDate) { + Hbox hbox = new Hbox(); + hbox.appendChild(getDeleteButton(subcontractorDeliverDate)); + row.appendChild(hbox); + } + + private Button getDeleteButton( + final SubcontractorDeliverDate subcontractorDeliverDate) { + + Button deleteButton = new Button(); + deleteButton.setDisabled(isNotUpdate(subcontractorDeliverDate)); + deleteButton.setSclass("icono"); + deleteButton.setImage("/common/img/ico_borrar1.png"); + deleteButton.setHoverImage("/common/img/ico_borrar.png"); + deleteButton.setTooltiptext(_("Delete")); + deleteButton.addEventListener(Events.ON_CLICK, new EventListener() { + @Override + public void onEvent(Event event) { + removeRequiredDeliverDate(subcontractorDeliverDate); + } + }); + + return deleteButton; + } + + private boolean isNotUpdate(final SubcontractorDeliverDate subDeliverDate){ + SubcontractorDeliverDate lastDeliverDate = getSubcontractedTaskData() + .getRequiredDeliveringDates().first(); + if ((lastDeliverDate != null) && (lastDeliverDate.equals(subDeliverDate))) { + return (lastDeliverDate.getCommunicationDate() != null); + } + return true; + } + } + + public void removeRequiredDeliverDate(SubcontractorDeliverDate subcontractorDeliverDate){ + subcontractModel.removeRequiredDeliverDate(subcontractorDeliverDate); + Util.reloadBindings(gridDeliverDate); + } + + public boolean isSent(){ + return !isNotSent(); + } + + public boolean isNotSent() { + if (this.getSubcontractedTaskData() != null && this.getSubcontractedTaskData().getState() != null) { + return ((this.getSubcontractedTaskData().getState() + .equals(SubcontractState.PENDING_INITIAL_SEND)) || (this + .getSubcontractedTaskData().getState() + .equals(SubcontractState.FAILED_SENT))); + } + return false; + } + + public String toString(Date date, String precision) { + if (date == null) { + return ""; + } + return new SimpleDateFormat(precision).format(date); + } + + public SortedSet getAskedEndDates() { + return subcontractModel.getAskedEndDates(); + } + + public EndDatesRenderer getEndDatesRenderer() { + return new EndDatesRenderer(); + } + + private class EndDatesRenderer implements RowRenderer { + + @Override + public void render(Row row, Object data) throws Exception { + EndDateCommunication endDateFromSubcontractor = (EndDateCommunication) data; + row.setValue(endDateFromSubcontractor); + + appendLabel(row, SubcontractController.this.toString( + endDateFromSubcontractor.getEndDate(), "dd/MM/yyyy")); + appendLabel(row, + SubcontractController.this.toString( + endDateFromSubcontractor.getCommunicationDate(), "dd/MM/yyyy HH:mm")); + appendOperations(row, endDateFromSubcontractor); + } + + + + private void appendLabel(Row row, String label) { + row.appendChild(new Label(label)); + } + + private void appendOperations(Row row, + EndDateCommunication endDateFromSubcontractor) { + Hbox hbox = new Hbox(); + hbox.appendChild(getUpdateButton(endDateFromSubcontractor)); + row.appendChild(hbox); + } + + private Button getUpdateButton(final EndDateCommunication endDateFromSubcontractor) { + + Button updateButton = new Button(_("Update task end")); + updateButton.setDisabled(!isUpgradeable(endDateFromSubcontractor)); + + updateButton.setTooltiptext(_("Update task end")); + updateButton.addEventListener(Events.ON_CLICK, new EventListener() { + @Override + public void onEvent(Event event) { + updateTaskEnd(endDateFromSubcontractor.getEndDate()); + } + }); + + return updateButton; + } + + private boolean isUpgradeable(EndDateCommunication endDateFromSubcontractor) { + EndDateCommunication lastEndDateReported = getSubcontractedTaskData() + .getLastEndDatesCommunicatedFromSubcontractor(); + if (lastEndDateReported != null) { + if (lastEndDateReported.equals(endDateFromSubcontractor)) { + Date newEndDate = lastEndDateReported.getEndDate(); + Date endDateTask = taskEditFormComposer.getTaskDTO().deadlineDate; + if (endDateTask != null) { + return (newEndDate.compareTo(endDateTask) != 0); + } + return true; + } + } + return false; + } + + } + + public void updateTaskEnd(Date date) { + if (taskEditFormComposer != null) { + taskEditFormComposer.getTaskDTO().deadlineDate = date; + } + refressGridEndDates(); + } + + public void refressGridEndDates() { + Util.reloadBindings(gridEndDates); + } + public String getMoneyFormat() { return Util.getMoneyFormat(); } - } \ No newline at end of file diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/planner/order/SubcontractModel.java b/libreplan-webapp/src/main/java/org/libreplan/web/planner/order/SubcontractModel.java index 8fb86529e..1d7e1cdb8 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/planner/order/SubcontractModel.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/planner/order/SubcontractModel.java @@ -23,25 +23,31 @@ package org.libreplan.web.planner.order; import java.util.Date; import java.util.List; +import java.util.SortedSet; +import java.util.TreeSet; +import org.joda.time.LocalDate; import org.libreplan.business.common.exceptions.ValidationException; import org.libreplan.business.externalcompanies.daos.IExternalCompanyDAO; +import org.libreplan.business.externalcompanies.entities.EndDateCommunication; import org.libreplan.business.externalcompanies.entities.ExternalCompany; import org.libreplan.business.planner.daos.ISubcontractedTaskDataDAO; +import org.libreplan.business.planner.entities.SubcontractState; import org.libreplan.business.planner.entities.SubcontractedTaskData; +import org.libreplan.business.planner.entities.SubcontractorDeliverDate; import org.libreplan.business.planner.entities.Task; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import org.zkoss.ganttz.data.GanttDate; /** * Model for UI operations related with subcontract process and * {@link SubcontractedTaskData} entity. * * @author Manuel Rego Casasnovas + * @author Susana Montes Pedreira */ @Service @Scope(BeanDefinition.SCOPE_PROTOTYPE) @@ -75,16 +81,39 @@ public class SubcontractModel implements ISubcontractModel { SubcontractedTaskData subcontractedTaskData = task .getSubcontractedTaskData(); + this.currentSubcontractedTaskData = subcontractedTaskData; if (subcontractedTaskData == null) { this.subcontractedTaskData = SubcontractedTaskData.create(task); + this.addDeliverDate(getEndDate()); } else { + subcontractedTaskDataDAO.reattach(subcontractedTaskData); + loadRequiredDeliveringDates(subcontractedTaskData); + loadAskedEndDatesFromSubcontractor(subcontractedTaskData); this.subcontractedTaskData = SubcontractedTaskData .createFrom(subcontractedTaskData); } } + private void loadAskedEndDatesFromSubcontractor(SubcontractedTaskData subcontractedTaskData) { + if (subcontractedTaskData != null) { + for (EndDateCommunication askedEndDate : subcontractedTaskData + .getEndDatesCommunicatedFromSubcontractor()) { + askedEndDate.getEndDate(); + } + } + } + + private void loadRequiredDeliveringDates(SubcontractedTaskData subcontractedTaskData){ + if(subcontractedTaskData != null){ + for (SubcontractorDeliverDate subDeliverDate : subcontractedTaskData + .getRequiredDeliveringDates()) { + subDeliverDate.getSaveDate(); + } + } + } + @Override public SubcontractedTaskData getSubcontractedTaskData() { return subcontractedTaskData; @@ -166,4 +195,92 @@ public class SubcontractModel implements ISubcontractModel { subcontractedTaskData = null; } + @Override + public SortedSet getDeliverDates(){ + if(subcontractedTaskData != null){ + return subcontractedTaskData.getRequiredDeliveringDates(); + } + return new TreeSet(); + } + + @Override + public void addDeliverDate(Date subDeliverDate){ + if(subcontractedTaskData != null){ + SubcontractorDeliverDate subcontractorDeliverDate = SubcontractorDeliverDate + .create(new Date(), subDeliverDate, null); + subcontractedTaskData + .addRequiredDeliveringDates(subcontractorDeliverDate); + + //update the end date of the task + updateEndDateWithDeliverDate(); + + //update the state of the subcontracted task data + updateStateToPendingUpdateDeliveringDate(); + } + } + + private void updateEndDateWithDeliverDate(){ + SubcontractorDeliverDate lastDeliverDate = this + .getSubcontractedTaskData().getRequiredDeliveringDates().last(); + task.setEndDate(lastDeliverDate.getSubcontractorDeliverDate()); + } + + private void updateStateToPendingUpdateDeliveringDate(){ + if ((subcontractedTaskData.getState() != null) + && (subcontractedTaskData.getState() + .equals(SubcontractState.SUCCESS_SENT))) { + subcontractedTaskData + .setState(SubcontractState.PENDING_UPDATE_DELIVERING_DATE); + } + } + + @Override + public boolean alreadyExistsRepeatedDeliverDate(Date newDeliverDate) { + if (this.getSubcontractedTaskData().getRequiredDeliveringDates() + .isEmpty()) { + return false; + } + + SubcontractorDeliverDate currentSubDeliverDate = this + .getSubcontractedTaskData().getRequiredDeliveringDates() + .first(); + + Date deliverDate = new LocalDate(newDeliverDate) + .toDateTimeAtStartOfDay().toDate(); + Date currentDeliverDate = new LocalDate( + currentSubDeliverDate.getSubcontractorDeliverDate()) + .toDateTimeAtStartOfDay().toDate(); + + return (currentDeliverDate.compareTo(deliverDate) == 0); + } + + @Override + public void removeRequiredDeliverDate( + SubcontractorDeliverDate subcontractorDeliverDate) { + if(subcontractedTaskData != null){ + subcontractedTaskData.removeRequiredDeliveringDates(subcontractorDeliverDate); + updateStateFromPendingDeliveringDateToSuccessSent(); + } + } + + private void updateStateFromPendingDeliveringDateToSuccessSent(){ + if (subcontractedTaskData.getState() != null) { + switch (subcontractedTaskData.getState()) { + case PENDING_UPDATE_DELIVERING_DATE: + case FAILED_UPDATE: + subcontractedTaskData + .setState(SubcontractState.SUCCESS_SENT); + break; + } + } + } + + @Override + public SortedSet getAskedEndDates() { + if (subcontractedTaskData != null) { + return subcontractedTaskData.getEndDatesCommunicatedFromSubcontractor(); + } + return new TreeSet(); + } + } diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/planner/tabs/IGlobalViewEntryPoints.java b/libreplan-webapp/src/main/java/org/libreplan/web/planner/tabs/IGlobalViewEntryPoints.java index 964387c35..a69ea396f 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/planner/tabs/IGlobalViewEntryPoints.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/planner/tabs/IGlobalViewEntryPoints.java @@ -4,6 +4,7 @@ * Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e * Desenvolvemento Tecnolóxico de Galicia * Copyright (C) 2010-2011 Igalia, S.L. + * Copyright (C) 2011 WirelessGalicia, S.L. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by @@ -22,6 +23,7 @@ package org.libreplan.web.planner.tabs; import org.libreplan.business.orders.entities.Order; import org.libreplan.business.orders.entities.OrderElement; +import org.libreplan.business.planner.entities.TaskElement; import org.libreplan.business.templates.entities.OrderTemplate; import org.libreplan.web.common.entrypoints.EntryPoint; import org.libreplan.web.common.entrypoints.EntryPoints; @@ -30,6 +32,7 @@ import org.libreplan.web.common.entrypoints.EntryPoints; * Entry points for {@link MultipleTabsPlannerController}
* * @author Óscar González Fernández + * @author Susana Montes Pedreira * @author Lorenzo Tilve Álvaro */ @EntryPoints(page = "/planner/index.zul", registerAs = "globalView") @@ -65,4 +68,10 @@ public interface IGlobalViewEntryPoints { @EntryPoint("order_advanced_allocation") void goToAdvancedAllocation(Order order); + @EntryPoint("create_order_from_template") + void goToCreateotherOrderFromTemplate(OrderTemplate template); + + @EntryPoint({"order","task"}) + void goToAdvanceTask(Order order,TaskElement task); + } diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/planner/tabs/MultipleTabsPlannerController.java b/libreplan-webapp/src/main/java/org/libreplan/web/planner/tabs/MultipleTabsPlannerController.java index 2dee18e7c..af3e640aa 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/planner/tabs/MultipleTabsPlannerController.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/planner/tabs/MultipleTabsPlannerController.java @@ -5,6 +5,8 @@ * Desenvolvemento Tecnolóxico de Galicia * Copyright (C) 2010-2011 Igalia, S.L. * + * Copyright (C) 2011 WirelessGalicia, S.L. + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or @@ -40,8 +42,6 @@ import org.libreplan.web.dashboard.DashboardController; import org.libreplan.web.limitingresources.LimitingResourcesController; import org.libreplan.web.montecarlo.MonteCarloController; import org.libreplan.web.orders.OrderCRUDController; -import org.libreplan.web.orders.assigntemplates.TemplateFinderPopup; -import org.libreplan.web.orders.assigntemplates.TemplateFinderPopup.IOnResult; import org.libreplan.web.planner.allocation.AdvancedAllocationController.IBack; import org.libreplan.web.planner.company.CompanyPlanningController; import org.libreplan.web.planner.order.IOrderPlanningGate; @@ -63,7 +63,6 @@ import org.zkoss.ganttz.adapters.TabsConfiguration; import org.zkoss.ganttz.adapters.TabsConfiguration.ChangeableTab; import org.zkoss.ganttz.extensions.ITab; import org.zkoss.ganttz.extensions.TabProxy; -import org.zkoss.ganttz.resourceload.ResourcesLoadPanel.IToolbarCommand; import org.zkoss.ganttz.util.LongOperationFeedback; import org.zkoss.ganttz.util.LongOperationFeedback.ILongOperation; import org.zkoss.zk.ui.Executions; @@ -71,7 +70,6 @@ import org.zkoss.zk.ui.event.Event; import org.zkoss.zk.ui.event.EventListener; import org.zkoss.zk.ui.event.Events; import org.zkoss.zk.ui.util.Composer; -import org.zkoss.zul.Button; /** * Creates and handles several tabs @@ -229,6 +227,7 @@ public class MultipleTabsPlannerController implements Composer, public void goToTaskResourceAllocation(Order order, TaskElement task) { orderPlanningController.setShowedTask(task); + orderPlanningController.setCurrentControllerToShow(orderPlanningController.getEditTaskController()); getTabsRegistry() .show(planningTab, changeModeTo(order)); } @@ -475,6 +474,21 @@ public class MultipleTabsPlannerController implements Composer, getTabsRegistry().show(advancedAllocationTab, changeModeTo(order)); } + @Override + public void goToCreateotherOrderFromTemplate(OrderTemplate template) { + getTabsRegistry().show(ordersTab); + orderCRUDController.showCreateFormFromTemplate(template); + } + + @Override + public void goToAdvanceTask(Order order,TaskElement task) { + orderPlanningController.setShowedTask(task); + orderPlanningController + .setCurrentControllerToShow(orderPlanningController + .getAdvanceAssignmentPlanningController()); + getTabsRegistry().show(planningTab, changeModeTo(order)); + } + private IBeforeShowAction changeModeTo(final Order order) { return new IBeforeShowAction() { @Override diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/planner/taskedition/EditTaskController.java b/libreplan-webapp/src/main/java/org/libreplan/web/planner/taskedition/EditTaskController.java index 4cd0a5fb9..236e95cfd 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/planner/taskedition/EditTaskController.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/planner/taskedition/EditTaskController.java @@ -174,7 +174,9 @@ public class EditTaskController extends GenericForwardComposer { if (ResourceAllocationTypeEnum.SUBCONTRACT .equals(resourceAllocationType)) { - subcontractController.init(asTask(taskElement), context); + + subcontractController.init(asTask(taskElement), context, + taskPropertiesController.getTaskEditFormComposer()); showSubcontractTab(); } else if (ResourceAllocationTypeEnum.NON_LIMITING_RESOURCES .equals(resourceAllocationType)) { @@ -186,7 +188,6 @@ public class EditTaskController extends GenericForwardComposer { planningState, messagesForUser); showLimitingResourcesTab(); } - } private void showSubcontractTab() { @@ -382,5 +383,4 @@ public class EditTaskController extends GenericForwardComposer { public Integer getStatus() { return (Integer) self.getVariable("status", true); } - } diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/planner/taskedition/TaskPropertiesController.java b/libreplan-webapp/src/main/java/org/libreplan/web/planner/taskedition/TaskPropertiesController.java index 8110c5d02..38a2d931a 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/planner/taskedition/TaskPropertiesController.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/planner/taskedition/TaskPropertiesController.java @@ -29,7 +29,12 @@ import java.util.Iterator; import java.util.List; import org.joda.time.LocalDate; +import org.libreplan.business.advance.bootstrap.PredefinedAdvancedTypes; +import org.libreplan.business.advance.entities.AdvanceType; +import org.libreplan.business.advance.entities.DirectAdvanceAssignment; +import org.libreplan.business.advance.exceptions.DuplicateAdvanceAssignmentForOrderElementException; import org.libreplan.business.orders.entities.Order; +import org.libreplan.business.orders.entities.OrderElement; import org.libreplan.business.planner.entities.ITaskPositionConstrained; import org.libreplan.business.planner.entities.PositionConstraintType; import org.libreplan.business.planner.entities.Task; @@ -94,6 +99,8 @@ public class TaskPropertiesController extends GenericForwardComposer { private Datebox endDateBox; + private Datebox deadLineDateBox; + private Combobox startConstraintTypes; private Datebox startConstraintDate; @@ -165,6 +172,12 @@ public class TaskPropertiesController extends GenericForwardComposer { return topTask.getParent().getOrderElement().getOrder(); } + private OrderElement findOrderElementIn(IContextWithPlannerTask context) { + TaskElement topTask = context.getMapper().findAssociatedDomainObject( + findTopMostTask(context)); + return topTask.getOrderElement(); + } + private org.zkoss.ganttz.data.Task findTopMostTask( IContextWithPlannerTask context) { List parents = context.getMapper().getParents( @@ -325,9 +338,20 @@ public class TaskPropertiesController extends GenericForwardComposer { restoreOldState(); editTaskController.showNonPermitChangeResourceAllocationType(); } else { - changeResourceAllocationType(oldState, newState); - editTaskController.selectAssignmentTab(lbResourceAllocationType + if(newState.equals(ResourceAllocationTypeEnum.SUBCONTRACT) && checkCompatibleAllocation()){ + changeResourceAllocationType(oldState, newState); + editTaskController.selectAssignmentTab(lbResourceAllocationType .getSelectedIndex() + 1); + }else{ + try { + restoreOldState(); + Messagebox.show(_("This resource allocation type is incompatible. The task has an associated order element which has a progress that is of type subcontractor. "), + _("Error"), Messagebox.OK , Messagebox.ERROR); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } } } if (oldState == null) { @@ -349,6 +373,30 @@ public class TaskPropertiesController extends GenericForwardComposer { } + private boolean checkCompatibleAllocation(){ + OrderElement orderElement = null; + AdvanceType advanceType = PredefinedAdvancedTypes.SUBCONTRACTOR.getType(); + + if (this.currentContext != null) { + orderElement = findOrderElementIn(this.currentContext ); + } else { + orderElement = this.currentTaskElement.getOrderElement(); + } + if(orderElement.getAdvanceAssignmentByType(advanceType) != null){ + return false; + } + try { + DirectAdvanceAssignment newAdvanceAssignment = DirectAdvanceAssignment + .create(); + newAdvanceAssignment.setAdvanceType(advanceType); + orderElement.checkAncestorsNoOtherAssignmentWithSameAdvanceType( + orderElement.getParent(), newAdvanceAssignment); + } catch (DuplicateAdvanceAssignmentForOrderElementException e) { + return false; + } + return true; + } + private boolean thereIsTransition(ResourceAllocationTypeEnum newState) { return getOldState() != null && !getOldState().equals(newState); } @@ -640,6 +688,14 @@ public class TaskPropertiesController extends GenericForwardComposer { Util.reloadBindings(startDateBox); } + public TaskEditFormComposer getTaskEditFormComposer() { + return taskEditFormComposer; + } + + public void refreshTaskDeadline() { + Util.reloadBindings(deadLineDateBox); + } + public String getMoneyFormat() { return Util.getMoneyFormat(); } diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/resourceload/ResourceLoadModel.java b/libreplan-webapp/src/main/java/org/libreplan/web/resourceload/ResourceLoadModel.java index 2370623d3..fcf380e52 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/resourceload/ResourceLoadModel.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/resourceload/ResourceLoadModel.java @@ -142,7 +142,7 @@ public class ResourceLoadModel implements IResourceLoadModel { allocationsFinder.lazilyGetResourcesIncluded(), allocationsFinder.lazilyGetAssignmentsShown()); } - + @Override @Transactional(readOnly = true) @@ -642,7 +642,7 @@ public class ResourceLoadModel implements IResourceLoadModel { } return result; } - + private LoadTimeLine buildTimeLine(Collection criterions, Task task, Resource resource, String type, List allocationsSortedByStartDate, @@ -1043,7 +1043,7 @@ class PeriodBuilderFactory { this.initDateFilter = initDateFilter; this.endDateFilter = endDateFilter; } - + public List build(LoadPeriodGeneratorFactory factory, List> sortedByStartDate){ if (initDateFilter == null && endDateFilter == null) { return PeriodsBuilder.build(factory, sortedByStartDate); @@ -1055,7 +1055,7 @@ class PeriodBuilderFactory { private Date asDate(LocalDate date) { return ResourceLoadModel.asDate(date); } - + } class PeriodsBuilder { diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/resourceload/ResourceLoadParameters.java b/libreplan-webapp/src/main/java/org/libreplan/web/resourceload/ResourceLoadParameters.java index 37c9d36e3..e19cffc6a 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/resourceload/ResourceLoadParameters.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/resourceload/ResourceLoadParameters.java @@ -104,7 +104,7 @@ public class ResourceLoadParameters { criteriaToShowList.clear(); criteriaToShowList.addAll(criteriaList); } - + public Paginator getEntities(Class type, Callable> allEntities, IReattacher reattacher) { Validate.isTrue( @@ -119,7 +119,7 @@ public class ResourceLoadParameters { allEntities, reattacher); } } - + private List listOfType(Class klass, Collection objects) { List result = new ArrayList(); for (Object each : objects) { diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/subcontract/CustomerCommunicationCRUDController.java b/libreplan-webapp/src/main/java/org/libreplan/web/subcontract/CustomerCommunicationCRUDController.java new file mode 100644 index 000000000..7fbf91842 --- /dev/null +++ b/libreplan-webapp/src/main/java/org/libreplan/web/subcontract/CustomerCommunicationCRUDController.java @@ -0,0 +1,225 @@ +/* + * This file is part of LibrePlan + * + * Copyright (C) 2011 WirelessGalicia, S.L. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.libreplan.web.subcontract; + +import static org.libreplan.web.I18nHelper._; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; + +import javax.annotation.Resource; + +import org.apache.commons.logging.LogFactory; +import org.libreplan.business.common.exceptions.ValidationException; +import org.libreplan.business.externalcompanies.entities.CommunicationType; +import org.libreplan.business.externalcompanies.entities.CustomerCommunication; +import org.libreplan.business.orders.entities.Order; +import org.libreplan.web.common.IMessagesForUser; +import org.libreplan.web.common.MessagesForUser; +import org.libreplan.web.planner.tabs.IGlobalViewEntryPoints; +import org.zkoss.util.Locales; +import org.zkoss.zk.ui.Component; +import org.zkoss.zk.ui.event.Event; +import org.zkoss.zk.ui.event.EventListener; +import org.zkoss.zk.ui.event.Events; +import org.zkoss.zk.ui.util.GenericForwardComposer; +import org.zkoss.zul.Button; +import org.zkoss.zul.Checkbox; +import org.zkoss.zul.Grid; +import org.zkoss.zul.Label; +import org.zkoss.zul.Row; +import org.zkoss.zul.RowRenderer; +import org.zkoss.zul.SimpleListModel; + +/** + * Controller for CRUD actions over a {@link CustomerCommunication} + * + * @author Susana Montes Pedreira + */ +@SuppressWarnings("serial") +public class CustomerCommunicationCRUDController extends GenericForwardComposer { + + private static final org.apache.commons.logging.Log LOG = LogFactory + .getLog(CustomerCommunicationCRUDController.class); + + private ICustomerCommunicationModel customerCommunicationModel; + + private CustomerCommunicationRenderer customerCommunicationRenderer = new CustomerCommunicationRenderer();; + + protected IMessagesForUser messagesForUser; + + private Component messagesContainer; + + private Grid listing; + + @Resource + private IGlobalViewEntryPoints globalView; + + @Override + public void doAfterCompose(Component comp) throws Exception { + super.doAfterCompose(comp); + comp.setAttribute("controller", this); + messagesForUser = new MessagesForUser(messagesContainer); + } + + public void goToEdit(CustomerCommunication customerCommunication) { + if(customerCommunication != null && customerCommunication.getOrder() != null){ + Order order = customerCommunication.getOrder(); + globalView.goToOrderDetails(order); + } + } + + public FilterCommunicationEnum[] getFilterItems(){ + return FilterCommunicationEnum.values(); + } + + public FilterCommunicationEnum getCurrentFilterItem() { + return customerCommunicationModel.getCurrentFilter(); + } + + public void setCurrentFilterItem(FilterCommunicationEnum selected) { + customerCommunicationModel.setCurrentFilter(selected); + refreshCustomerCommunicationsList(); + } + + private void refreshCustomerCommunicationsList(){ + // update the customer communication list + listing.setModel(new SimpleListModel(getCustomerCommunications())); + listing.invalidate(); + } + + protected void save(CustomerCommunication customerCommunication) + throws ValidationException { + customerCommunicationModel.confirmSave(customerCommunication); + } + + public List getCustomerCommunications() { + FilterCommunicationEnum currentFilter = customerCommunicationModel.getCurrentFilter(); + switch(currentFilter){ + case ALL: return customerCommunicationModel.getCustomerAllCommunications(); + case NOT_REVIEWED: return customerCommunicationModel.getCustomerCommunicationWithoutReviewed(); + default: return customerCommunicationModel.getCustomerAllCommunications(); + } + } + + public CustomerCommunicationRenderer getCustomerCommunicationRenderer() { + return customerCommunicationRenderer; + } + + private class CustomerCommunicationRenderer implements + RowRenderer { + + @Override + public void render(Row row, Object data) { + CustomerCommunication customerCommunication = (CustomerCommunication) data; + row.setValue(customerCommunication); + + final CommunicationType type = customerCommunication.getCommunicationType(); + final boolean reviewed = customerCommunication.getReviewed(); + if(!customerCommunication.getReviewed()){ + row.setSclass("communication-not-reviewed"); + } + + appendLabel(row, toString(type)); + appendLabel(row, customerCommunication.getOrder().getName()); + appendLabel(row, + toString(customerCommunication.getDeadline(), "dd/MM/yyyy")); + appendLabel(row, customerCommunication.getOrder().getCode()); + appendLabel(row, customerCommunication.getOrder() + .getCustomerReference()); + appendLabel(row, + toString(customerCommunication.getCommunicationDate(), + "dd/MM/yyyy HH:mm")); + appendCheckbox(row, customerCommunication); + appendOperations(row, customerCommunication); + } + + private String toString(Date date, String precision) { + if (date == null) { + return ""; + } + return new SimpleDateFormat(precision, Locales.getCurrent()) + .format(date); + } + + private String toString(Object object) { + if (object == null) { + return ""; + } + return object.toString(); + } + + private void appendLabel(Row row, String label) { + row.appendChild(new Label(label)); + } + + private void appendCheckbox(final Row row, + final CustomerCommunication customerCommunication) { + final Checkbox checkBoxReviewed = new Checkbox(); + checkBoxReviewed.setChecked(customerCommunication.getReviewed()); + + checkBoxReviewed.addEventListener(Events.ON_CHECK, + new EventListener() { + + @Override + public void onEvent(Event arg0) throws Exception { + customerCommunication.setReviewed(checkBoxReviewed.isChecked()); + save(customerCommunication); + updateRowClass(row,checkBoxReviewed.isChecked()); + } + + }); + + row.appendChild(checkBoxReviewed); + } + + private void updateRowClass(final Row row, Boolean reviewed){ + row.setSclass(""); + if(!reviewed){ + row.setSclass("communication-not-reviewed"); + } + } + + private void appendOperations(Row row, + final CustomerCommunication customerCommunication) { + Button buttonEdit = new Button(); + buttonEdit.setSclass("icono"); + buttonEdit.setImage("/common/img/ico_editar1.png"); + buttonEdit.setHoverImage("/common/img/ico_editar.png"); + buttonEdit.setTooltiptext(_("Edit")); + buttonEdit.addEventListener(Events.ON_CLICK, new EventListener() { + @Override + public void onEvent(Event arg0) throws Exception { + goToEdit(customerCommunication); + } + }); + row.appendChild(buttonEdit); + } + } + + /** + * Apply filter to customers communications + * @param event + */ + public void onApplyFilter(Event event) { + refreshCustomerCommunicationsList(); + } +} \ No newline at end of file diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/subcontract/CustomerCommunicationModel.java b/libreplan-webapp/src/main/java/org/libreplan/web/subcontract/CustomerCommunicationModel.java new file mode 100644 index 000000000..319310868 --- /dev/null +++ b/libreplan-webapp/src/main/java/org/libreplan/web/subcontract/CustomerCommunicationModel.java @@ -0,0 +1,82 @@ +/* + * This file is part of LibrePlan + * + * Copyright (C) 2011 WirelessGalicia, S.L. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.libreplan.web.subcontract; + +import java.util.List; + +import org.libreplan.business.externalcompanies.daos.ICustomerCommunicationDAO; +import org.libreplan.business.externalcompanies.entities.CustomerCommunication; +import org.libreplan.web.common.concurrentdetection.OnConcurrentModification; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@Scope(BeanDefinition.SCOPE_PROTOTYPE) +@OnConcurrentModification(goToPage = "/subcontract/customerCommunication.zul") +public class CustomerCommunicationModel implements ICustomerCommunicationModel{ + + @Autowired + private ICustomerCommunicationDAO customerCommunicationDAO; + + private FilterCommunicationEnum currentFilter = FilterCommunicationEnum.NOT_REVIEWED; + + @Override + @Transactional + public void confirmSave(CustomerCommunication customerCommunication){ + customerCommunicationDAO.save(customerCommunication); + } + + @Override + @Transactional + public List getCustomerAllCommunications(){ + List list = customerCommunicationDAO.getAll(); + forceLoadAssociatedData(list); + return list; + } + + @Override + @Transactional + public List getCustomerCommunicationWithoutReviewed(){ + List list = customerCommunicationDAO.getAllNotReviewed(); + forceLoadAssociatedData(list); + return list; + } + + private void forceLoadAssociatedData(List customerCommunicationList){ + if (customerCommunicationList != null) { + for (CustomerCommunication customerCommunication : customerCommunicationList) { + customerCommunication.getOrder().getName(); + } + } + } + + @Override + public void setCurrentFilter(FilterCommunicationEnum currentFilter) { + this.currentFilter = currentFilter; + } + + @Override + public FilterCommunicationEnum getCurrentFilter() { + return currentFilter; + } +} diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/subcontract/FilterCommunicationEnum.java b/libreplan-webapp/src/main/java/org/libreplan/web/subcontract/FilterCommunicationEnum.java new file mode 100644 index 000000000..06282dedf --- /dev/null +++ b/libreplan-webapp/src/main/java/org/libreplan/web/subcontract/FilterCommunicationEnum.java @@ -0,0 +1,51 @@ +/* + * This file is part of LibrePlan + * + * Copyright (C) 2011 WirelessGalicia, S.L. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.libreplan.web.subcontract; + +/** + * Enum to filter the {@link CustomerCommunication} list. + * + * @author Susana Montes Pedreira + */ +public enum FilterCommunicationEnum { + ALL(_("All")), NOT_REVIEWED(_("Not Reviewed")); + + /** + * Forces to mark the string as needing translation + */ + private static String _(String string) { + return string; + } + + private String displayName; + + private FilterCommunicationEnum(String displayName) { + this.displayName = displayName; + } + + public static FilterCommunicationEnum getDefault() { + return ALL; + } + + @Override + public String toString() { + return displayName; + } +} \ No newline at end of file diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/subcontract/ICustomerCommunicationModel.java b/libreplan-webapp/src/main/java/org/libreplan/web/subcontract/ICustomerCommunicationModel.java new file mode 100644 index 000000000..7ec86cf0e --- /dev/null +++ b/libreplan-webapp/src/main/java/org/libreplan/web/subcontract/ICustomerCommunicationModel.java @@ -0,0 +1,38 @@ +/* + * This file is part of LibrePlan + * + * Copyright (C) 2011 WirelessGalicia, S.L. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.libreplan.web.subcontract; + +import java.util.List; + +import org.libreplan.business.externalcompanies.entities.CustomerCommunication; + +public interface ICustomerCommunicationModel { + + void confirmSave(CustomerCommunication customerCommunication); + + List getCustomerCommunicationWithoutReviewed(); + + List getCustomerAllCommunications(); + + void setCurrentFilter(FilterCommunicationEnum currentFilter); + + FilterCommunicationEnum getCurrentFilter(); + +} diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/subcontract/IReportAdvancesModel.java b/libreplan-webapp/src/main/java/org/libreplan/web/subcontract/IReportAdvancesModel.java index 3887b3d6e..eb8a05a7e 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/subcontract/IReportAdvancesModel.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/subcontract/IReportAdvancesModel.java @@ -53,4 +53,6 @@ public interface IReportAdvancesModel { boolean isAnyAdvanceMeasurementNotReported( DirectAdvanceAssignment directAdvanceAssignment); + String getStatus(Order order); + } \ No newline at end of file diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/subcontract/ISubcontractedTasksModel.java b/libreplan-webapp/src/main/java/org/libreplan/web/subcontract/ISubcontractedTasksModel.java index 50a45d576..cb258bb07 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/subcontract/ISubcontractedTasksModel.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/subcontract/ISubcontractedTasksModel.java @@ -23,6 +23,7 @@ package org.libreplan.web.subcontract; import java.util.List; import org.libreplan.business.common.exceptions.ValidationException; +import org.libreplan.business.orders.entities.Order; import org.libreplan.business.planner.entities.SubcontractedTaskData; import org.libreplan.web.subcontract.exceptions.ConnectionProblemsException; import org.libreplan.web.subcontract.exceptions.UnrecoverableErrorServiceException; @@ -36,12 +37,11 @@ public interface ISubcontractedTasksModel { List getSubcontractedTasks(); - String getOrderCode(SubcontractedTaskData subcontractedTaskData); - void sendToSubcontractor(SubcontractedTaskData subcontractedTaskData) throws ValidationException, ConnectionProblemsException, UnrecoverableErrorServiceException; String exportXML(SubcontractedTaskData subcontractedTaskData); + Order getOrder(SubcontractedTaskData subcontractedTaskData); } \ No newline at end of file diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/subcontract/ISubcontractorCommunicationModel.java b/libreplan-webapp/src/main/java/org/libreplan/web/subcontract/ISubcontractorCommunicationModel.java new file mode 100644 index 000000000..f78d05e76 --- /dev/null +++ b/libreplan-webapp/src/main/java/org/libreplan/web/subcontract/ISubcontractorCommunicationModel.java @@ -0,0 +1,47 @@ +/* + * This file is part of LibrePlan + * + * Copyright (C) 2011 WirelessGalicia, S.L. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.libreplan.web.subcontract; + +import java.util.List; + +import org.libreplan.business.orders.entities.Order; +import org.libreplan.business.orders.entities.OrderElement; +import org.libreplan.business.planner.entities.SubcontractedTaskData; +import org.libreplan.business.planner.entities.SubcontractorCommunication; + +public interface ISubcontractorCommunicationModel { + + void confirmSave(SubcontractorCommunication customerCommunication); + + List getSubcontractorCommunicationWithoutReviewed(); + + List getSubcontractorAllCommunications(); + + void setCurrentFilter(FilterCommunicationEnum currentFilter); + + FilterCommunicationEnum getCurrentFilter(); + + String getOrderCode(SubcontractedTaskData subcontractedTaskData); + + String getOrderName(SubcontractedTaskData subcontractedTaskData); + + Order getOrder(OrderElement orderElement); + +} diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/subcontract/ReportAdvancesController.java b/libreplan-webapp/src/main/java/org/libreplan/web/subcontract/ReportAdvancesController.java index b16a5dac6..6a0ad6a37 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/subcontract/ReportAdvancesController.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/subcontract/ReportAdvancesController.java @@ -107,9 +107,9 @@ public class ReportAdvancesController extends GenericForwardComposer { DirectAdvanceAssignment directAdvanceAssignment = order .getDirectAdvanceAssignmentOfTypeSubcontractor(); + // append the last advance measurement reported AdvanceMeasurement lastAdvanceMeasurementReported = reportAdvancesModel .getLastAdvanceMeasurementReported(directAdvanceAssignment); - if (lastAdvanceMeasurementReported != null) { appendLabel(row, toString(lastAdvanceMeasurementReported.getDate())); appendLabel(row, toString(lastAdvanceMeasurementReported.getValue())); @@ -118,9 +118,9 @@ public class ReportAdvancesController extends GenericForwardComposer { appendLabel(row, ""); } + // append the last advance measurement not reported AdvanceMeasurement lastAdvanceMeasurement = reportAdvancesModel .getLastAdvanceMeasurement(directAdvanceAssignment); - if (lastAdvanceMeasurement != null) { appendLabel(row, toString(lastAdvanceMeasurement.getDate())); appendLabel(row, toString(lastAdvanceMeasurement.getValue())); @@ -129,15 +129,16 @@ public class ReportAdvancesController extends GenericForwardComposer { appendLabel(row, ""); } - if (reportAdvancesModel - .isAnyAdvanceMeasurementNotReported(directAdvanceAssignment)) { - appendLabel(row, _("Pending update")); - appendOperations(row, order, false); - } else { - appendLabel(row, _("Updated")); - appendOperations(row, order, true); - } + // append the status + String status = reportAdvancesModel.getStatus(order); + appendLabel(row, _(status)); + // append the operations + if (status.equals("Updated")) { + appendOperations(row, order, true); + } else { + appendOperations(row, order, false); + } } private String toString(Object object) { diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/subcontract/ReportAdvancesModel.java b/libreplan-webapp/src/main/java/org/libreplan/web/subcontract/ReportAdvancesModel.java index e6ea07bdf..d07f9e3bb 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/subcontract/ReportAdvancesModel.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/subcontract/ReportAdvancesModel.java @@ -41,6 +41,7 @@ import org.apache.cxf.jaxrs.client.WebClient; import org.libreplan.business.advance.entities.AdvanceMeasurement; import org.libreplan.business.advance.entities.DirectAdvanceAssignment; import org.libreplan.business.common.daos.IConfigurationDAO; +import org.libreplan.business.externalcompanies.entities.EndDateCommunication; import org.libreplan.business.externalcompanies.entities.ExternalCompany; import org.libreplan.business.orders.daos.IOrderDAO; import org.libreplan.business.orders.daos.IOrderElementDAO; @@ -55,8 +56,9 @@ import org.libreplan.ws.common.api.InstanceConstraintViolationsDTO; import org.libreplan.ws.common.api.InstanceConstraintViolationsListDTO; import org.libreplan.ws.common.impl.OrderElementConverter; import org.libreplan.ws.common.impl.Util; -import org.libreplan.ws.subcontract.api.OrderElementWithAdvanceMeasurementsDTO; -import org.libreplan.ws.subcontract.api.OrderElementWithAdvanceMeasurementsListDTO; +import org.libreplan.ws.subcontract.api.EndDateCommunicationToCustomerDTO; +import org.libreplan.ws.subcontract.api.OrderElementWithAdvanceMeasurementsOrEndDateDTO; +import org.libreplan.ws.subcontract.api.OrderElementWithAdvanceMeasurementsOrEndDateListDTO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Scope; @@ -83,7 +85,6 @@ public class ReportAdvancesModel implements IReportAdvancesModel { @Autowired private IConfigurationDAO configurationDAO; - @Override @Transactional(readOnly = true) public List getOrdersWithExternalCodeInAnyOrderElement() { @@ -97,12 +98,17 @@ public class ReportAdvancesModel implements IReportAdvancesModel { ordersMap.put(order.getId(), order); forceLoadHoursGroups(order); forceLoadAdvanceAssignments(order); + forceLoadAskedEndDate(order); } } return new ArrayList(ordersMap.values()); } + private void forceLoadAskedEndDate(Order order) { + order.getEndDateCommunicationToCustomer().size(); + } + private void forceLoadHoursGroups(OrderElement orderElement) { orderElement.getHoursGroups().size(); for (OrderElement child : orderElement.getChildren()) { @@ -182,7 +188,7 @@ public class ReportAdvancesModel implements IReportAdvancesModel { ConnectionProblemsException { orderDAO.save(order); - OrderElementWithAdvanceMeasurementsListDTO orderElementWithAdvanceMeasurementsListDTO = getOrderElementWithAdvanceMeasurementsListDTO(order); + OrderElementWithAdvanceMeasurementsOrEndDateListDTO orderElementWithAdvanceMeasurementsListDTO = getOrderElementWithAdvanceMeasurementsListDTO(order); ExternalCompany externalCompany = order.getCustomer(); NaiveTrustProvider.setAlwaysTrust(true); @@ -211,6 +217,7 @@ public class ReportAdvancesModel implements IReportAdvancesModel { throw new UnrecoverableErrorServiceException(message); } + } catch (WebApplicationException e) { LOG.error("Problems connecting with client web service", e); @@ -223,33 +230,45 @@ public class ReportAdvancesModel implements IReportAdvancesModel { } } - private OrderElementWithAdvanceMeasurementsListDTO getOrderElementWithAdvanceMeasurementsListDTO( + private OrderElementWithAdvanceMeasurementsOrEndDateListDTO getOrderElementWithAdvanceMeasurementsListDTO( Order order) { - List orderElementWithAdvanceMeasurementsDTOs = new ArrayList(); + List orderElementWithAdvanceMeasurementsDTOs = new ArrayList(); + // create the asked end dates + EndDateCommunicationToCustomerDTO endDateCommunicationToCustomerDTO = null; + if (isAnyEndDateNotReported(order)) { + EndDateCommunication lastEndDateCommunicationToCustomerReported = order + .getLastEndDateCommunicationToCustomer(); + lastEndDateCommunicationToCustomerReported.setCommunicationDate(new Date()); + endDateCommunicationToCustomerDTO = OrderElementConverter.toDTO(lastEndDateCommunicationToCustomerReported); + } + // create the progress DirectAdvanceAssignment directAdvanceAssignment = order .getDirectAdvanceAssignmentOfTypeSubcontractor(); Set advanceMeasurementDTOs = new HashSet(); - for (AdvanceMeasurement advanceMeasurement : directAdvanceAssignment - .getAdvanceMeasurements()) { - if (advanceMeasurement.getCommunicationDate() == null) { - AdvanceMeasurementDTO advanceMeasurementDTO = OrderElementConverter - .toDTO(advanceMeasurement); - advanceMeasurement.updateCommunicationDate(new Date()); - advanceMeasurementDTOs.add(advanceMeasurementDTO); + if (directAdvanceAssignment != null) { + for (AdvanceMeasurement advanceMeasurement : directAdvanceAssignment + .getAdvanceMeasurements()) { + if (advanceMeasurement.getCommunicationDate() == null) { + AdvanceMeasurementDTO advanceMeasurementDTO = OrderElementConverter + .toDTO(advanceMeasurement); + advanceMeasurement.updateCommunicationDate(new Date()); + advanceMeasurementDTOs.add(advanceMeasurementDTO); + } } } - if (!advanceMeasurementDTOs.isEmpty()) { - OrderElementWithAdvanceMeasurementsDTO orderElementWithAdvanceMeasurementsDTO = new OrderElementWithAdvanceMeasurementsDTO( - directAdvanceAssignment.getOrderElement().getExternalCode(), - advanceMeasurementDTOs); + // add the updates + if (endDateCommunicationToCustomerDTO != null || !advanceMeasurementDTOs.isEmpty()) { + OrderElementWithAdvanceMeasurementsOrEndDateDTO orderElementWithAdvanceMeasurementsOrEndDateDTO = new OrderElementWithAdvanceMeasurementsOrEndDateDTO( + order.getExternalCode(), + advanceMeasurementDTOs, endDateCommunicationToCustomerDTO); orderElementWithAdvanceMeasurementsDTOs - .add(orderElementWithAdvanceMeasurementsDTO); + .add(orderElementWithAdvanceMeasurementsOrEndDateDTO); } - return new OrderElementWithAdvanceMeasurementsListDTO(getCompanyCode(), + return new OrderElementWithAdvanceMeasurementsOrEndDateListDTO(getCompanyCode(), orderElementWithAdvanceMeasurementsDTOs); } @@ -260,12 +279,12 @@ public class ReportAdvancesModel implements IReportAdvancesModel { @Override @Transactional(readOnly = true) public String exportXML(Order order) { - OrderElementWithAdvanceMeasurementsListDTO orderElementWithAdvanceMeasurementsListDTO = getOrderElementWithAdvanceMeasurementsListDTO(order); + OrderElementWithAdvanceMeasurementsOrEndDateListDTO orderElementWithAdvanceMeasurementsListDTO = getOrderElementWithAdvanceMeasurementsListDTO(order); StringWriter xml = new StringWriter(); try { JAXBContext jaxbContext = JAXBContext - .newInstance(OrderElementWithAdvanceMeasurementsListDTO.class); + .newInstance(OrderElementWithAdvanceMeasurementsOrEndDateListDTO.class); Marshaller marshaller = jaxbContext.createMarshaller(); marshaller.marshal(orderElementWithAdvanceMeasurementsListDTO, xml); } catch (Exception e) { @@ -275,4 +294,31 @@ public class ReportAdvancesModel implements IReportAdvancesModel { return xml.toString(); } + @Override + public String getStatus(Order order) { + DirectAdvanceAssignment directAdvanceAssignment = order + .getDirectAdvanceAssignmentOfTypeSubcontractor(); + + boolean advancesNotReported = isAnyAdvanceMeasurementNotReported(directAdvanceAssignment); + boolean endDateNotReported = isAnyEndDateNotReported(order); + if (advancesNotReported && endDateNotReported) { + return "Pending update of progress and communication date"; + } else if (endDateNotReported) { + return "Pending update for communication date"; + } else if (advancesNotReported) { + return "Pending update of progress"; + } + return "Updated"; + } + + private boolean isAnyEndDateNotReported(Order order) { + if (order != null && order.getEndDateCommunicationToCustomer() != null) { + EndDateCommunication lastAskedEndDate = order + .getLastEndDateCommunicationToCustomer(); + return lastAskedEndDate != null ? (lastAskedEndDate.getCommunicationDate() == null) + : false; + } + return false; + } + } diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/subcontract/SubcontractedTasksController.java b/libreplan-webapp/src/main/java/org/libreplan/web/subcontract/SubcontractedTasksController.java index b9eda626f..231141702 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/subcontract/SubcontractedTasksController.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/subcontract/SubcontractedTasksController.java @@ -5,6 +5,8 @@ * Desenvolvemento Tecnolóxico de Galicia * Copyright (C) 2010-2011 Igalia, S.L. * + * Copyright (C) 2011-2012 WirelessGalicia, S.L. + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or @@ -32,6 +34,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.libreplan.business.common.exceptions.ValidationException; +import org.libreplan.business.orders.entities.Order; import org.libreplan.business.planner.entities.SubcontractedTaskData; import org.libreplan.web.common.IMessagesForUser; import org.libreplan.web.common.Level; @@ -52,16 +55,19 @@ import org.zkoss.zk.ui.event.EventListener; import org.zkoss.zk.ui.event.Events; import org.zkoss.zk.ui.util.GenericForwardComposer; import org.zkoss.zul.Button; +import org.zkoss.zul.Column; +import org.zkoss.zul.Grid; import org.zkoss.zul.Hbox; import org.zkoss.zul.Label; +import org.zkoss.zul.ListModelExt; import org.zkoss.zul.Row; import org.zkoss.zul.RowRenderer; import org.zkoss.zul.api.Window; /** * Controller for operations related with subcontracted tasks. - * * @author Manuel Rego Casasnovas + * @author Susana Montes Pedreira */ @org.springframework.stereotype.Component @Scope(BeanDefinition.SCOPE_PROTOTYPE) @@ -69,6 +75,10 @@ public class SubcontractedTasksController extends GenericForwardComposer { private Window window; + private Column columnBySort; + + private Grid listing; + private Component messagesContainer; private IMessagesForUser messagesForUser; @@ -100,21 +110,39 @@ public class SubcontractedTasksController extends GenericForwardComposer { SubcontractedTaskData subcontractedTaskData = (SubcontractedTaskData) data; row.setValue(subcontractedTaskData); - appendLabel(row, toString(subcontractedTaskData - .getSubcontratationDate())); - appendLabel(row, toString(subcontractedTaskData - .getSubcontractCommunicationDate())); + Order order = getOrder(subcontractedTaskData); + + appendLabel(row, + toString(subcontractedTaskData.getSubcontratationDate(), "dd/MM/yyyy HH:mm")); + appendLabel( + row, + toString(subcontractedTaskData.getSubcontractCommunicationDate(), + "dd/MM/yyyy HH:mm")); appendLabel(row, getExternalCompany(subcontractedTaskData)); - appendLabel(row, getOrderCode(subcontractedTaskData)); + appendLabel(row, getOrderCode(order)); + appendLabel(row, getOrderName(order)); appendLabel(row, subcontractedTaskData.getSubcontractedCode()); appendLabel(row, getTaskName(subcontractedTaskData)); appendLabel(row, subcontractedTaskData.getWorkDescription()); - appendLabel(row, Util.addCurrencySymbol(subcontractedTaskData - .getSubcontractPrice())); + appendLabel(row, Util.addCurrencySymbol(subcontractedTaskData.getSubcontractPrice())); + appendLabel(row, + toString(subcontractedTaskData.getLastRequiredDeliverDate(), "dd/MM/yyyy")); appendLabel(row, _(toString(subcontractedTaskData.getState()))); appendOperations(row, subcontractedTaskData); } + private String getOrderCode(Order order) { + return (order != null) ? order.getCode() : ""; + } + + private String getOrderName(Order order) { + return (order != null) ? order.getName() : ""; + } + + private Order getOrder(SubcontractedTaskData subcontractedTaskData) { + return subcontractedTasksModel.getOrder(subcontractedTaskData); + } + private String toString(Object object) { if (object == null) { return ""; @@ -123,22 +151,17 @@ public class SubcontractedTasksController extends GenericForwardComposer { return object.toString(); } - private String toString(Date date) { + private String toString(Date date, String precision) { if (date == null) { return ""; } - - return new SimpleDateFormat("dd/MM/yyyy HH:mm").format(date); + return new SimpleDateFormat(precision).format(date); } private void appendLabel(Row row, String label) { row.appendChild(new Label(label)); } - private String getOrderCode(SubcontractedTaskData subcontractedTaskData) { - return subcontractedTasksModel.getOrderCode(subcontractedTaskData); - } - private String getTaskName(SubcontractedTaskData subcontractedTaskData) { return subcontractedTaskData.getTask().getName(); } @@ -211,7 +234,7 @@ public class SubcontractedTasksController extends GenericForwardComposer { } catch (ValidationException e) { messagesForUser.showInvalidValues(e); } - Util.reloadBindings(window); + reload(); } }); @@ -223,4 +246,23 @@ public class SubcontractedTasksController extends GenericForwardComposer { } + public void reload() { + Util.reloadBindings(window); + forceSortGridSatisfaction(); + } + + public void forceSortGridSatisfaction() { + Column column = (Column) columnBySort; + ListModelExt model = (ListModelExt) listing.getModel(); + if ("ascending".equals(column.getSortDirection())) { + model.sort(column.getSortAscending(), true); + } + if ("descending".equals(column.getSortDirection())) { + model.sort(column.getSortDescending(), false); + } + } + + public void initRender() { + forceSortGridSatisfaction(); + } } diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/subcontract/SubcontractedTasksModel.java b/libreplan-webapp/src/main/java/org/libreplan/web/subcontract/SubcontractedTasksModel.java index f76b20c82..c8c4ad983 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/web/subcontract/SubcontractedTasksModel.java +++ b/libreplan-webapp/src/main/java/org/libreplan/web/subcontract/SubcontractedTasksModel.java @@ -23,6 +23,8 @@ package org.libreplan.web.subcontract; import static org.libreplan.web.I18nHelper._; import java.io.StringWriter; +import java.util.Collections; +import java.util.Comparator; import java.util.Date; import java.util.List; @@ -39,11 +41,13 @@ import org.libreplan.business.common.exceptions.ValidationException; import org.libreplan.business.externalcompanies.entities.ExternalCompany; import org.libreplan.business.orders.daos.IOrderDAO; import org.libreplan.business.orders.daos.IOrderElementDAO; +import org.libreplan.business.orders.entities.Order; import org.libreplan.business.orders.entities.OrderElement; import org.libreplan.business.planner.daos.ISubcontractedTaskDataDAO; import org.libreplan.business.planner.entities.SubcontractState; import org.libreplan.business.planner.entities.SubcontractedTaskData; import org.libreplan.business.planner.entities.Task; +import org.libreplan.web.common.concurrentdetection.OnConcurrentModification; import org.libreplan.web.subcontract.exceptions.ConnectionProblemsException; import org.libreplan.web.subcontract.exceptions.UnrecoverableErrorServiceException; import org.libreplan.ws.cert.NaiveTrustProvider; @@ -71,6 +75,7 @@ import org.springframework.transaction.annotation.Transactional; */ @Component @Scope(BeanDefinition.SCOPE_PROTOTYPE) +@OnConcurrentModification(goToPage = "/subcontract/subcontractedTasks.zul") public class SubcontractedTasksModel implements ISubcontractedTasksModel { private static Log LOG = LogFactory.getLog(SubcontractedTasksModel.class); @@ -94,8 +99,13 @@ public class SubcontractedTasksModel implements ISubcontractedTasksModel { .getAllForMasterScenario(); for (SubcontractedTaskData subcontractedTaskData : result) { forceLoadExternalCompany(subcontractedTaskData); + forceLastDeliveryDate(subcontractedTaskData); } - return result; + return sortByState(result); + } + + private void forceLastDeliveryDate(SubcontractedTaskData subcontractedTaskData) { + subcontractedTaskData.getLastRequiredDeliverDate(); } private void forceLoadExternalCompany( @@ -103,13 +113,44 @@ public class SubcontractedTasksModel implements ISubcontractedTasksModel { subcontractedTaskData.getExternalCompany().getName(); } + private List sortByState(List tasks){ + Collections.sort(tasks, new Comparator(){ + + @Override + public int compare(SubcontractedTaskData arg0, SubcontractedTaskData arg1) { + if((arg0 == null) || (arg0.getState() == null)){ + return -1; + } + if((arg1 == null) || (arg1.getState() == null)){ + return 1; + } + if(arg0.getState().equals(arg1.getState())){ + return 0; + } + if (arg0.getState().equals( + SubcontractState.PENDING_INITIAL_SEND)) { + return 1; + } + if (arg1.getState().equals( + SubcontractState.PENDING_INITIAL_SEND)) { + return -1; + } + if( arg0.getState().equals(SubcontractState.PENDING_UPDATE_DELIVERING_DATE)) { + return 1; + } + return -1; + } + }); + return tasks; + } + @Override @Transactional(readOnly = true) - public String getOrderCode(SubcontractedTaskData subcontractedTaskData) { + public Order getOrder(SubcontractedTaskData subcontractedTaskData) { Task task = subcontractedTaskData.getTask(); OrderElement orderElement = orderDAO.loadOrderAvoidingProxyFor(task .getOrderElement()); - return orderElement.getOrder().getCode(); + return orderElement.getOrder(); } @Override @@ -119,7 +160,14 @@ public class SubcontractedTasksModel implements ISubcontractedTasksModel { UnrecoverableErrorServiceException { subcontractedTaskDataDAO.save(subcontractedTaskData); - subcontractedTaskData.setState(SubcontractState.FAILED_SENT); + SubcontractState currentState = subcontractedTaskData.getState(); + + if (currentState.equals(SubcontractState.PENDING_INITIAL_SEND)) { + subcontractedTaskData.setState(SubcontractState.FAILED_SENT); + } else if (currentState + .equals(SubcontractState.PENDING_UPDATE_DELIVERING_DATE)) { + subcontractedTaskData.setState(SubcontractState.FAILED_UPDATE); + } if (!subcontractedTaskData.isSendable()) { throw new RuntimeException("Subcontracted task already sent"); @@ -131,13 +179,82 @@ public class SubcontractedTasksModel implements ISubcontractedTasksModel { "External company has not interaction fields filled"); } - makeSubcontractRequestRequest(subcontractedTaskData); + makeSubcontractRequestRequest(subcontractedTaskData,currentState); - subcontractedTaskData.setSubcontractCommunicationDate(new Date()); + Date today = new Date(); + if ((currentState.equals(SubcontractState.PENDING_INITIAL_SEND)) + || (currentState.equals(SubcontractState.FAILED_SENT))) { + subcontractedTaskData.setSubcontractCommunicationDate(today); + } + + //update the first required deliver date + subcontractedTaskData.updateFirstRequiredDeliverDate(today); + subcontractedTaskData.setSubcontractCommunicationDate(today); subcontractedTaskData.setState(SubcontractState.SUCCESS_SENT); } private void makeSubcontractRequestRequest( + SubcontractedTaskData subcontractedTaskData, + SubcontractState currentState) throws ConnectionProblemsException, + UnrecoverableErrorServiceException { + if (subcontractedTaskData.getState() != null) { + if ((currentState.equals(SubcontractState.PENDING_INITIAL_SEND) || (currentState + .equals(SubcontractState.FAILED_SENT)))) { + makeSubcontractRequestRequest_InitialSent(subcontractedTaskData); + } else if ((currentState + .equals(SubcontractState.PENDING_UPDATE_DELIVERING_DATE) || currentState + .equals(SubcontractState.FAILED_UPDATE))) { + makeSubcontractRequestRequest_UpdateDeliverDate(subcontractedTaskData); + } + } + } + + private void makeSubcontractRequestRequest_UpdateDeliverDate(SubcontractedTaskData subcontractedTaskData) + throws ConnectionProblemsException, UnrecoverableErrorServiceException { + UpdateDeliveringDateDTO updateDeliveringDateDTO = SubcontractedTaskDataConverter + .toUpdateDeliveringDateDTO(subcontractedTaskData); + ExternalCompany externalCompany = subcontractedTaskData + .getExternalCompany(); + + NaiveTrustProvider.setAlwaysTrust(true); + + try { + WebClient client = WebClient.create(externalCompany.getAppURI()); + client.path("ws/rest/subcontract/update"); + + Util.addAuthorizationHeader(client, + externalCompany.getOurCompanyLogin(), + externalCompany.getOurCompanyPassword()); + + InstanceConstraintViolationsListDTO instanceConstraintViolationsListDTO = client + .post(updateDeliveringDateDTO, + InstanceConstraintViolationsListDTO.class); + + List instanceConstraintViolationsList = instanceConstraintViolationsListDTO.instanceConstraintViolationsList; + if ((instanceConstraintViolationsList != null) + && (!instanceConstraintViolationsList.isEmpty())) { + String message = ""; + + for (ConstraintViolationDTO constraintViolationDTO : instanceConstraintViolationsList + .get(0).constraintViolations) { + message += constraintViolationDTO.toString() + "\n"; + } + + throw new UnrecoverableErrorServiceException(message); + } + } catch (WebApplicationException e) { + LOG.error("Problems connecting with subcontractor web service", e); + + String message = _("Problems connecting with subcontractor web service"); + if (e.getMessage() != null) { + message += ". " + _("Error: {0}", e.getMessage()); + } + + throw new ConnectionProblemsException(message, e); + } + } + + private void makeSubcontractRequestRequest_InitialSent( SubcontractedTaskData subcontractedTaskData) throws ConnectionProblemsException, UnrecoverableErrorServiceException { SubcontractedTaskDataDTO subcontractedTaskDataDTO = getSubcontractedTaskData(subcontractedTaskData); @@ -150,7 +267,7 @@ public class SubcontractedTasksModel implements ISubcontractedTasksModel { try { WebClient client = WebClient.create(externalCompany.getAppURI()); - client.path("ws/rest/subcontract"); + client.path("ws/rest/subcontract/create"); Util.addAuthorizationHeader(client, externalCompany .getOurCompanyLogin(), externalCompany @@ -239,6 +356,16 @@ public class SubcontractedTasksModel implements ISubcontractedTasksModel { @Override @Transactional(readOnly = true) public String exportXML(SubcontractedTaskData subcontractedTaskData) { + SubcontractState currentState = subcontractedTaskData.getState(); + if ((currentState.equals(SubcontractState.PENDING_INITIAL_SEND) || (currentState + .equals(SubcontractState.FAILED_SENT)))) { + return exportXML_CreateSubcontractor(subcontractedTaskData); + } else { + return exportXML_UpdateSubcontractor(subcontractedTaskData); + } + } + + public String exportXML_CreateSubcontractor(SubcontractedTaskData subcontractedTaskData){ SubcontractedTaskDataDTO subcontractedTaskDataDTO = getSubcontractedTaskData(subcontractedTaskData); StringWriter xml = new StringWriter(); @@ -254,4 +381,21 @@ public class SubcontractedTasksModel implements ISubcontractedTasksModel { return xml.toString(); } + public String exportXML_UpdateSubcontractor(SubcontractedTaskData subcontractedTaskData){ + subcontractedTaskDataDAO.reattachUnmodifiedEntity(subcontractedTaskData); + UpdateDeliveringDateDTO updateDeliveringDateDTO = SubcontractedTaskDataConverter + .toUpdateDeliveringDateDTO(subcontractedTaskData); + + StringWriter xml = new StringWriter(); + try { + JAXBContext jaxbContext = JAXBContext + .newInstance(UpdateDeliveringDateDTO.class); + Marshaller marshaller = jaxbContext.createMarshaller(); + marshaller.marshal(updateDeliveringDateDTO, xml); + } catch (Exception e) { + throw new RuntimeException(e); + } + + return xml.toString(); + } } \ No newline at end of file diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/subcontract/SubcontractorCommunicationCRUDController.java b/libreplan-webapp/src/main/java/org/libreplan/web/subcontract/SubcontractorCommunicationCRUDController.java new file mode 100644 index 000000000..54d954f96 --- /dev/null +++ b/libreplan-webapp/src/main/java/org/libreplan/web/subcontract/SubcontractorCommunicationCRUDController.java @@ -0,0 +1,268 @@ +/* + * This file is part of LibrePlan + * + * Copyright (C) 2011 WirelessGalicia, S.L. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.libreplan.web.subcontract; + +import static org.libreplan.web.I18nHelper._; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; + +import javax.annotation.Resource; + +import org.apache.commons.logging.LogFactory; +import org.libreplan.business.common.exceptions.ValidationException; +import org.libreplan.business.externalcompanies.entities.CommunicationType; +import org.libreplan.business.orders.entities.Order; +import org.libreplan.business.orders.entities.OrderElement; +import org.libreplan.business.planner.entities.SubcontractedTaskData; +import org.libreplan.business.planner.entities.SubcontractorCommunication; +import org.libreplan.business.planner.entities.SubcontractorCommunicationValue; +import org.libreplan.business.planner.entities.TaskElement; +import org.libreplan.web.common.IMessagesForUser; +import org.libreplan.web.common.MessagesForUser; +import org.libreplan.web.planner.tabs.IGlobalViewEntryPoints; +import org.zkoss.zk.ui.Component; +import org.zkoss.zk.ui.event.Event; +import org.zkoss.zk.ui.event.EventListener; +import org.zkoss.zk.ui.event.Events; +import org.zkoss.zk.ui.util.GenericForwardComposer; +import org.zkoss.zul.Button; +import org.zkoss.zul.Checkbox; +import org.zkoss.zul.Grid; +import org.zkoss.zul.Label; +import org.zkoss.zul.Popup; +import org.zkoss.zul.Row; +import org.zkoss.zul.RowRenderer; +import org.zkoss.zul.SimpleListModel; + +/** + * Controller for CRUD actions over a {@link SubcontractorCommunication} + * + * @author Susana Montes Pedreira + */ +@SuppressWarnings("serial") +public class SubcontractorCommunicationCRUDController extends GenericForwardComposer { + + private static final org.apache.commons.logging.Log LOG = LogFactory + .getLog(SubcontractorCommunicationCRUDController.class); + + private ISubcontractorCommunicationModel subcontractorCommunicationModel; + + private SubcontractorCommunicationRenderer subcontractorCommunicationRenderer = new SubcontractorCommunicationRenderer(); + + protected IMessagesForUser messagesForUser; + + private Component messagesContainer; + + private Grid listing; + + private Grid listingValues; + + private Label labelValue; + + private Popup pp; + + @Resource + private IGlobalViewEntryPoints globalView; + + @Override + public void doAfterCompose(Component comp) throws Exception { + super.doAfterCompose(comp); + comp.setAttribute("controller", this); + messagesForUser = new MessagesForUser(messagesContainer); + } + + public void goToEdit(SubcontractorCommunication subcontractorCommunication) { + if(subcontractorCommunication != null){ + TaskElement task = subcontractorCommunication.getSubcontractedTaskData().getTask(); + OrderElement orderElement = task.getOrderElement(); + Order order = subcontractorCommunicationModel.getOrder(orderElement); + + if(subcontractorCommunication.getCommunicationType().equals(CommunicationType.PROGRESS_UPDATE)){ + globalView.goToAdvanceTask(order,task); + }else{ + globalView.goToOrderDetails(order); + } + } + } + + public FilterCommunicationEnum[] getFilterItems(){ + return FilterCommunicationEnum.values(); + } + + public FilterCommunicationEnum getCurrentFilterItem() { + return subcontractorCommunicationModel.getCurrentFilter(); + } + + public void setCurrentFilterItem(FilterCommunicationEnum selected) { + subcontractorCommunicationModel.setCurrentFilter(selected); + refreshSubcontractorCommunicationsList(); + } + + private void refreshSubcontractorCommunicationsList() { + // update the subcontractor communication list + listing.setModel(new SimpleListModel(getSubcontractorCommunications())); + listing.invalidate(); + } + + protected void save(SubcontractorCommunication subcontractorCommunication) + throws ValidationException { + subcontractorCommunicationModel.confirmSave(subcontractorCommunication); + } + + public List getSubcontractorCommunications() { + FilterCommunicationEnum currentFilter = subcontractorCommunicationModel.getCurrentFilter(); + switch(currentFilter){ + case ALL: return subcontractorCommunicationModel.getSubcontractorAllCommunications(); + case NOT_REVIEWED: return subcontractorCommunicationModel.getSubcontractorCommunicationWithoutReviewed(); + default: return subcontractorCommunicationModel.getSubcontractorAllCommunications(); + } + } + + public SubcontractorCommunicationRenderer getSubcontractorCommunicationRenderer() { + return subcontractorCommunicationRenderer; + } + + private class SubcontractorCommunicationRenderer implements + RowRenderer { + + @Override + public void render(Row row, Object data) { + SubcontractorCommunication subcontractorCommunication = (SubcontractorCommunication) data; + row.setValue(subcontractorCommunication); + + final boolean reviewed = subcontractorCommunication.getReviewed(); + if(!reviewed){ + row.setSclass("communication-not-reviewed"); + } + appendLabel(row, subcontractorCommunication.getCommunicationType().toString()); + appendLabel(row, subcontractorCommunication.getSubcontractedTaskData().getTask().getName()); + appendLabel(row, getOrderName(subcontractorCommunication.getSubcontractedTaskData())); + appendLabel(row, getOrderCode(subcontractorCommunication.getSubcontractedTaskData())); + appendLabel(row, subcontractorCommunication.getSubcontractedTaskData().getExternalCompany().getName()); + appendLabel(row, toString(subcontractorCommunication.getCommunicationDate())); + appendLabelWithTooltip(row, subcontractorCommunication); + appendCheckbox(row, subcontractorCommunication); + appendOperations(row, subcontractorCommunication); + } + + private String toString(Date date) { + if (date == null) { + return ""; + } + + return new SimpleDateFormat("dd/MM/yyyy HH:mm").format(date); + } + + private String getOrderCode(SubcontractedTaskData subcontractedTaskData) { + return subcontractorCommunicationModel.getOrderCode(subcontractedTaskData); + } + + private String getOrderName(SubcontractedTaskData subcontractedTaskData) { + return subcontractorCommunicationModel.getOrderName(subcontractedTaskData); + } + + private String getLastValue( + SubcontractorCommunication subcontractorCommunication) { + SubcontractorCommunicationValue value = subcontractorCommunication + .getLastSubcontractorCommunicationValues(); + return (value != null) ? value.toString() : ""; + } + + private void appendLabel(Row row, String label) { + row.appendChild(new Label(label)); + } + + private void appendLabelWithTooltip(final Row row, + final SubcontractorCommunication subcontractorCommunication) { + String lastValue = getLastValue(subcontractorCommunication); + final Label compLabel = new Label(lastValue); + + if (subcontractorCommunication.getCommunicationType().equals( + CommunicationType.PROGRESS_UPDATE)) { + compLabel.setTooltip(pp); + compLabel.addEventListener(Events.ON_MOUSE_OVER, + new EventListener() { + @Override + public void onEvent(Event arg0) throws Exception { + List model = subcontractorCommunication + .getSubcontractorCommunicationValues(); + listingValues.setModel(new SimpleListModel(model)); + listingValues.invalidate(); + } + }); + } + row.appendChild(compLabel); + } + + private void appendCheckbox(final Row row, + final SubcontractorCommunication subcontractorCommunication) { + final Checkbox checkBoxReviewed = new Checkbox(); + checkBoxReviewed.setChecked(subcontractorCommunication.getReviewed()); + + checkBoxReviewed.addEventListener(Events.ON_CHECK, + new EventListener() { + + @Override + public void onEvent(Event arg0) throws Exception { + subcontractorCommunication.setReviewed(checkBoxReviewed.isChecked()); + save(subcontractorCommunication); + updateRowClass(row, checkBoxReviewed.isChecked()); + } + + }); + + row.appendChild(checkBoxReviewed); + } + + private void updateRowClass(final Row row, Boolean reviewed){ + row.setSclass(""); + if(!reviewed){ + row.setSclass("communication-not-reviewed"); + } + } + + private void appendOperations(Row row, + final SubcontractorCommunication subcontractorCommunication) { + Button buttonEdit = new Button(); + buttonEdit.setSclass("icono"); + buttonEdit.setImage("/common/img/ico_editar1.png"); + buttonEdit.setHoverImage("/common/img/ico_editar.png"); + buttonEdit.setTooltiptext(_("Edit")); + buttonEdit.addEventListener(Events.ON_CLICK, new EventListener() { + @Override + public void onEvent(Event arg0) throws Exception { + goToEdit(subcontractorCommunication); + } + }); + row.appendChild(buttonEdit); + } + } + + /** + * Apply filter to subcontractors communications + * + * @param event + */ + public void onApplyFilter(Event event) { + refreshSubcontractorCommunicationsList(); + } +} \ No newline at end of file diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/subcontract/SubcontractorCommunicationModel.java b/libreplan-webapp/src/main/java/org/libreplan/web/subcontract/SubcontractorCommunicationModel.java new file mode 100644 index 000000000..7f96c6db2 --- /dev/null +++ b/libreplan-webapp/src/main/java/org/libreplan/web/subcontract/SubcontractorCommunicationModel.java @@ -0,0 +1,126 @@ +/* + * This file is part of LibrePlan + * + * Copyright (C) 2011 WirelessGalicia, S.L. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.libreplan.web.subcontract; + +import java.util.List; + +import org.libreplan.business.orders.daos.IOrderDAO; +import org.libreplan.business.orders.entities.Order; +import org.libreplan.business.orders.entities.OrderElement; +import org.libreplan.business.planner.daos.ISubcontractorCommunicationDAO; +import org.libreplan.business.planner.entities.SubcontractedTaskData; +import org.libreplan.business.planner.entities.SubcontractorCommunication; +import org.libreplan.business.planner.entities.SubcontractorCommunicationValue; +import org.libreplan.business.planner.entities.Task; +import org.libreplan.web.common.concurrentdetection.OnConcurrentModification; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@Scope(BeanDefinition.SCOPE_PROTOTYPE) +@OnConcurrentModification(goToPage = "/subcontract/subcontractorCommunication.zul") +public class SubcontractorCommunicationModel implements ISubcontractorCommunicationModel{ + + @Autowired + private ISubcontractorCommunicationDAO subcontractorCommunicationDAO; + + @Autowired + IOrderDAO orderDAO; + + private FilterCommunicationEnum currentFilter = FilterCommunicationEnum.NOT_REVIEWED; + + @Override + @Transactional + public void confirmSave(SubcontractorCommunication subcontractorCommunication){ + subcontractorCommunicationDAO.save(subcontractorCommunication); + } + + @Override + @Transactional + public List getSubcontractorAllCommunications(){ + List list = subcontractorCommunicationDAO.getAll(); + forceLoadAssociatedData(list); + return list; + } + + @Override + @Transactional + public List getSubcontractorCommunicationWithoutReviewed(){ + List list = subcontractorCommunicationDAO.getAllNotReviewed(); + forceLoadAssociatedData(list); + return list; + } + + private void forceLoadAssociatedData(List subcontractorCommunicationList){ + if (subcontractorCommunicationList != null) { + for (SubcontractorCommunication subcontractorCommunication : subcontractorCommunicationList) { + subcontractorCommunication.getSubcontractedTaskData().getExternalCompany().getName(); + subcontractorCommunication.getSubcontractedTaskData().getTask().getName(); + subcontractorCommunication.getSubcontractedTaskData().getTask().getOrderElement().getName(); + subcontractorCommunication.getLastSubcontractorCommunicationValues().getDate(); + } + } + } + + private void forceLoadAssociatedDataValue(List subcontractorCommunicationValueList){ + if (subcontractorCommunicationValueList != null) { + for (SubcontractorCommunicationValue value : subcontractorCommunicationValueList) { + value.getDate(); + } + } + } + + @Override + @Transactional(readOnly = true) + public String getOrderCode(SubcontractedTaskData subcontractedTaskData) { + Task task = subcontractedTaskData.getTask(); + OrderElement orderElement = orderDAO.loadOrderAvoidingProxyFor(task + .getOrderElement()); + return orderElement.getOrder().getCode(); + } + + @Override + @Transactional(readOnly = true) + public String getOrderName(SubcontractedTaskData subcontractedTaskData) { + Task task = subcontractedTaskData.getTask(); + OrderElement orderElement = orderDAO.loadOrderAvoidingProxyFor(task + .getOrderElement()); + return orderElement.getOrder().getName(); + } + + @Override + public void setCurrentFilter(FilterCommunicationEnum currentFilter) { + this.currentFilter = currentFilter; + } + + @Override + public FilterCommunicationEnum getCurrentFilter() { + return currentFilter; + } + + @Override + @Transactional(readOnly = true) + public Order getOrder(OrderElement orderElement) { + return (orderDAO.loadOrderAvoidingProxyFor(orderElement)).getOrder(); + } +} \ No newline at end of file diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/subcontract/UpdateDeliveringDateDTO.java b/libreplan-webapp/src/main/java/org/libreplan/web/subcontract/UpdateDeliveringDateDTO.java new file mode 100644 index 000000000..0a5d139a5 --- /dev/null +++ b/libreplan-webapp/src/main/java/org/libreplan/web/subcontract/UpdateDeliveringDateDTO.java @@ -0,0 +1,61 @@ +/* + * This file is part of LibrePlan + * + * Copyright (C) 2011 WirelessGalicia, S.L. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.libreplan.web.subcontract; + +import java.util.Date; + +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.datatype.XMLGregorianCalendar; + +import org.libreplan.business.planner.entities.SubcontractedTaskData; +import org.libreplan.ws.common.impl.DateConverter; + +/** + * DTO UpdateDeliveringDate + * + * @author Susana Montes Pedreira + */ +@XmlRootElement(name = "update-delivering-date") +public class UpdateDeliveringDateDTO { + + @XmlAttribute(name = "customer-reference") + public String customerReference; + + @XmlAttribute(name = "external-code") + public String externalCode; + + @XmlAttribute(name = "external-company-nif") + public String companyNif; + + @XmlAttribute(name = "deliver-date") + public XMLGregorianCalendar deliverDate; + + public UpdateDeliveringDateDTO(){ + } + + public UpdateDeliveringDateDTO(String customerReference, + String externalCode, String companyNif, XMLGregorianCalendar deliverDate) { + this.customerReference = customerReference; + this.deliverDate = deliverDate; + this.companyNif = companyNif; + this.externalCode = externalCode; + } +} diff --git a/libreplan-webapp/src/main/java/org/libreplan/ws/common/impl/OrderElementConverter.java b/libreplan-webapp/src/main/java/org/libreplan/ws/common/impl/OrderElementConverter.java index 977973105..f5aac30f8 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/ws/common/impl/OrderElementConverter.java +++ b/libreplan-webapp/src/main/java/org/libreplan/ws/common/impl/OrderElementConverter.java @@ -43,6 +43,7 @@ import org.libreplan.business.calendars.entities.BaseCalendar; import org.libreplan.business.common.Registry; import org.libreplan.business.common.exceptions.InstanceNotFoundException; import org.libreplan.business.common.exceptions.ValidationException; +import org.libreplan.business.externalcompanies.entities.EndDateCommunication; import org.libreplan.business.labels.entities.Label; import org.libreplan.business.materials.bootstrap.PredefinedMaterialCategories; import org.libreplan.business.materials.entities.Material; @@ -74,6 +75,7 @@ import org.libreplan.ws.common.api.OrderElementDTO; import org.libreplan.ws.common.api.OrderLineDTO; import org.libreplan.ws.common.api.OrderLineGroupDTO; import org.libreplan.ws.common.api.ResourceEnumDTO; +import org.libreplan.ws.subcontract.api.EndDateCommunicationToCustomerDTO; /** * Converter from/to {@link OrderElement} entities to/from DTOs. @@ -846,4 +848,26 @@ public final class OrderElementConverter { .getValue()); } + public static EndDateCommunication toEntity( + EndDateCommunicationToCustomerDTO endDateCommunicationToCustomerDTO) { + Date endDate = DateConverter.toDate(endDateCommunicationToCustomerDTO.endDate); + Date communicationDate = DateConverter + .toDate(endDateCommunicationToCustomerDTO.communicationDate); + Date saveDate = DateConverter.toDate(endDateCommunicationToCustomerDTO.saveDate); + EndDateCommunication endDateCommunicationToCustomer = EndDateCommunication + .create(saveDate, endDate, communicationDate); + return endDateCommunicationToCustomer; + } + + public static EndDateCommunicationToCustomerDTO toDTO( + EndDateCommunication endDateCommunicationToCustomer) { + XMLGregorianCalendar endDate = DateConverter + .toXMLGregorianCalendar(endDateCommunicationToCustomer.getEndDate()); + XMLGregorianCalendar saveDate = DateConverter + .toXMLGregorianCalendar(endDateCommunicationToCustomer.getSaveDate()); + XMLGregorianCalendar communicationDate = DateConverter + .toXMLGregorianCalendar(endDateCommunicationToCustomer.getCommunicationDate()); + return new EndDateCommunicationToCustomerDTO(saveDate, endDate, communicationDate); + } + } diff --git a/libreplan-webapp/src/main/java/org/libreplan/ws/subcontract/api/EndDateCommunicationToCustomerDTO.java b/libreplan-webapp/src/main/java/org/libreplan/ws/subcontract/api/EndDateCommunicationToCustomerDTO.java new file mode 100644 index 000000000..632b7fa5a --- /dev/null +++ b/libreplan-webapp/src/main/java/org/libreplan/ws/subcontract/api/EndDateCommunicationToCustomerDTO.java @@ -0,0 +1,55 @@ +/* + * This file is part of LibrePlan + * + * Copyright (C) 2012 WirelessGalicia, S.L. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.libreplan.ws.subcontract.api; + +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.datatype.XMLGregorianCalendar; + +import org.libreplan.business.externalcompanies.entities.EndDateCommunication; + +/** + * DTO for {@link EndDateCommunication} just with information about end date asked by subcontractors + * + * @author Susana Montes Pedreira + */ + +public class EndDateCommunicationToCustomerDTO { + + @XmlAttribute + public XMLGregorianCalendar communicationDate; + + @XmlAttribute + public XMLGregorianCalendar endDate; + + @XmlAttribute + public XMLGregorianCalendar saveDate; + + public EndDateCommunicationToCustomerDTO() { + + } + + public EndDateCommunicationToCustomerDTO(XMLGregorianCalendar saveDate, + XMLGregorianCalendar endDate, + XMLGregorianCalendar communicationDate) { + this.saveDate = saveDate; + this.communicationDate = communicationDate; + this.endDate = endDate; + } +} diff --git a/libreplan-webapp/src/main/java/org/libreplan/ws/subcontract/api/IReportAdvancesService.java b/libreplan-webapp/src/main/java/org/libreplan/ws/subcontract/api/IReportAdvancesService.java index 85d181f73..0e82201a9 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/ws/subcontract/api/IReportAdvancesService.java +++ b/libreplan-webapp/src/main/java/org/libreplan/ws/subcontract/api/IReportAdvancesService.java @@ -30,7 +30,7 @@ import org.libreplan.ws.common.api.InstanceConstraintViolationsListDTO; */ public interface IReportAdvancesService { - InstanceConstraintViolationsListDTO updateAdvances( - OrderElementWithAdvanceMeasurementsListDTO orderElementWithAdvanceMeasurementsListDTO); + InstanceConstraintViolationsListDTO updateAdvancesOrEndDate( + OrderElementWithAdvanceMeasurementsOrEndDateListDTO orderElementWithAdvanceMeasurementsListDTO); } diff --git a/libreplan-webapp/src/main/java/org/libreplan/ws/subcontract/api/ISubcontractService.java b/libreplan-webapp/src/main/java/org/libreplan/ws/subcontract/api/ISubcontractService.java index a7a214329..c622b1196 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/ws/subcontract/api/ISubcontractService.java +++ b/libreplan-webapp/src/main/java/org/libreplan/ws/subcontract/api/ISubcontractService.java @@ -21,6 +21,7 @@ package org.libreplan.ws.subcontract.api; +import org.libreplan.web.subcontract.UpdateDeliveringDateDTO; import org.libreplan.ws.common.api.InstanceConstraintViolationsListDTO; @@ -34,4 +35,8 @@ public interface ISubcontractService { InstanceConstraintViolationsListDTO subcontract( SubcontractedTaskDataDTO subcontractedTaskDataDTO); + + InstanceConstraintViolationsListDTO updateDeliveringDates( + UpdateDeliveringDateDTO updateDeliveringDateDTO); + } \ No newline at end of file diff --git a/libreplan-webapp/src/main/java/org/libreplan/ws/subcontract/api/OrderElementWithAdvanceMeasurementsDTO.java b/libreplan-webapp/src/main/java/org/libreplan/ws/subcontract/api/OrderElementWithAdvanceMeasurementsOrEndDateDTO.java similarity index 76% rename from libreplan-webapp/src/main/java/org/libreplan/ws/subcontract/api/OrderElementWithAdvanceMeasurementsDTO.java rename to libreplan-webapp/src/main/java/org/libreplan/ws/subcontract/api/OrderElementWithAdvanceMeasurementsOrEndDateDTO.java index e6f43303b..0052b5954 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/ws/subcontract/api/OrderElementWithAdvanceMeasurementsDTO.java +++ b/libreplan-webapp/src/main/java/org/libreplan/ws/subcontract/api/OrderElementWithAdvanceMeasurementsOrEndDateDTO.java @@ -38,7 +38,7 @@ import org.libreplan.ws.common.api.AdvanceMeasurementDTO; * @author Manuel Rego Casasnovas */ @XmlRootElement(name = "order-element") -public class OrderElementWithAdvanceMeasurementsDTO { +public class OrderElementWithAdvanceMeasurementsOrEndDateDTO { @XmlAttribute public String code; @@ -47,13 +47,18 @@ public class OrderElementWithAdvanceMeasurementsDTO { @XmlElement(name = "advance-measurement") public Set advanceMeasurements = new HashSet(); - public OrderElementWithAdvanceMeasurementsDTO() { + @XmlElement(name = "end-date-communication-to-customer") + public EndDateCommunicationToCustomerDTO endDateCommunicationToCustomerDTO; + + public OrderElementWithAdvanceMeasurementsOrEndDateDTO() { } - public OrderElementWithAdvanceMeasurementsDTO(String code, - Set advanceMeasurements) { + public OrderElementWithAdvanceMeasurementsOrEndDateDTO(String code, + Set advanceMeasurements, + EndDateCommunicationToCustomerDTO endDateCommunicationToCustomerDTO) { this.code = code; this.advanceMeasurements = advanceMeasurements; + this.endDateCommunicationToCustomerDTO = endDateCommunicationToCustomerDTO; } } diff --git a/libreplan-webapp/src/main/java/org/libreplan/ws/subcontract/api/OrderElementWithAdvanceMeasurementsListDTO.java b/libreplan-webapp/src/main/java/org/libreplan/ws/subcontract/api/OrderElementWithAdvanceMeasurementsOrEndDateListDTO.java similarity index 78% rename from libreplan-webapp/src/main/java/org/libreplan/ws/subcontract/api/OrderElementWithAdvanceMeasurementsListDTO.java rename to libreplan-webapp/src/main/java/org/libreplan/ws/subcontract/api/OrderElementWithAdvanceMeasurementsOrEndDateListDTO.java index 7c2e7952d..9dbe77abc 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/ws/subcontract/api/OrderElementWithAdvanceMeasurementsListDTO.java +++ b/libreplan-webapp/src/main/java/org/libreplan/ws/subcontract/api/OrderElementWithAdvanceMeasurementsOrEndDateListDTO.java @@ -36,19 +36,19 @@ import org.libreplan.business.orders.entities.OrderElement; * @author Manuel Rego Casasnovas */ @XmlRootElement(name = "order-element-list") -public class OrderElementWithAdvanceMeasurementsListDTO { +public class OrderElementWithAdvanceMeasurementsOrEndDateListDTO { @XmlAttribute(name = "external-company-nif") public String externalCompanyNif; @XmlElement(name = "order-element") - public List orderElements = new ArrayList(); + public List orderElements = new ArrayList(); - public OrderElementWithAdvanceMeasurementsListDTO() { + public OrderElementWithAdvanceMeasurementsOrEndDateListDTO() { } - public OrderElementWithAdvanceMeasurementsListDTO(String externalCompanyNif, - List orderElements) { + public OrderElementWithAdvanceMeasurementsOrEndDateListDTO(String externalCompanyNif, + List orderElements) { this.externalCompanyNif = externalCompanyNif; this.orderElements = orderElements; } diff --git a/libreplan-webapp/src/main/java/org/libreplan/ws/subcontract/api/SubcontractedTaskDataDTO.java b/libreplan-webapp/src/main/java/org/libreplan/ws/subcontract/api/SubcontractedTaskDataDTO.java index bc73fde3f..04a653480 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/ws/subcontract/api/SubcontractedTaskDataDTO.java +++ b/libreplan-webapp/src/main/java/org/libreplan/ws/subcontract/api/SubcontractedTaskDataDTO.java @@ -27,6 +27,7 @@ import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElements; import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.datatype.XMLGregorianCalendar; import org.libreplan.business.planner.entities.SubcontractedTaskData; import org.libreplan.ws.common.api.OrderDTO; @@ -54,6 +55,9 @@ public class SubcontractedTaskDataDTO { @XmlAttribute(name = "subcontracted-code") public String subcontractedCode; + @XmlAttribute(name = "deliver-date") + public XMLGregorianCalendar deliverDate; + @XmlElements( { @XmlElement(name = "order-line", type = OrderLineDTO.class), @XmlElement(name = "order-line-group", type = OrderLineGroupDTO.class), @@ -65,12 +69,14 @@ public class SubcontractedTaskDataDTO { public SubcontractedTaskDataDTO(String externalCompanyNif, String workDescription, BigDecimal subcontractPrice, - String subcontractedCode, OrderElementDTO orderElementDTO) { + String subcontractedCode, OrderElementDTO orderElementDTO, + XMLGregorianCalendar deliverDate) { this.externalCompanyNif = externalCompanyNif; this.workDescription = workDescription; this.subcontractPrice = subcontractPrice; this.subcontractedCode = subcontractedCode; this.orderElementDTO = orderElementDTO; + this.deliverDate = deliverDate; } } diff --git a/libreplan-webapp/src/main/java/org/libreplan/ws/subcontract/impl/ReportAdvancesServiceREST.java b/libreplan-webapp/src/main/java/org/libreplan/ws/subcontract/impl/ReportAdvancesServiceREST.java index 030218bd9..bb2c7df23 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/ws/subcontract/impl/ReportAdvancesServiceREST.java +++ b/libreplan-webapp/src/main/java/org/libreplan/ws/subcontract/impl/ReportAdvancesServiceREST.java @@ -5,6 +5,8 @@ * Desenvolvemento Tecnolóxico de Galicia * Copyright (C) 2010-2011 Igalia, S.L. * + * Copyright (C) 2011 WirelessGalicia, S.L. + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or @@ -24,7 +26,9 @@ package org.libreplan.ws.subcontract.impl; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Arrays; +import java.util.Date; import java.util.List; +import java.util.Set; import javax.ws.rs.Consumes; import javax.ws.rs.POST; @@ -41,11 +45,19 @@ import org.libreplan.business.advance.exceptions.DuplicateValueTrueReportGlobalA import org.libreplan.business.common.exceptions.InstanceNotFoundException; import org.libreplan.business.common.exceptions.ValidationException; import org.libreplan.business.externalcompanies.daos.IExternalCompanyDAO; +import org.libreplan.business.externalcompanies.entities.CommunicationType; +import org.libreplan.business.externalcompanies.entities.EndDateCommunication; import org.libreplan.business.externalcompanies.entities.ExternalCompany; import org.libreplan.business.orders.daos.IOrderDAO; import org.libreplan.business.orders.daos.IOrderElementDAO; import org.libreplan.business.orders.entities.Order; import org.libreplan.business.orders.entities.OrderElement; +import org.libreplan.business.planner.daos.ISubcontractedTaskDataDAO; +import org.libreplan.business.planner.daos.ISubcontractorCommunicationDAO; +import org.libreplan.business.planner.entities.SubcontractedTaskData; +import org.libreplan.business.planner.entities.SubcontractorCommunication; +import org.libreplan.business.planner.entities.SubcontractorCommunicationValue; +import org.libreplan.business.planner.entities.Task; import org.libreplan.business.scenarios.bootstrap.PredefinedScenarios; import org.libreplan.business.scenarios.entities.OrderVersion; import org.libreplan.business.scenarios.entities.Scenario; @@ -56,9 +68,10 @@ import org.libreplan.ws.common.impl.ConstraintViolationConverter; import org.libreplan.ws.common.impl.DateConverter; import org.libreplan.ws.common.impl.OrderElementConverter; import org.libreplan.ws.common.impl.Util; +import org.libreplan.ws.subcontract.api.EndDateCommunicationToCustomerDTO; import org.libreplan.ws.subcontract.api.IReportAdvancesService; -import org.libreplan.ws.subcontract.api.OrderElementWithAdvanceMeasurementsDTO; -import org.libreplan.ws.subcontract.api.OrderElementWithAdvanceMeasurementsListDTO; +import org.libreplan.ws.subcontract.api.OrderElementWithAdvanceMeasurementsOrEndDateDTO; +import org.libreplan.ws.subcontract.api.OrderElementWithAdvanceMeasurementsOrEndDateListDTO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -67,6 +80,7 @@ import org.springframework.transaction.annotation.Transactional; * REST-based implementation of {@link IReportAdvancesService}. * * @author Manuel Rego Casasnovas + * @author Susana Montes Pedreira */ @Path("/reportadvances/") @Produces("application/xml") @@ -76,32 +90,37 @@ public class ReportAdvancesServiceREST implements IReportAdvancesService { @Autowired private IOrderElementDAO orderElementDAO; + @Autowired + private ISubcontractedTaskDataDAO subcontractedTaskDataDAO; + + @Autowired + private ISubcontractorCommunicationDAO subcontractorCommunicationDAO; + @Autowired private IOrderDAO orderDAO; @Autowired private IExternalCompanyDAO externalCompanyDAO; - private InstanceConstraintViolationsListDTO getErrorMessage(String code, - String message) { + private InstanceConstraintViolationsListDTO getErrorMessage(String code, String message) { // FIXME review errors returned - return new InstanceConstraintViolationsListDTO(Arrays - .asList(InstanceConstraintViolationsDTO.create(Util - .generateInstanceId(1, code), message))); + return new InstanceConstraintViolationsListDTO( + Arrays.asList(InstanceConstraintViolationsDTO.create( + Util.generateInstanceId(1, code), message))); } @Override @POST @Consumes("application/xml") @Transactional - public InstanceConstraintViolationsListDTO updateAdvances(OrderElementWithAdvanceMeasurementsListDTO orderElementWithAdvanceMeasurementsListDTO) { + public InstanceConstraintViolationsListDTO updateAdvancesOrEndDate( + OrderElementWithAdvanceMeasurementsOrEndDateListDTO orderElementWithAdvanceMeasurementsListDTO) { List instanceConstraintViolationsList = new ArrayList(); InstanceConstraintViolationsDTO instanceConstraintViolationsDTO = null; - if (StringUtils - .isEmpty(orderElementWithAdvanceMeasurementsListDTO.externalCompanyNif)) { + if (StringUtils.isEmpty(orderElementWithAdvanceMeasurementsListDTO.externalCompanyNif)) { return getErrorMessage("", "external company ID not specified"); } @@ -110,109 +129,203 @@ public class ReportAdvancesServiceREST implements IReportAdvancesService { externalCompany = externalCompanyDAO .findUniqueByNif(orderElementWithAdvanceMeasurementsListDTO.externalCompanyNif); } catch (InstanceNotFoundException e1) { - return getErrorMessage( - orderElementWithAdvanceMeasurementsListDTO.externalCompanyNif, + return getErrorMessage(orderElementWithAdvanceMeasurementsListDTO.externalCompanyNif, "external company not found"); } if (!externalCompany.isSubcontractor()) { - return getErrorMessage( - orderElementWithAdvanceMeasurementsListDTO.externalCompanyNif, + return getErrorMessage(orderElementWithAdvanceMeasurementsListDTO.externalCompanyNif, "external company is not registered as subcontractor"); } - List orderElements = orderElementWithAdvanceMeasurementsListDTO.orderElements; - for (OrderElementWithAdvanceMeasurementsDTO orderElementWithAdvanceMeasurementsDTO : orderElements) { + List orderElements = orderElementWithAdvanceMeasurementsListDTO.orderElements; + for (OrderElementWithAdvanceMeasurementsOrEndDateDTO orderElementWithAdvanceMeasurementsOrEndDateDTO : orderElements) { try { OrderElement orderElement = orderElementDAO - .findUniqueByCode(orderElementWithAdvanceMeasurementsDTO.code); + .findUniqueByCode(orderElementWithAdvanceMeasurementsOrEndDateDTO.code); - DirectAdvanceAssignment advanceAssignmentSubcontractor = orderElement - .getDirectAdvanceAssignmentSubcontractor(); - - if (advanceAssignmentSubcontractor == null) { - DirectAdvanceAssignment reportGlobal = orderElement - .getReportGlobalAdvanceAssignment(); - - advanceAssignmentSubcontractor = DirectAdvanceAssignment - .create((reportGlobal == null), new BigDecimal(100)); - advanceAssignmentSubcontractor - .setAdvanceType(PredefinedAdvancedTypes.SUBCONTRACTOR - .getType()); - advanceAssignmentSubcontractor - .setOrderElement(orderElement); - - try { - orderElement - .addAdvanceAssignment(advanceAssignmentSubcontractor); - } catch (DuplicateValueTrueReportGlobalAdvanceException e) { - // This shouldn't happen, because new advance is only - // marked as report global if there is not other advance - // as report global - throw new RuntimeException(e); - } catch (DuplicateAdvanceAssignmentForOrderElementException e) { - return getErrorMessage( - orderElementWithAdvanceMeasurementsDTO.code, - "someone in the same branch has the same advance type"); - } - } - - for (AdvanceMeasurementDTO advanceMeasurementDTO : orderElementWithAdvanceMeasurementsDTO.advanceMeasurements) { - AdvanceMeasurement advanceMeasurement = advanceAssignmentSubcontractor - .getAdvanceMeasurementAtExactDate(DateConverter - .toLocalDate(advanceMeasurementDTO.date)); - if (advanceMeasurement == null) { - advanceAssignmentSubcontractor - .addAdvanceMeasurements(OrderElementConverter - .toEntity(advanceMeasurementDTO)); - } else { - advanceMeasurement - .setValue(advanceMeasurementDTO.value); - } - } - - // set the advance assingment subcontractor like spread - AdvanceAssignment spreadAdvance = orderElement - .getReportGlobalAdvanceAssignment(); - if (spreadAdvance != null - && !spreadAdvance - .equals(advanceAssignmentSubcontractor)) { - spreadAdvance.setReportGlobalAdvance(false); - advanceAssignmentSubcontractor.setReportGlobalAdvance(true); - } - // update the advance percentage in its related task - Scenario scenarioMaster = PredefinedScenarios.MASTER - .getScenario(); + Scenario scenarioMaster = PredefinedScenarios.MASTER.getScenario(); Order order = orderDAO.loadOrderAvoidingProxyFor(orderElement); - OrderVersion orderVersion = order.getScenarios().get( - scenarioMaster); - updateAdvancePercentage(orderVersion, orderElement); + OrderVersion orderVersion = order.getScenarios().get(scenarioMaster); + + if (orderElementWithAdvanceMeasurementsOrEndDateDTO.advanceMeasurements != null + && !orderElementWithAdvanceMeasurementsOrEndDateDTO.advanceMeasurements + .isEmpty()) { + updateAdvances(orderVersion, orderElement, + orderElementWithAdvanceMeasurementsOrEndDateDTO); + } + + if (orderElementWithAdvanceMeasurementsOrEndDateDTO.endDateCommunicationToCustomerDTO != null) { + updateEndDate(orderVersion, orderElement, + orderElementWithAdvanceMeasurementsOrEndDateDTO); + } - orderElement.validate(); - orderElementDAO.save(orderElement); } catch (ValidationException e) { - instanceConstraintViolationsDTO = ConstraintViolationConverter - .toDTO(Util.generateInstanceId(1, - orderElementWithAdvanceMeasurementsDTO.code), e + instanceConstraintViolationsDTO = ConstraintViolationConverter.toDTO( + Util.generateInstanceId(1, + orderElementWithAdvanceMeasurementsOrEndDateDTO.code), e .getInvalidValues()); } catch (InstanceNotFoundException e) { - return getErrorMessage( - orderElementWithAdvanceMeasurementsDTO.code, + return getErrorMessage(orderElementWithAdvanceMeasurementsOrEndDateDTO.code, "instance not found"); } } if (instanceConstraintViolationsDTO != null) { - instanceConstraintViolationsList - .add(instanceConstraintViolationsDTO); + instanceConstraintViolationsList.add(instanceConstraintViolationsDTO); } - return new InstanceConstraintViolationsListDTO( - instanceConstraintViolationsList); + return new InstanceConstraintViolationsListDTO(instanceConstraintViolationsList); } - private void updateAdvancePercentage(OrderVersion orderVersion, - OrderElement orderElement) { + @Transactional + public InstanceConstraintViolationsListDTO updateAdvances( + OrderVersion orderVersion, + OrderElement orderElement, + OrderElementWithAdvanceMeasurementsOrEndDateDTO orderElementWithAdvanceMeasurementsOrEndDateDTO) { + DirectAdvanceAssignment advanceAssignmentSubcontractor = orderElement + .getDirectAdvanceAssignmentSubcontractor(); + + if (advanceAssignmentSubcontractor == null) { + DirectAdvanceAssignment reportGlobal = orderElement.getReportGlobalAdvanceAssignment(); + + advanceAssignmentSubcontractor = DirectAdvanceAssignment.create((reportGlobal == null), + new BigDecimal(100)); + advanceAssignmentSubcontractor.setAdvanceType(PredefinedAdvancedTypes.SUBCONTRACTOR + .getType()); + advanceAssignmentSubcontractor.setOrderElement(orderElement); + + try { + orderElement.addAdvanceAssignment(advanceAssignmentSubcontractor); + } catch (DuplicateValueTrueReportGlobalAdvanceException e) { + // This shouldn't happen, because new advance is only + // marked as report global if there is not other advance + // as report global + throw new RuntimeException(e); + } catch (DuplicateAdvanceAssignmentForOrderElementException e) { + return getErrorMessage(orderElementWithAdvanceMeasurementsOrEndDateDTO.code, + "someone in the same branch has the same advance type"); + } + } + + for (AdvanceMeasurementDTO advanceMeasurementDTO : orderElementWithAdvanceMeasurementsOrEndDateDTO.advanceMeasurements) { + AdvanceMeasurement advanceMeasurement = advanceAssignmentSubcontractor + .getAdvanceMeasurementAtExactDate(DateConverter + .toLocalDate(advanceMeasurementDTO.date)); + if (advanceMeasurement == null) { + advanceAssignmentSubcontractor.addAdvanceMeasurements(OrderElementConverter + .toEntity(advanceMeasurementDTO)); + } else { + advanceMeasurement.setValue(advanceMeasurementDTO.value); + } + } + + // set the advance assingment subcontractor like spread + AdvanceAssignment spreadAdvance = orderElement.getReportGlobalAdvanceAssignment(); + if (spreadAdvance != null && !spreadAdvance.equals(advanceAssignmentSubcontractor)) { + spreadAdvance.setReportGlobalAdvance(false); + advanceAssignmentSubcontractor.setReportGlobalAdvance(true); + } + // update the advance percentage in its related task + updateAdvancePercentage(orderVersion, orderElement); + + orderElement.validate(); + orderElementDAO.save(orderElement); + + /* + * If the order element is subcontrated then create the subcontrated + * communication for the subcontrated task data to which the order + * element belongs. + */ + try { + createSubcontractorCommunicationWithNewProgress(orderElement, + orderElementWithAdvanceMeasurementsOrEndDateDTO.advanceMeasurements); + } catch (InstanceNotFoundException e) { + return getErrorMessage(orderElementWithAdvanceMeasurementsOrEndDateDTO.code, + "instance not found"); + } + + return null; + } + + public void createSubcontractorCommunicationWithNewProgress(OrderElement orderElement, + Set advanceMeasurementDTOs) throws InstanceNotFoundException { + if (orderElement != null && orderElement.getTaskSource() != null + && orderElement.getTaskSource().getTask().isSubcontracted()) { + Task task = (Task) orderElement.getTaskSource().getTask(); + SubcontractedTaskData subcontractedTaskData = task.getSubcontractedTaskData(); + if (subcontractedTaskData != null) { + SubcontractorCommunication subcontractorCommunication = SubcontractorCommunication + .create(subcontractedTaskData, CommunicationType.PROGRESS_UPDATE, + new Date(), false); + + for (AdvanceMeasurementDTO advanceMeasurementDTO : advanceMeasurementDTOs) { + // add subcontractorCommunicationValue + addSubcontractorCommunicationValue(advanceMeasurementDTO, + subcontractorCommunication); + } + subcontractorCommunicationDAO.save(subcontractorCommunication); + } + } + } + + @Transactional + public InstanceConstraintViolationsListDTO updateEndDate( + OrderVersion orderVersion, + OrderElement orderElement, + OrderElementWithAdvanceMeasurementsOrEndDateDTO orderElementWithAdvanceMeasurementsOrEndDateDTO) { + try { + orderElement.useSchedulingDataFor(orderVersion); + + if (orderElement != null && orderElement.getTaskSource() != null + && orderElement.getTaskSource().getTask().isSubcontracted()) { + + Task task = (Task) orderElement.getTaskSource().getTask(); + SubcontractedTaskData subcontractedTaskData = task.getSubcontractedTaskData(); + EndDateCommunicationToCustomerDTO endDateDTO = orderElementWithAdvanceMeasurementsOrEndDateDTO.endDateCommunicationToCustomerDTO; + + Date endDate = DateConverter.toDate(endDateDTO.endDate); + Date communicationDate = DateConverter.toDate(endDateDTO.communicationDate); + + subcontractedTaskData.getEndDatesCommunicatedFromSubcontractor().add( + EndDateCommunication.create(new Date(), endDate, + communicationDate)); + subcontractedTaskDataDAO.save(subcontractedTaskData); + + createSubcontractorCommunicationWithNewEndDate(subcontractedTaskData, endDateDTO); + } + } catch (InstanceNotFoundException e) { + return getErrorMessage(orderElementWithAdvanceMeasurementsOrEndDateDTO.code, + "instance not found"); + } + return null; + } + + public void createSubcontractorCommunicationWithNewEndDate( + SubcontractedTaskData subcontractedTaskData, + EndDateCommunicationToCustomerDTO endDateDTO) throws InstanceNotFoundException { + + if (subcontractedTaskData != null) { + SubcontractorCommunication subcontractorCommunication = SubcontractorCommunication + .create(subcontractedTaskData, CommunicationType.END_DATE_UPDATE, + new Date(), false); + Date dateValue = DateConverter.toDate(endDateDTO.endDate); + SubcontractorCommunicationValue value = SubcontractorCommunicationValue.create( + dateValue, null); + subcontractorCommunication.getSubcontractorCommunicationValues().add(value); + subcontractorCommunicationDAO.save(subcontractorCommunication); + } + } + + private void addSubcontractorCommunicationValue(AdvanceMeasurementDTO advanceMeasurementDTO, + SubcontractorCommunication subcontractorCommunication) { + Date dateValue = DateConverter.toDate(advanceMeasurementDTO.date); + SubcontractorCommunicationValue value = SubcontractorCommunicationValue.create(dateValue, + advanceMeasurementDTO.value); + subcontractorCommunication.getSubcontractorCommunicationValues().add(value); + } + + private void updateAdvancePercentage(OrderVersion orderVersion, OrderElement orderElement) { orderElement.useSchedulingDataFor(orderVersion); OrderElement parent = orderElement.getParent(); while (parent != null) { diff --git a/libreplan-webapp/src/main/java/org/libreplan/ws/subcontract/impl/SubcontractServiceREST.java b/libreplan-webapp/src/main/java/org/libreplan/ws/subcontract/impl/SubcontractServiceREST.java index 6d4055770..a65a577f4 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/ws/subcontract/impl/SubcontractServiceREST.java +++ b/libreplan-webapp/src/main/java/org/libreplan/ws/subcontract/impl/SubcontractServiceREST.java @@ -5,6 +5,8 @@ * Desenvolvemento Tecnolóxico de Galicia * Copyright (C) 2010-2011 Igalia, S.L. * + * Copyright (C) 2011 WirelessGalicia, S.L. + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or @@ -22,6 +24,7 @@ package org.libreplan.ws.subcontract.impl; import java.util.Arrays; +import java.util.Date; import java.util.List; import javax.ws.rs.Consumes; @@ -32,6 +35,7 @@ import javax.ws.rs.Produces; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.Validate; import org.hibernate.NonUniqueResultException; +import org.joda.time.LocalDate; import org.libreplan.business.calendars.entities.BaseCalendar; import org.libreplan.business.common.IAdHocTransactionService; import org.libreplan.business.common.IOnTransaction; @@ -42,7 +46,11 @@ import org.libreplan.business.common.entities.EntityNameEnum; import org.libreplan.business.common.entities.EntitySequence; import org.libreplan.business.common.exceptions.InstanceNotFoundException; import org.libreplan.business.common.exceptions.ValidationException; +import org.libreplan.business.externalcompanies.daos.ICustomerCommunicationDAO; import org.libreplan.business.externalcompanies.daos.IExternalCompanyDAO; +import org.libreplan.business.externalcompanies.entities.CommunicationType; +import org.libreplan.business.externalcompanies.entities.CustomerCommunication; +import org.libreplan.business.externalcompanies.entities.DeadlineCommunication; import org.libreplan.business.externalcompanies.entities.ExternalCompany; import org.libreplan.business.orders.daos.IOrderElementDAO; import org.libreplan.business.orders.entities.Order; @@ -55,6 +63,7 @@ import org.libreplan.business.planner.daos.ITaskSourceDAO; import org.libreplan.business.scenarios.daos.IScenarioDAO; import org.libreplan.business.scenarios.entities.OrderVersion; import org.libreplan.business.scenarios.entities.Scenario; +import org.libreplan.web.subcontract.UpdateDeliveringDateDTO; import org.libreplan.ws.common.api.InstanceConstraintViolationsDTO; import org.libreplan.ws.common.api.InstanceConstraintViolationsDTOId; import org.libreplan.ws.common.api.InstanceConstraintViolationsListDTO; @@ -62,6 +71,7 @@ import org.libreplan.ws.common.api.OrderDTO; import org.libreplan.ws.common.api.OrderElementDTO; import org.libreplan.ws.common.impl.ConfigurationOrderElementConverter; import org.libreplan.ws.common.impl.ConstraintViolationConverter; +import org.libreplan.ws.common.impl.DateConverter; import org.libreplan.ws.common.impl.OrderElementConverter; import org.libreplan.ws.common.impl.Util; import org.libreplan.ws.subcontract.api.ISubcontractService; @@ -73,6 +83,7 @@ import org.springframework.stereotype.Service; * REST-based implementation of {@link ISubcontractService} * * @author Manuel Rego Casasnovas + * @author Susana Montes Pedreira */ @Path("/subcontract/") @Produces("application/xml") @@ -94,6 +105,9 @@ public class SubcontractServiceREST implements ISubcontractService { @Autowired private IScenarioDAO scenarioDAO; + @Autowired + private ICustomerCommunicationDAO customerCommunicationDAO; + @Autowired private ITaskSourceDAO taskSourceDAO; @@ -123,6 +137,104 @@ public class SubcontractServiceREST implements ISubcontractService { @Override @POST + @Path("update/") + @Consumes("application/xml") + public InstanceConstraintViolationsListDTO updateDeliveringDates( + final UpdateDeliveringDateDTO updateDeliveringDateDTO) { + try { + updateSubcontract(updateDeliveringDateDTO); + } catch (ViolationError e) { + return e.toViolationList(); + } + return new InstanceConstraintViolationsListDTO(); + } + + + private void updateSubcontract( + final UpdateDeliveringDateDTO updateDeliveringDateDTO) { + + if (StringUtils.isEmpty(updateDeliveringDateDTO.companyNif)) { + throw new ViolationError(updateDeliveringDateDTO.companyNif, + "external company Nif not specified"); + } + + if (StringUtils.isEmpty(updateDeliveringDateDTO.externalCode)) { + throw new ViolationError(updateDeliveringDateDTO.externalCode, + "external order code not specified"); + } + + if ((updateDeliveringDateDTO.deliverDate) == null) { + throw new ViolationError(updateDeliveringDateDTO.deliverDate.toString(), + "deliver date not specified"); + } + + final ExternalCompany externalCompany = adHocTransactionService + .runOnTransaction(new IOnTransaction() { + @Override + public ExternalCompany execute() { + return findExternalCompanyFor(updateDeliveringDateDTO.companyNif); + } + }); + if (!externalCompany.isClient()) { + throw new ViolationError(updateDeliveringDateDTO.companyNif, + "external company is not registered as client"); + } + try { + adHocTransactionService + .runOnTransaction(new IOnTransaction() { + + @Override + public Void execute() { + updateDeliveringDateInOrder(updateDeliveringDateDTO); + return null; + } + }); + } catch (ValidationException e) { + InstanceConstraintViolationsDTO violation = ConstraintViolationConverter + .toDTO(new InstanceConstraintViolationsDTOId(Long.valueOf(1), + updateDeliveringDateDTO.companyNif, OrderDTO.ENTITY_TYPE), e); + throw new ViolationError(violation); + } + } + + private void updateDeliveringDateInOrder(UpdateDeliveringDateDTO updateDeliveringDateDTO){ + try { + OrderElement orderElement = orderElementDAO + .findByExternalCode(updateDeliveringDateDTO.externalCode); + + if((orderElement != null) && (orderElement instanceof Order)) { + Order order = (Order)orderElement; + + Date newDeliverDate = DateConverter.toDate(updateDeliveringDateDTO.deliverDate); + DeadlineCommunication deadlineCommunication = DeadlineCommunication + .create(new Date(), newDeliverDate); + order.getDeliveringDates().add(deadlineCommunication); + + LocalDate newLocalDeliverDate = new LocalDate(newDeliverDate); + OrderVersion orderVersion = order.getOrderVersionFor(Registry + .getScenarioManager().getCurrent()); + order.useSchedulingDataFor(orderVersion); + if (order.getAssociatedTaskElement() != null) { + order.getAssociatedTaskElement().setDeadline( + newLocalDeliverDate); + } + createCustomerCommunication(order, CommunicationType.UPDATE_DELIVERING_DATE); + orderElementDAO.save(order); + + } else { + throw new ViolationError( + updateDeliveringDateDTO.customerReference, + "It do not exist any order with this reference"); + } + } catch (InstanceNotFoundException e) { + throw new ViolationError(updateDeliveringDateDTO.customerReference, + "It do not exist any order with this reference"); + } + } + + @Override + @POST + @Path("create/") @Consumes("application/xml") public InstanceConstraintViolationsListDTO subcontract( final SubcontractedTaskDataDTO subcontractedTaskDataDTO) { @@ -193,6 +305,7 @@ public class SubcontractServiceREST implements ISubcontractService { order = (Order) orderElement; order.setVersionForScenario(current, version); order.useSchedulingDataFor(version); + order.setExternalCode(order.getCode()); } else { order = wrapInOrder(current, version, orderElement); @@ -223,8 +336,22 @@ public class SubcontractServiceREST implements ISubcontractService { TaskSource.persistTaskSources(taskSourceDAO)); order.writeSchedulingDataChanges(); + if (subcontractedTaskDataDTO.deliverDate != null) { + DeadlineCommunication deadlineCommunication = DeadlineCommunication + .create(new Date(), DateConverter + .toDate(subcontractedTaskDataDTO.deliverDate)); + order.getDeliveringDates().add(deadlineCommunication); + } + order.validate(); orderElementDAO.save(order); + + /* + * create the customer communication to a new subcontrating project. + */ + if(!StringUtils.isBlank(order.getExternalCode())){ + createCustomerCommunication(order, CommunicationType.NEW_PROJECT); + } } private void synchronizeWithSchedule(OrderElement orderElement, @@ -286,13 +413,18 @@ public class SubcontractServiceREST implements ISubcontractService { } private ExternalCompany findExternalCompanyFor( - final SubcontractedTaskDataDTO subcontractedTask) + final SubcontractedTaskDataDTO subcontractedTask){ + return findExternalCompanyFor(subcontractedTask.externalCompanyNif); + } + + private ExternalCompany findExternalCompanyFor( + final String externalCompanyNif) throws ViolationError { try { return externalCompanyDAO - .findUniqueByNif(subcontractedTask.externalCompanyNif); + .findUniqueByNif(externalCompanyNif); } catch (InstanceNotFoundException e) { - throw new ViolationError(subcontractedTask.externalCompanyNif, + throw new ViolationError(externalCompanyNif, "external company not found"); } } @@ -303,4 +435,17 @@ public class SubcontractServiceREST implements ISubcontractService { Util.generateInstanceId(1, code), message); } + private void createCustomerCommunication(Order order, CommunicationType type){ + Date communicationDate = new Date(); + Date deadline = null; + if(type.equals(CommunicationType.NEW_PROJECT)){ + deadline = order.getDeadline(); + }else{ + deadline = order.getDeliveringDates().first().getDeliverDate(); + } + CustomerCommunication customerCommunication = CustomerCommunication + .create(deadline, communicationDate, + type, order); + customerCommunicationDAO.save(customerCommunication); + } } diff --git a/libreplan-webapp/src/main/java/org/libreplan/ws/subcontract/impl/SubcontractedTaskDataConverter.java b/libreplan-webapp/src/main/java/org/libreplan/ws/subcontract/impl/SubcontractedTaskDataConverter.java index 37867613b..62a80f1aa 100644 --- a/libreplan-webapp/src/main/java/org/libreplan/ws/subcontract/impl/SubcontractedTaskDataConverter.java +++ b/libreplan-webapp/src/main/java/org/libreplan/ws/subcontract/impl/SubcontractedTaskDataConverter.java @@ -21,8 +21,14 @@ package org.libreplan.ws.subcontract.impl; +import java.util.Date; + +import javax.xml.datatype.XMLGregorianCalendar; + import org.libreplan.business.planner.entities.SubcontractedTaskData; +import org.libreplan.web.subcontract.UpdateDeliveringDateDTO; import org.libreplan.ws.common.api.OrderElementDTO; +import org.libreplan.ws.common.impl.DateConverter; import org.libreplan.ws.subcontract.api.SubcontractedTaskDataDTO; /** @@ -38,10 +44,35 @@ public final class SubcontractedTaskDataConverter { public final static SubcontractedTaskDataDTO toDTO(String companyCode, SubcontractedTaskData subcontractedTaskData, OrderElementDTO orderElementDTO) { - return new SubcontractedTaskDataDTO(companyCode, subcontractedTaskData - .getWorkDescription(), subcontractedTaskData - .getSubcontractPrice(), subcontractedTaskData - .getSubcontractedCode(), orderElementDTO); + return new SubcontractedTaskDataDTO(companyCode, + subcontractedTaskData.getWorkDescription(), + subcontractedTaskData.getSubcontractPrice(), + subcontractedTaskData.getSubcontractedCode(), orderElementDTO, + toXmlDate(getDeliverDate(subcontractedTaskData))); } + public final static UpdateDeliveringDateDTO toUpdateDeliveringDateDTO(SubcontractedTaskData subTaskData){ + String customerReference = subTaskData.getSubcontractedCode(); + XMLGregorianCalendar deliverDate = toXmlDate(getDeliverDate(subTaskData)); + if(!subTaskData.getRequiredDeliveringDates().isEmpty()){ + deliverDate = toXmlDate(subTaskData.getRequiredDeliveringDates().first().getSubcontractorDeliverDate()); + } + String companyNif = subTaskData.getExternalCompany().getNif(); + String externalCode = subTaskData.getTask().getOrderElement().getCode(); + return new UpdateDeliveringDateDTO(customerReference, externalCode, companyNif,deliverDate); + } + + private final static XMLGregorianCalendar toXmlDate(Date date) { + XMLGregorianCalendar xmlDate = (date != null) ? DateConverter + .toXMLGregorianCalendar(date) : null; + return xmlDate; + } + + private final static Date getDeliverDate(SubcontractedTaskData subcontractedTaskData){ + Date deliverDate = null; + if((subcontractedTaskData != null) && (!subcontractedTaskData.getRequiredDeliveringDates().isEmpty())){ + deliverDate = subcontractedTaskData.getRequiredDeliveringDates().first().getSubcontractorDeliverDate(); + } + return deliverDate; + } } \ No newline at end of file diff --git a/libreplan-webapp/src/main/resources/i18n/es.po b/libreplan-webapp/src/main/resources/i18n/es.po index a1b65ec47..731db6ea4 100644 --- a/libreplan-webapp/src/main/resources/i18n/es.po +++ b/libreplan-webapp/src/main/resources/i18n/es.po @@ -13,7 +13,7 @@ msgstr "" "Project-Id-Version: libreplan-1.2.4\n" "Report-Msgid-Bugs-To: http://bugs.libreplan.org/\n" "POT-Creation-Date: 2012-05-04 13:15+0200\n" -"PO-Revision-Date: 2012-05-04 11:18+0000\n" +"PO-Revision-Date: 2012-05-07 19:43+0200\n" "Last-Translator: Manuel Rego Casasnovas \n" "Language-Team: Español\n" "MIME-Version: 1.0\n" @@ -3280,7 +3280,7 @@ msgstr "Tipo de horas" #: libreplan-webapp/src/main/webapp/planner/print_configuration.zul:43 msgid "Show money cost bar" -msgstr "Monstrar barra de coste monetario" +msgstr "Mostrar barra de coste monetario" #: libreplan-webapp/src/main/java/org/libreplan/ws/resources/impl/ResourceConverter.java:156 #: libreplan-business/src/main/java/org/libreplan/business/resources/entities/CriterionType.java:211 diff --git a/libreplan-webapp/src/main/resources/i18n/gl.po b/libreplan-webapp/src/main/resources/i18n/gl.po index fa0a0e27a..d94b4fe8a 100644 --- a/libreplan-webapp/src/main/resources/i18n/gl.po +++ b/libreplan-webapp/src/main/resources/i18n/gl.po @@ -12,7 +12,7 @@ msgstr "" "Project-Id-Version: libreplan-1.2.4\n" "Report-Msgid-Bugs-To: http://bugs.libreplan.org/\n" "POT-Creation-Date: 2012-05-04 13:15+0200\n" -"PO-Revision-Date: 2012-05-04 11:22+0000\n" +"PO-Revision-Date: 2012-05-07 19:43+0200\n" "Last-Translator: Manuel Rego Casasnovas \n" "Language-Team: Galego\n" "MIME-Version: 1.0\n" @@ -3279,7 +3279,7 @@ msgstr "Tipo de horas" #: libreplan-webapp/src/main/webapp/planner/print_configuration.zul:43 msgid "Show money cost bar" -msgstr "Monstrar barra de coste monetario" +msgstr "Mostrar barra de coste monetario" #: libreplan-webapp/src/main/java/org/libreplan/ws/resources/impl/ResourceConverter.java:156 #: libreplan-business/src/main/java/org/libreplan/business/resources/entities/CriterionType.java:211 diff --git a/libreplan-webapp/src/main/webapp/common/css/libreplan_zk.css b/libreplan-webapp/src/main/webapp/common/css/libreplan_zk.css index 748b78181..e5ed46586 100644 --- a/libreplan-webapp/src/main/webapp/common/css/libreplan_zk.css +++ b/libreplan-webapp/src/main/webapp/common/css/libreplan_zk.css @@ -1894,3 +1894,7 @@ select { .bandbox-workreport-task input { width: 250px; } + +.communication-not-reviewed div.z-row-cnt .z-label{ font-weight: bold; } + +.current-delivery-date div.z-row-cnt .z-label{ font-weight: bold; } diff --git a/libreplan-webapp/src/main/webapp/common/img/favicon.ico b/libreplan-webapp/src/main/webapp/common/img/favicon.ico new file mode 100644 index 000000000..ef5fbd340 Binary files /dev/null and b/libreplan-webapp/src/main/webapp/common/img/favicon.ico differ diff --git a/libreplan-webapp/src/main/webapp/common/layout/login.zul b/libreplan-webapp/src/main/webapp/common/layout/login.zul index d0a4f0855..809c1a0b4 100644 --- a/libreplan-webapp/src/main/webapp/common/layout/login.zul +++ b/libreplan-webapp/src/main/webapp/common/layout/login.zul @@ -20,6 +20,7 @@ --> + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +