Package org.ofbiz.pos

Source Code of org.ofbiz.pos.PosTransaction

/*******************************************************************************
* 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.ofbiz.pos;

import java.io.PrintWriter;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Locale;
import java.util.Map;

import javolution.util.FastList;
import javolution.util.FastMap;
import net.xoetrope.xui.data.XModel;
import net.xoetrope.xui.helper.SwingWorker;

import org.ofbiz.accounting.payment.PaymentGatewayServices;
import org.ofbiz.base.util.Debug;
import org.ofbiz.base.util.GeneralException;
import org.ofbiz.base.util.Log4jLoggerWriter;
import org.ofbiz.base.util.UtilDateTime;
import org.ofbiz.base.util.UtilFormatOut;
import org.ofbiz.base.util.UtilGenerics;
import org.ofbiz.base.util.UtilMisc;
import org.ofbiz.base.util.UtilNumber;
import org.ofbiz.base.util.UtilProperties;
import org.ofbiz.base.util.UtilValidate;
import org.ofbiz.base.util.collections.LifoSet;
import org.ofbiz.entity.Delegator;
import org.ofbiz.entity.GenericEntityException;
import org.ofbiz.entity.GenericValue;
import org.ofbiz.entity.condition.EntityCondition;
import org.ofbiz.entity.condition.EntityFunction;
import org.ofbiz.entity.condition.EntityOperator;
import org.ofbiz.entity.model.DynamicViewEntity;
import org.ofbiz.entity.model.ModelKeyMap;
import org.ofbiz.entity.transaction.GenericTransactionException;
import org.ofbiz.entity.transaction.TransactionUtil;
import org.ofbiz.entity.util.EntityFindOptions;
import org.ofbiz.entity.util.EntityListIterator;
import org.ofbiz.entity.util.EntityUtil;
import org.ofbiz.guiapp.xui.XuiSession;
import org.ofbiz.order.shoppingcart.CartItemModifyException;
import org.ofbiz.order.shoppingcart.CheckOutHelper;
import org.ofbiz.order.shoppingcart.ItemNotFoundException;
import org.ofbiz.order.shoppingcart.ShoppingCart;
import org.ofbiz.order.shoppingcart.ShoppingCartItem;
import org.ofbiz.order.shoppinglist.ShoppingListEvents;
import org.ofbiz.party.contact.ContactMechWorker;
import org.ofbiz.pos.component.Journal;
import org.ofbiz.pos.component.Output;
import org.ofbiz.pos.device.DeviceLoader;
import org.ofbiz.pos.device.impl.Receipt;
import org.ofbiz.pos.screen.ClientProfile;
import org.ofbiz.pos.screen.LoadSale;
import org.ofbiz.pos.screen.PosScreen;
import org.ofbiz.pos.screen.SaveSale;
import org.ofbiz.product.config.ProductConfigWrapper;
import org.ofbiz.product.config.ProductConfigWrapper.ConfigOption;
import org.ofbiz.product.product.ProductWorker;
import org.ofbiz.product.store.ProductStoreWorker;
import org.ofbiz.service.GenericServiceException;
import org.ofbiz.service.LocalDispatcher;
import org.ofbiz.service.ServiceUtil;

@SuppressWarnings("serial")
public class PosTransaction implements Serializable {

    public static final int scale = UtilNumber.getBigDecimalScale("order.decimals");
    public static final int rounding = UtilNumber.getBigDecimalRoundingMode("order.rounding");
    public static final BigDecimal ZERO = (BigDecimal.ZERO).setScale(scale, rounding);

    public static final String resource = "PosUiLabels";
    public static final String module = PosTransaction.class.getName();
    public static final int NO_PAYMENT = 0;
    public static final int INTERNAL_PAYMENT = 1;
    public static final int EXTERNAL_PAYMENT = 2;

    private static PrintWriter defaultPrintWriter = new Log4jLoggerWriter(Debug.getLogger(module));
    private static PosTransaction currentTx = null;
    private static LifoSet<PosTransaction> savedTx = new LifoSet<PosTransaction>();

    protected XuiSession session = null;
    protected ShoppingCart cart = null;
    protected CheckOutHelper ch = null;
    protected PrintWriter trace = null;
    protected GenericValue txLog = null;

    protected String productStoreId = null;
    protected String transactionId = null;
    protected String facilityId = null;
    protected String terminalId = null;
    protected String currency = null;
    protected String orderId = null;
    protected String partyId = null;
    protected Locale locale = null;
    protected boolean isOpen = false;
    protected int drawerIdx = 0;

    private GenericValue shipAddress = null;
    private Map<String, Integer> skuDiscounts = FastMap.newInstance();
    private int cartDiscount = -1;


    public PosTransaction(XuiSession session) {
        this.session = session;
        this.terminalId = session.getId();
        this.partyId = "_NA_";
        this.trace = defaultPrintWriter;

        this.productStoreId = (String) session.getAttribute("productStoreId");
        this.facilityId = (String) session.getAttribute("facilityId");
        this.currency = (String) session.getAttribute("currency");
//        this.locale = (Locale) session.getAttribute("locale"); This is legacy code and may come (demo) from ProductStore.defaultLocaleString defined in demoRetail and is incompatible with how localisation is handled in the POS
        this.locale = Locale.getDefault();

        this.cart = new ShoppingCart(session.getDelegator(), productStoreId, locale, currency);
        this.ch = new CheckOutHelper(session.getDispatcher(), session.getDelegator(), cart);
        cart.setChannelType("POS_SALES_CHANNEL");
        cart.setTransactionId(transactionId);
        cart.setFacilityId(facilityId);
        cart.setTerminalId(terminalId);
        if (session.getUserLogin() != null) {
            cart.addAdditionalPartyRole(session.getUserLogin().getString("partyId"), "SALES_REP");
        }

        // setup the TX log
        this.transactionId = session.getDelegator().getNextSeqId("PosTerminalLog");
        txLog = session.getDelegator().makeValue("PosTerminalLog");
        txLog.set("posTerminalLogId", this.transactionId);
        txLog.set("posTerminalId", terminalId);
        txLog.set("transactionId", transactionId);
        txLog.set("userLoginId", session.getUserId());
        txLog.set("statusId", "POSTX_ACTIVE");
        txLog.set("logStartDateTime", UtilDateTime.nowTimestamp());
        try {
            txLog.create();
        } catch (GenericEntityException e) {
            Debug.logError(e, "Unable to create TX log - not fatal", module);
        }

        currentTx = this;
        trace("transaction created");
    }

    public XuiSession getSession() {
        return session;
    }

    public String getUserId() {
        return session.getUserId();
    }

    public String getPartyId() {
        return partyId;
    }

    public void setPartyId(String partyId) {
        this.partyId = partyId;
        this.cart.setPlacingCustomerPartyId(partyId);
        try {
            this.cart.setUserLogin(session.getUserLogin(), session.getDispatcher());
        } catch (CartItemModifyException e) {
            Debug.logError(e, module);
        }
    }

    public int getDrawerNumber() {
        return drawerIdx + 1;
    }

    public void popDrawer() {
        DeviceLoader.drawer[drawerIdx].openDrawer();
    }

    public String getTransactionId() {
        return this.transactionId;
    }

    public String getTerminalId() {
        return this.terminalId;
    }

    public String getFacilityId() {
        return this.facilityId;
    }

    public String getTerminalLogId() {
        return txLog.getString("posTerminalLogId");
    }

    public boolean isOpen() {
        if (!this.isOpen) {
            GenericValue terminalState = this.getTerminalState();
            if (terminalState != null) {
                this.isOpen = true;
            } else {
                this.isOpen = false;
            }
        }
        return this.isOpen;
    }

    public boolean isEmpty() {
        return (UtilValidate.isEmpty(cart));
    }

    public List<GenericValue> lookupItem(String sku) throws GeneralException {
        return ProductWorker.findProductsById(session.getDelegator(), sku, null);
    }

    public String getOrderId() {
        return this.orderId;
    }

    public BigDecimal getTaxTotal() {
        return cart.getTotalSalesTax();
    }

    public BigDecimal getGrandTotal() {
        return cart.getGrandTotal();
    }

    public int getNumberOfPayments() {
        return cart.selectedPayments();
    }

    public BigDecimal getPaymentTotal() {
        return cart.getPaymentTotal();
    }

    public BigDecimal getTotalDue() {
        BigDecimal grandTotal = this.getGrandTotal();
        BigDecimal paymentAmt = this.getPaymentTotal();
        return grandTotal.subtract(paymentAmt);
    }

    public int size() {
        return cart.size();
    }

    public Map<String, Object> getItemInfo(int index) {
        ShoppingCartItem item = cart.findCartItem(index);
        Map<String, Object> itemInfo = FastMap.newInstance();
        itemInfo.put("productId", item.getProductId());
        String description = item.getDescription();
        if (UtilValidate.isEmpty(description)) {
            itemInfo.put("description", item.getName());
        } else {
            itemInfo.put("description", description);
        }
        itemInfo.put("quantity", UtilFormatOut.formatQuantity(item.getQuantity()));
        itemInfo.put("subtotal", UtilFormatOut.formatPrice(item.getItemSubTotal()));
        itemInfo.put("isTaxable", item.taxApplies() ? "T" : " ");

        itemInfo.put("discount", "");
        itemInfo.put("adjustments", "");
        if (item.getOtherAdjustments().compareTo(BigDecimal.ZERO) != 0) {
            itemInfo.put("itemDiscount", UtilFormatOut.padString(
                    UtilProperties.getMessage(resource, "PosItemDiscount", locale), Receipt.pridLength[0] + 1, true, ' '));
            itemInfo.put("adjustments", UtilFormatOut.formatPrice(item.getOtherAdjustments()));
        }

        if (isAggregatedItem(item.getProductId())) {
            ProductConfigWrapper pcw = null;
            pcw = item.getConfigWrapper();
            itemInfo.put("basePrice", UtilFormatOut.formatPrice(pcw.getDefaultPrice()));
        } else {
            itemInfo.put("basePrice", UtilFormatOut.formatPrice(item.getBasePrice()));
        }
        return itemInfo;
    }

    public List<Map<String, Object>> getItemConfigInfo(int index) {
        List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
        // I think I need to initialize the list in a special way
        // to use foreach in receipt.java

        ShoppingCartItem item = cart.findCartItem(index);
        if (this.isAggregatedItem(item.getProductId())) {
            ProductConfigWrapper pcw = null;
            pcw = item.getConfigWrapper();
            List<ConfigOption> selected = pcw.getSelectedOptions();
            for(ConfigOption configoption : selected) {
                Map<String, Object> itemInfo = FastMap.newInstance();
                if (configoption.isSelected() && !configoption.isDefault()) {
                    itemInfo.put("productId", "");
                    itemInfo.put("sku", "");
                    itemInfo.put("configDescription", configoption.getDescription(locale));
                    itemInfo.put("configQuantity", UtilFormatOut.formatQuantity(item.getQuantity()));
                    itemInfo.put("configBasePrice", UtilFormatOut.formatPrice(configoption.getOffsetPrice()));
                    //itemInfo.put("isTaxable", item.taxApplies() ? "T" : " ");
                    list.add(itemInfo);
                }
            }
        }
        return list;
    }

    public Map<String, Object> getPaymentInfo(int index) {
        ShoppingCart.CartPaymentInfo inf = cart.getPaymentInfo(index);
        GenericValue infValue = inf.getValueObject(session.getDelegator());
        GenericValue paymentPref = null;
        try {
            Map<String, Object> fields = FastMap.newInstance();
            fields.put("paymentMethodTypeId", inf.paymentMethodTypeId);
            if (inf.paymentMethodId != null) {
                fields.put("paymentMethodId", inf.paymentMethodId);
            }
            fields.put("maxAmount", inf.amount);
            fields.put("orderId", this.getOrderId());

            List<GenericValue> paymentPrefs = session.getDelegator().findByAnd("OrderPaymentPreference", fields);
            if (UtilValidate.isNotEmpty(paymentPrefs)) {
                //Debug.log("Found some prefs - " + paymentPrefs.size(), module);
                if (paymentPrefs.size() > 1) {
                    Debug.logError("Multiple OrderPaymentPreferences found for the same payment method!", module);
                } else {
                    paymentPref = EntityUtil.getFirst(paymentPrefs);
                    //Debug.log("Got the first pref - " + paymentPref, module);
                }
            } else {
                Debug.logError("No OrderPaymentPreference found - " + fields, module);
            }
        } catch (GenericEntityException e) {
            Debug.logError(e, module);
        }
        //Debug.log("PaymentPref - " + paymentPref, module);

        Map<String, Object> payInfo = FastMap.newInstance();

        // locate the auth info
        GenericValue authTrans = null;
        if (paymentPref != null) {
            authTrans = PaymentGatewayServices.getAuthTransaction(paymentPref);
            if (authTrans != null) {
                payInfo.putAll(authTrans);

                String authInfoString = "Ref: " + authTrans.getString("referenceNum") + " Auth: " + authTrans.getString("gatewayCode");
                payInfo.put("authInfoString", authInfoString);
            } else {
                Debug.logError("No Authorization transaction found for payment preference - " + paymentPref, module);
            }
        } else {
            Debug.logError("Payment preference is empty!", module);
            return payInfo;
        }
        //Debug.log("AuthTrans - " + authTrans, module);

        if ("PaymentMethodType".equals(infValue.getEntityName())) {
            payInfo.put("description", infValue.get("description", locale));
            payInfo.put("payInfo", infValue.get("description", locale));
            payInfo.put("amount", UtilFormatOut.formatPrice(inf.amount));
        } else {
            String paymentMethodTypeId = infValue.getString("paymentMethodTypeId");
            GenericValue pmt = null;
            try {
                 pmt = infValue.getRelatedOne("PaymentMethodType");
            } catch (GenericEntityException e) {
                Debug.logError(e, module);
            }
            if (pmt != null) {
                payInfo.put("description", pmt.get("description", locale));
                payInfo.put("amount", UtilFormatOut.formatPrice(inf.amount));
            }

            if ("CREDIT_CARD".equals(paymentMethodTypeId)) {
                GenericValue cc = null;
                try {
                    cc = infValue.getRelatedOne("CreditCard");
                } catch (GenericEntityException e) {
                    Debug.logError(e, module);
                }
                String nameOnCard = cc.getString("firstNameOnCard") + " " + cc.getString("lastNameOnCard");
                nameOnCard = nameOnCard.trim();
                payInfo.put("nameOnCard", nameOnCard);

                String cardNum = cc.getString("cardNumber");
                String cardStr = UtilFormatOut.formatPrintableCreditCard(cardNum);

                String expDate = cc.getString("expireDate");
                String infoString = cardStr + " " + expDate;
                payInfo.put("payInfo", infoString);
                payInfo.putAll(cc);
                payInfo.put("cardNumber", cardStr)// masked cardNumber

            } else if ("GIFT_CARD".equals(paymentMethodTypeId)) {
                /*
                GenericValue gc = null;
                try {
                    gc = infValue.getRelatedOne("GiftCard"); //FIXME is this really useful ? (Maybe later...)
                } catch (GenericEntityException e) {
                    Debug.logError(e, module);
                }
                */
            }
        }

        return payInfo;
    }

    public BigDecimal getItemQuantity(String productId) {
        trace("request item quantity", productId);
        ShoppingCartItem item = cart.findCartItem(productId, null, null, null, BigDecimal.ZERO);
        if (item != null) {
            return item.getQuantity();
        } else {
            trace("item not found", productId);
            return BigDecimal.ZERO;
        }
    }

    public boolean isAggregatedItem(String productId) {
        trace("is Aggregated Item", productId);
        try {
            Delegator delegator = cart.getDelegator();
            GenericValue product = null;
            product = delegator.findByPrimaryKeyCache("Product", UtilMisc.toMap("productId", productId));
            if (UtilValidate.isNotEmpty(product) && "AGGREGATED".equals(product.getString("productTypeId"))) {
                return true;
            }
        } catch (GenericEntityException e) {
            trace("item lookup error", e);
            Debug.logError(e, module);
        } catch (Exception e) {
            trace("general exception", e);
            Debug.logError(e, module);
        }
        return false;
    }

    public ProductConfigWrapper getProductConfigWrapper(String productId) {
        //Get a PCW for a new product
        trace("get Product Config Wrapper", productId);
        ProductConfigWrapper pcw = null;
        try {
            Delegator delegator = cart.getDelegator();
            pcw = new ProductConfigWrapper(delegator, session.getDispatcher(),
                    productId, null, null, null, null, null, null);
        } catch (ItemNotFoundException e) {
            trace("item not found", e);
            //throw e;
        } catch (CartItemModifyException e) {
            trace("add item error", e);
            //throw e;
        } catch (GenericEntityException e) {
            trace("item lookup error", e);
            Debug.logError(e, module);
        } catch (Exception e) {
            trace("general exception", e);
            Debug.logError(e, module);
        }
        return pcw;
    }

    public ProductConfigWrapper getProductConfigWrapper(String productId, String cartIndex) {
        // Get a PCW for a pre-configured product
         trace("get Product Config Wrapper", productId + "/" + cartIndex);
         ProductConfigWrapper pcw = null;
         try {
            int index = Integer.parseInt(cartIndex);
            ShoppingCartItem product = cart.findCartItem(index);
            pcw = product.getConfigWrapper();
        } catch (Exception e) {
            trace("general exception", e);
            Debug.logError(e, module);
        }
        return pcw;
    }

    public void addItem(String productId, BigDecimal quantity) throws CartItemModifyException, ItemNotFoundException {
        trace("add item", productId + "/" + quantity);
        try {
            Delegator delegator = cart.getDelegator();
            GenericValue product = null;
            ProductConfigWrapper pcw = null;
            product = delegator.findByPrimaryKeyCache("Product", UtilMisc.toMap("productId", productId));
            if (UtilValidate.isNotEmpty(product) && "AGGREGATED".equals(product.getString("productTypeId"))) {
                // if it's an aggregated item, load the configwrapper and set to defaults
                pcw = new ProductConfigWrapper(delegator, session.getDispatcher(), productId, null, null, null, null, null, null);
                pcw.setDefaultConfig();
            }
            //cart.addOrIncreaseItem(productId, null, quantity, null, null, null, null, null, null, null, null, null, null, null, null, session.getDispatcher());
            cart.addOrIncreaseItem(productId, null, quantity, null, null, null, null, null, null, null, null, pcw, null, null, null, session.getDispatcher());
        } catch (ItemNotFoundException e) {
            trace("item not found", e);
            throw e;
        } catch (CartItemModifyException e) {
            trace("add item error", e);
            throw e;
        } catch (GenericEntityException e) {
            trace("item lookup error", e);
            Debug.logError(e, module);
        } catch (Exception e) {
            trace("general exception", e);
            Debug.logError(e, module);
        }
    }

    public void addItem(String productId, ProductConfigWrapper pcw)
        throws ItemNotFoundException, CartItemModifyException {
        trace("add item with ProductConfigWrapper", productId);
        try {
            cart.addOrIncreaseItem(productId, null, BigDecimal.ONE, null, null, null, null, null, null, null, null, pcw, null, null, null, session.getDispatcher());
        } catch (ItemNotFoundException e) {
            trace("item not found", e);
            throw e;
        } catch (CartItemModifyException e) {
            trace("add item error", e);
            throw e;
        } catch (Exception e) {
            trace("general exception", e);
            Debug.logError(e, module);
        }
    }

    public void modifyConfig(String productId, ProductConfigWrapper pcw, String cartIndex)
        throws CartItemModifyException, ItemNotFoundException {
        trace("modify item config", cartIndex);
        try {
            int cartIndexInt = Integer.parseInt(cartIndex);
            ShoppingCartItem cartItem = cart.findCartItem(cartIndexInt);
            BigDecimal quantity = cartItem.getQuantity();
            cart.removeCartItem(cartIndexInt, session.getDispatcher());
            cart.addOrIncreaseItem(productId, null, quantity, null, null, null, null, null, null, null, null, pcw, null, null, null, session.getDispatcher());
        } catch (CartItemModifyException e) {
            Debug.logError(e, module);
            trace("void or add item error", productId, e);
            throw e;
        } catch (ItemNotFoundException e) {
            trace("item not found", e);
            throw e;
        } catch (Exception e) {
            trace("general exception", e);
            Debug.logError(e, module);
        }
        return;
    }

    public void modifyQty(String productId, BigDecimal quantity) throws CartItemModifyException {
        trace("modify item quantity", productId + "/" + quantity);
        ShoppingCartItem item = cart.findCartItem(productId, null, null, null, BigDecimal.ZERO);
        if (item != null) {
            try {
                item.setQuantity(quantity, session.getDispatcher(), cart, true);
            } catch (CartItemModifyException e) {
                Debug.logError(e, module);
                trace("modify item error", e);
                throw e;
            }
        } else {
            trace("item not found", productId);
        }
    }

    public void modifyPrice(String productId, BigDecimal price) {
        trace("modify item price", productId + "/" + price);
        ShoppingCartItem item = cart.findCartItem(productId, null, null, null, BigDecimal.ZERO);
        if (item != null) {
            item.setBasePrice(price);
        } else {
            trace("item not found", productId);
        }
    }

    public void addDiscount(String productId, BigDecimal discount, boolean percent) {
        GenericValue adjustment = session.getDelegator().makeValue("OrderAdjustment");
        adjustment.set("orderAdjustmentTypeId", "DISCOUNT_ADJUSTMENT");
        if (percent) {
            adjustment.set("sourcePercentage", discount.movePointRight(2));
        } else {
            adjustment.set("amount", discount);
        }

        if (productId != null) {
            trace("add item adjustment");
            ShoppingCartItem item = cart.findCartItem(productId, null, null, null, BigDecimal.ZERO);
            Integer itemAdj = skuDiscounts.get(productId);
            if (itemAdj != null) {
                item.removeAdjustment(itemAdj.intValue());
            }
               int idx = item.addAdjustment(adjustment);
            skuDiscounts.put(productId, idx);
        } else {
            trace("add sale adjustment");
            if (cartDiscount > -1) {
                cart.removeAdjustment(cartDiscount);
            }
            cartDiscount = cart.addAdjustment(adjustment);
        }
    }

    public void clearDiscounts() {
        if (cartDiscount > -1) {
            cart.removeAdjustment(cartDiscount);
            cartDiscount = -1;
        }
        for(String productId : skuDiscounts.keySet()) {
            ShoppingCartItem item = cart.findCartItem(productId, null, null, null, BigDecimal.ZERO);
            Integer itemAdj = skuDiscounts.remove(productId);
            if (itemAdj != null) {
                item.removeAdjustment(itemAdj.intValue());
            }
        }
    }

    public BigDecimal GetTotalDiscount() {
        return cart.getOrderOtherAdjustmentTotal();
    }

    public void voidItem(String productId) throws CartItemModifyException {
        trace("void item", productId);
        ShoppingCartItem item = cart.findCartItem(productId, null, null, null, BigDecimal.ZERO);
        if (item != null) {
            try {
                int itemIdx = cart.getItemIndex(item);
                cart.removeCartItem(itemIdx, session.getDispatcher());
            } catch (CartItemModifyException e) {
                Debug.logError(e, module);
                trace("void item error", productId, e);
                throw e;
            }
        } else {
            trace("item not found", productId);
        }
    }

    public void voidSale(PosScreen pos) {
        trace("void sale");
        txLog.set("statusId", "POSTX_VOIDED");
        txLog.set("itemCount", new Long(cart.size()));
        txLog.set("logEndDateTime", UtilDateTime.nowTimestamp());
        try {
            txLog.store();
        } catch (GenericEntityException e) {
            Debug.logError(e, "Unable to store TX log - not fatal", module);
        }
        cart.clear();
        pos.getPromoStatusBar().clear();
        currentTx = null;
    }

    public void closeTx() {
        trace("transaction closed");
        txLog.set("statusId", "POSTX_CLOSED");
        txLog.set("itemCount", new Long(cart.size()));
        txLog.set("logEndDateTime", UtilDateTime.nowTimestamp());
        try {
            txLog.store();
        } catch (GenericEntityException e) {
            Debug.logError(e, "Unable to store TX log - not fatal", module);
        }
        cart.clear();
        currentTx = null;
    }

    public void paidInOut(String type) {
        trace("paid " + type);
        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);
        }
        currentTx = null;
    }

    public void calcTax() {
        try {
            ch.calcAndAddTax(this.getStoreOrgAddress());
        } catch (GeneralException e) {
            Debug.logError(e, module);
        }
    }

    public void clearTax() {
        cart.removeAdjustmentByType("SALES_TAX");
    }

    public int checkPaymentMethodType(String paymentMethodTypeId) {
        Map<String, String> fields = UtilMisc.toMap("paymentMethodTypeId", paymentMethodTypeId, "productStoreId", productStoreId);
        List<GenericValue> values = null;
        try {
            values = session.getDelegator().findByAndCache("ProductStorePaymentSetting", fields);
        } 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.log("Testing [" + paymentMethodTypeId + "] - " + v, module);
                if (!externalCode.equals(v.getString("paymentServiceTypeEnumId"))) {
                    isExternal = false;
                }
            }

            if (isExternal) {
                return EXTERNAL_PAYMENT;
            } else {
                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) {
        trace("added payment", id + "/" + amount);
        if ("CASH".equals(id)) {
            // clear cash payments first; so there is only one
            cart.clearPayment(id);
        }
        cart.addPaymentAmount(id, amount, refNum, authCode, true, true, false);
        return this.getTotalDue();
    }

    public void setPaymentRefNum(int paymentIndex, String refNum, String authCode) {
        trace("setting payment index reference number", paymentIndex + " / " + refNum + " / " + authCode);
        ShoppingCart.CartPaymentInfo inf = cart.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) {
        trace("setting payment security code", paymentId);
        int paymentIndex = cart.getPaymentInfoIndex(paymentId, refNum);
        ShoppingCart.CartPaymentInfo inf = cart.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) {
        trace("setting payment security code", paymentId);
        int paymentIndex = cart.getPaymentInfoIndex(paymentId, refNum);
        ShoppingCart.CartPaymentInfo inf = cart.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) {
        trace("setting payment security code", paymentId);
        int paymentIndex = cart.getPaymentInfoIndex(paymentId, refNum);
        ShoppingCart.CartPaymentInfo inf = cart.getPaymentInfo(paymentIndex);
        inf.postalCode = postalCode;
    }

    public void clearPayments() {
        trace("all payments cleared from sale");
        cart.clearPayments();
    }

    public void clearPayment(int index) {
        trace("removing payment", "" + index);
        cart.clearPayment(index);
    }

    public void clearPayment(String id) {
        trace("removing payment", id);
        cart.clearPayment(id);
    }

    public int selectedPayments() {
        return cart.selectedPayments();
    }

    public void setTxAsReturn(String returnId) {
        trace("returned sale");
        txLog.set("statusId", "POSTX_RETURNED");
        txLog.set("returnId", returnId);
        txLog.set("logEndDateTime", UtilDateTime.nowTimestamp());
        try {
            txLog.store();
        } catch (GenericEntityException e) {
            Debug.logError(e, "Unable to store TX log - not fatal", module);
        }
        cart.clear();
        currentTx = null;
    }

    public BigDecimal processSale(Output output) throws GeneralException {
        trace("process sale");
        BigDecimal grandTotal = this.getGrandTotal();
        BigDecimal paymentAmt = this.getPaymentTotal();
        if (grandTotal.compareTo(paymentAmt) > 0) {
            throw new IllegalStateException();
        }

        // attach the party ID to the cart
        cart.setOrderPartyId(partyId);
        // Set the shipping type
        cart.setShipmentMethodTypeId("NO_SHIPPING");
       // cart.setCarrierPartyId();

        // validate payment methods
        output.print(UtilProperties.getMessage(resource, "PosValidating", locale));
        Map<String, Object> valRes = ch.validatePaymentMethods();
        if (valRes != null && ServiceUtil.isError(valRes)) {
            throw new GeneralException(ServiceUtil.getErrorMessage(valRes));
        }

        // store the "order"
         if (UtilValidate.isEmpty(this.orderId)) {  // if order does not exist
             output.print(UtilProperties.getMessage(resource, "PosSaving", locale));
             Map<String, Object> orderRes = ch.createOrder(session.getUserLogin());
             //Debug.log("Create Order Resp : " + orderRes, module);

             if (orderRes != null && ServiceUtil.isError(orderRes)) {
                 throw new GeneralException(ServiceUtil.getErrorMessage(orderRes));
             } else if (orderRes != null) {
                 this.orderId = (String) orderRes.get("orderId");
             }
         } else { // if the order has already been created
             Map<?, ?> changeMap = UtilMisc.toMap("itemReasonMap",
                     UtilMisc.toMap("reasonEnumId", "EnumIdHere"), // TODO: where does this come from?
                     "itemCommentMap", UtilMisc.toMap("changeComments", "change Comments here")); //TODO

             Map<String, Object> svcCtx = FastMap.newInstance();
             svcCtx.put("userLogin", session.getUserLogin());
             svcCtx.put("orderId", orderId);
             svcCtx.put("shoppingCart", cart);
             svcCtx.put("locale", locale);
             svcCtx.put("changeMap", changeMap);

             Map<String, Object> svcRes = null;
             try {
                 LocalDispatcher dispatcher = session.getDispatcher();
                 svcRes = dispatcher.runSync("saveUpdatedCartToOrder", svcCtx);
             } catch (GenericServiceException e) {
                 Debug.logError(e, module);
                 //pos.showDialog("dialog/error/exception", e.getMessage());
                 throw new GeneralException(ServiceUtil.getErrorMessage(svcRes));
             }
          }

        // process the payment(s)
        output.print(UtilProperties.getMessage(resource, "PosProcessing", locale));
        Map<String, Object> payRes = null;
        try {
            payRes = ch.processPayment(ProductStoreWorker.getProductStore(productStoreId, session.getDelegator()), session.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);

        // notify the change due
        output.print(UtilProperties.getMessage(resource, "PosChange",locale) + " " + UtilFormatOut.formatPrice(this.getTotalDue().negate()));

        // threaded drawer/receipt printing
        final PosTransaction currentTrans = this;
        final SwingWorker worker = new SwingWorker() {
            @Override
            public Object construct() {
                // open the drawer
                currentTrans.popDrawer();

                // print the receipt
                DeviceLoader.receipt.printReceipt(currentTrans, true);

                return null;
            }
        };
        worker.start();

        // save the TX Log
        txLog.set("statusId", "POSTX_SOLD");
        txLog.set("orderId", orderId);
        txLog.set("itemCount", new Long(cart.size()));
        txLog.set("logEndDateTime", UtilDateTime.nowTimestamp());
        try {
            txLog.store();
        } catch (GenericEntityException e) {
            Debug.logError(e, "Unable to store TX log - not fatal", module);
        }

        // clear the tx
        currentTx = null;
        partyId = "_NA_";

        return change;
    }

    private synchronized GenericValue getStoreOrgAddress() {
        if (this.shipAddress == null) {
            // locate the store's physical address - use this for tax
            GenericValue facility = (GenericValue) session.getAttribute("facility");
            if (facility == null) {
                return null;
            }

            Delegator delegator = session.getDelegator();
            GenericValue facilityContactMech = ContactMechWorker.getFacilityContactMechByPurpose(delegator, facilityId, UtilMisc.toList("SHIP_ORIG_LOCATION", "PRIMARY_LOCATION"));
            if (facilityContactMech != null) {
                try {
                    this.shipAddress = session.getDelegator().findByPrimaryKey("PostalAddress",
                            UtilMisc.toMap("contactMechId", facilityContactMech.getString("contactMechId")));
                } catch (GenericEntityException e) {
                    Debug.logError(e, module);
                }
            }
        }
        return this.shipAddress;
    }

    public void saveTx() {
        savedTx.push(this);
        currentTx = null;
        trace("transaction saved");
    }

    public void appendItemDataModel(XModel model) {
        if (cart != null) {
            Iterator<?> i = cart.iterator();
            while (i.hasNext()) {
                ShoppingCartItem item = (ShoppingCartItem) i.next();
                BigDecimal quantity = item.getQuantity();
                BigDecimal unitPrice = item.getBasePrice();
                BigDecimal subTotal = unitPrice.multiply(quantity);
                BigDecimal adjustment = item.getOtherAdjustments();

                XModel line = Journal.appendNode(model, "tr", ""+cart.getItemIndex(item), "");
                Journal.appendNode(line, "td", "sku", item.getProductId());
                Journal.appendNode(line, "td", "desc", item.getName());
                Journal.appendNode(line, "td", "qty", UtilFormatOut.formatQuantity(quantity));
                Journal.appendNode(line, "td", "price", UtilFormatOut.formatPrice(subTotal));
                Journal.appendNode(line, "td", "index", Integer.toString(cart.getItemIndex(item)));

                if (this.isAggregatedItem(item.getProductId())) {
                    // put alterations here
                    ProductConfigWrapper pcw = null;
                    // product = delegator.findByPrimaryKeyCache("Product", UtilMisc.toMap("productId", productId));
                    // pcw = new ProductConfigWrapper(delegator, session.getDispatcher(), productId, null, null, null, null, null, null);
                    pcw = item.getConfigWrapper();
                    List<ConfigOption> selected = pcw.getSelectedOptions();
                    for (ConfigOption configoption : selected) {
                        if (configoption.isSelected()) {
                            XModel option = Journal.appendNode(model, "tr", ""+cart.getItemIndex(item), "");
                            Journal.appendNode(option, "td", "sku", "");
                            Journal.appendNode(option, "td", "desc", configoption.getDescription());
                            Journal.appendNode(option, "td", "qty", "");
                            Journal.appendNode(option, "td", "price", UtilFormatOut.formatPrice(configoption.getPrice()));
                            Journal.appendNode(option, "td", "index", Integer.toString(cart.getItemIndex(item)));
                        }
                    }
                }

                if (adjustment.compareTo(BigDecimal.ZERO) != 0) {
                    // append the promo info
                    XModel promo = Journal.appendNode(model, "tr", "itemadjustment", "");
                    Journal.appendNode(promo, "td", "sku", "");
                    Journal.appendNode(promo, "td", "desc", UtilProperties.getMessage(resource, "PosItemDiscount", locale));
                    Journal.appendNode(promo, "td", "qty", "");
                    Journal.appendNode(promo, "td", "price", UtilFormatOut.formatPrice(adjustment));
                }
            }
        }
    }

    public void appendTotalDataModel(XModel model) {
        if (cart != null) {
            BigDecimal taxAmount = cart.getTotalSalesTax();
            BigDecimal total = cart.getGrandTotal();
            List<GenericValue> adjustments = cart.getAdjustments();
            BigDecimal itemsAdjustmentsAmount = BigDecimal.ZERO;

            Iterator<?> i = cart.iterator();
            while (i.hasNext()) {
                ShoppingCartItem item = (ShoppingCartItem) i.next();
                BigDecimal adjustment = item.getOtherAdjustments();
                if (adjustment.compareTo(BigDecimal.ZERO) != 0) {
                    itemsAdjustmentsAmount = itemsAdjustmentsAmount.add(adjustment);
                }
            }

            for (GenericValue orderAdjustment : adjustments) {
                BigDecimal amount = orderAdjustment.getBigDecimal("amount");
                BigDecimal sourcePercentage = orderAdjustment.getBigDecimal("sourcePercentage");
                XModel adjustmentLine = Journal.appendNode(model, "tr", "adjustment", "");
                Journal.appendNode(adjustmentLine, "td", "sku", "");
                Journal.appendNode(adjustmentLine, "td", "desc",
                        UtilProperties.getMessage(resource, "PosSalesDiscount", locale));
                if (UtilValidate.isNotEmpty(amount)) {
                    Journal.appendNode(adjustmentLine, "td", "qty", "");
                    Journal.appendNode(adjustmentLine, "td", "price", UtilFormatOut.formatPrice(amount));
                } else if (UtilValidate.isNotEmpty(sourcePercentage)) {
                    BigDecimal percentage = sourcePercentage.movePointLeft(2).negate(); // sourcePercentage is negative and must be show as a positive value (it's a discount not an amount)
                    Journal.appendNode(adjustmentLine, "td", "qty", UtilFormatOut.formatPercentage(percentage));
                    amount = cart.getItemTotal().add(itemsAdjustmentsAmount).multiply(percentage); // itemsAdjustmentsAmount is negative
                    Journal.appendNode(adjustmentLine, "td", "price", UtilFormatOut.formatPrice(amount.negate())); // amount must be shown as a negative value
                }
                Journal.appendNode(adjustmentLine, "td", "index", "-1");
            }

            XModel taxLine = Journal.appendNode(model, "tr", "tax", "");
            Journal.appendNode(taxLine, "td", "sku", "");

            Journal.appendNode(taxLine, "td", "desc", UtilProperties.getMessage(resource, "PosSalesTax", locale));
            Journal.appendNode(taxLine, "td", "qty", "");
            Journal.appendNode(taxLine, "td", "price", UtilFormatOut.formatPrice(taxAmount));
            Journal.appendNode(taxLine, "td", "index", "-1");

            XModel totalLine = Journal.appendNode(model, "tr", "total", "");
            Journal.appendNode(totalLine, "td", "sku", "");
            Journal.appendNode(totalLine, "td", "desc", UtilProperties.getMessage(resource, "PosGrandTotal", locale));
            Journal.appendNode(totalLine, "td", "qty", "");
            Journal.appendNode(totalLine, "td", "price", UtilFormatOut.formatPrice(total));
            Journal.appendNode(totalLine, "td", "index", "-1");
        }
    }

    public void appendPaymentDataModel(XModel model) {
        if (cart != null) {
            int paymentInfoSize = cart.selectedPayments();
            for (int i = 0; i < paymentInfoSize; i++) {
                ShoppingCart.CartPaymentInfo inf = cart.getPaymentInfo(i);
                GenericValue paymentInfoObj = inf.getValueObject(session.getDelegator());

                GenericValue paymentMethodType = null;
                GenericValue paymentMethod = null;
                if ("PaymentMethod".equals(paymentInfoObj.getEntityName())) {
                    paymentMethod = paymentInfoObj;
                    try {
                        paymentMethodType = paymentMethod.getRelatedOne("PaymentMethodType");
                    } catch (GenericEntityException e) {
                        Debug.logError(e, module);
                    }
                } else {
                    paymentMethodType = paymentInfoObj;
                }

                Object desc = paymentMethodType != null ? paymentMethodType.get("description",locale) : "??";
                String descString = desc.toString();
                BigDecimal amount = BigDecimal.ZERO;
                if (inf.amount == null) {
                    amount = cart.getGrandTotal().subtract(cart.getPaymentTotal());
                } else {
                    amount = inf.amount;
                }

                XModel paymentLine = Journal.appendNode(model, "tr", Integer.toString(i), "");
                Journal.appendNode(paymentLine, "td", "sku", "");
                Journal.appendNode(paymentLine, "td", "desc", descString);
                Journal.appendNode(paymentLine, "td", "qty", "-");
                Journal.appendNode(paymentLine, "td", "price", UtilFormatOut.formatPrice(amount.negate()));
                Journal.appendNode(paymentLine, "td", "index", Integer.toString(i));
            }
        }
    }

    public void appendChangeDataModel(XModel model) {
        if (cart != null) {
            BigDecimal changeDue = this.getTotalDue().negate();
            if (changeDue.compareTo(BigDecimal.ZERO) >= 0) {
                XModel changeLine = Journal.appendNode(model, "tr", "", "");
                Journal.appendNode(changeLine, "td", "sku", "");
                Journal.appendNode(changeLine, "td", "desc", "Change");
                Journal.appendNode(changeLine, "td", "qty", "-");
                Journal.appendNode(changeLine, "td", "price", UtilFormatOut.formatPrice(changeDue));
            }
        }
    }

    public String makeCreditCardVo(String cardNumber, String expDate, String firstName, String lastName) {
        LocalDispatcher dispatcher = session.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 = FastMap.newInstance();
        svcCtx.put("userLogin", session.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));

        //Debug.log("Create CC : " + svcCtx, module);
        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 GenericValue getTerminalState() {
        Delegator delegator = session.getDelegator();
        List<GenericValue> states = null;
        try {
            states = delegator.findByAnd("PosTerminalState", UtilMisc.toMap("posTerminalId", this.getTerminalId()));
        } catch (GenericEntityException e) {
            Debug.logError(e, module);
        }
        states = EntityUtil.filterByDate(states, UtilDateTime.nowTimestamp(), "openedDate", "closedDate", true);
        return EntityUtil.getFirst(states);
    }

    public void setPrintWriter(PrintWriter writer) {
        this.trace = writer;
    }

    private void trace(String s) {
        trace(s, null, null);
    }

    private void trace(String s, Throwable t) {
        trace(s, null, t);
    }

    private void trace(String s1, String s2) {
        trace(s1, s2, null);
    }

    private void trace(String s1, String s2, Throwable t) {
        if (trace != null) {
            String msg = s1;
            if (UtilValidate.isNotEmpty(s2)) {
                msg = msg + "(" + s2 + ")";
            }
            if (t != null) {
                msg = msg + " : " + t.getMessage();
            }

            // print the trace line
            trace.println("[POS @ " + terminalId + " TX:" + transactionId + "] - " + msg);
            trace.flush();
        }
    }

    public static synchronized PosTransaction getCurrentTx(XuiSession session) {
        if (currentTx == null) {
            if (session.getUserLogin() != null) {
                currentTx = new PosTransaction(session);
            }
        }
        return currentTx;
    }

    public void loadSale(PosScreen pos) {
        trace("Load a sale");
        List<GenericValue> shoppingLists = createShoppingLists();
        if (!shoppingLists.isEmpty()) {
            Map<String, String> salesMap = createSalesMap(shoppingLists);
            if (!salesMap.isEmpty()) {
                LoadSale loadSale = new LoadSale(salesMap, this, pos);
                loadSale.openDlg();
            }
            else {
                pos.showDialog("dialog/error/nosales");
            }
        } else {
            pos.showDialog("dialog/error/nosales");
        }
    }

    public void loadOrder(PosScreen pos) {
        List<GenericValue> orders = findOrders();
        if (!orders.isEmpty()) {
            LoadSale loadSale = new LoadSale(createOrderHash(orders), this, pos);
            loadSale.openDlg();
        } else {
            pos.showDialog("dialog/error/nosales");
        }
    }

    private List<GenericValue> findOrders() {
        LocalDispatcher dispatcher = session.getDispatcher();

        Map<String, Object> svcCtx = FastMap.newInstance();
        svcCtx.put("userLogin", session.getUserLogin());
        svcCtx.put("partyId", partyId);
        List<String> orderStatusIds = FastList.newInstance();
        orderStatusIds.add("ORDER_CREATED");
        svcCtx.put("orderStatusId", orderStatusIds);
        svcCtx.put("viewIndex", 1);
        svcCtx.put("viewSize", 25);
        svcCtx.put("showAll", "Y");

        Map<String, Object> svcRes = null;
        try {
            svcRes = dispatcher.runSync("findOrders", svcCtx);
        } catch (GenericServiceException e) {
            Debug.logError(e, module);
        }

        if (svcRes == null) {
            Debug.log(UtilProperties.getMessage("EcommerceUiLabels", "EcommerceNoShoppingListsCreate", locale), module);
        } else if (ServiceUtil.isError(svcRes)) {
            Debug.logError(ServiceUtil.getErrorMessage(svcRes) + " - " + svcRes, module);
        } else{
            Integer orderListSize = (Integer) svcRes.get("orderListSize");
            if (orderListSize > 0) {
               List<GenericValue> orderList = UtilGenerics.checkList(svcRes.get("orderList"), GenericValue.class);
               return orderList;
            }
        }
        return null;
    }

/*    public void configureItem(String cartIndex, PosScreen pos) {
         trace("configure item", cartIndex);
         try {
            int index = Integer.parseInt(cartIndex);
            ShoppingCartItem product = cart.findCartItem(index);
            Delegator delegator = cart.getDelegator();
            ProductConfigWrapper pcw = null;
            pcw = product.getConfigWrapper();
            if (pcw != null) {
                ConfigureItem configItem = new ConfigureItem(cartIndex, pcw, this, pos);
                configItem.openDlg();
            }
            else {
                pos.showDialog("dialog/error/itemnotconfigurable");
            }
        } catch (Exception e) {
            trace("general exception", e);
            Debug.logError(e, module);
        }
    }  */

    public List<GenericValue> createShoppingLists() {
        List<GenericValue> shoppingLists = null;
        Delegator delegator = this.session.getDelegator();
        try {
            shoppingLists = delegator.findList("ShoppingList", null, null, null, null, false);
        } catch (GenericEntityException e) {
            Debug.logError(e, module);
            return null;
        }

        if (shoppingLists == null) {
            Debug.log(UtilProperties.getMessage("EcommerceUiLabels", "EcommerceNoShoppingListsCreate", locale), module);
        }
        return shoppingLists;
    }

    public Map<String, String> createSalesMap(List<GenericValue> shoppingLists) {
        Map<String, String> salesMap = FastMap.newInstance();
        for (GenericValue shoppingList : shoppingLists) {
            List<GenericValue> items = null;
            try {
                items = shoppingList.getRelated("ShoppingListItem", UtilMisc.toList("shoppingListItemSeqId"));
            } catch (GenericEntityException e) {
                Debug.logError(e, module);
            }
            if (UtilValidate.isNotEmpty(items)) {
                String listName = shoppingList.getString("listName");
                String shoppingListId = shoppingList.getString("shoppingListId");
                salesMap.put(shoppingListId, listName);
            }
        }
        return salesMap;
    }

    public Map<String, String> createOrderHash(List<GenericValue> orders) {
        Map<String, String> hash = FastMap.newInstance();
        for (GenericValue order : orders) {
            String orderName = order.getString("orderName");
            String orderId = order.getString("orderId");
            if (orderName != null) {
                hash.put(orderId, orderName);
            }
        }
        return hash;
    }

    public boolean addListToCart(String shoppingListId, PosScreen pos, boolean append) {
        trace("Add list to cart");
        Delegator delegator = session.getDelegator();
        LocalDispatcher dispatcher = session.getDispatcher();
        String includeChild = null; // Perhaps will be used later ...
        String prodCatalogId =  null;

        try {
            ShoppingListEvents.addListToCart(delegator, dispatcher, cart, prodCatalogId, shoppingListId, (includeChild != null), true, append);
        } catch (IllegalArgumentException e) {
            Debug.logError(e, module);
            pos.showDialog("dialog/error/exception", e.getMessage());
            return false;
        }
        return true;
    }

    public boolean restoreOrder(String orderId, PosScreen pos, boolean append) {
        trace("Restore an order");

        LocalDispatcher dispatcher = session.getDispatcher();

        Map<String, Object> svcCtx = FastMap.newInstance();
        svcCtx.put("userLogin", session.getUserLogin());
        svcCtx.put("orderId", orderId);
        svcCtx.put("skipInventoryChecks", Boolean.TRUE);
        svcCtx.put("skipProductChecks", Boolean.TRUE);

        Map<String, Object> svcRes = null;
        try {
            svcRes = dispatcher.runSync("loadCartFromOrder", svcCtx);
        } catch (GenericServiceException e) {
            Debug.logError(e, module);
            pos.showDialog("dialog/error/exception", e.getMessage());
        }

        if (svcRes == null) {
            Debug.log(UtilProperties.getMessage("EcommerceUiLabels", "EcommerceNoShoppingListsCreate", locale), module);
        } else if (ServiceUtil.isError(svcRes)) {
            Debug.logError(ServiceUtil.getErrorMessage(svcRes) + " - " + svcRes, module);
        } else{
            ShoppingCart restoredCart = (ShoppingCart) svcRes.get("shoppingCart");
            if (append) {
                // TODO: add stuff to append items
                this.cart = restoredCart;
                this.orderId = orderId;
            } else {
                this.cart = restoredCart;
                this.orderId = orderId;
            }
            this.ch = new CheckOutHelper(session.getDispatcher(), session.getDelegator(), cart);
            if (session.getUserLogin() != null) {
                cart.addAdditionalPartyRole(session.getUserLogin().getString("partyId"), "SALES_REP");
            }
            cart.setFacilityId(facilityId);
            cart.setTerminalId(terminalId);
            cart.setOrderId(orderId);
            return true;
        }
        return false;
    }

    public boolean clearList(String shoppingListId, PosScreen pos) {
        Delegator delegator = session.getDelegator();
        try {
        ShoppingListEvents.clearListInfo(delegator, shoppingListId);
        } catch (GenericEntityException e) {
            Debug.logError(e, module);
            pos.showDialog("dialog/error/exception", e.getMessage());
            return false;
        }
        return true;
    }


    public void clientProfile(PosScreen pos) {
        ClientProfile clientProfile = new ClientProfile(this, pos);
        clientProfile.openDlg();
    }

    public void saveSale(PosScreen pos) {
        SaveSale saveSale = new SaveSale(this, pos);
        saveSale.openDlg();
    }

    public void saveOrder(String shoppingListName, PosScreen pos) {
        trace("Save an order");
        if (cart.size() == 0) {
            pos.showDialog("dialog/error/exception", UtilProperties.getMessage("OrderErrorUiLabels", "OrderUnableToCreateNewShoppingList", locale));
            return;
        }
        if (!UtilValidate.isEmpty(shoppingListName)) {
            // attach the party ID to the cart
            cart.setOrderPartyId(partyId);
            cart.setOrderName(shoppingListName);
            //cart.setExternalId(shoppingListName);
            //cart.setInternalCode("Internal Code");
            //ch.setCheckOutOptions(null, null, null, null, null, "shipping instructions", null, null, null, "InternalId", null, null, null);
            Map<String, Object> orderRes = ch.createOrder(session.getUserLogin());

            if (orderRes != null && ServiceUtil.isError(orderRes)) {
                Debug.logError(ServiceUtil.getErrorMessage(orderRes), module);
                //throw new GeneralException(ServiceUtil.getErrorMessage(orderRes));
            } else if (orderRes != null) {
                this.orderId = (String) orderRes.get("orderId");
            }
        }
    }

    public void saveSale(String  shoppingListName, PosScreen pos) {
        trace("Save a sale");
        if (cart.size() == 0) {
            pos.showDialog("dialog/error/exception", UtilProperties.getMessage("OrderErrorUiLabels", "OrderUnableToCreateNewShoppingList", locale));
            return;
        }
        Delegator delegator = this.session.getDelegator();
        LocalDispatcher dispatcher = session.getDispatcher();
        GenericValue userLogin = session.getUserLogin();
        String shoppingListId = null;

        if (!UtilValidate.isEmpty(shoppingListName)) {
            // create a new shopping list with partyId = user connected (POS clerk, etc.) and not buyer (_NA_ in POS)
            Map<String, Object> serviceCtx = UtilMisc.toMap("userLogin", session.getUserLogin(), "partyId", session.getUserPartyId(),
                    "productStoreId", productStoreId, "listName", shoppingListName);

            serviceCtx.put("shoppingListTypeId", "SLT_SPEC_PURP");
            Map<String, Object> newListResult = null;
            try {

                newListResult = dispatcher.runSync("createShoppingList", serviceCtx);
            } catch (GenericServiceException e) {
                Debug.logError(e, "Problem while creating new ShoppingList", module);
                pos.showDialog("dialog/error/exception", UtilProperties.getMessage("OrderErrorUiLabels", "OrderUnableToCreateNewShoppingList", locale));
                return;
            }

            // check for errors
            if (ServiceUtil.isError(newListResult)) {
                String error = ServiceUtil.getErrorMessage(newListResult);
                Debug.logError(error, module);
                pos.showDialog("dialog/error/exception", error);
                return;
            }

            // get the new list id
            if (newListResult != null) {
                shoppingListId = (String) newListResult.get("shoppingListId");
            } else {
                Debug.logError("Problem while creating new ShoppingList", module);
                pos.showDialog("dialog/error/exception", UtilProperties.getMessage("OrderErrorUiLabels", "OrderUnableToCreateNewShoppingList", locale));
                return;
            }
        }

        String selectedCartItems[] = new String[cart.size()];
        for(int i = 0; i < cart.size(); i++) {
            Integer integer = i;
            selectedCartItems[i] = integer.toString();
        }

        try {
            ShoppingListEvents.addBulkFromCart(delegator, dispatcher, cart, userLogin, shoppingListId, null, selectedCartItems, true, true);
        } catch (IllegalArgumentException e) {
            Debug.logError(e, "Problem while creating new ShoppingList", module);
            pos.showDialog("dialog/error/exception", UtilProperties.getMessage("OrderErrorUiLabels", "OrderUnableToCreateNewShoppingList", locale));
        }
    }

    public String addProductPromoCode(String code) {
        trace("Add a promo code");
        LocalDispatcher dispatcher = session.getDispatcher();
        String result = cart.addProductPromoCode(code, dispatcher);
        calcTax();
        return result;
    }

    private List<Map<String, String>> searchContactMechs(Delegator delegator, PosScreen pos, List<Map<String, String>> partyList, String valueToCompare, String contactMechType) {
        ListIterator<Map<String, String>>  partyListIt = partyList.listIterator();
        while(partyListIt.hasNext()) {
            Map<String, String> party = partyListIt.next();
            String partyId = party.get("partyId");
            List<Map<String, Object>> partyContactMechValueMaps = ContactMechWorker.getPartyContactMechValueMaps(delegator, partyId, false, contactMechType);
            Integer nb = 0;
            for (Map<String, Object> partyContactMechValueMap : partyContactMechValueMaps) {
                nb++;
                String keyType = null;
                String key = null;
                if ("TELECOM_NUMBER".equals(contactMechType)) {
                    keyType = "telecomNumber";
                    key = "contactNumber";
                } else if ("EMAIL_ADDRESS".equals(contactMechType)) {
                    keyType = "contactMech";
                    key = "infoString";
                }
                Map<String, Object> keyTypeMap = UtilGenerics.checkMap(partyContactMechValueMap.get(keyType));
                String keyTypeValue = ((String) keyTypeMap.get(key)).trim();
                if (valueToCompare.equals(keyTypeValue) || UtilValidate.isEmpty(valueToCompare)) {
                    if (nb == 1) {
                        party.put(key, keyTypeValue);
                        partyListIt.set(party);
                    } else {
                        Map<String, String> partyClone = FastMap.newInstance();
                        partyClone.putAll(party);
                        partyClone.put(key, keyTypeValue);
                        partyListIt.add(partyClone);
                    }
                }
            }
        }
        return partyList;
    }


    public List<Map<String, String>> searchClientProfile(String name, String email, String  phone, String card, PosScreen pos, Boolean equalsName) {
        Delegator delegator = this.session.getDelegator();

        List<GenericValue> partyList = null;
        List<Map<String, String>> resultList = null;

        // create the dynamic view entity
        DynamicViewEntity dynamicView = new DynamicViewEntity();

        // Person (name + card)
        dynamicView.addMemberEntity("PT", "Party");
        dynamicView.addAlias("PT", "partyId");
        dynamicView.addAlias("PT", "statusId");
        dynamicView.addAlias("PT", "partyTypeId");
        dynamicView.addMemberEntity("PE", "Person");
        dynamicView.addAlias("PE", "partyId");
        dynamicView.addAlias("PE", "lastName");
        dynamicView.addAlias("PE", "cardId");
        dynamicView.addViewLink("PT", "PE", Boolean.FALSE, ModelKeyMap.makeKeyMapList("partyId"));

        if (UtilValidate.isNotEmpty(email)) {
            // ContactMech (email)
            dynamicView.addMemberEntity("PM", "PartyContactMechPurpose");
            dynamicView.addAlias("PM", "contactMechId");
            dynamicView.addAlias("PM", "contactMechPurposeTypeId");
            dynamicView.addAlias("PM", "thruDate");
            dynamicView.addMemberEntity("CM", "ContactMech");
            dynamicView.addAlias("CM", "infoString");
            dynamicView.addViewLink("PT", "PM", Boolean.FALSE, ModelKeyMap.makeKeyMapList("partyId"));
            dynamicView.addViewLink("PM", "CM", Boolean.FALSE, ModelKeyMap.makeKeyMapList("contactMechId"));
        } else if (UtilValidate.isNotEmpty(phone)) {
            dynamicView.addMemberEntity("PM", "PartyContactMechPurpose");
            dynamicView.addAlias("PM", "contactMechId");
            dynamicView.addAlias("PM", "thruDate");
            dynamicView.addAlias("PM", "contactMechPurposeTypeId");
            dynamicView.addMemberEntity("TN", "TelecomNumber");
            dynamicView.addAlias("TN", "contactNumber");
            dynamicView.addViewLink("PT", "PM", Boolean.FALSE, ModelKeyMap.makeKeyMapList("partyId"));
            dynamicView.addViewLink("PM", "TN", Boolean.FALSE, ModelKeyMap.makeKeyMapList("contactMechId"));
        }

            // define the main condition & expression list
            List<EntityCondition> andExprs = FastList.newInstance();
            EntityCondition mainCond = null;

            List<String> orderBy = FastList.newInstance();
            List<String> fieldsToSelect = FastList.newInstance();
            // fields we need to select; will be used to set distinct
            fieldsToSelect.add("partyId");
            fieldsToSelect.add("lastName");
            fieldsToSelect.add("cardId");
            if (UtilValidate.isNotEmpty(email)) {
                fieldsToSelect.add("infoString");
            } else if (UtilValidate.isNotEmpty(phone)) {
                fieldsToSelect.add("contactNumber");
            }

            // NOTE: _must_ explicitly allow null as it is not included in a not equal in many databases... odd but true
            // This allows to get all clients when any informations has been entered
            andExprs.add(EntityCondition.makeCondition(EntityCondition.makeCondition("statusId", EntityOperator.EQUALS, null), EntityOperator.OR, EntityCondition.makeCondition("statusId", EntityOperator.NOT_EQUAL, "PARTY_DISABLED")));
            andExprs.add(EntityCondition.makeCondition("partyTypeId", EntityOperator.EQUALS, "PERSON")); // Only persons for now...
            if (UtilValidate.isNotEmpty(name)) {
                if (equalsName) {
                    andExprs.add(EntityCondition.makeCondition("lastName", EntityOperator.EQUALS, name))// Plain name
                } else {
                    // andExprs.add(EntityCondition.makeCondition("lastName", EntityOperator.LIKE, "%"+name+"%")); // Less restrictive
                     andExprs.add(EntityCondition.makeCondition(EntityFunction.UPPER_FIELD("lastName"), EntityOperator.LIKE, EntityFunction.UPPER("%"+name+"%"))); // Even less restrictive
                }
            }
            if (UtilValidate.isNotEmpty(card)) {
                andExprs.add(EntityCondition.makeCondition("cardId", EntityOperator.EQUALS, card));
            }
            if (UtilValidate.isNotEmpty(email)) {
                andExprs.add(EntityCondition.makeCondition("infoString", EntityOperator.EQUALS, email));
                andExprs.add(EntityCondition.makeCondition("contactMechPurposeTypeId", EntityOperator.EQUALS, "PRIMARY_EMAIL"));
                andExprs.add(EntityCondition.makeCondition("thruDate", EntityOperator.EQUALS, null));
            } else if (UtilValidate.isNotEmpty(phone)) {
                andExprs.add(EntityCondition.makeCondition("contactNumber", EntityOperator.EQUALS, phone));
                andExprs.add(EntityCondition.makeCondition("contactMechPurposeTypeId", EntityOperator.EQUALS, "PHONE_HOME"));
                andExprs.add(EntityCondition.makeCondition("thruDate", EntityOperator.EQUALS, null));
            }

            mainCond = EntityCondition.makeCondition(andExprs, EntityOperator.AND);
            orderBy.add("lastName");

            Debug.logInfo("In searchClientProfile mainCond=" + mainCond, module);

            Integer maxRows = Integer.MAX_VALUE;
            // attempt to start a transaction
            boolean beganTransaction = false;
            try {
                beganTransaction = TransactionUtil.begin();

                try {
                    // set distinct on so we only get one row per person
                    EntityFindOptions findOpts = new EntityFindOptions(true, EntityFindOptions.TYPE_SCROLL_INSENSITIVE, EntityFindOptions.CONCUR_READ_ONLY, -1, maxRows, true);
                    // using list iterator
                    EntityListIterator pli = delegator.findListIteratorByCondition(dynamicView, mainCond, null, fieldsToSelect, orderBy, findOpts);

                    // get the partial list for this page
                    partyList = pli.getPartialList(1, maxRows);

                    // close the list iterator
                    pli.close();
                    } catch (GenericEntityException e) {
                        Debug.logError(e, module);
                        pos.showDialog("dialog/error/exception", e.getMessage());
                    }
            } catch (GenericTransactionException e) {
                Debug.logError(e, module);
                try {
                    TransactionUtil.rollback(beganTransaction, e.getMessage(), e);
                } catch (GenericTransactionException e2) {
                    Debug.logError(e2, "Unable to rollback transaction", module);
                    pos.showDialog("dialog/error/exception", e2.getMessage());
                }
                pos.showDialog("dialog/error/exception", e.getMessage());
            } finally {
                try {
                    TransactionUtil.commit(beganTransaction);
                } catch (GenericTransactionException e) {
                    Debug.logError(e, "Unable to commit transaction", module);
                    pos.showDialog("dialog/error/exception", e.getMessage());
                }
            }

            if (partyList != null) {
                resultList = FastList.newInstance();
                for (GenericValue party : partyList) {
                    Map<String, String> partyMap = FastMap.newInstance();
                    partyMap.put("partyId", party.getString("partyId"));
                    partyMap.put("lastName", party.getString("lastName"));
                    partyMap.put("cardId", party.getString("cardId"));
                    if (UtilValidate.isNotEmpty(email)) {
                        partyMap.put("infoString", party.getString("infoString"));
                        partyMap.put("contactNumber", "");
                    } else if (UtilValidate.isNotEmpty(phone)) {
                        partyMap.put("infoString", "");
                        partyMap.put("contactNumber", party.getString("contactNumber"));
                    } else { // both empty
                        partyMap.put("infoString", "");
                        partyMap.put("contactNumber", "");

                    }
                    resultList.add(partyMap);
                }

                if (UtilValidate.isNotEmpty(email)) {
                    resultList = searchContactMechs(delegator, pos, resultList, phone, "TELECOM_NUMBER");
                } else if (UtilValidate.isNotEmpty(phone)) {
                    resultList = searchContactMechs(delegator, pos, resultList, "", "EMAIL_ADDRESS"); // "" is more clear than email which is by definition here is empty
                } else { // both empty
                    resultList = searchContactMechs(delegator, pos, resultList, "", "TELECOM_NUMBER"); // "" is more clear than phone which is by definition here is empty
                    resultList = searchContactMechs(delegator, pos, resultList, "", "EMAIL_ADDRESS");
                }
            } else {
            resultList = FastList.newInstance();
        }
        return resultList;
    }

    public String editClientProfile(String name, String email, String  phone, String card, PosScreen pos, String editType, String partyId) {
        // We suppose here that a cardId (card number) can only belongs to one person (it's used as owned PromoCode)
        // We use the 1st party's login (it may change and be multiple since it depends on email and card)
        // We suppose only one email address (should be ok anyway because of the contactMechPurposeTypeId == "PRIMARY_EMAIL")
        // we suppose only one phone number (should be ok anyway because of the contactMechPurposeTypeId == "PHONE_HOME")
        LocalDispatcher dispatcher = session.getDispatcher();
        GenericValue userLogin = session.getUserLogin();
        GenericValue partyUserLogin = null;
        String result = null;

        Map<String, Object> svcCtx = FastMap.newInstance();
        Map<String, Object>  svcRes = null;

        // Create
        if ("create".equals(editType)) {
            trace("Create a client profile");
            // createPersonAndUserLogin
            trace("createPersonAndUserLogin");
            if (UtilValidate.isNotEmpty(card)) {
                svcCtx.put("cardId", card);
            }
            svcCtx.put("userLogin", userLogin);
            svcCtx.put("lastName", name);
            svcCtx.put("firstName", ""); // Needed by service createPersonAndUserLogin
            if (UtilValidate.isNotEmpty(email) && UtilValidate.isNotEmpty(phone)) {
                svcCtx.put("userLoginId", email);
                svcCtx.put("currentPassword", phone);
                svcCtx.put("currentPasswordVerify", phone);
                try {
                    svcRes = dispatcher.runSync("createPersonAndUserLogin", svcCtx);
                } catch (GenericServiceException e) {
                    Debug.logError(e, module);
                    pos.showDialog("dialog/error/exception", e.getMessage());
                   return null;
                }
                if (ServiceUtil.isError(svcRes)) {
                    pos.showDialog("dialog/error/exceptionLargeSmallFont", ServiceUtil.getErrorMessage(svcRes)); // exceptionLargeSmallFont used to show duplicate key error message for card
                    return null;
                }
                partyId = (String) svcRes.get("partyId");
                partyUserLogin = (GenericValue) svcRes.get("newUserLogin");
            } else {
                // createPerson
                trace("createPerson");
                try {
                    svcRes = dispatcher.runSync("createPerson", svcCtx);
                } catch (GenericServiceException e) {
                    Debug.logError(e, module);
                    pos.showDialog("dialog/error/exception", e.getMessage());
                   return result;
                }
                if (ServiceUtil.isError(svcRes)) {
                    pos.showDialog("dialog/error/exception", ServiceUtil.getErrorMessage(svcRes));
                    return result;
                }
                partyId = (String) svcRes.get("partyId");
                partyUserLogin = userLogin;
            }

            if (UtilValidate.isNotEmpty(email)) {
                // createPartyEmailAddress
                trace("createPartyEmailAddress");
                svcCtx.clear();
                svcCtx.put("userLogin", partyUserLogin);
                svcCtx.put("emailAddress", email);
                svcCtx.put("partyId", partyId);
                svcCtx.put("contactMechPurposeTypeId", "PRIMARY_EMAIL");
                try {
                    svcRes = dispatcher.runSync("createPartyEmailAddress", svcCtx);
                } catch (GenericServiceException e) {
                    Debug.logError(e, module);
                    pos.showDialog("dialog/error/exception", e.getMessage());
                    return null;
                }
                if (ServiceUtil.isError(svcRes)) {
                    pos.showDialog("dialog/error/exception", ServiceUtil.getErrorMessage(svcRes));
                    return null;
                }
            }

            if (UtilValidate.isNotEmpty(phone)) {
                if (phone.length() < 5 ) {
                    pos.showDialog("dialog/error/exception", UtilProperties.getMessage(PosTransaction.resource, "PosPhoneField5Required", locale));
                } else {
                    // createPartyTelecomNumber
                    trace("createPartyTelecomNumber");
                    svcCtx.clear();
                    svcCtx.put("userLogin", partyUserLogin);
                    svcCtx.put("contactNumber", phone);
                    svcCtx.put("partyId", partyId);
                    svcCtx.put("contactMechPurposeTypeId", "PHONE_HOME");
                    try {
                        svcRes = dispatcher.runSync("createPartyTelecomNumber", svcCtx);
                    } catch (GenericServiceException e) {
                        Debug.logError(e, module);
                        pos.showDialog("dialog/error/exception", e.getMessage());
                        return null;
                    }
                    if (ServiceUtil.isError(svcRes)) {
                        pos.showDialog("dialog/error/exception", ServiceUtil.getErrorMessage(svcRes));
                        return null;
                    }
                }
            }

            result = partyId;

        // Update
        } else if (UtilValidate.isNotEmpty(partyId)){
            trace("Update a client profile");
            GenericValue  person = null;

            try {
                person = session.getDelegator().findByPrimaryKey("Person", UtilMisc.toMap("partyId", partyId));
            } catch (GenericEntityException e) {
                Debug.logError(e, module);
                pos.showDialog("dialog/error/exception", e.getMessage());
                return null;
            }

            Boolean newLogin = true;
            try {
                List<GenericValue>  userLogins = session.getDelegator().findByAnd("UserLogin", UtilMisc.toMap("partyId", partyId));
                if (UtilValidate.isNotEmpty(userLogins)) {
                    userLogin = userLogins.get(0);
                    newLogin = false;
                }
            } catch (GenericEntityException e) {
                Debug.logError(e, module);
                pos.showDialog("dialog/error/exception", e.getMessage());
                return null;
            }

            if (!person.getString("lastName").equals(name)
                    || UtilValidate.isNotEmpty(card) && !card.equals(person.getString("cardId"))) {
                // Update name and possibly card (cardId)
                svcCtx.put("userLogin", userLogin);
                svcCtx.put("partyId", partyId);
                svcCtx.put("firstName", ""); // Needed by service updatePerson
                svcCtx.put("lastName", name);
                if (UtilValidate.isNotEmpty(card)) {
                    svcCtx.put("cardId", card);
                }
                try {
                    // updatePerson
                    trace("updatePerson");
                    svcRes = dispatcher.runSync("updatePerson", svcCtx);
                } catch (GenericServiceException e) {
                    Debug.logError(e, module);
                    pos.showDialog("dialog/error/exception", e.getMessage());
                    return null;
                }
                if (ServiceUtil.isError(svcRes)) {
                    pos.showDialog("dialog/error/exceptionLargeSmallFont", ServiceUtil.getErrorMessage(svcRes));
                    return null;
                }
            }


            if (UtilValidate.isNotEmpty(phone)) {
                // Create or update phone
                if (phone.length() < 5 ) {
                    pos.showDialog("dialog/error/exception", UtilProperties.getMessage(PosTransaction.resource, "PosPhoneField5Required", locale));
                } else {
                    String contactNumber = null;
                    String contactMechId = null;
                    svcCtx.clear();
                    svcCtx.put("partyId", partyId);
                    svcCtx.put("thruDate", null); // last one
                    try {
                        List<GenericValue>  PartyTelecomNumbers = session.getDelegator().findByAnd("PartyAndTelecomNumber", svcCtx);
                        if (UtilValidate.isNotEmpty(PartyTelecomNumbers)) {
                            GenericValue PartyTelecomNumber = PartyTelecomNumbers.get(0); // There is  only one phone number (contactMechPurposeTypeId == "PHONE_HOME")
                            contactNumber = PartyTelecomNumber.getString("contactNumber");
                            contactMechId = PartyTelecomNumber.getString("contactMechId");
                        }
                    } catch (GenericEntityException e) {
                        Debug.logError(e, module);
                        pos.showDialog("dialog/error/exception", e.getMessage());
                        return null;
                    }

                    // Create or update phone
                    trace("createUpdatePartyTelecomNumber");
                    svcCtx.remove("thruDate");
                    svcCtx.put("userLogin", userLogin);
                    svcCtx.put("contactNumber", phone);
                    svcCtx.put("contactMechPurposeTypeId", "PHONE_HOME");
                    if (UtilValidate.isNotEmpty(contactMechId)) {
                        svcCtx.put("contactMechId", contactMechId);
                    }
                    try {
                        svcRes = dispatcher.runSync("createUpdatePartyTelecomNumber", svcCtx);
                    } catch (GenericServiceException e) {
                        Debug.logError(e, module);
                        pos.showDialog("dialog/error/exception", e.getMessage());
                        return null;
                    }
                    if (ServiceUtil.isError(svcRes)) {
                        pos.showDialog("dialog/error/exception", ServiceUtil.getErrorMessage(svcRes));
                        return null;
                    }

                    // Handle login aspect where phone is taken as pwd
                    if (UtilValidate.isNotEmpty(contactNumber) && !phone.equals(contactNumber)) {
                        if (!newLogin) { // to create a new login we need also an email address
                            // Update password, we need to temporary set password.accept.encrypted.and.plain to "true"
                            // This is done only for the properties loaded for the session in memory (we don't persist the value)
                            trace("updatePassword");
                            String passwordAcceptEncryptedAndPlain = null;
                            try {
                                passwordAcceptEncryptedAndPlain = UtilProperties.getPropertyValue("security.properties", "password.accept.encrypted.and.plain");
                                UtilProperties.setPropertyValueInMemory("security.properties", "password.accept.encrypted.and.plain", "true");
                                svcRes = dispatcher.runSync("updatePassword",
                                        UtilMisc.toMap("userLogin", userLogin,
                                        "userLoginId", userLogin.getString("userLoginId"),
                                        "currentPassword", userLogin.getString("currentPassword"),
                                        "newPassword", phone,
                                        "newPasswordVerify", phone));
                            } catch (GenericServiceException e) {
                                Debug.logError(e, "Error calling updatePassword service", module);
                                pos.showDialog("dialog/error/exception", e.getMessage());
                                UtilProperties.setPropertyValueInMemory("security.properties", "password.accept.encrypted.and.plain", passwordAcceptEncryptedAndPlain);
                                return null;
                            } finally {
                                // Put back passwordAcceptEncryptedAndPlain value in memory
                                UtilProperties.setPropertyValueInMemory("security.properties", "password.accept.encrypted.and.plain", passwordAcceptEncryptedAndPlain);
                            }
                            if (ServiceUtil.isError(svcRes)) {
                                pos.showDialog("dialog/error/exception", ServiceUtil.getErrorMessage(svcRes));
                                return null;
                            }
                        }
                    }
                }
            }

            if (UtilValidate.isNotEmpty(email)) {
            // Update email
                svcCtx.clear();
                svcCtx.put("partyId", partyId);
                svcCtx.put("thruDate", null); // last one
                svcCtx.put("contactMechTypeId", "EMAIL_ADDRESS");
                String contactMechId = null;
                String infoString = null;
                try {
                    List<GenericValue>  PartyEmails = session.getDelegator().findByAnd("PartyAndContactMech", svcCtx);
                    if (UtilValidate.isNotEmpty(PartyEmails)) {
                        GenericValue PartyEmail = PartyEmails.get(0); // There is  only one email address (contactMechPurposeTypeId == "PRIMARY_EMAIL")
                        contactMechId = PartyEmail.getString("contactMechId");
                        infoString = PartyEmail.getString("infoString");
                    }
                } catch (GenericEntityException e) {
                    Debug.logError(e, module);
                    pos.showDialog("dialog/error/exception", e.getMessage());
                    return null;
                }

                svcCtx.remove("thruDate");
                svcCtx.remove("contactMechTypeId");
                svcCtx.put("userLogin", userLogin);
                svcCtx.put("emailAddress", email);
                svcCtx.put("contactMechPurposeTypeId", "PRIMARY_EMAIL");
                if (UtilValidate.isNotEmpty(contactMechId)) {
                    svcCtx.put("contactMechId", contactMechId);
                }
                if (UtilValidate.isNotEmpty(infoString) && !email.equals(infoString)
                        || UtilValidate.isEmpty(infoString)) {
                    // Create or update email
                    trace("createUpdatePartyEmailAddress");
                    try {
                        svcRes = dispatcher.runSync("createUpdatePartyEmailAddress", svcCtx);
                    } catch (GenericServiceException e) {
                        Debug.logError(e, module);
                        pos.showDialog("dialog/error/exception", e.getMessage());
                        return null;
                    }
                    if (ServiceUtil.isError(svcRes)) {
                        pos.showDialog("dialog/error/exception", ServiceUtil.getErrorMessage(svcRes));
                        return null;
                    }
                }


                if (!newLogin && UtilValidate.isNotEmpty(infoString) && !email.equals(infoString)) {
                    // create a new UserLogin (Update a UserLoginId by creating a new one and expiring the old one). Keep the same password possibly changed just above if phone has also changed.
                    trace("updateUserLoginId");
                    try {
                        svcRes = dispatcher.runSync("updateUserLoginId", UtilMisc.toMap("userLoginId", email, "userLogin", userLogin));
                    } catch (GenericServiceException e) {
                        Debug.logError(e, module);
                        pos.showDialog("dialog/error/exception", e.getMessage());
                       return null;
                    }
                    if (ServiceUtil.isError(svcRes)) {
                        pos.showDialog("dialog/error/exception", ServiceUtil.getErrorMessage(svcRes));
                        return null;
                    }
                } else if (newLogin && UtilValidate.isNotEmpty(phone)) {
                    // createUserLogin
                    trace("createUserLogin");
                    try {
                        svcRes = dispatcher.runSync("createUserLogin",
                                UtilMisc.toMap("userLogin", userLogin,
                                        "userLoginId", email,
                                        "currentPassword", phone,
                                        "currentPasswordVerify", phone,
                                        "partyId", partyId));
                    } catch (GenericServiceException e) {
                        Debug.logError(e, "Error calling updatePassword service", module);
                        pos.showDialog("dialog/error/exception", e.getMessage());
                        return null;
                    }
                    if (ServiceUtil.isError(svcRes)) {
                        pos.showDialog("dialog/error/exception", ServiceUtil.getErrorMessage(svcRes));
                        return null;
                    }
                }
            }
        } else {
            pos.showDialog("dialog/error/exception", UtilProperties.getMessage(resource, "PosNoClientProfile", locale));
            return null;
        }
        return null;
    }
}
TOP

Related Classes of org.ofbiz.pos.PosTransaction

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.