Package com.excilys.ebi.bank.service.impl

Source Code of com.excilys.ebi.bank.service.impl.BankServiceImpl

/**
* Copyright 2011-2012 eBusiness Information, Groupe Excilys (www.excilys.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.excilys.ebi.bank.service.impl;

import static com.excilys.ebi.bank.model.entity.Operation.newOperation;
import static com.excilys.ebi.bank.util.Asserts.isTrue;
import static com.excilys.ebi.bank.util.Asserts.notNull;
import static com.google.common.collect.Maps.newHashMap;
import static com.google.common.collect.Maps.uniqueIndex;
import static java.math.BigDecimal.ZERO;
import static java.util.Collections.reverse;
import static org.hibernate.Hibernate.initialize;
import static org.joda.time.DateTime.now;

import java.math.BigDecimal;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.validation.constraints.Min;

import org.joda.time.DateMidnight;
import org.joda.time.DateTime;
import org.joda.time.YearMonth;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.security.access.prepost.PostAuthorize;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import com.excilys.ebi.bank.dao.AccountDao;
import com.excilys.ebi.bank.dao.CardDao;
import com.excilys.ebi.bank.dao.OperationDao;
import com.excilys.ebi.bank.dao.OperationStatusRefDao;
import com.excilys.ebi.bank.dao.OperationTypeRefDao;
import com.excilys.ebi.bank.dao.UserDao;
import com.excilys.ebi.bank.model.Calendar;
import com.excilys.ebi.bank.model.IConstants;
import com.excilys.ebi.bank.model.entity.Account;
import com.excilys.ebi.bank.model.entity.Card;
import com.excilys.ebi.bank.model.entity.Operation;
import com.excilys.ebi.bank.model.entity.User;
import com.excilys.ebi.bank.model.entity.ref.OperationSign;
import com.excilys.ebi.bank.model.entity.ref.OperationStatus;
import com.excilys.ebi.bank.model.entity.ref.OperationStatusRef;
import com.excilys.ebi.bank.model.entity.ref.OperationType;
import com.excilys.ebi.bank.model.entity.ref.OperationTypeRef;
import com.excilys.ebi.bank.service.BankService;
import com.excilys.ebi.bank.service.UnsufficientBalanceException;
import com.google.common.base.Function;
import com.googlecode.ehcache.annotations.Cacheable;
import com.googlecode.ehcache.annotations.KeyGenerator;

@Service
@Transactional(readOnly = true)
@Validated
public class BankServiceImpl implements BankService {

  private static final int PAGE_SIZE = 20;

  @Autowired
  private UserDao userDao;

  @Autowired
  private AccountDao accountDao;

  @Autowired
  private OperationDao operationDao;

  @Autowired
  private CardDao cardDao;

  @Autowired
  private OperationStatusRefDao operationStatusDao;

  @Autowired
  private OperationTypeRefDao operationTypeDao;

  @Override
  @Cacheable(cacheName = IConstants.Cache.ENTITY_CACHE, keyGenerator = @KeyGenerator(name = IConstants.Cache.KEY_GENERATOR))
  public Integer findAccountIdByNumber(String accountNumber) {
    Account account = accountDao.findByNumber(accountNumber);
    notNull(account, "account with number {} not found", accountNumber);
    return account.getId();
  }

  @Override
  @Cacheable(cacheName = IConstants.Cache.ENTITY_CACHE, keyGenerator = @KeyGenerator(name = IConstants.Cache.KEY_GENERATOR))
  public Integer findCardIdByNumber(String cardNumber) {
    Card card = cardDao.findByNumber(cardNumber);
    notNull(cardNumber, "card with number {} not found", cardNumber);
    return card.getId();
  }

  @Override
  public List<Account> findAccountsByUser(User user) {
    return accountDao.findByUsersOrderByNumberAsc(user);
  }

  @Override
  public List<Account> findAccountsByUserFetchCardsOrderByNumberAsc(User user) {
    return accountDao.findByUserFetchCardsOrderByNumberAsc(user);
  }

  @Override
  @PostAuthorize("hasPermission(returnObject, 'read')")
  public Account findAccountByNumberFetchCards(String accountNumber) {

    Account account = accountDao.findByNumber(accountNumber);
    notNull(account, "account with number {} not found", accountNumber);
    initialize(account.getCards());
    return account;
  }

  @Override
  public Page<Operation> findNonCardOperationsByAccountIdAndYearMonth(Integer accountId, YearMonth yearMonth, int page) {

    Pageable pageable = new PageRequest(page, PAGE_SIZE);
    return operationDao.findNonCardByAccountIdAndYearMonth(accountId, yearMonth, pageable);
  }

  @Override
  public Map<Card, BigDecimal[]> sumResolvedCardOperationsByAccountIdAndYearMonth(Integer accountId, YearMonth yearMonth) {

    Collection<Card> cards = cardDao.findByAccountIdOrderByNumberAsc(accountId);

    Map<Card, BigDecimal[]> sums = newHashMap();
    for (Card card : cards) {
      sums.put(card, new BigDecimal[] { ZERO, ZERO });
    }

    Map<Integer, Operation> creditSumsIndexedByCardId = uniqueIndex(
        operationDao.sumResolvedAmountByAccountIdAndYearMonthAndSignGroupByCard(accountId, yearMonth, OperationSign.CREDIT), new Function<Operation, Integer>() {
          @Override
          public Integer apply(Operation input) {
            return input.getCard().getId();
          }
        });

    Map<Integer, Operation> debitSumsIndexedByCardId = uniqueIndex(
        operationDao.sumResolvedAmountByAccountIdAndYearMonthAndSignGroupByCard(accountId, yearMonth, OperationSign.DEBIT), new Function<Operation, Integer>() {
          @Override
          public Integer apply(Operation input) {
            return input.getCard().getId();
          }
        });

    for (Entry<Card, BigDecimal[]> entry : sums.entrySet()) {
      Operation creditSum = creditSumsIndexedByCardId.get(entry.getKey().getId());
      if (creditSum != null) {
        entry.getValue()[0] = creditSum.getAmount();
      }
      Operation debitSum = debitSumsIndexedByCardId.get(entry.getKey().getId());
      if (debitSum != null) {
        entry.getValue()[1] = debitSum.getAmount();
      }
    }

    return sums;
  }

  @Override
  public BigDecimal sumResolvedAmountByAccountIdAndYearMonthAndSign(Integer accountId, YearMonth yearMonth, OperationSign sign) {
    return operationDao.sumResolvedAmountByAccountIdAndYearMonthAndSign(accountId, yearMonth, sign);
  }

  @Override
  public Page<Operation> findResolvedCardOperationsByAccountIdAndYearMonth(Integer accountId, YearMonth yearMonth, int page) {
    return operationDao.findCardOperationsByAccountIdAndYearMonthAndStatus(accountId, yearMonth, OperationStatus.RESOLVED, new PageRequest(page, PAGE_SIZE));
  }

  @Override
  public BigDecimal sumResolvedCardAmountByAccountIdAndYearMonthAndSign(Integer accountId, YearMonth yearMonth, OperationSign sign) {
    return operationDao.sumCardAmountByAccountIdAndYearMonthAndSignAndStatus(accountId, yearMonth, sign, OperationStatus.RESOLVED);
  }

  @Override
  public Page<Operation> findResolvedCardOperationsByCardIdAndYearMonth(Integer cardId, YearMonth yearMonth, int page) {
    return operationDao.findCardOperationsByCardIdAndYearMonthAndStatus(cardId, yearMonth, OperationStatus.RESOLVED, new PageRequest(page, PAGE_SIZE));
  }

  @Override
  public BigDecimal sumResolvedCardAmountByCardIdAndYearMonthAndSign(Integer cardId, YearMonth yearMonth, OperationSign sign) {
    return operationDao.sumCardAmountByCardIdAndYearMonthAndSignAndStatus(cardId, yearMonth, sign, OperationStatus.RESOLVED);
  }

  @Override
  public Page<Operation> findPendingCardOperationsByAccountId(Integer accountId, int page) {
    return operationDao.findCardOperationsByAccountIdAndYearMonthAndStatus(accountId, null, OperationStatus.PENDING, new PageRequest(page, PAGE_SIZE));
  }

  @Override
  public BigDecimal sumPendingCardAmountByAccountIdAndSign(Integer accountId, OperationSign sign) {

    return operationDao.sumCardAmountByAccountIdAndYearMonthAndSignAndStatus(accountId, null, sign, OperationStatus.PENDING);
  }

  @Override
  public Page<Operation> findPendingCardOperationsByCardId(Integer cardId, int page) {
    return operationDao.findCardOperationsByCardIdAndYearMonthAndStatus(cardId, null, OperationStatus.PENDING, new PageRequest(page, PAGE_SIZE));
  }

  @Override
  public BigDecimal sumPendingCardAmountByCardIdAndSign(Integer cardId, OperationSign sign) {
    return operationDao.sumCardAmountByCardIdAndYearMonthAndSignAndStatus(cardId, null, sign, OperationStatus.PENDING);
  }

  @Override
  public Page<Operation> findTransferOperationsByAccountId(Integer accountId, int page) {
    return operationDao.findTransferByAccountId(accountId, null);
  }

  @Override
  @Transactional(readOnly = false)
  public void performTransfer(Integer debitedAccountId, Integer creditedAccountId, @Min(10) BigDecimal amount) throws UnsufficientBalanceException {

    isTrue(!debitedAccountId.equals(creditedAccountId), "accounts must be different");

    Account debitedAccount = accountDao.findOne(debitedAccountId);
    notNull(debitedAccount, "account with number {} not found", debitedAccount);

    if (debitedAccount.getBalance().compareTo(amount) < 0) {
      throw new UnsufficientBalanceException();
    }

    Account creditedAccount = accountDao.findOne(creditedAccountId);
    notNull(creditedAccount, "account with number {} not found", creditedAccount);

    debitedAccount.setBalance(debitedAccount.getBalance().subtract(amount));
    creditedAccount.setBalance(creditedAccount.getBalance().add(amount));

    DateTime now = now();
    OperationStatusRef status = operationStatusDao.findOne(OperationStatus.RESOLVED);
    OperationTypeRef type = operationTypeDao.findOne(OperationType.TRANSFER);

    Operation debitOperation = newOperation().withName("transfert -" + amount).withAccount(debitedAccount).withAmount(amount.negate()).withDate(now).withStatus(status)
        .withType(type).build();
    Operation creditOperation = newOperation().withName("transfert +" + amount).withAccount(creditedAccount).withAmount(amount).withDate(now).withStatus(status).withType(type)
        .build();

    operationDao.save(debitOperation);
    operationDao.save(creditOperation);
  }

  @Override
  public long countUsers() {
    return userDao.count();
  }

  @Override
  public long countAccounts() {
    return accountDao.count();
  }

  @Override
  public long countOperations() {
    return operationDao.count();
  }

  @Override
  public Calendar getCalendar(Integer year, Integer month) {

    Calendar calendar = new Calendar();

    // build months
    List<DateTime> months = calendar.getMonths();

    DateMidnight thisMonth = getDefaultDateTime().toDateMidnight().withDayOfMonth(1);
    months.add(thisMonth.toDateTime());

    // display last 6 months
    while (months.size() < 6) {
      thisMonth = thisMonth.minusMonths(1);
      months.add(thisMonth.toDateTime());
    }

    reverse(months);

    // build selectedMonth
    if (year != null) {
      notNull(month, "month is required if year is specified");
      DateTime selectedMonth = new DateMidnight().withDayOfMonth(1).withYear(year).withMonthOfYear(month).toDateTime();
      calendar.setSelectedMonth(selectedMonth);
    }

    return calendar;
  }

  @Override
  public DateTime getDefaultDateTime() {
    return operationDao.getLastOperationDate();
    // return now();
  }
}
TOP

Related Classes of com.excilys.ebi.bank.service.impl.BankServiceImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.