BillBean.java 12 KB
/*
 * Copyright Codecrew Ry
 * 
 * All rights reserved.
 * 
 * This license applies to any software containing a notice placed by the 
 * copyright holder. Such software is herein referred to as the Software. 
 * This license covers modification, distribution and use of the Software. 
 * 
 * Any distribution and use in source and binary forms, with or without 
 * modification is not permitted without explicit written permission from the 
 * copyright owner. 
 * 
 * A non-exclusive royalty-free right is granted to the copyright owner of the 
 * Software to use, modify and distribute all modifications to the Software in 
 * future versions of the Software. 
 * 
 */
package fi.codecrew.moya.beans;

import java.io.OutputStream;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.List;

import javax.annotation.security.DeclareRoles;
import javax.annotation.security.RolesAllowed;
import javax.ejb.EJB;
import javax.ejb.EJBAccessException;
import javax.ejb.LocalBean;
import javax.ejb.Stateless;

import fi.codecrew.moya.model.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import fi.codecrew.moya.beanutil.PdfPrinter;
import fi.codecrew.moya.bortal.views.BillSummary;
import fi.codecrew.moya.entitysearch.BillSearchQuery;
import fi.codecrew.moya.enums.apps.BillPermission;
import fi.codecrew.moya.enums.apps.SpecialPermission;
import fi.codecrew.moya.exceptions.BillException;
import fi.codecrew.moya.facade.BillFacade;
import fi.codecrew.moya.facade.BillLineFacade;
import fi.codecrew.moya.facade.EventUserFacade;
import fi.codecrew.moya.facade.PlaceSlotFacade;
import fi.codecrew.moya.utilities.SearchResult;
import fi.codecrew.moya.utilities.moyamessage.MoyaEventType;

/**
 * Session Bean implementation class BillBean
 */
@Stateless
@LocalBean
@DeclareRoles({ BillPermission.S_CREATE_BILL, BillPermission.S_READ_ALL, BillPermission.S_VIEW_OWN, BillPermission.S_WRITE_ALL, SpecialPermission.S_USER, })
public class BillBean implements BillBeanLocal {

	private static final Logger logger = LoggerFactory.getLogger(BillBean.class);
	@EJB
	private BillFacade billFacade;

	@EJB
	private EventBean eventbean;
	@EJB
	private BillLineFacade billLineFacade;

	@EJB
	private PermissionBean permbean;
	@EJB
	private ProductBean productBean;

	@EJB
	private PlaceBean placebean;

	@EJB
	private UtilBean utilbean;

	@EJB
	private LoggingBeanLocal loggingBean;
	@EJB
	private EventUserFacade eventUserFacade;
	@EJB
	private ProductPBean productPBean;

	@EJB
	private DiscountBean discountBean;

	@EJB
	private LoggingBeanLocal logbean;
	@EJB
	private BillPBean billpbean;
	@EJB
	private PlaceSlotFacade psFacade;

	/**
	 * Default constructor.
	 */
	public BillBean() {

	}

	@Override
	@RolesAllowed(BillPermission.S_VIEW_OWN)
	public Bill findById(int id) {
		if (id <= 0) {
			return null;
		}
		Bill bill = billFacade.find(id);
		EventUser currentuser = permbean.getCurrentUser();
		logger.debug("bill {} user {}", bill, currentuser);

		if (bill != null && !currentuser.equals(bill.getUser())
				&& !permbean.hasPermission(BillPermission.READ_ALL)) {
			bill = null;
		}
		return bill;

	}

	@Override
	public void getPdfBillStream(Bill bill, OutputStream ostream) {
		if (bill == null) {
			return;
		}
		if (bill.getBillNumber() == null || bill.getBillNumber() <= 0) {
			generateBillNumber(bill);
		}
		new PdfPrinter(bill).output(ostream);
	}

	private void generateBillNumber(Bill bill) {
		if (bill.getBillNumber() == null || bill.getBillNumber() == 0) {
			LanEvent currEvent = eventbean.getCurrentEvent();

			Integer billnr = billFacade.getBiggestBillNumber();

			if (billnr == null || billnr < currEvent.getNextBillNumber()) {
				billnr = currEvent.getNextBillNumber();
			} else {
				++billnr;
			}
			bill.setBillNumber(billnr);
			billFacade.merge(bill);
		}

	}

	// @Override
	// public Bill createEmptyBill(User shoppingUser) throws
	// PermissionDeniedException {
	// if (permbean.isCurrentUser(shoppingUser)) {
	// permbean.fatalPermission(BillPermission.CREATE_BILL,
	// "No permission to create empty bill for self");
	// } else {
	// permbean.fatalPermission(BillPermission.WRITE_ALL,
	// "Trying to create bill to someone else without sufficient permission");
	// }
	//
	// LanEvent event = eventbean.getCurrentEvent();
	// Bill ret = new Bill(event, shoppingUser);
	// billFacade.create(ret);
	// ret.setUser(shoppingUser);
	// em.flush();
	// logger.debug("Created bill with id {} and user {}", ret.getId(),
	// ret.getUser());
	// return ret;
	// }

	// @Override
	// @RolesAllowed("SHOP/EXECUTE")
	// public BillLine addProductToBill(Bill bill, Product product, BigDecimal
	// count) throws PermissionDeniedException {
	//
	// // If bill number > 0 bill has been sent and extra privileges are needed
	// // to modify.
	// boolean iscurrent = permissionbean.isCurrentUser(bill.getUser());
	// Integer billnr = bill.getBillNumber();
	// if (!iscurrent || billnr != null) {
	// permbean.fatalPermission(BillPermission.WRITE_ALL,
	// "User tried to modify bill ", bill, "without sufficient permissions");
	// }
	// BillLine line = new BillLine(bill, product.getName(),
	// product.getUnitName(), count, product.getPrice(), product.getVat());
	// line.setLineProduct(product);
	// billLineFacade.create(line);
	//
	// List<Discount> discounts = productBean.getActiveDiscounts(product,
	// count);
	//
	// for (Discount disc : discounts) {
	// BigDecimal unitPrice =
	// product.getPrice().subtract(product.getPrice().multiply(disc.getPercentage())).negate().setScale(2,
	// RoundingMode.HALF_UP);
	// BigDecimal vatPrice =
	// product.getVat().subtract(product.getVat().multiply(disc.getPercentage())).negate().setScale(2,
	// RoundingMode.HALF_UP);
	//
	// BillLine discountLine = new BillLine(bill, disc.getShortdesc(),
	// product.getUnitName(), count, unitPrice, vatPrice);
	// billLineFacade.create(discountLine);
	//
	// }
	//
	// em.flush();
	// return line;
	// }

	@Override
	@RolesAllowed(BillPermission.S_READ_ALL)
	public List<Bill> findAll() {

		return billFacade.findAll();
	}

	@Override
	@RolesAllowed(BillPermission.S_READ_ALL)
	public Collection<BillSummary> getBillLineSummary() {
		Collection<BillSummary> ret = billLineFacade.getLineSummary(eventbean
				.getCurrentEvent());

		return ret;
	}

	/**
	 * We will mark bill paid in different transaction. That's because we don't
	 * wont it to fail if something other fails.
	 *
	 * @param bill
	 * @param when
	 * @param useCredits
	 * @return
	 * @throws BillException
	 */

	@Override
	@RolesAllowed({ BillPermission.S_WRITE_ALL })
	public Bill markPaid(Bill bill, Calendar when, boolean useCredits) throws BillException {

		return billpbean.markPaid(bill, when, useCredits);
	}

	@Override
	@RolesAllowed({ BillPermission.S_CREATE_BILL, BillPermission.S_WRITE_ALL })
	public Bill createBill(Bill bill) {
		if (!permbean.isCurrentUser(bill.getUser()) && !permbean.hasPermission(BillPermission.WRITE_ALL)) {
			loggingBean.sendMessage(MoyaEventType.BILL_ERROR, permbean.getCurrentUser(), "Not enought rights to create bill for user: ", bill.getUser());
			throw new EJBAccessException("Could not create bill for another user");
		}

		// if there is autoproducts, check that they exists in bill
		for (Product product : productBean.listUserShoppableProducts()) {
			if (product.isUsershopAutoproduct()) {
				boolean containsAutoproduct = false;
				for (BillLine line : bill.getBillLines()) {
					if (line.getLineProduct() != null && line.getLineProduct().equals(product) && line.getQuantity().compareTo(BigDecimal.ONE) >= 0) {
						containsAutoproduct = true;
					}
				}

				if (!containsAutoproduct) {
					BillLine line = new BillLine(bill, product, BigDecimal.ONE);

					bill.getBillLines().add(line);
				}
			}
		}

		billFacade.create(bill);
		generateBillNumber(bill);
		createPlaceslots(bill);

		// If we allow bills with zero price
		// we will mark them immediately as paid.
		if (bill.getTotalPrice().compareTo(BigDecimal.ZERO) == 0
			&& eventbean.getPropertyBoolean(LanEventPropertyKey.ALLOW_FREE_BILLS)) {
			bill = billpbean.markPaid(bill, Calendar.getInstance(), false);
		}

		return bill;
	}

	/**
	 * Create place slots for the bill
	 *
	 * @param bill
	 */
	private void createPlaceslots(Bill bill) {
		for (BillLine bl : bill.getBillLines()) {
			if (bl == null) {
				continue;
			}
			Product prod = bl.getLineProduct();
			if (prod == null || prod.getPlaces() == null || prod.getPlaces().isEmpty()) {
				// Not a place product.
				continue;
			}
			int count = bl.getQuantity().intValue();

			Date now = new Date();

			/*
			THIS PLACE IS NOT ON HERE
			now it is possibly to create and pay bill, but not receive any placeslots!!

			long reserveLimit = eventbean.getPropertyLong(LanEventPropertyKey.RESERVE_UNPAID_SLOT_PERCENT);
			if (count * 100 / prod.getPlaces().size() > reserveLimit) {
				long limitCount = prod.getPlaces().size() * reserveLimit / 100;
				logbean.sendMessage(MoyaEventType.BILL_INFO, bill.getUser(), "User created bill ", bill.getId(), " for product '", prod.getName(), "' for '", count, "' products, which is bigger than reservation limit. '", limitCount, "', (", reserveLimit, "%)");
			}
			*/

			for (int i = 0; i < count; ++i) {
				PlaceSlot ps = new PlaceSlot();
				ps.setBill(bill);
				ps.setCreated(now);
				ps.setProduct(prod);
				psFacade.create(ps);
			}
		}
	}

	@RolesAllowed({ BillPermission.S_WRITE_ALL })
	public Bill save(Bill bill) {
		return billFacade.merge(bill);
	}

	@Override
	@RolesAllowed({ BillPermission.S_VIEW_OWN, BillPermission.S_READ_ALL })
	public List<Bill> find(EventUser user) {
		if (!permbean.isCurrentUser(user) && !permbean.hasPermission(BillPermission.READ_ALL)) {
			loggingBean.sendMessage(MoyaEventType.BILL_ERROR, permbean.getCurrentUser(), "Not enought rights to get bill list for user ", user);
			throw new EJBAccessException("Could not list bills for another user");
		}
		return billFacade.find(user);
	}

	@Override
	@RolesAllowed({ BillPermission.S_VIEW_OWN, BillPermission.S_WRITE_ALL })
	public Bill expireBill(Bill bill) {
		if (!permbean.isCurrentUser(bill.getUser()) && !permbean.hasPermission(BillPermission.WRITE_ALL)) {
			loggingBean.sendMessage(MoyaEventType.BILL_ERROR, permbean.getCurrentUser(), "Not enought rights to expire a bill for user", bill);
			throw new EJBAccessException("Could not list bills for another user");
		}

		bill = billFacade.reload(bill);
		bill.markExpired();
		return bill;
	}

	@Override
	public Bill addProductToBill(Bill bill, Product product, BigDecimal count) {
		return this.addProductToBill(bill, product, count, null, null);
	}

	@Override
	public Bill addProductToBill(Bill bill, Product product, BigDecimal count, FoodWave foodwave, String additionalDescription) {

		// If bill number > 0 bill has been sent and extra privileges are needed
		// to modify.
		// if (!iscurrent || billnr != null) {
		// permbean.fatalPermission(BillPermission.WRITE_ALL,
		// "User tried to modify bill ", bill,
		// "without sufficient permissions");
		// }
		if (bill.getBillLines() == null) {
			bill.setBillLines(new ArrayList<BillLine>());
		}

		bill.getBillLines().add(new BillLine(bill, product, count, foodwave, additionalDescription));
		BigDecimal discountPrice = product.getPrice();
		for (Discount disc : discountBean.getActiveDiscountsByProduct(product, count, bill.getSentDate(), bill.getUser())) {
			BillLine line = new BillLine(bill, product, disc,discountPrice, count);
			// unitPrice is negative, so add. Do not subtract
			discountPrice = discountPrice.add(line.getUnitPrice());
			bill.getBillLines().add(line);
		}

		return bill;
	}

	@Override
	@RolesAllowed(BillPermission.S_READ_ALL)
	public SearchResult<Bill> findAll(BillSearchQuery q) {
		return billFacade.find(q);
	}

	@RolesAllowed(BillPermission.S_VIEW_OWN)
	public SearchResult<Bill> findUsers(BillSearchQuery q) {
		if (q.getUser() == null) {
			q.setUser(permbean.getCurrentUser());
		} else if (!permbean.hasPermission(BillPermission.VIEW_OWN) && !permbean.isCurrentUser(q.getUser())) {
			throw new EJBAccessException("Tried to fetch bills for wrong user");
		}

		return billFacade.find(q);
	}

}