diff --git a/libreplan-webapp/src/test/java/org/libreplan/importers/JiraOrderElementSynchronizerTest.java b/libreplan-webapp/src/test/java/org/libreplan/importers/JiraOrderElementSynchronizerTest.java
new file mode 100644
index 000000000..28bfc75d5
--- /dev/null
+++ b/libreplan-webapp/src/test/java/org/libreplan/importers/JiraOrderElementSynchronizerTest.java
@@ -0,0 +1,418 @@
+/*
+ * 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.importers;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.libreplan.business.BusinessGlobalNames.BUSINESS_SPRING_CONFIG_FILE;
+import static org.libreplan.web.WebappGlobalNames.WEBAPP_SPRING_CONFIG_FILE;
+import static org.libreplan.web.WebappGlobalNames.WEBAPP_SPRING_SECURITY_CONFIG_FILE;
+import static org.libreplan.web.test.WebappGlobalNames.WEBAPP_SPRING_CONFIG_TEST_FILE;
+import static org.libreplan.web.test.WebappGlobalNames.WEBAPP_SPRING_SECURITY_CONFIG_TEST_FILE;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Properties;
+import java.util.UUID;
+
+import javax.annotation.Resource;
+
+import org.joda.time.LocalDate;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.libreplan.business.IDataBootstrap;
+import org.libreplan.business.advance.bootstrap.PredefinedAdvancedTypes;
+import org.libreplan.business.advance.entities.AdvanceMeasurement;
+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.advance.exceptions.DuplicateValueTrueReportGlobalAdvanceException;
+import org.libreplan.business.common.IAdHocTransactionService;
+import org.libreplan.business.common.IOnTransaction;
+import org.libreplan.business.common.daos.IConfigurationDAO;
+import org.libreplan.business.common.exceptions.InstanceNotFoundException;
+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.OrderElement;
+import org.libreplan.business.orders.entities.OrderLine;
+import org.libreplan.business.scenarios.IScenarioManager;
+import org.libreplan.business.scenarios.entities.OrderVersion;
+import org.libreplan.business.scenarios.entities.Scenario;
+import org.libreplan.importers.jira.Issue;
+import org.libreplan.importers.jira.TimeTracking;
+import org.libreplan.importers.jira.WorkLog;
+import org.libreplan.importers.jira.WorkLogItem;
+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;
+
+/**
+ * Test for {@link JiraOrderElementSynchronizer }
+ *
+ * @author Miciele Ghiorghis
+ */
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration(locations = { BUSINESS_SPRING_CONFIG_FILE,
+ WEBAPP_SPRING_CONFIG_FILE, WEBAPP_SPRING_CONFIG_TEST_FILE,
+ WEBAPP_SPRING_SECURITY_CONFIG_FILE,
+ WEBAPP_SPRING_SECURITY_CONFIG_TEST_FILE })
+@Transactional
+public class JiraOrderElementSynchronizerTest {
+
+ @Resource
+ private IDataBootstrap defaultAdvanceTypesBootstrapListener;
+
+ @Resource
+ private IDataBootstrap scenariosBootstrap;
+
+ @Resource
+ private IDataBootstrap configurationBootstrap;
+
+ @Autowired
+ private IAdHocTransactionService transactionService;
+
+ @Autowired
+ private IConfigurationDAO configurationDAO;
+
+ @Autowired
+ private IScenarioManager scenarioManager;
+
+ private static final String PATH = "rest/api/latest/search";
+ private static final String LABEL = "labels=epd_12a_ZorgActiviteiten";
+
+ private List issues;
+
+ @Autowired
+ private IOrderDAO orderDAO;
+
+
+ @Before
+ public void loadRequiredaData() {
+
+ IOnTransaction load = new IOnTransaction() {
+
+ @Override
+ public Void execute() {
+ defaultAdvanceTypesBootstrapListener.loadRequiredData();
+ configurationBootstrap.loadRequiredData();
+ scenariosBootstrap.loadRequiredData();
+ issues = getJiraIssues();
+ return null;
+ }
+ };
+
+ transactionService.runOnAnotherTransaction(load);
+ }
+
+ private List getJiraIssues() {
+ List issues = new ArrayList();
+ try {
+ Properties properties = loadProperties();
+ issues = JiraRESTClient.getIssues(properties.getProperty("url"),
+ properties.getProperty("username"),
+ properties.getProperty("password"), PATH, LABEL);
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return issues;
+ }
+
+ private Properties loadProperties() throws FileNotFoundException,
+ IOException {
+
+ String filename = System.getProperty("user.dir")
+ + "/../scripts/jira-connector/jira-conn.properties";
+
+ Properties properties = new Properties();
+ properties.load(new FileInputStream(filename));
+ return properties;
+
+ }
+
+ private Order givenOrder() {
+ return transactionService
+ .runOnAnotherTransaction(new IOnTransaction() {
+ @Override
+ public Order execute() {
+ return givenValidOrderAlreadyStored();
+ }
+ });
+ }
+
+
+
+ private Order givenValidOrderAlreadyStored() {
+ Order order = Order.create();
+ order.setCode(UUID.randomUUID().toString());
+ order.setName("Order name " + UUID.randomUUID());
+ order.setInitDate(new Date());
+ order.setCalendar(configurationDAO.getConfiguration()
+ .getDefaultCalendar());
+ OrderVersion version = setupVersionUsing(scenarioManager, order);
+ order.useSchedulingDataFor(version);
+
+ orderDAO.save(order);
+ orderDAO.flush();
+ try {
+ return orderDAO.find(order.getId());
+ } catch (InstanceNotFoundException e) {
+ return null;
+ }
+ }
+
+ private Order givenOrderWithValidOrderLines() {
+ return transactionService
+ .runOnAnotherTransaction(new IOnTransaction() {
+ @Override
+ public Order execute() {
+ return givenValidOrderWithValidOrderLinesAlreadyStored();
+ }
+ });
+ }
+
+ private Order givenValidOrderWithValidOrderLinesAlreadyStored() {
+ Order order = Order.create();
+ order.setCode(UUID.randomUUID().toString());
+ order.setName("Order name " + UUID.randomUUID());
+ order.setInitDate(new Date());
+ order.setCalendar(configurationDAO.getConfiguration()
+ .getDefaultCalendar());
+ OrderVersion version = setupVersionUsing(scenarioManager, order);
+ order.useSchedulingDataFor(version);
+ for (Issue issue : issues) {
+ String code = "JIRA-" + order.getCode() + "-" + issue.getKey();
+ String name = issue.getFields().getSummary();
+
+ syncOrderLine(order, code, name);
+
+ int estimatedHours = getEstimatedHours(
+ issue.getFields().getTimetracking()).intValue();
+
+ syncHoursGroup((OrderLine) order.getOrderElement(code), code,
+ estimatedHours);
+
+ syncPorgressMeasurement(order.getOrderElement(code), issue);
+
+ }
+ orderDAO.save(order);
+ orderDAO.flush();
+ try {
+ return orderDAO.find(order.getId());
+ } catch (InstanceNotFoundException e) {
+ return null;
+ }
+ }
+
+ private OrderVersion setupVersionUsing(IScenarioManager scenarioManager,
+ Order order) {
+ Scenario current = scenarioManager.getCurrent();
+ OrderVersion result = OrderVersion.createInitialVersion(current);
+ order.setVersionForScenario(current, result);
+ return result;
+ }
+
+
+
+ private void syncOrderLine(Order order, String code, String name) {
+ OrderLine orderLine = (OrderLine) order.getOrderElement(code);
+ if (orderLine == null) {
+ orderLine = OrderLine.createOrderLineWithUnfixedPercentage(1000);
+ order.add(orderLine);
+ orderLine.setCode(code);
+ }
+ orderLine.setName(name);
+
+ }
+
+ private void syncHoursGroup(OrderLine orderLine, String code,
+ Integer workingHours) {
+ HoursGroup hoursGroup = orderLine.getHoursGroup(code);
+ if (hoursGroup == null) {
+ hoursGroup = HoursGroup.create(orderLine);
+ hoursGroup.setCode(code);
+ orderLine.addHoursGroup(hoursGroup);
+ }
+
+ hoursGroup.setWorkingHours(workingHours);
+
+ }
+
+ private void syncPorgressMeasurement(OrderElement orderElement, Issue issue) {
+
+ WorkLog workLog = issue.getFields().getWorklog();
+
+ if (workLog == null) {
+ return;
+ }
+ if (orderElement == null) {
+ return;
+ }
+
+ List workLogItems = workLog.getWorklogs();
+ if (workLogItems.isEmpty()) {
+ return;
+ }
+
+ Integer estimatedHours = getEstimatedHours(issue.getFields()
+ .getTimetracking());
+
+ if (estimatedHours == 0) {
+ return;
+ }
+
+ Integer loggedHours = getLoggedHours(issue.getFields()
+ .getTimetracking());
+
+ BigDecimal percentage = new BigDecimal((loggedHours * 100)
+ / (loggedHours + estimatedHours));
+
+ LocalDate latestWorkLogDate = new LocalDate();
+
+ updateOrCreateProgressAssignmentAndMeasurement(orderElement,
+ percentage, latestWorkLogDate);
+
+ }
+
+ private void updateOrCreateProgressAssignmentAndMeasurement(
+ OrderElement orderElement, BigDecimal percentage,
+ LocalDate latestWorkLogDate) {
+
+ AdvanceType advanceType = PredefinedAdvancedTypes.PERCENTAGE.getType();
+
+ DirectAdvanceAssignment directAdvanceAssignment = orderElement
+ .getDirectAdvanceAssignmentByType(advanceType);
+ if (directAdvanceAssignment == null) {
+ directAdvanceAssignment = DirectAdvanceAssignment.create(false,
+ new BigDecimal(100));
+ directAdvanceAssignment.setAdvanceType(advanceType);
+ }
+ directAdvanceAssignment.setOrderElement(orderElement);
+
+ AdvanceMeasurement advanceMeasurement = directAdvanceAssignment
+ .getAdvanceMeasurementAtExactDate(latestWorkLogDate);
+ if (advanceMeasurement == null) {
+ advanceMeasurement = AdvanceMeasurement.create();
+ }
+
+ advanceMeasurement.setValue(percentage);
+ advanceMeasurement.setDate(latestWorkLogDate);
+
+ directAdvanceAssignment.addAdvanceMeasurements(advanceMeasurement);
+
+ advanceMeasurement.setAdvanceAssignment(directAdvanceAssignment);
+
+ if (directAdvanceAssignment.isNewObject()) {
+ try {
+ directAdvanceAssignment.getOrderElement().addAdvanceAssignment(
+ directAdvanceAssignment);
+ } catch (DuplicateValueTrueReportGlobalAdvanceException e) {
+ } catch (DuplicateAdvanceAssignmentForOrderElementException e) {
+ }
+ }
+
+ }
+
+ private Integer getEstimatedHours(TimeTracking timeTracking) {
+ if (timeTracking == null) {
+ return 0;
+ }
+
+ Integer timeestimate = timeTracking.getRemainingEstimateSeconds();
+ if (timeestimate != null && timeestimate > 0) {
+ return timeestimate / 3600;
+ }
+
+ Integer timeoriginalestimate = timeTracking
+ .getOriginalEstimateSeconds();
+ if (timeoriginalestimate != null) {
+ return timeoriginalestimate / 3600;
+ }
+ return 0;
+ }
+
+ private Integer getLoggedHours(TimeTracking timeTracking) {
+ if (timeTracking == null) {
+ return 0;
+ }
+
+ Integer timespentInSec = timeTracking.getTimeSpentSeconds();
+ if (timespentInSec != null && timespentInSec > 0) {
+ return timespentInSec / 3600;
+ }
+
+ return 0;
+ }
+
+ @Test
+ public void testSyncOrderElementsOfAnExistingOrderWithNoOrderLines() {
+ Order order = givenOrder();
+ for (Issue issue : issues) {
+ String code = "JIRA-" + order.getCode() + "-" + issue.getKey();
+ String name = issue.getFields().getSummary();
+
+ syncOrderLine(order, code, name);
+
+ syncHoursGroup((OrderLine) order.getOrderElement(code), code,
+ getEstimatedHours(issue.getFields().getTimetracking()));
+
+ syncPorgressMeasurement(order.getOrderElement(code), issue);
+
+ }
+ assertEquals(order.getOrderElements().size(), issues.size());
+ assertTrue(order.getOrderElements().get(0).getHoursGroups().size() > 0);
+ assertTrue(!order.getAdvancePercentage().equals(BigDecimal.ZERO));
+ }
+
+
+ @Test
+ public void testReSyncOrderElementsOfAnExistingOrderWithOrderLines() {
+ Order order = givenOrderWithValidOrderLines();
+ Integer workingHours = order.getWorkHours();
+ for (Issue issue : issues) {
+ String code = "JIRA-" + order.getCode() + "-" + issue.getKey();
+ String name = issue.getFields().getSummary();
+
+ syncOrderLine(order, code, name);
+
+ Integer estimatedHours = getEstimatedHours(issue.getFields()
+ .getTimetracking()) * 10;
+
+ syncHoursGroup((OrderLine) order.getOrderElement(code), code,
+ estimatedHours);
+
+ syncPorgressMeasurement(order.getOrderElement(code), issue);
+
+ }
+ assertEquals(order.getOrderElements().size(), issues.size());
+ assertEquals(workingHours.intValue(),
+ (order.getWorkHours().intValue() / 10));
+ }
+
+}