Java tutorial
/* * Copyright 2012-2013 Danylo Vashchilenko * * 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 org.key2gym.business.services; import java.math.BigDecimal; import java.util.Date; import java.util.LinkedList; import java.util.List; import javax.annotation.security.RolesAllowed; import javax.persistence.LockModeType; import javax.persistence.NoResultException; import javax.persistence.Query; import org.joda.time.DateMidnight; import org.key2gym.business.api.BusinessException; import org.key2gym.business.api.SecurityRoles; import org.key2gym.business.api.SecurityViolationException; import org.key2gym.business.api.ValidationException; import org.key2gym.business.api.dtos.OrderDTO; import org.key2gym.business.api.dtos.OrderLineDTO; import org.key2gym.business.api.services.OrdersService; import org.key2gym.business.entities.Attendance; import org.key2gym.business.entities.Client; import org.key2gym.business.entities.Item; import org.key2gym.business.entities.OrderEntity; import org.key2gym.business.entities.Discount; import org.key2gym.business.entities.OrderLine; import org.key2gym.business.entities.Property; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; /** * @author Danylo Vashchilenko */ @Service("org.key2gym.business.api.services.OrdersService") @RolesAllowed({ SecurityRoles.JUNIOR_ADMINISTRATOR, SecurityRoles.SENIOR_ADMINISTRATOR, SecurityRoles.MANAGER }) @Transactional public class OrdersServiceBean extends BasicBean implements OrdersService { @Override public Integer findByClientIdAndDate(Integer clientId, DateMidnight date, Boolean createIfDoesNotExist) throws ValidationException { if (clientId == null) { throw new NullPointerException("The clientId is null."); //NOI18N } if (date == null) { throw new NullPointerException("The date is null."); //NOI18N } if (createIfDoesNotExist == null) { throw new NullPointerException("The createIfDoesNotExist is null."); //NOI18N } Client client; OrderEntity order; /* * Finds the client. */ client = em.find(Client.class, clientId); if (client == null) { throw new ValidationException(getString("Invalid.Client.ID")); } /* * Finds an order associtead with the client and issued * today. */ try { order = (OrderEntity) em.createNamedQuery("OrderEntity.findByClientAndDateRecorded") //NOI18N .setParameter("client", client) //NOI18N .setParameter("dateRecorded", date.toDate()) //NOI18N .setMaxResults(1).getSingleResult(); return order.getId(); } catch (NoResultException ex) { /* * If none is found but a new one was requested, creates one. */ if (createIfDoesNotExist) { order = OrderEntity.apply(client); em.persist(order); em.flush(); return order.getId(); } } return null; } @Override public Integer findCurrentForClientByCard(Integer card, Boolean createIfDoesNotExist) throws ValidationException { if (card == null) { throw new NullPointerException("The card is null."); //NOI18N } if (createIfDoesNotExist == null) { throw new NullPointerException("The createIfDoesNotExist is null"); //NOI18N } Client client; OrderEntity order; try { client = (Client) em.createNamedQuery("Client.findByCard") //NOI18N .setParameter("card", card) //NOI18N .setMaxResults(1).getSingleResult(); } catch (NoResultException ex) { throw new ValidationException(getString("Invalid.Client.Card")); } try { order = (OrderEntity) em.createNamedQuery("OrderEntity.findByClientAndDateRecorded") //NOI18N .setParameter("client", client) //NOI18N .setParameter("dateRecorded", getToday()) //NOI18N .setMaxResults(1).getSingleResult(); return order.getId(); } catch (NoResultException ex) { /* * If none is found but a new one was requested, create one. */ if (createIfDoesNotExist) { order = OrderEntity.apply(client); em.persist(order); em.flush(); return order.getId(); } } return null; } @Override public Integer findForAttendanceById(Integer attendanceId) throws ValidationException, BusinessException { /* * Arguments validation. */ if (attendanceId == null) { throw new NullPointerException("The attendanceId is null."); //NOI18N } Attendance attendance = null; OrderEntity order = null; attendance = (Attendance) em.find(Attendance.class, attendanceId); if (attendance == null) { throw new ValidationException(getString("Invalid.Attendance.ID")); } if (attendance.getClient() != null) { throw new BusinessException(getString("BusinessRule.Attendance.MustBeAnonymous")); } try { order = (OrderEntity) em.createNamedQuery("OrderEntity.findByAttendance") //NOI18N .setParameter("attendance", attendance) //NOI18N .setMaxResults(1).getSingleResult(); return order.getId(); } catch (NoResultException ex) { } return null; } @Override public Integer findCurrentDefault(Boolean createIfDoesNotExist) throws ValidationException { /* * Arguments validation. */ if (createIfDoesNotExist == null) { throw new NullPointerException("The createIfDoesNotExist is null."); //NOI18N } OrderEntity order; try { order = (OrderEntity) em.createNamedQuery("OrderEntity.findDefaultByDateRecorded") //NOI18N .setParameter("dateRecorded", new Date()) //NOI18N .setMaxResults(1).getSingleResult(); return order.getId(); } catch (NoResultException ex) { /* * If none was found but a new one was requested, create one. */ if (createIfDoesNotExist) { order = OrderEntity.apply(); em.persist(order); em.flush(); return order.getId(); } } return null; } @Override public List<OrderDTO> findAllByDate(DateMidnight date) throws SecurityViolationException { if (date == null) { throw new NullPointerException("The begin is null."); //NOI18N } if (!DateMidnight.now().equals(date) && !callerHasRole(SecurityRoles.MANAGER)) { throw new SecurityViolationException(getString("Security.Access.Denied")); } List<OrderEntity> orders = em.createNamedQuery("OrderEntity.findByDateRecordedOrderByIdDesc") //NOI18N .setParameter("dateRecorded", date.toDate()) //NOI18N .getResultList(); List<OrderDTO> result = new LinkedList<OrderDTO>(); for (OrderEntity order : orders) { result.add(wrapOrderEntity(order)); } return result; } @Override public List<OrderDTO> findForClientWithinPeriod(Integer id, DateMidnight begin, DateMidnight end) throws ValidationException { if (id == null) { throw new NullPointerException("The id is null."); //NOI18N } if (begin == null) { throw new NullPointerException("The begin is null."); //NOI18N } if (end == null) { throw new NullPointerException("The end is null."); //NOI18N } if (begin.isAfter(end)) { throw new ValidationException(getString("Invalid.DateRange.BeginningAfterEnding")); } Client client = em.find(Client.class, id); if (client == null) { throw new ValidationException(getString("Invalid.Client.ID")); } List<OrderEntity> financialActivities = em .createNamedQuery("OrderEntity.findByClientAndDateRecordedRangeOrderByDateRecordedDesc") //NOI18N .setParameter("client", client) //NOI18N .setParameter("rangeBegin", begin.toDate()) //NOI18N .setParameter("rangeEnd", end.toDate()) //NOI18N .getResultList(); List<OrderDTO> result = new LinkedList<OrderDTO>(); for (OrderEntity order : financialActivities) { result.add(wrapOrderEntity(order)); } return result; } @Override public void addPurchase(Integer orderId, Integer itemId, Integer discountId, Integer quantity) throws BusinessException, ValidationException, SecurityViolationException { /* * Checks the caller's roles. */ if (!callerHasAnyRole(SecurityRoles.JUNIOR_ADMINISTRATOR, SecurityRoles.SENIOR_ADMINISTRATOR, SecurityRoles.MANAGER)) { throw new SecurityViolationException(getString("Security.Operation.Denied")); } if (orderId == null) { throw new NullPointerException("The orderId is null."); } if (itemId == null) { throw new NullPointerException("The itemId is null."); //NOI18N } if (quantity == null) { throw new NullPointerException("The quantity is null."); } OrderEntity order; Item item; Discount discount; order = em.find(OrderEntity.class, orderId, LockModeType.OPTIMISTIC); if (order == null) { throw new ValidationException(getString("Invalid.Order.ID")); } /* * Performs additional roles checks. */ Boolean managerRoleRequired = (order.getAttendance() != null && !order.getAttendance().isOpen()) || !isToday(order.getDate()); if (managerRoleRequired && !callerHasRole(SecurityRoles.MANAGER)) { throw new SecurityViolationException(getString("Security.Operation.Denied")); } if (quantity <= 0) { throw new ValidationException(getString("Invalid.OrderLine.Quantity")); } item = em.find(Item.class, itemId); if (item == null) { throw new ValidationException(getString("Invalid.Item.ID")); } if (item.getItemSubscription() != null && order.getClient() == null) { throw new BusinessException(getString("BusinessRule.Order.Casual.CanNotPurchaseSubscriptions")); } if (discountId == null) { discount = null; } else { discount = em.find(Discount.class, discountId); if (discount == null) { throw new ValidationException(getString("Invalid.Discount.ID")); } } /* * Frozen items can not be ordered. */ if (item.isFrozen()) { throw new ValidationException(getString("BusinessRule.Order.ItemFrozen")); } /* * Checks the item's quantity, if it's countable. */ Integer currentQuantity = item.getQuantity(); if (currentQuantity != null) { /* * If the item is not in stock, throws a BusinessException. * The item's quantity will be decreased after all checks are performed. */ if (currentQuantity == 0) { throw new BusinessException(getString("BusinessRule.Order.ItemNotInStock")); } } /* * Business logic specific to orders associated with clients. */ if (order.getClient() != null) { Client client = order.getClient(); client.charge(item, quantity, discount); if (item.getItemSubscription() != null) { client.activate(item.getItemSubscription()); } } /* * The change should be here, because we have to change the date after * all checks have been performed. */ if (currentQuantity != null) { item.setQuantity(currentQuantity - quantity); } /* * Attempts to find an appropriate order line. */ OrderLine orderLine; Query query; if (discount == null) { query = em.createNamedQuery("OrderLine.findByOrderAndItemAndNoDiscount"); } else { query = em.createNamedQuery("OrderLine.findByOrderAndItemAndDiscount").setParameter("discount", discount); } query.setParameter("order", order).setParameter("item", item); try { orderLine = (OrderLine) query.setMaxResults(1).getSingleResult(); } catch (NoResultException ex) { orderLine = null; } /* * Creates a new order line, if none was found. */ if (orderLine == null) { orderLine = new OrderLine(); orderLine.setItem(item); orderLine.setOrder(order); orderLine.setQuantity(quantity); orderLine.setDiscount(discount); em.persist(orderLine); List<OrderLine> orderLines = order.getOrderLines(); if (orderLines == null) { orderLines = new LinkedList<>(); order.setOrderLines(orderLines); } orderLines.add(orderLine); } else { orderLine.setQuantity(orderLine.getQuantity() + quantity); } } @Override public void removePurchase(Integer orderLineId, Integer quantity) throws BusinessException, ValidationException, SecurityViolationException { if (orderLineId == null) { throw new NullPointerException("The orderLineId is null."); } if (quantity == null) { throw new NullPointerException("The quantity is null."); } /* * Checks the caller's roles. */ if (!callerHasAnyRole(SecurityRoles.JUNIOR_ADMINISTRATOR, SecurityRoles.SENIOR_ADMINISTRATOR, SecurityRoles.MANAGER)) { throw new SecurityViolationException(getString("Security.Operation.Denied")); } if (orderLineId < 0) { throw new BusinessException(getString("BusinessRule.Order.OrderLineForcedAndCanNotBeRemoved")); } if (quantity <= 0) { throw new ValidationException(getString("Invalid.OrderLine.Quantity")); } OrderEntity order; OrderLine orderLine; Item item; orderLine = em.find(OrderLine.class, orderLineId); if (orderLine == null) { throw new ValidationException(getString("Invalid.OrderLine.ID")); } order = orderLine.getOrder(); item = orderLine.getItem(); Boolean managerRoleRequired = (order.getAttendance() != null && !order.getAttendance().isOpen()) || !isToday(order.getDate()); if (managerRoleRequired && !callerHasRole(SecurityRoles.MANAGER)) { throw new SecurityViolationException(getString("Security.Operation.Denied")); } if (order.getAttendance() != null && item.getItemSubscription() != null) { throw new BusinessException(getString("BusinessRule.Order.Casual.SubscriptionCanNotBeRemoved")); } Property timeRangeMismatch = em.find(Property.class, "time_range_mismatch_penalty_item_id"); if (orderLine.getItem().getId() == timeRangeMismatch.getInteger()) { throw new BusinessException(getString("BusinessRule.Order.OrderLineForcedAndCanNotBeRemoved")); } /* * Business logic specific to orders associated with clients. */ if (order.getClient() != null) { Client client = order.getClient(); client.uncharge(item, quantity, orderLine.getDiscount()); if (item.getItemSubscription() != null) { client.deactivate(item.getItemSubscription()); } } /* * Restores the item's quantity, if it's finite. It's impossible to get * overflow here, for the item's quantity counter is being restored to * the state it already had before the item was purchased. */ if (item.getQuantity() != null) { item.setQuantity(item.getQuantity() + quantity); } /* * Decreases the quantity of the order line, and removes it, if the * quantity is now zero. */ orderLine.setQuantity(orderLine.getQuantity() - quantity); if (orderLine.getQuantity() == 0) { // EntityManager won't remove this relationship upon EntityManager.remove call order.getOrderLines().remove(orderLine); em.remove(orderLine); } } @Override public void addPayment(Integer orderId, BigDecimal amount) throws BusinessException, ValidationException, SecurityViolationException { /* * Checks the caller's roles. */ if (!callerHasAnyRole(SecurityRoles.JUNIOR_ADMINISTRATOR, SecurityRoles.SENIOR_ADMINISTRATOR, SecurityRoles.MANAGER)) { throw new SecurityViolationException(getString("Security.Operation.Denied")); } if (amount == null) { throw new NullPointerException("The amount is null."); //NOI18N } OrderEntity order = em.find(OrderEntity.class, orderId, LockModeType.OPTIMISTIC); if (order == null) { throw new ValidationException(getString("Invalid.Order.ID")); } Boolean managerRoleRequired = (order.getAttendance() != null && !order.getAttendance().isOpen()) || !isToday(order.getDate()); if (managerRoleRequired && !callerHasRole(SecurityRoles.MANAGER)) { throw new SecurityViolationException(getString("Security.Operation.Denied")); } order.recordPayment(amount); /* * If the order is associted with a client, * transfers the money to or from the client's * account. */ if (order.getClient() != null) { Client client = order.getClient(); client.transfer(new scala.math.BigDecimal(amount)); } } @Override public BigDecimal getTotalForDate(DateMidnight date) throws SecurityViolationException { if (date == null) { throw new NullPointerException("The date is null."); //NOI18N } if (!DateMidnight.now().equals(date) && !callerHasRole(SecurityRoles.MANAGER)) { throw new SecurityViolationException(getString("Security.Access.Denied")); } BigDecimal result = (BigDecimal) em.createNamedQuery("OrderEntity.sumPaymentsForDateRecorded") //NOI18N .setParameter("dateRecorded", date.toDate()) //NOI18N .getSingleResult(); if (result == null) { result = BigDecimal.ZERO; } return result.setScale(2); } @Override public OrderDTO getById(Integer id) throws ValidationException { /* * Arguments validation. */ if (id == null) { throw new NullPointerException("The id is null."); //NOI18N } OrderEntity order = em.find(OrderEntity.class, id); if (order == null) { throw new ValidationException(getString("Invalid.Order.ID")); } OrderDTO orderDTO = wrapOrderEntity(order); return orderDTO; } @Override public BigDecimal getPayment(Integer orderId) throws ValidationException { /* * Arguments validation. */ if (orderId == null) { throw new NullPointerException("The orderId is null."); //NOI18N } OrderEntity order = em.find(OrderEntity.class, orderId); if (order == null) { throw new ValidationException(getString("Invalid.Order.ID")); } return order.getPayment(); } public static OrderDTO wrapOrderEntity(OrderEntity order) { OrderDTO orderDTO = new OrderDTO(); orderDTO.setId(order.getId()); orderDTO.setDate(new DateMidnight(order.getDate())); orderDTO.setPayment(order.getPayment().setScale(2)); /* * Order lines */ List<OrderLineDTO> orderLineDTOs = new LinkedList<OrderLineDTO>(); BigDecimal total = BigDecimal.ZERO.setScale(2); if (order.getOrderLines() != null) { for (OrderLine orderLine : order.getOrderLines()) { Item item = orderLine.getItem(); Discount discount = orderLine.getDiscount(); OrderLineDTO orderLineDTO = new OrderLineDTO(); orderLineDTO.setId(orderLine.getId()); orderLineDTO.setItemId(item.getId()); orderLineDTO.setItemTitle(item.getTitle()); orderLineDTO.setItemPrice(item.getPrice()); if (discount != null) { orderLineDTO.setDiscountPercent(discount.getPercent()); orderLineDTO.setDiscountTitle(discount.getTitle()); orderLineDTO.setDiscountId(discount.getId()); } orderLineDTO.setQuantity(orderLine.getQuantity()); orderLineDTO.setTotal(orderLine.getTotal()); total = total.add(orderLine.getTotal()); orderLineDTOs.add(orderLineDTO); } } orderDTO.setOrderLines(orderLineDTOs); /* * Client */ if (order.getClient() != null) { orderDTO.setClientId(order.getClient().getId()); orderDTO.setClientFullName(order.getClient().getFullName()); } /* * Attendance */ if (order.getAttendance() != null) { orderDTO.setAttendanceId(order.getAttendance().getId()); orderDTO.setKeyTitle(order.getAttendance().getKey().getTitle()); } /* * Total */ orderDTO.setTotal(total); /* * Due */ orderDTO.setDue(total.subtract(order.getPayment())); /* * Money balance */ if (order.getClient() != null) { orderDTO.setMoneyBalance(order.getClient().getMoneyBalance().setScale(2).underlying()); } return orderDTO; } /** * Returns a Date instance that represents the today's midnight. * * @return a Date instance */ public Date getToday() { return new DateMidnight().toDate(); } /** * Checks whether the provided date is today. * * @param date the date to check * @return true, if the date's time is past the today's midnight. */ public boolean isToday(Date date) { DateMidnight today = new DateMidnight(); DateMidnight tomorrow = today.plusDays(1); return today.getMillis() <= date.getTime() && tomorrow.getMillis() > date.getTime(); } }