blob: 953af7b79814f14d0e666f10b596f3c832cf8e4c [file] [log] [blame]
/*******************************************************************************
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.ofbiz.webpos.transaction;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.ofbiz.base.util.Debug;
import org.apache.ofbiz.base.util.GeneralException;
import org.apache.ofbiz.base.util.UtilDateTime;
import org.apache.ofbiz.base.util.UtilGenerics;
import org.apache.ofbiz.base.util.UtilMisc;
import org.apache.ofbiz.base.util.UtilProperties;
import org.apache.ofbiz.base.util.UtilValidate;
import org.apache.ofbiz.entity.Delegator;
import org.apache.ofbiz.entity.GenericEntityException;
import org.apache.ofbiz.entity.GenericValue;
import org.apache.ofbiz.entity.condition.EntityCondition;
import org.apache.ofbiz.entity.util.EntityUtil;
import org.apache.ofbiz.order.shoppingcart.CheckOutHelper;
import org.apache.ofbiz.order.shoppingcart.ShoppingCart;
import org.apache.ofbiz.order.shoppingcart.ShoppingCartItem;
import org.apache.ofbiz.order.shoppingcart.ShoppingCart.CartPaymentInfo;
import org.apache.ofbiz.product.store.ProductStoreWorker;
import org.apache.ofbiz.service.GenericServiceException;
import org.apache.ofbiz.service.LocalDispatcher;
import org.apache.ofbiz.service.ServiceUtil;
import org.apache.ofbiz.webpos.session.WebPosSession;
public class WebPosTransaction {
public static final String resource = "WebPosUiLabels";
public static final String module = WebPosTransaction.class.getName();
public static final int NO_PAYMENT = 0;
public static final int INTERNAL_PAYMENT = 1;
public static final int EXTERNAL_PAYMENT = 2;
private CheckOutHelper ch = null;
private GenericValue txLog = null;
private String transactionId = null;
private String orderId = null;
private String partyId = null;
private boolean isOpen = false;
private int drawerIdx = 0;
private GenericValue shipAddress = null;
private WebPosSession webPosSession = null;
public WebPosTransaction(WebPosSession session) {
this.webPosSession = session;
this.partyId = "_NA_";
Delegator delegator = session.getDelegator();
ShoppingCart cart = session.getCart();
this.ch = new CheckOutHelper(session.getDispatcher(), delegator, cart);
cart.setChannelType("POS_SALES_CHANNEL");
cart.setFacilityId(session.getFacilityId());
cart.setTerminalId(session.getId());
if (session.getUserLogin() != null) {
cart.addAdditionalPartyRole(session.getUserLogin().getString("partyId"), "SALES_REP");
}
// setup the TX log
this.transactionId = delegator.getNextSeqId("PosTerminalLog");
txLog = delegator.makeValue("PosTerminalLog");
txLog.set("posTerminalLogId", this.transactionId);
txLog.set("posTerminalId", session.getId());
txLog.set("transactionId", transactionId);
txLog.set("userLoginId", session.getUserLoginId());
txLog.set("statusId", "POSTX_ACTIVE");
txLog.set("logStartDateTime", UtilDateTime.nowTimestamp());
try {
txLog.create();
cart.setTransactionId(transactionId);
} catch (GenericEntityException e) {
Debug.logError(e, "Unable to create TX log - not fatal", module);
}
Debug.logInfo("Created WebPosTransaction [" + this.transactionId + "]", module);
}
public String getUserLoginId() {
return webPosSession.getUserLoginId();
}
public int getDrawerNumber() {
return drawerIdx + 1;
}
public String getTransactionId() {
return this.transactionId;
}
public String getTerminalId() {
return webPosSession.getId();
}
public String getFacilityId() {
return webPosSession.getFacilityId();
}
public String getTerminalLogId() {
return txLog.getString("posTerminalLogId");
}
public boolean isOpen() {
this.isOpen = false;
GenericValue terminalState = this.getTerminalState();
if (terminalState != null) {
if ((terminalState.getDate("closedDate")) == null) {
this.isOpen = true;
}
}
return this.isOpen;
}
public GenericValue getTerminalState() {
Delegator delegator = webPosSession.getDelegator();
List<GenericValue> states = null;
try {
states = delegator.findList("PosTerminalState", EntityCondition.makeCondition(UtilMisc.toMap("posTerminalId", webPosSession.getId(), "startingTxId", getTransactionId())), null, null, null, false);
} catch (GenericEntityException e) {
Debug.logError(e, module);
}
states = EntityUtil.filterByDate(states, UtilDateTime.nowTimestamp(), "openedDate", "closedDate", true);
return EntityUtil.getFirst(states);
}
public void closeTx() {
if (UtilValidate.isNotEmpty(txLog)) {
txLog.set("statusId", "POSTX_CLOSED");
txLog.set("itemCount", new Long(getCart().size()));
txLog.set("logEndDateTime", UtilDateTime.nowTimestamp());
try {
txLog.store();
} catch (GenericEntityException e) {
Debug.logError(e, "Unable to store TX log - not fatal", module);
}
getCart().clear();
Debug.logInfo("Transaction closed", module);
}
}
public void paidInOut(String type) {
if (UtilValidate.isNotEmpty(txLog)) {
txLog.set("statusId", "POSTX_PAID_" + type);
txLog.set("logEndDateTime", UtilDateTime.nowTimestamp());
try {
txLog.store();
} catch (GenericEntityException e) {
Debug.logError(e, "Unable to store TX log - not fatal", module);
}
webPosSession.setCurrentTransaction(null);
Debug.logInfo("Paid "+ type, module);
}
}
public void modifyPrice(int cartLineIdx, BigDecimal price) {
ShoppingCartItem item = getCart().findCartItem(cartLineIdx);
if (UtilValidate.isNotEmpty(item)) {
Debug.logInfo("Modify item price " + item.getProductId() + "/" + price, module);
item.setBasePrice(price);
} else {
Debug.logInfo("Item " + cartLineIdx + " not found", module);
}
}
public void calcTax() {
try {
ch.calcAndAddTax(this.getStoreOrgAddress());
} catch (GeneralException e) {
Debug.logError(e, module);
}
}
public BigDecimal processSale() throws GeneralException {
Debug.logInfo("Process sale", module);
BigDecimal grandTotal = this.getGrandTotal();
BigDecimal paymentAmt = this.getPaymentTotal();
if (grandTotal.compareTo(paymentAmt) > 0) {
throw new GeneralException(UtilProperties.getMessage(resource, "WebPosNotEnoughFunds", webPosSession.getLocale()));
}
// attach the party ID to the cart
getCart().setOrderPartyId(partyId);
// validate payment methods
Debug.logInfo("Validating payment methods", module);
Map<String, ? extends Object> valRes = UtilGenerics.cast(ch.validatePaymentMethods());
if (valRes != null && ServiceUtil.isError(valRes)) {
throw new GeneralException(ServiceUtil.getErrorMessage(valRes));
}
// store the order
Debug.logInfo("Store order", module);
Map<String, ? extends Object> orderRes = UtilGenerics.cast(ch.createOrder(webPosSession.getUserLogin()));
if (orderRes != null && ServiceUtil.isError(orderRes)) {
throw new GeneralException(ServiceUtil.getErrorMessage(orderRes));
} else if (orderRes != null) {
this.orderId = (String) orderRes.get("orderId");
}
// process the payment(s)
Debug.logInfo("Processing the payment(s)", module);
Map<String, ? extends Object> payRes = null;
try {
payRes = UtilGenerics.cast(ch.processPayment(ProductStoreWorker.getProductStore(webPosSession.getProductStoreId(), webPosSession.getDelegator()), webPosSession.getUserLogin(), true));
} catch (GeneralException e) {
Debug.logError(e, module);
throw e;
}
if (payRes != null && ServiceUtil.isError(payRes)) {
throw new GeneralException(ServiceUtil.getErrorMessage(payRes));
}
// get the change due
BigDecimal change = grandTotal.subtract(paymentAmt);
// TODO notify the change due
// threaded drawer/receipt printing
// TODO open the drawer
// TODO print the receipt
// save the TX Log
txLog.set("statusId", "POSTX_SOLD");
txLog.set("orderId", orderId);
txLog.set("itemCount", new Long(getCart().size()));
txLog.set("logEndDateTime", UtilDateTime.nowTimestamp());
try {
txLog.store();
} catch (GenericEntityException e) {
Debug.logError(e, "Unable to store TX log - not fatal", module);
}
return change;
}
private synchronized GenericValue getStoreOrgAddress() {
if (UtilValidate.isEmpty(this.shipAddress)) {
// locate the store's physical address - use this for tax
GenericValue facility = null;
try {
facility = webPosSession.getDelegator().findOne("Facility", UtilMisc.toMap("facilityId", webPosSession.getFacilityId()), false);
} catch (GenericEntityException e) {
Debug.logError(e, module);
}
if (UtilValidate.isEmpty(facility)) {
return null;
}
List<GenericValue> fcp = null;
try {
fcp = facility.getRelated("FacilityContactMechPurpose", UtilMisc.toMap("contactMechPurposeTypeId", "SHIP_ORIG_LOCATION"), null, false);
} catch (GenericEntityException e) {
Debug.logError(e, module);
}
fcp = EntityUtil.filterByDate(fcp);
GenericValue purp = EntityUtil.getFirst(fcp);
if (UtilValidate.isNotEmpty(purp)) {
try {
this.shipAddress = webPosSession.getDelegator().findOne("PostalAddress",
UtilMisc.toMap("contactMechId", purp.getString("contactMechId")), false);
} catch (GenericEntityException e) {
Debug.logError(e, module);
}
}
}
return this.shipAddress;
}
public void clearPayments() {
Debug.logInfo("all payments cleared from sale", module);
getCart().clearPayments();
}
public void clearPayment(int index) {
Debug.logInfo("removing payment " + index, module);
getCart().clearPayment(index);
}
public void clearPayment(String id) {
Debug.logInfo("removing payment " + id, module);
getCart().clearPayment(id);
}
public CartPaymentInfo getPaymentInfo(int index) {
return getCart().getPaymentInfo(index);
}
public String getPaymentMethodTypeId(int index) {
return getCart().getPaymentInfo(index).paymentMethodTypeId;
}
public int checkPaymentMethodType(String paymentMethodTypeId) {
Map<String, ? extends Object> fields = UtilMisc.toMap("paymentMethodTypeId", paymentMethodTypeId, "productStoreId", webPosSession.getProductStoreId());
List<GenericValue> values = null;
try {
values = webPosSession.getDelegator().findList("ProductStorePaymentSetting", EntityCondition.makeCondition(fields), null, null, null, true);
} catch (GenericEntityException e) {
Debug.logError(e, module);
}
final String externalCode = "PRDS_PAY_EXTERNAL";
if (UtilValidate.isEmpty(values)) {
return NO_PAYMENT;
} else {
boolean isExternal = true;
Iterator<GenericValue> i = values.iterator();
while (i.hasNext() && isExternal) {
GenericValue v = i.next();
//Debug.logInfo("Testing [" + paymentMethodTypeId + "] - " + v, module);
if (!externalCode.equals(v.getString("paymentServiceTypeEnumId"))) {
isExternal = false;
}
}
if (isExternal) {
return EXTERNAL_PAYMENT;
} else {
return INTERNAL_PAYMENT;
}
}
}
public static int getNoPaymentCode() {
return NO_PAYMENT;
}
public static int getExternalPaymentCode() {
return EXTERNAL_PAYMENT;
}
public static int getInternalPaymentCode() {
return INTERNAL_PAYMENT;
}
public BigDecimal addPayment(String id, BigDecimal amount) {
return this.addPayment(id, amount, null, null);
}
public BigDecimal addPayment(String id, BigDecimal amount, String refNum, String authCode) {
Debug.logInfo("Added payment " + id + "/" + amount, module);
if ("CASH".equals(id)) {
// clear cash payments first; so there is only one
getCart().clearPayment(id);
}
getCart().addPaymentAmount(id, amount, refNum, authCode, true, true, false);
return this.getTotalDue();
}
public BigDecimal processAmount(BigDecimal amount) throws GeneralException {
if (UtilValidate.isEmpty(amount)) {
Debug.logInfo("Amount is empty; assumption is full amount : " + this.getTotalDue(), module);
amount = this.getTotalDue();
if (amount.compareTo(BigDecimal.ZERO) <= 0) {
throw new GeneralException();
}
}
return amount;
}
public synchronized void processNoPayment(String paymentMethodTypeId) {
try {
BigDecimal amount = processAmount(null);
Debug.logInfo("Processing [" + paymentMethodTypeId + "] Amount : " + amount, module);
// add the payment
addPayment(paymentMethodTypeId, amount, null, null);
} catch (GeneralException e) {
// errors handled
}
}
public synchronized void processExternalPayment(String paymentMethodTypeId, BigDecimal amount, String refNum) {
if (refNum == null) {
//TODO handle error message
return;
}
try {
amount = processAmount(amount);
Debug.logInfo("Processing [" + paymentMethodTypeId + "] Amount : " + amount, module);
// add the payment
addPayment(paymentMethodTypeId, amount, refNum, null);
} catch (GeneralException e) {
// errors handled
}
}
public String makeCreditCardVo(String cardNumber, String expDate, String firstName, String lastName) {
LocalDispatcher dispatcher = webPosSession.getDispatcher();
String expMonth = expDate.substring(0, 2);
String expYear = expDate.substring(2);
// two digit year check -- may want to re-think this
if (expYear.length() == 2) {
expYear = "20" + expYear;
}
Map<String, Object> svcCtx = new HashMap<String, Object>();
svcCtx.put("userLogin", webPosSession.getUserLogin());
svcCtx.put("partyId", partyId);
svcCtx.put("cardNumber", cardNumber);
svcCtx.put("firstNameOnCard", firstName == null ? "" : firstName);
svcCtx.put("lastNameOnCard", lastName == null ? "" : lastName);
svcCtx.put("expMonth", expMonth);
svcCtx.put("expYear", expYear);
svcCtx.put("cardType", UtilValidate.getCardType(cardNumber));
Map<String, Object> svcRes = null;
try {
svcRes = dispatcher.runSync("createCreditCard", svcCtx);
} catch (GenericServiceException e) {
Debug.logError(e, module);
return null;
}
if (ServiceUtil.isError(svcRes)) {
Debug.logError(ServiceUtil.getErrorMessage(svcRes) + " - " + svcRes, module);
return null;
} else {
return (String) svcRes.get("paymentMethodId");
}
}
public void setPaymentRefNum(int paymentIndex, String refNum, String authCode) {
Debug.logInfo("setting payment index reference number " + paymentIndex + " / " + refNum + " / " + authCode, module);
ShoppingCart.CartPaymentInfo inf = getCart().getPaymentInfo(paymentIndex);
inf.refNum[0] = refNum;
inf.refNum[1] = authCode;
}
/* CVV2 code should be entered when a card can't be swiped */
public void setPaymentSecurityCode(String paymentId, String refNum, String securityCode) {
Debug.logInfo("setting payment security code " + paymentId, module);
int paymentIndex = getCart().getPaymentInfoIndex(paymentId, refNum);
ShoppingCart.CartPaymentInfo inf = getCart().getPaymentInfo(paymentIndex);
inf.securityCode = securityCode;
inf.isSwiped = false;
}
/* Track2 data should be sent to processor when a card is swiped. */
public void setPaymentTrack2(String paymentId, String refNum, String securityCode) {
Debug.logInfo("setting payment security code " + paymentId, module);
int paymentIndex = getCart().getPaymentInfoIndex(paymentId, refNum);
ShoppingCart.CartPaymentInfo inf = getCart().getPaymentInfo(paymentIndex);
inf.securityCode = securityCode;
inf.isSwiped = true;
}
/* Postal code should be entered when a card can't be swiped */
public void setPaymentPostalCode(String paymentId, String refNum, String postalCode) {
Debug.logInfo("setting payment security code " + paymentId, module);
int paymentIndex = getCart().getPaymentInfoIndex(paymentId, refNum);
ShoppingCart.CartPaymentInfo inf = getCart().getPaymentInfo(paymentIndex);
inf.postalCode = postalCode;
}
public BigDecimal getTaxTotal() {
return getCart().getTotalSalesTax();
}
public BigDecimal getGrandTotal() {
return getCart().getGrandTotal();
}
public int getNumberOfPayments() {
return getCart().selectedPayments();
}
public BigDecimal getPaymentTotal() {
return getCart().getPaymentTotal();
}
public BigDecimal getTotalQuantity() {
return getCart().getTotalQuantity();
}
public BigDecimal getTotalDue() {
BigDecimal grandTotal = this.getGrandTotal();
BigDecimal paymentAmt = this.getPaymentTotal();
return grandTotal.subtract(paymentAmt);
}
public String addProductPromoCode(String code) {
String result = getCart().addProductPromoCode(code, webPosSession.getDispatcher());
calcTax();
return result;
}
public ShoppingCart getCart() {
return webPosSession.getCart();
}
}