package de.rwth.idsg.bikeman.app.repository;
import de.rwth.idsg.bikeman.app.dto.CreateAddressDTO;
import de.rwth.idsg.bikeman.app.dto.CreateCustomerDTO;
import de.rwth.idsg.bikeman.app.dto.ViewCustomerDTO;
import de.rwth.idsg.bikeman.app.exception.AppErrorCode;
import de.rwth.idsg.bikeman.app.exception.AppException;
import de.rwth.idsg.bikeman.domain.*;
import de.rwth.idsg.bikeman.domain.Address_;
import de.rwth.idsg.bikeman.domain.Customer_;
import de.rwth.idsg.bikeman.security.AuthoritiesConstants;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.LocalDateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.EntityExistsException;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;
import javax.persistence.criteria.*;
import java.util.HashSet;
import java.util.Optional;
import java.util.Random;
@Repository
@Slf4j
public class AppCustomerRepositoryImpl implements AppCustomerRepository {
@PersistenceContext
private EntityManager em;
@Autowired
private de.rwth.idsg.bikeman.repository.TariffRepository tariffRepository;
public ViewCustomerDTO findOne(Long userId) throws AppException {
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<ViewCustomerDTO> criteria = builder.createQuery(ViewCustomerDTO.class);
Root<Customer> customer = criteria.from(Customer.class);
Join<Customer, Address> address = customer.join(Customer_.address, JoinType.LEFT);
criteria.select(
builder.construct(
ViewCustomerDTO.class,
customer.get(Customer_.customerId),
customer.get(Customer_.login),
customer.get(Customer_.firstname),
customer.get(Customer_.lastname),
customer.get(Customer_.isActivated),
customer.get(Customer_.birthday),
address.get(Address_.streetAndHousenumber),
address.get(Address_.zip),
address.get(Address_.city),
address.get(Address_.country)
)
).where(
builder.equal(customer.get(Customer_.userId), userId)
);
try {
return em.createQuery(criteria).getSingleResult();
} catch (NoResultException e) {
throw new AppException("Failed to find customer with userId " + userId, e, AppErrorCode.CONSTRAINT_FAILED);
} catch (Exception e) {
throw new AppException("Failed during database operation", e, AppErrorCode.DATABASE_OPERATION_FAILED);
}
}
@Override
@Transactional
public Optional<Customer> findByLogin (String login) {
final String q = "SELECT c FROM Customer c WHERE UPPER(c.login) = UPPER(:login)";
try {
return Optional.of(em.createQuery(q, Customer.class)
.setParameter("login", login)
.getSingleResult());
} catch (NoResultException ex) {
return Optional.empty();
}
}
@Override
@Transactional(readOnly = false)
public CreateCustomerDTO create(CreateCustomerDTO dto) {
final String openTransactionsQuery = "SELECT COUNT(c) FROM Customer c " +
"WHERE c.login = :login";
Long customerExists = em.createQuery(openTransactionsQuery, Long.class)
.setParameter("login", dto.getLogin())
.getSingleResult();
if (customerExists > 0) {
throw new AppException("Login name already exists.", AppErrorCode.CONSTRAINT_FAILED);
}
Customer customer = new Customer();
customer.setLogin(dto.getLogin());
// TODO: change to a "real" customerId
customer.setCustomerId("A" + LocalDateTime.now().getYear() + LocalDateTime.now().getMillisOfDay());
customer.setFirstname(dto.getFirstname());
customer.setLastname(dto.getLastname());
customer.setBirthday(dto.getBirthday());
customer.setIsActivated(false);
Address newAdd = new Address();
CreateAddressDTO newDtoAdd = dto.getAddress();
newAdd.setStreetAndHousenumber(newDtoAdd.getStreetAndHousenumber());
newAdd.setZip(newDtoAdd.getZip());
newAdd.setCity(newDtoAdd.getCity());
newAdd.setCountry(newDtoAdd.getCountry());
customer.setAddress(newAdd);
CardAccount newCardAccount = new CardAccount();
newCardAccount.setOwnerType(CustomerType.CUSTOMER);
// TODO: let database assign a CardId
newCardAccount.setCardId("0A" + Integer.toHexString(new Random().nextInt(Integer.MAX_VALUE)));
newCardAccount.setCardPin(dto.getCardPin());
newCardAccount.setUser(customer);
newCardAccount.setOperationState(OperationState.OPERATIVE);
customer.setCardAccount(newCardAccount);
BookedTariff newBookedTariff = new BookedTariff();
newBookedTariff.setBookedFrom(new LocalDateTime());
newBookedTariff.setBookedUntil(null);
newBookedTariff.setTariff(tariffRepository.findByName(TariffType.Ticket2000));
newBookedTariff.setUsedCardAccount(newCardAccount);
newCardAccount.setCurrentTariff(newBookedTariff);
newCardAccount.setAutoRenewTariff(true);
HashSet<Authority> authorities = new HashSet<>();
authorities.add(new Authority(AuthoritiesConstants.CUSTOMER));
customer.setAuthorities(authorities);
try {
em.persist(customer);
log.debug("Created new customer {}", customer);
} catch (EntityExistsException e) {
throw new AppException("This customer exists already.", e, AppErrorCode.CONSTRAINT_FAILED);
} catch (Exception e) {
throw new AppException("Failed to create a new customer.", e, AppErrorCode.DATABASE_OPERATION_FAILED);
}
dto.getBankAccount().setIBAN(dto.getBankAccount().getIBAN().substring(0, 5) + "*************");
return dto;
}
}