diff --git a/libreplan-webapp/src/main/java/org/libreplan/importers/JiraTimesheetSynchronizer.java b/libreplan-webapp/src/main/java/org/libreplan/importers/JiraTimesheetSynchronizer.java
new file mode 100644
index 000000000..0d3f6ceab
--- /dev/null
+++ b/libreplan-webapp/src/main/java/org/libreplan/importers/JiraTimesheetSynchronizer.java
@@ -0,0 +1,370 @@
+/*
+ * 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 java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.hibernate.NonUniqueResultException;
+import org.libreplan.business.common.IAdHocTransactionService;
+import org.libreplan.business.common.exceptions.InstanceNotFoundException;
+import org.libreplan.business.costcategories.daos.ITypeOfWorkHoursDAO;
+import org.libreplan.business.costcategories.entities.TypeOfWorkHours;
+import org.libreplan.business.orders.entities.Order;
+import org.libreplan.business.orders.entities.OrderElement;
+import org.libreplan.business.resources.daos.IWorkerDAO;
+import org.libreplan.business.resources.entities.Resource;
+import org.libreplan.business.resources.entities.Worker;
+import org.libreplan.business.workingday.EffortDuration;
+import org.libreplan.business.workreports.daos.IWorkReportDAO;
+import org.libreplan.business.workreports.daos.IWorkReportLineDAO;
+import org.libreplan.business.workreports.daos.IWorkReportTypeDAO;
+import org.libreplan.business.workreports.entities.WorkReport;
+import org.libreplan.business.workreports.entities.WorkReportLine;
+import org.libreplan.business.workreports.entities.WorkReportType;
+import org.libreplan.business.workreports.valueobjects.DescriptionField;
+import org.libreplan.business.workreports.valueobjects.DescriptionValue;
+import org.libreplan.importers.jira.Issue;
+import org.libreplan.importers.jira.WorkLog;
+import org.libreplan.importers.jira.WorkLogItem;
+import org.libreplan.web.workreports.IWorkReportModel;
+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;
+
+@Component
+@Scope(BeanDefinition.SCOPE_PROTOTYPE)
+public class JiraTimesheetSynchronizer implements IJiraTimesheetSynchronizer {
+
+ private static String CODE_PREFIX = "JIRA-";
+
+ private JiraSyncInfo jiraSyncInfo;
+
+ private List workers;
+
+ private WorkReportType workReportType;
+
+ private TypeOfWorkHours typeOfWorkHours;
+
+ @Autowired
+ private IWorkerDAO workerDAO;
+
+ @Autowired
+ private IWorkReportTypeDAO workReportTypeDAO;
+
+ @Autowired
+ private IWorkReportDAO workReportDAO;
+
+ @Autowired
+ private IWorkReportLineDAO workReportLineDAO;
+
+ @Autowired
+ private IWorkReportModel workReportModel;
+
+ @Autowired
+ private ITypeOfWorkHoursDAO typeOfWorkHoursDAO;
+
+ @Autowired
+ private IAdHocTransactionService adHocTransactionService;
+
+ @Override
+ @Transactional
+ public void syncJiraTimesheetWithJiraIssues(List issues, Order order) {
+ startSync(issues, order);
+
+ }
+
+ /**
+ * Start synchronization of timesheets
+ *
+ * @param issues
+ * the jira issues
+ * @param order
+ * an existing order
+ */
+ private void startSync(List issues, Order order) {
+
+ jiraSyncInfo = new JiraSyncInfo();
+
+ workReportType = findWorkReportType("Jira-connector");
+ if (workReportType == null) {
+ return;
+ }
+
+ typeOfWorkHours = findTypeOfWorkHours("Default");
+ if (typeOfWorkHours == null) {
+ return;
+ }
+
+ workers = getWorkers();
+ if (workers == null && workers.isEmpty()) {
+ jiraSyncInfo.addSyncFailedReason("No workers found");
+ return;
+ }
+
+ String code = order.getCode() + " " + order.getImportedLabel();
+
+ WorkReport workReport = updateOrCreateWorkReport(code);
+
+ for (Issue issue : issues) {
+ WorkLog worklog = issue.getFields().getWorklog();
+ if (worklog == null) {
+ jiraSyncInfo.addSyncFailedReason("No worklogs found for '"
+ + issue.getKey() + "'");
+ } else {
+ List workLogItems = worklog.getWorklogs();
+ if (workLogItems == null || workLogItems.isEmpty()) {
+ jiraSyncInfo
+ .addSyncFailedReason("No worklog items found for '"
+ + issue.getKey() + "' issue");
+ } else {
+
+ String codeOrderElement = CODE_PREFIX + order.getCode() + "-"
+ + issue.getKey();
+
+ OrderElement orderElement = order.getOrderElement(codeOrderElement);
+
+ if (orderElement == null) {
+ jiraSyncInfo.addSyncFailedReason("Order element("
+ + code + ") not found");
+ } else {
+ updateOrCreateWorkReportLineAndAddToWorkReport(workReport, orderElement,
+ workLogItems);
+ }
+ }
+ }
+ }
+
+ if (workReportModel.getWorkReport().getWorkReportLines().size() > 0) {
+ workReportModel.confirmSave();
+ }
+ }
+
+ /**
+ * Updates {@link WorkReport} if exist, if not creates new one
+ *
+ * @param code
+ * search criteria for workReport
+ * @return the workReport
+ */
+ private WorkReport updateOrCreateWorkReport(String code) {
+ WorkReport workReport = findWorkReport(code);
+ if (workReport == null) {
+ workReportModel.initCreate(workReportType);
+ } else {
+ workReportModel.initEdit(workReport);
+ }
+ workReportModel.setCodeAutogenerated(false);
+
+ workReport = workReportModel.getWorkReport();
+ workReport.setCode(code);
+ return workReport;
+ }
+
+ /**
+ * Updates {@link WorkReportLine} if exist. If not creates new one and adds
+ * to workReport
+ *
+ * @param workReport
+ * an existing or new created workReport
+ * @param orderElement
+ * the orderElement
+ * @param workLogItems
+ * jira's workLog items to be added to workReportLine
+ */
+ private void updateOrCreateWorkReportLineAndAddToWorkReport(WorkReport workReport,
+ OrderElement orderElement,
+ List workLogItems) {
+
+ for (WorkLogItem workLogItem : workLogItems) {
+ WorkReportLine workReportLine;
+ try {
+ workReportLine = workReport
+ .getWorkReportLineByCode(orderElement.getCode() + "-"
+ + workLogItem.getId());
+ } catch (InstanceNotFoundException e) {
+ workReportLine = WorkReportLine.create(workReport);
+ }
+
+ Resource resource = getWorker(workLogItem.getAuthor().getName());
+ if (resource != null) {
+
+ updateWorkReportLine(workReportLine, orderElement,
+ workLogItem, resource);
+ if (workReportLine.isNewObject()) {
+ workReport.addWorkReportLine(workReportLine);
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Updates {@link WorkReportLine} with workLogItem
+ *
+ * @param workReportLine
+ * workReportLine to be updated
+ * @param orderElement
+ * the orderElement
+ * @param workLogItem
+ * workLogItem to update the workReportLine
+ * @param resource
+ * the resource
+ */
+ private void updateWorkReportLine(WorkReportLine workReportLine,
+ OrderElement orderElement, WorkLogItem workLogItem,
+ Resource resource) {
+
+ String code = orderElement.getCode() + "-" + workLogItem.getId();
+ int timeSpent = workLogItem.getTimeSpentSeconds().intValue();
+
+ workReportLine.setCode(code);
+ workReportLine.setDate(workLogItem.getStarted());
+ workReportLine.setResource(resource);
+ workReportLine.setOrderElement(orderElement);
+ workReportLine.setEffort(EffortDuration
+ .hours(EffortDuration.Granularity.HOURS
+ .convertFromSeconds(timeSpent)));
+ workReportLine.setTypeOfWorkHours(typeOfWorkHours);
+
+ updateOrCreateDescriptionValuesAndAddToWorkReportLine(workReportLine,
+ workLogItem.getComment());
+ }
+
+ /**
+ * Updates {@link DescriptionValue} if exist. if not creates new one and
+ * adds to workReportLine
+ *
+ * @param workReportLine
+ * workReprtLinew where descriptionvalues to be added to
+ * @param comment
+ * the description value
+ */
+ private void updateOrCreateDescriptionValuesAndAddToWorkReportLine(WorkReportLine workReportLine,
+ String comment) {
+ Set descriptionValues = new HashSet();
+ for (DescriptionField descriptionField : workReportType.getLineFields()) {
+ DescriptionValue descriptionValue;
+ try {
+ descriptionValue = workReportLine
+ .getDescriptionValueByFieldName(descriptionField
+ .getFieldName());
+ descriptionValue.setValue(comment.substring(0,
+ Math.min(comment.length(), 254)));
+ } catch (InstanceNotFoundException e) {
+ descriptionValue = DescriptionValue.create(
+ descriptionField.getFieldName(), comment);
+ }
+ descriptionValues.add(descriptionValue);
+ }
+ workReportLine.setDescriptionValues(descriptionValues);
+ }
+
+ /**
+ * Searches for {@link WorkReportType} for the specified parameter
+ * name
+ *
+ * @param name
+ * unique name
+ * @return WorkReportType if found, null otherwise
+ */
+ private WorkReportType findWorkReportType(String name) {
+ try {
+ return workReportTypeDAO.findUniqueByName(name);
+ } catch (NonUniqueResultException e) {
+ jiraSyncInfo
+ .addSyncFailedReason("Work report type 'Jira-connector' is not unique");
+ } catch (InstanceNotFoundException e) {
+ jiraSyncInfo
+ .addSyncFailedReason("Work report type 'Jira-connector' not found");
+ }
+ return null;
+ }
+
+ /**
+ * Searches for {@link TypeOfWorkHours} for the specified parameter
+ * name
+ *
+ * @param name
+ * unique name
+ * @return TypeOfWorkHours if found, null otherwise
+ */
+ private TypeOfWorkHours findTypeOfWorkHours(String name) {
+ try {
+ return typeOfWorkHoursDAO.findUniqueByName(name);
+ } catch (InstanceNotFoundException e) {
+ jiraSyncInfo.addSyncFailedReason("Type of workhours '" + name
+ + "' not found");
+ }
+ return null;
+ }
+
+ /**
+ * Searches for {@link WorkReport} for the specified parameter
+ * code
+ *
+ * @param code
+ * unique code
+ * @return workReportType if found, null otherwise
+ */
+ private WorkReport findWorkReport(String code) {
+ try {
+ return workReportDAO.findByCodeAnotherTransaction(code);
+ } catch (InstanceNotFoundException e) {
+ }
+ return null;
+ }
+
+
+ /**
+ * Gets all libreplan workers
+ *
+ * @return list of workers
+ */
+ private List getWorkers() {
+ return workerDAO.findAll();
+ }
+
+ /**
+ * Searches for {@link Worker} for the specified parameter nif
+ *
+ * @param nif
+ * unique id
+ * @return worker if found, null otherwise
+ */
+ private Worker getWorker(String nif) {
+ for (Worker worker : workers) {
+ if (worker.getNif().equals(nif)) {
+ return worker;
+ }
+ }
+ jiraSyncInfo.addSyncFailedReason("Worker('" + nif + "') not found");
+ return null;
+ }
+
+
+ @Override
+ public JiraSyncInfo getJiraSyncInfo() {
+ return jiraSyncInfo;
+ }
+}