/*
* This file is part of LibrePlan
*
* Copyright (C) 2013 St. Antoniusziekenhuis
*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.libreplan.importers;
import static org.libreplan.web.I18nHelper._;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.joda.time.LocalDate;
import org.libreplan.business.common.IAdHocTransactionService;
import org.libreplan.business.common.IOnTransaction;
import org.libreplan.business.common.daos.IConnectorDAO;
import org.libreplan.business.common.entities.Connector;
import org.libreplan.business.common.entities.ConnectorException;
import org.libreplan.business.common.entities.PredefinedConnectorProperties;
import org.libreplan.business.common.entities.PredefinedConnectors;
import org.libreplan.business.common.exceptions.InstanceNotFoundException;
import org.libreplan.business.orders.daos.IOrderSyncInfoDAO;
import org.libreplan.business.orders.entities.Order;
import org.libreplan.business.orders.entities.OrderSyncInfo;
import org.libreplan.business.resources.daos.IWorkerDAO;
import org.libreplan.business.resources.entities.Worker;
import org.libreplan.business.workreports.entities.WorkReportLine;
import org.libreplan.importers.tim.DurationDTO;
import org.libreplan.importers.tim.PersonDTO;
import org.libreplan.importers.tim.ProductDTO;
import org.libreplan.importers.tim.RegistrationDateDTO;
import org.libreplan.importers.tim.TimOptions;
import org.libreplan.importers.tim.TimeRegistrationDTO;
import org.libreplan.importers.tim.TimeRegistrationRequestDTO;
import org.libreplan.importers.tim.TimeRegistrationResponseDTO;
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;
/**
* Implementation of export timesheets to tim
*
* @author Miciele Ghiorghis <m.ghiorghis@antoniusziekenhuis.nl>
*/
@Component
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class ExportTimesheetsToTim implements IExportTimesheetsToTim {
private static final Log LOG = LogFactory
.getLog(ExportTimesheetsToTim.class);
@Autowired
private IWorkerDAO workerDAO;
@Autowired
IOrderSyncInfoDAO orderSyncInfoDAO;
@Autowired
private IAdHocTransactionService adHocTransactionService;
@Autowired
private IConnectorDAO connectorDAO;
private SynchronizationInfo synchronizationInfo;
@Override
@Transactional(readOnly = true)
public List<SynchronizationInfo> exportTimesheets() throws ConnectorException {
Connector connector = getTimConnector();
if (connector == null) {
throw new ConnectorException(_("Tim connector not found"));
}
if (!connector.areConnectionValuesValid()) {
throw new ConnectorException(
_("Connection values of Tim connector are invalid"));
}
synchronizationInfo = new SynchronizationInfo(_("Export"));
List<SynchronizationInfo> syncInfos = new ArrayList<SynchronizationInfo>();
List<OrderSyncInfo> orderSyncInfos = orderSyncInfoDAO.findByConnectorName(PredefinedConnectors.TIM.getName());
if (orderSyncInfos == null || orderSyncInfos.isEmpty()) {
LOG.warn("No items found in 'OrderSyncInfo' to export to Tim");
synchronizationInfo.addFailedReason(_("No items found in 'OrderSyncInfo' to export to Tim"));
syncInfos.add(synchronizationInfo);
return syncInfos;
}
for (OrderSyncInfo orderSyncInfo : orderSyncInfos) {
LOG.info("Exporting '" + orderSyncInfo.getOrder().getName() + "'");
exportTimesheets(orderSyncInfo.getKey(), orderSyncInfo.getOrder(),
connector);
if (!synchronizationInfo.isSuccessful()) {
syncInfos.add(synchronizationInfo);
}
}
return syncInfos;
}
@Override
@Transactional(readOnly = true)
public void exportTimesheets(String productCode, Order order)
throws ConnectorException {
if (productCode == null || productCode.isEmpty()) {
throw new ConnectorException(_("Product code should not be empty"));
}
if (order == null) {
throw new ConnectorException(_("Order should not be empty"));
}
Connector connector = getTimConnector();
if (connector == null) {
throw new ConnectorException(_("Tim connector not found"));
}
if (!connector.areConnectionValuesValid()) {
throw new ConnectorException(
_("Connection values of Tim connector are invalid"));
}
exportTimesheets(productCode, order, connector);
}
/**
* exports time sheets to Tim
*
* @param productCode
* the product code
* @param order
* the order
* @param connector
* the connector
*
* @return true if export is succeeded, false otherwise
*/
private void exportTimesheets(String productCode, Order order,
Connector connector) {
synchronizationInfo = new SynchronizationInfo(_(
"Export product code {0}, project {1}", productCode,
order.getName()));
Map<String, String> properties = connector.getPropertiesAsMap();
String url = properties.get(PredefinedConnectorProperties.SERVER_URL);
String userName = properties
.get(PredefinedConnectorProperties.USERNAME);
String password = properties
.get(PredefinedConnectorProperties.PASSWORD);
int nrDaysTimesheetToTim = Integer.parseInt(properties
.get(PredefinedConnectorProperties.TIM_NR_DAYS_TIMESHEET));
LocalDate dateNrOfDaysBack = new LocalDate()
.minusDays(nrDaysTimesheetToTim);
List<WorkReportLine> workReportLines = order.getWorkReportLines(
dateNrOfDaysBack.toDateTimeAtStartOfDay().toDate(), new Date(),
true);
if (workReportLines == null || workReportLines.isEmpty()) {
LOG.warn("No work reportlines are found for order: '"
+ order.getName() + "'");
synchronizationInfo.addFailedReason(_(
"No work reportlines are found for order: \"{0}\"",
order.getName()));
return;
}
List<TimeRegistrationDTO> timeRegistrationDTOs = new ArrayList<TimeRegistrationDTO>();
for (WorkReportLine workReportLine : workReportLines) {
TimeRegistrationDTO timeRegistrationDTO = createExportTimeRegistration(
productCode, workReportLine);
if (timeRegistrationDTO != null) {
timeRegistrationDTOs.add(timeRegistrationDTO);
}
}
if (timeRegistrationDTOs.isEmpty()) {
LOG.warn("Unable to crate timeregistration for request");
synchronizationInfo
.addFailedReason(_("Unable to crate time registration for request"));
return;
}
TimeRegistrationRequestDTO timeRegistrationRequestDTO = new TimeRegistrationRequestDTO();
timeRegistrationRequestDTO.setTimeRegistrations(timeRegistrationDTOs);
TimeRegistrationResponseDTO timeRegistrationResponseDTO = TimSoapClient
.sendRequestReceiveResponse(url, userName, password,
timeRegistrationRequestDTO, TimeRegistrationResponseDTO.class);
if (timeRegistrationResponseDTO == null) {
LOG.error("No response or exception in response");
synchronizationInfo
.addFailedReason(_("No response or exception in response"));
return;
}
if (isRefsListEmpty(timeRegistrationResponseDTO.getRefs())) {
LOG.warn("Registration response with empty refs");
synchronizationInfo
.addFailedReason(_("Registration response with empty refs"));
return;
}
saveSyncInfoOnAnotherTransaction(productCode, order);
}
/**
* checks if list of refs is empty
*
* @param refs
* the list of refs
* @return true if list is empty otherwise false
*/
private boolean isRefsListEmpty(List<Integer> refs) {
if (refs == null) {
return true;
}
refs.removeAll(Collections.singleton(0));
return refs.isEmpty();
}
/**
* Saves synchronization info
*
* @param productCode
* the productcode
* @param order
* the order
*/
private void saveSyncInfoOnAnotherTransaction(final String productCode,
final Order order) {
adHocTransactionService
.runOnAnotherTransaction(new IOnTransaction<Void>() {
@Override
public Void execute() {
OrderSyncInfo orderSyncInfo = orderSyncInfoDAO
.findByKeyOrderAndConnectorName(productCode,
order,
PredefinedConnectors.TIM.getName());
if (orderSyncInfo == null) {
orderSyncInfo = OrderSyncInfo.create(productCode,
order, PredefinedConnectors.TIM.getName());
}
orderSyncInfo.setLastSyncDate(new Date());
orderSyncInfoDAO.save(orderSyncInfo);
return null;
}
});
}
/**
* Creates export time registration
*
* @param productCode
* the product code
* @param workReportLine
* the workreportLine
* @return timeRegistration DTO
*/
private TimeRegistrationDTO createExportTimeRegistration(String productCode,
WorkReportLine workReportLine) {
Worker worker;
String workerCode = workReportLine.getResource().getCode();
try {
worker = workerDAO.findByCode(workerCode);
} catch (InstanceNotFoundException e) {
LOG.warn("Worker '" + workerCode + "' not found");
synchronizationInfo.addFailedReason(_("Worker \"{0}\" not found",
workerCode));
return null;
}
PersonDTO personDTO = new PersonDTO();
personDTO.setName(worker.getName());
personDTO.setOptions(TimOptions.UPDATE_OR_INSERT);
ProductDTO productDTO = new ProductDTO();
productDTO.setOptions(TimOptions.UPDATE_OR_INSERT);
productDTO.setCode(productCode);
RegistrationDateDTO registrationDTO = new RegistrationDateDTO();
registrationDTO.setOptions(TimOptions.UPDATE_OR_INSERT);
registrationDTO.setDate(workReportLine.getLocalDate());
DurationDTO durationDTO = new DurationDTO();
durationDTO.setOptions(TimOptions.DECIMAL);
durationDTO.setDuration(workReportLine.getEffort()
.toHoursAsDecimalWithScale(2).doubleValue());
TimeRegistrationDTO timeRegistrationDTO = new TimeRegistrationDTO();
timeRegistrationDTO.setPerson(personDTO);
timeRegistrationDTO.setProduct(productDTO);
timeRegistrationDTO.setRegistrationDate(registrationDTO);
timeRegistrationDTO.setDuration(durationDTO);
return timeRegistrationDTO;
}
@Override
@Transactional(readOnly = true)
public OrderSyncInfo getOrderLastSyncInfo(Order order) {
return orderSyncInfoDAO.findLastSynchronizedInfoByOrderAndConnectorName(
order, PredefinedConnectors.TIM.getName());
}
/**
* finds and returns a Tim connector
*/
private Connector getTimConnector() {
return connectorDAO
.findUniqueByName(PredefinedConnectors.TIM.getName());
}
@Override
public SynchronizationInfo getSynchronizationInfo() {
return synchronizationInfo;
}
}