Search in sources :

Example 41 with LocalDispatcher

use of org.apache.ofbiz.service.LocalDispatcher in project ofbiz-framework by apache.

the class ExternalLoginKeysManager method externalServerLoginCheck.

public static String externalServerLoginCheck(HttpServletRequest request, HttpServletResponse response) {
    Delegator delegator = (Delegator) request.getAttribute("delegator");
    HttpSession session = request.getSession();
    try {
        String userLoginId = null;
        String authorizationHeader = request.getHeader("Authorization");
        if (authorizationHeader != null) {
            Claims claims = returnsClaims(authorizationHeader);
            userLoginId = getSourceUserLoginId(claims);
            boolean jwtOK = checkJwt(authorizationHeader, userLoginId, "", "");
            if (!jwtOK) {
                // Something unexpected happened here
                Debug.logWarning("*** There was a problem with the JWT token, not signin in the user login " + userLoginId, module);
                return "success";
            }
        } else {
            // Nothing to do here
            return "success";
        }
        GenericValue userLogin = EntityQuery.use(delegator).from("UserLogin").where("userLoginId", userLoginId).queryOne();
        if (userLogin != null) {
            // Check it's the right tenant in case username and password are the same in different tenants
            // Not sure this is really useful in the case of external server, should not hurt anyway
            LocalDispatcher dispatcher = (LocalDispatcher) request.getAttribute("dispatcher");
            String oldDelegatorName = delegator.getDelegatorName();
            ServletContext servletContext = session.getServletContext();
            if (!oldDelegatorName.equals(userLogin.getDelegator().getDelegatorName())) {
                delegator = DelegatorFactory.getDelegator(userLogin.getDelegator().getDelegatorName());
                dispatcher = WebAppUtil.makeWebappDispatcher(servletContext, delegator);
                LoginWorker.setWebContextObjects(request, response, delegator, dispatcher);
            }
            String enabled = userLogin.getString("enabled");
            if (enabled == null || "Y".equals(enabled)) {
                userLogin.set("hasLoggedOut", "N");
                userLogin.store();
            }
        } else {
            Debug.logWarning("*** There was a problem with the JWT token. Could not find userLogin " + userLoginId, module);
        }
        LoginWorker.doBasicLogin(userLogin, request);
    } catch (GenericEntityException e) {
        Debug.logError(e, "Cannot get autoUserLogin information: " + e.getMessage(), module);
    }
    // make sure the autoUserLogin is set to the same and that the client cookie has the correct userLoginId
    LoginWorker.autoLoginSet(request, response);
    return "success";
}
Also used : GenericValue(org.apache.ofbiz.entity.GenericValue) LocalDispatcher(org.apache.ofbiz.service.LocalDispatcher) Claims(io.jsonwebtoken.Claims) Delegator(org.apache.ofbiz.entity.Delegator) HttpSession(javax.servlet.http.HttpSession) GenericEntityException(org.apache.ofbiz.entity.GenericEntityException) ServletContext(javax.servlet.ServletContext)

Example 42 with LocalDispatcher

use of org.apache.ofbiz.service.LocalDispatcher in project ofbiz-framework by apache.

the class CoreEvents method runService.

/**
 * Run a service.
 *  Request Parameters which are used for this event:
 *  SERVICE_NAME      - Name of the service to invoke
 *
 * @param request HttpServletRequest
 * @param response HttpServletResponse
 * @return Response code string
 */
public static String runService(HttpServletRequest request, HttpServletResponse response) {
    // get the mode and service name
    String serviceName = request.getParameter("serviceName");
    String mode = request.getParameter("mode");
    Locale locale = UtilHttp.getLocale(request);
    if (UtilValidate.isEmpty(serviceName)) {
        String errMsg = UtilProperties.getMessage(CoreEvents.err_resource, "coreEvents.must_specify_service_name", locale);
        request.setAttribute("_ERROR_MESSAGE_", errMsg);
        return "error";
    }
    if (UtilValidate.isEmpty(mode)) {
        mode = "sync";
    }
    // now do a security check
    Security security = (Security) request.getAttribute("security");
    LocalDispatcher dispatcher = (LocalDispatcher) request.getAttribute("dispatcher");
    // lookup the service definition to see if this service is externally available, if not require the SERVICE_INVOKE_ANY permission
    ModelService modelService = null;
    try {
        modelService = dispatcher.getDispatchContext().getModelService(serviceName);
    } catch (GenericServiceException e) {
        Debug.logError(e, "Error looking up ModelService for serviceName [" + serviceName + "]", module);
        String errMsg = UtilProperties.getMessage(CoreEvents.err_resource, "coreEvents.error_modelservice_for_srv_name", locale);
        request.setAttribute("_ERROR_MESSAGE_", errMsg + "[" + serviceName + "]: " + e.toString());
        return "error";
    }
    if (modelService == null) {
        String errMsg = UtilProperties.getMessage(CoreEvents.err_resource, "coreEvents.service_name_not_find", locale);
        request.setAttribute("_ERROR_MESSAGE_", errMsg + "[" + serviceName + "]");
        return "error";
    }
    if (!modelService.export && !security.hasPermission("SERVICE_INVOKE_ANY", request.getSession())) {
        String errMsg = UtilProperties.getMessage(CoreEvents.err_resource, "coreEvents.not_authorized_to_call", locale);
        request.setAttribute("_ERROR_MESSAGE_", errMsg + ".");
        return "error";
    }
    Debug.logInfo("Running service named [" + serviceName + "] from event with mode [" + mode + "]", module);
    // call the service via the ServiceEventHandler which
    // adapts an event to a service.
    Event event = new Event("service", mode, serviceName, false);
    try {
        return seh.invoke(event, null, request, response);
    } catch (EventHandlerException e) {
        String errMsg = UtilProperties.getMessage(CoreEvents.err_resource, "coreEvents.service_eventhandler_exception", locale);
        request.setAttribute("_ERROR_MESSAGE_", errMsg + ": " + e.getMessage());
        return "error";
    }
}
Also used : Locale(java.util.Locale) LocalDispatcher(org.apache.ofbiz.service.LocalDispatcher) Event(org.apache.ofbiz.webapp.control.ConfigXMLReader.Event) GenericServiceException(org.apache.ofbiz.service.GenericServiceException) Security(org.apache.ofbiz.security.Security) ModelService(org.apache.ofbiz.service.ModelService)

Example 43 with LocalDispatcher

use of org.apache.ofbiz.service.LocalDispatcher in project ofbiz-framework by apache.

the class CoreEvents method scheduleService.

/**
 * Schedule a service for a specific time or recurrence
 *  Request Parameters which are used for this service:
 *
 *  SERVICE_NAME      - Name of the service to invoke
 *  SERVICE_TIME      - First time the service will occur
 *  SERVICE_FREQUENCY - The type of recurrence (SECONDLY,MINUTELY,DAILY,etc)
 *  SERVICE_INTERVAL  - The interval of the frequency (every 5 minutes, etc)
 *
 * @param request HttpServletRequest
 * @param response HttpServletResponse
 * @return Response code string
 */
public static String scheduleService(HttpServletRequest request, HttpServletResponse response) {
    Security security = (Security) request.getAttribute("security");
    GenericValue userLogin = (GenericValue) request.getSession().getAttribute("userLogin");
    LocalDispatcher dispatcher = (LocalDispatcher) request.getAttribute("dispatcher");
    // Delegator delegator = (Delegator) request.getAttribute("delegator");
    Locale locale = UtilHttp.getLocale(request);
    TimeZone timeZone = UtilHttp.getTimeZone(request);
    Map<String, Object> params = UtilHttp.getParameterMap(request);
    // get the schedule parameters
    String jobName = (String) params.remove("JOB_NAME");
    String serviceName = (String) params.remove("SERVICE_NAME");
    String poolName = (String) params.remove("POOL_NAME");
    String serviceTime = (String) params.remove("SERVICE_TIME");
    String serviceEndTime = (String) params.remove("SERVICE_END_TIME");
    String serviceFreq = (String) params.remove("SERVICE_FREQUENCY");
    String serviceIntr = (String) params.remove("SERVICE_INTERVAL");
    String serviceCnt = (String) params.remove("SERVICE_COUNT");
    String retryCnt = (String) params.remove("SERVICE_MAXRETRY");
    // the frequency map
    Map<String, Integer> freqMap = new HashMap<String, Integer>();
    freqMap.put("SECONDLY", Integer.valueOf(1));
    freqMap.put("MINUTELY", Integer.valueOf(2));
    freqMap.put("HOURLY", Integer.valueOf(3));
    freqMap.put("DAILY", Integer.valueOf(4));
    freqMap.put("WEEKLY", Integer.valueOf(5));
    freqMap.put("MONTHLY", Integer.valueOf(6));
    freqMap.put("YEARLY", Integer.valueOf(7));
    // some defaults
    long startTime = (new Date()).getTime();
    long endTime = 0;
    int maxRetry = -1;
    int count = 1;
    int interval = 1;
    int frequency = RecurrenceRule.DAILY;
    StringBuilder errorBuf = new StringBuilder();
    // make sure we passed a service
    if (serviceName == null) {
        String errMsg = UtilProperties.getMessage(CoreEvents.err_resource, "coreEvents.must_specify_service", locale);
        request.setAttribute("_ERROR_MESSAGE_", errMsg);
        return "error";
    }
    // lookup the service definition to see if this service is externally available, if not require the SERVICE_INVOKE_ANY permission
    ModelService modelService = null;
    try {
        modelService = dispatcher.getDispatchContext().getModelService(serviceName);
    } catch (GenericServiceException e) {
        Debug.logError(e, "Error looking up ModelService for serviceName [" + serviceName + "]", module);
        String errMsg = UtilProperties.getMessage(CoreEvents.err_resource, "coreEvents.error_modelservice_for_srv_name", locale);
        request.setAttribute("_ERROR_MESSAGE_", errMsg + " [" + serviceName + "]: " + e.toString());
        return "error";
    }
    if (modelService == null) {
        String errMsg = UtilProperties.getMessage(CoreEvents.err_resource, "coreEvents.service_name_not_find", locale);
        request.setAttribute("_ERROR_MESSAGE_", errMsg + " [" + serviceName + "]");
        return "error";
    }
    // make the context valid; using the makeValid method from ModelService
    Map<String, Object> serviceContext = new HashMap<String, Object>();
    Iterator<String> ci = modelService.getInParamNames().iterator();
    while (ci.hasNext()) {
        String name = ci.next();
        // don't include userLogin, that's taken care of below
        if ("userLogin".equals(name))
            continue;
        // don't include locale, that is also taken care of below
        if ("locale".equals(name))
            continue;
        Object value = request.getParameter(name);
        // if the parameter wasn't passed and no other value found, don't pass on the null
        if (value == null) {
            value = request.getAttribute(name);
        }
        if (value == null) {
            value = request.getSession().getAttribute(name);
        }
        if (value == null) {
            // still null, give up for this one
            continue;
        }
        if (value instanceof String && ((String) value).length() == 0) {
            // interpreting empty fields as null values for each in back end handling...
            value = null;
        }
        // set even if null so that values will get nulled in the db later on
        serviceContext.put(name, value);
    }
    serviceContext = modelService.makeValid(serviceContext, ModelService.IN_PARAM, true, null, timeZone, locale);
    if (userLogin != null) {
        serviceContext.put("userLogin", userLogin);
    }
    if (locale != null) {
        serviceContext.put("locale", locale);
    }
    if (!modelService.export && !security.hasPermission("SERVICE_INVOKE_ANY", request.getSession())) {
        String errMsg = UtilProperties.getMessage(CoreEvents.err_resource, "coreEvents.not_authorized_to_call", locale);
        request.setAttribute("_ERROR_MESSAGE_", errMsg);
        return "error";
    }
    // some conversions
    if (UtilValidate.isNotEmpty(serviceTime)) {
        try {
            Timestamp ts1 = Timestamp.valueOf(serviceTime);
            startTime = ts1.getTime();
        } catch (IllegalArgumentException e) {
            try {
                startTime = Long.parseLong(serviceTime);
            } catch (NumberFormatException nfe) {
                String errMsg = UtilProperties.getMessage(CoreEvents.err_resource, "coreEvents.invalid_format_time", locale);
                errorBuf.append(errMsg);
            }
        }
        if (startTime < (new Date()).getTime()) {
            String errMsg = UtilProperties.getMessage(CoreEvents.err_resource, "coreEvents.service_time_already_passed", locale);
            errorBuf.append(errMsg);
        }
    }
    if (UtilValidate.isNotEmpty(serviceEndTime)) {
        try {
            Timestamp ts1 = Timestamp.valueOf(serviceEndTime);
            endTime = ts1.getTime();
        } catch (IllegalArgumentException e) {
            try {
                endTime = Long.parseLong(serviceTime);
            } catch (NumberFormatException nfe) {
                String errMsg = UtilProperties.getMessage(CoreEvents.err_resource, "coreEvents.invalid_format_time", locale);
                errorBuf.append(errMsg);
            }
        }
        if (endTime < (new Date()).getTime()) {
            String errMsg = UtilProperties.getMessage(CoreEvents.err_resource, "coreEvents.service_time_already_passed", locale);
            errorBuf.append(errMsg);
        }
    }
    if (UtilValidate.isNotEmpty(serviceIntr)) {
        try {
            interval = Integer.parseInt(serviceIntr);
        } catch (NumberFormatException nfe) {
            String errMsg = UtilProperties.getMessage(CoreEvents.err_resource, "coreEvents.invalid_format_interval", locale);
            errorBuf.append(errMsg);
        }
    }
    if (UtilValidate.isNotEmpty(serviceCnt)) {
        try {
            count = Integer.parseInt(serviceCnt);
        } catch (NumberFormatException nfe) {
            String errMsg = UtilProperties.getMessage(CoreEvents.err_resource, "coreEvents.invalid_format_count", locale);
            errorBuf.append(errMsg);
        }
    }
    if (UtilValidate.isNotEmpty(serviceFreq)) {
        int parsedValue = 0;
        try {
            parsedValue = Integer.parseInt(serviceFreq);
            if (parsedValue > 0 && parsedValue < 8)
                frequency = parsedValue;
        } catch (NumberFormatException nfe) {
            parsedValue = 0;
        }
        if (parsedValue == 0) {
            if (!freqMap.containsKey(serviceFreq.toUpperCase())) {
                String errMsg = UtilProperties.getMessage(CoreEvents.err_resource, "coreEvents.invalid_format_frequency", locale);
                errorBuf.append(errMsg);
            } else {
                frequency = freqMap.get(serviceFreq.toUpperCase()).intValue();
            }
        }
    }
    if (UtilValidate.isNotEmpty(retryCnt)) {
        int parsedValue = -2;
        try {
            parsedValue = Integer.parseInt(retryCnt);
        } catch (NumberFormatException e) {
            parsedValue = -2;
        }
        if (parsedValue > -2) {
            maxRetry = parsedValue;
        } else {
            maxRetry = modelService.maxRetry;
        }
    } else {
        maxRetry = modelService.maxRetry;
    }
    // return the errors
    if (errorBuf.length() > 0) {
        request.setAttribute("_ERROR_MESSAGE_", errorBuf.toString());
        return "error";
    }
    Map<String, Object> syncServiceResult = null;
    // schedule service
    try {
        if (null != request.getParameter("_RUN_SYNC_") && "Y".equals(request.getParameter("_RUN_SYNC_"))) {
            syncServiceResult = dispatcher.runSync(serviceName, serviceContext);
        } else {
            dispatcher.schedule(jobName, poolName, serviceName, serviceContext, startTime, frequency, interval, count, endTime, maxRetry);
        }
    } catch (GenericServiceException e) {
        String errMsg = UtilProperties.getMessage(CoreEvents.err_resource, "coreEvents.service_dispatcher_exception", locale);
        request.setAttribute("_ERROR_MESSAGE_", errMsg + e.getMessage());
        return "error";
    }
    String errMsg = UtilProperties.getMessage(CoreEvents.err_resource, "coreEvents.service_scheduled", locale);
    request.setAttribute("_EVENT_MESSAGE_", errMsg);
    if (null != syncServiceResult) {
        request.getSession().setAttribute("_RUN_SYNC_RESULT_", syncServiceResult);
        return "sync_success";
    }
    return "success";
}
Also used : Locale(java.util.Locale) GenericValue(org.apache.ofbiz.entity.GenericValue) LocalDispatcher(org.apache.ofbiz.service.LocalDispatcher) HashMap(java.util.HashMap) Security(org.apache.ofbiz.security.Security) Timestamp(java.sql.Timestamp) Date(java.util.Date) ModelService(org.apache.ofbiz.service.ModelService) TimeZone(java.util.TimeZone) GenericServiceException(org.apache.ofbiz.service.GenericServiceException)

Example 44 with LocalDispatcher

use of org.apache.ofbiz.service.LocalDispatcher in project ofbiz-framework by apache.

the class InvoiceServices method createInvoiceForOrder.

/* Service to create an invoice for an order */
public static Map<String, Object> createInvoiceForOrder(DispatchContext dctx, Map<String, Object> context) {
    Delegator delegator = dctx.getDelegator();
    LocalDispatcher dispatcher = dctx.getDispatcher();
    GenericValue userLogin = (GenericValue) context.get("userLogin");
    Locale locale = (Locale) context.get("locale");
    if (DECIMALS == -1 || ROUNDING == null) {
        return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingAritmeticPropertiesNotConfigured", locale));
    }
    String orderId = (String) context.get("orderId");
    List<GenericValue> billItems = UtilGenerics.checkList(context.get("billItems"));
    String invoiceId = (String) context.get("invoiceId");
    if (UtilValidate.isEmpty(billItems)) {
        if (Debug.verboseOn())
            Debug.logVerbose("No order items to invoice; not creating invoice; returning success", module);
        return ServiceUtil.returnSuccess(UtilProperties.getMessage(resource, "AccountingNoOrderItemsToInvoice", locale));
    }
    try {
        GenericValue orderHeader = EntityQuery.use(delegator).from("OrderHeader").where("orderId", orderId).queryOne();
        if (orderHeader == null) {
            return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingNoOrderHeader", locale));
        }
        // figure out the invoice type
        String invoiceType = null;
        String orderType = orderHeader.getString("orderTypeId");
        if ("SALES_ORDER".equals(orderType)) {
            invoiceType = "SALES_INVOICE";
        } else if ("PURCHASE_ORDER".equals(orderType)) {
            invoiceType = "PURCHASE_INVOICE";
        }
        // Set the precision depending on the type of invoice
        int invoiceTypeDecimals = UtilNumber.getBigDecimalScale("invoice." + invoiceType + ".decimals");
        if (invoiceTypeDecimals == -1) {
            invoiceTypeDecimals = DECIMALS;
        }
        // Make an order read helper from the order
        OrderReadHelper orh = new OrderReadHelper(orderHeader);
        // get the product store
        GenericValue productStore = orh.getProductStore();
        // get the shipping adjustment mode (Y = Pro-Rate; N = First-Invoice)
        String prorateShipping = productStore != null ? productStore.getString("prorateShipping") : "Y";
        if (prorateShipping == null) {
            prorateShipping = "Y";
        }
        // get the billing parties
        String billToCustomerPartyId = orh.getBillToParty().getString("partyId");
        String billFromVendorPartyId = orh.getBillFromParty().getString("partyId");
        // get some price totals
        BigDecimal shippableAmount = orh.getShippableTotal(null);
        BigDecimal shippableQuantity = orh.getShippableQuantity(null);
        BigDecimal orderSubTotal = orh.getOrderItemsSubTotal();
        BigDecimal orderQuantity = orh.getTotalOrderItemsQuantity();
        // these variables are for pro-rating order amounts across invoices, so they should not be rounded off for maximum accuracy
        BigDecimal invoiceShipProRateAmount = BigDecimal.ZERO;
        BigDecimal invoiceShippableQuantity = BigDecimal.ZERO;
        BigDecimal invoiceSubTotal = BigDecimal.ZERO;
        BigDecimal invoiceQuantity = BigDecimal.ZERO;
        GenericValue billingAccount = orderHeader.getRelatedOne("BillingAccount", false);
        String billingAccountId = billingAccount != null ? billingAccount.getString("billingAccountId") : null;
        Timestamp invoiceDate = (Timestamp) context.get("eventDate");
        if (UtilValidate.isEmpty(invoiceDate)) {
            // TODO: ideally this should be the same time as when a shipment is sent and be passed in as a parameter
            invoiceDate = UtilDateTime.nowTimestamp();
        }
        // TODO: perhaps consider billing account net days term as well?
        Long orderTermNetDays = orh.getOrderTermNetDays();
        Timestamp dueDate = null;
        if (orderTermNetDays != null) {
            dueDate = UtilDateTime.getDayEnd(invoiceDate, orderTermNetDays);
        }
        // create the invoice record
        if (UtilValidate.isEmpty(invoiceId)) {
            Map<String, Object> createInvoiceContext = new HashMap<>();
            createInvoiceContext.put("partyId", billToCustomerPartyId);
            createInvoiceContext.put("partyIdFrom", billFromVendorPartyId);
            createInvoiceContext.put("billingAccountId", billingAccountId);
            createInvoiceContext.put("invoiceDate", invoiceDate);
            createInvoiceContext.put("dueDate", dueDate);
            createInvoiceContext.put("invoiceTypeId", invoiceType);
            // start with INVOICE_IN_PROCESS, in the INVOICE_READY we can't change the invoice (or shouldn't be able to...)
            createInvoiceContext.put("statusId", "INVOICE_IN_PROCESS");
            createInvoiceContext.put("currencyUomId", orderHeader.getString("currencyUom"));
            createInvoiceContext.put("userLogin", userLogin);
            // store the invoice first
            Map<String, Object> createInvoiceResult = dispatcher.runSync("createInvoice", createInvoiceContext);
            if (ServiceUtil.isError(createInvoiceResult)) {
                return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingErrorCreatingInvoiceFromOrder", locale), null, null, createInvoiceResult);
            }
            // call service, not direct entity op: delegator.create(invoice);
            invoiceId = (String) createInvoiceResult.get("invoiceId");
        }
        // order roles to invoice roles
        List<GenericValue> orderRoles = orderHeader.getRelated("OrderRole", null, null, false);
        Map<String, Object> createInvoiceRoleContext = new HashMap<>();
        createInvoiceRoleContext.put("invoiceId", invoiceId);
        createInvoiceRoleContext.put("userLogin", userLogin);
        for (GenericValue orderRole : orderRoles) {
            createInvoiceRoleContext.put("partyId", orderRole.getString("partyId"));
            createInvoiceRoleContext.put("roleTypeId", orderRole.getString("roleTypeId"));
            Map<String, Object> createInvoiceRoleResult = dispatcher.runSync("createInvoiceRole", createInvoiceRoleContext);
            if (ServiceUtil.isError(createInvoiceRoleResult)) {
                return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingErrorCreatingInvoiceFromOrder", locale), null, null, createInvoiceRoleResult);
            }
        }
        // order terms to invoice terms.
        // TODO: it might be nice to filter OrderTerms to only copy over financial terms.
        List<GenericValue> orderTerms = orh.getOrderTerms();
        createInvoiceTerms(delegator, dispatcher, invoiceId, orderTerms, userLogin, locale);
        // for billing accounts we will use related information
        if (billingAccount != null) {
            /*
                 * jacopoc: billing account terms were already copied as order terms
                 *          when the order was created.
                // get the billing account terms
                billingAccountTerms = billingAccount.getRelated("BillingAccountTerm", null, null, false);

                // set the invoice terms as defined for the billing account
                createInvoiceTerms(delegator, dispatcher, invoiceId, billingAccountTerms, userLogin, locale);
                */
            // set the invoice bill_to_customer from the billing account
            List<GenericValue> billToRoles = billingAccount.getRelated("BillingAccountRole", UtilMisc.toMap("roleTypeId", "BILL_TO_CUSTOMER"), null, false);
            for (GenericValue billToRole : billToRoles) {
                if (!(billToRole.getString("partyId").equals(billToCustomerPartyId))) {
                    createInvoiceRoleContext = UtilMisc.toMap("invoiceId", invoiceId, "partyId", billToRole.get("partyId"), "roleTypeId", "BILL_TO_CUSTOMER", "userLogin", userLogin);
                    Map<String, Object> createInvoiceRoleResult = dispatcher.runSync("createInvoiceRole", createInvoiceRoleContext);
                    if (ServiceUtil.isError(createInvoiceRoleResult)) {
                        return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingErrorCreatingInvoiceRoleFromOrder", locale), null, null, createInvoiceRoleResult);
                    }
                }
            }
            // set the bill-to contact mech as the contact mech of the billing account
            if (UtilValidate.isNotEmpty(billingAccount.getString("contactMechId"))) {
                Map<String, Object> createBillToContactMechContext = UtilMisc.toMap("invoiceId", invoiceId, "contactMechId", billingAccount.getString("contactMechId"), "contactMechPurposeTypeId", "BILLING_LOCATION", "userLogin", userLogin);
                Map<String, Object> createBillToContactMechResult = dispatcher.runSync("createInvoiceContactMech", createBillToContactMechContext);
                if (ServiceUtil.isError(createBillToContactMechResult)) {
                    return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingErrorCreatingInvoiceContactMechFromOrder", locale), null, null, createBillToContactMechResult);
                }
            }
        } else {
            List<GenericValue> billingLocations = orh.getBillingLocations();
            if (UtilValidate.isNotEmpty(billingLocations)) {
                for (GenericValue ocm : billingLocations) {
                    Map<String, Object> createBillToContactMechContext = UtilMisc.toMap("invoiceId", invoiceId, "contactMechId", ocm.getString("contactMechId"), "contactMechPurposeTypeId", "BILLING_LOCATION", "userLogin", userLogin);
                    Map<String, Object> createBillToContactMechResult = dispatcher.runSync("createInvoiceContactMech", createBillToContactMechContext);
                    if (ServiceUtil.isError(createBillToContactMechResult)) {
                        return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingErrorCreatingInvoiceContactMechFromOrder", locale), null, null, createBillToContactMechResult);
                    }
                }
            } else {
                Debug.logWarning("No billing locations found for order [" + orderId + "] and none were created for Invoice [" + invoiceId + "]", module);
            }
        }
        // get a list of the payment method types
        // DEJ20050705 doesn't appear to be used: List paymentPreferences = orderHeader.getRelated("OrderPaymentPreference", null, null, false);
        // create the bill-from (or pay-to) contact mech as the primary PAYMENT_LOCATION of the party from the store
        GenericValue payToAddress = null;
        if ("PURCHASE_INVOICE".equals(invoiceType)) {
            // for purchase orders, the pay to address is the BILLING_LOCATION of the vendor
            GenericValue billFromVendor = orh.getPartyFromRole("BILL_FROM_VENDOR");
            if (billFromVendor != null) {
                List<GenericValue> billingContactMechs = billFromVendor.getRelatedOne("Party", false).getRelated("PartyContactMechPurpose", UtilMisc.toMap("contactMechPurposeTypeId", "BILLING_LOCATION"), null, false);
                if (UtilValidate.isNotEmpty(billingContactMechs)) {
                    payToAddress = EntityUtil.getFirst(EntityUtil.filterByDate(billingContactMechs));
                }
            }
        } else {
            // for sales orders, it is the payment address on file for the store
            payToAddress = PaymentWorker.getPaymentAddress(delegator, productStore.getString("payToPartyId"));
        }
        if (payToAddress != null) {
            Map<String, Object> createPayToContactMechContext = UtilMisc.toMap("invoiceId", invoiceId, "contactMechId", payToAddress.getString("contactMechId"), "contactMechPurposeTypeId", "PAYMENT_LOCATION", "userLogin", userLogin);
            Map<String, Object> createPayToContactMechResult = dispatcher.runSync("createInvoiceContactMech", createPayToContactMechContext);
            if (ServiceUtil.isError(createPayToContactMechResult)) {
                return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingErrorCreatingInvoiceContactMechFromOrder", locale), null, null, createPayToContactMechResult);
            }
        }
        // sequence for items - all OrderItems or InventoryReservations + all Adjustments
        int invoiceItemSeqNum = 1;
        String invoiceItemSeqId = UtilFormatOut.formatPaddedNumber(invoiceItemSeqNum, INVOICE_ITEM_SEQUENCE_ID_DIGITS);
        // create the item records
        for (GenericValue currentValue : billItems) {
            GenericValue itemIssuance = null;
            GenericValue orderItem = null;
            GenericValue shipmentReceipt = null;
            if ("ItemIssuance".equals(currentValue.getEntityName())) {
                itemIssuance = currentValue;
            } else if ("OrderItem".equals(currentValue.getEntityName())) {
                orderItem = currentValue;
            } else if ("ShipmentReceipt".equals(currentValue.getEntityName())) {
                shipmentReceipt = currentValue;
            } else {
                Debug.logError("Unexpected entity " + currentValue + " of type " + currentValue.getEntityName(), module);
            }
            if (orderItem == null && itemIssuance != null) {
                orderItem = itemIssuance.getRelatedOne("OrderItem", false);
            } else if ((orderItem == null) && (shipmentReceipt != null)) {
                orderItem = shipmentReceipt.getRelatedOne("OrderItem", false);
            }
            if (orderItem == null) {
                Debug.logError("Cannot create invoice when orderItem, itemIssuance, and shipmentReceipt are all null", module);
                return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingIllegalValuesPassedToCreateInvoiceService", locale));
            }
            GenericValue product = null;
            if (orderItem.get("productId") != null) {
                product = orderItem.getRelatedOne("Product", false);
            }
            // get some quantities
            BigDecimal billingQuantity = null;
            if (itemIssuance != null) {
                billingQuantity = itemIssuance.getBigDecimal("quantity");
                BigDecimal cancelQty = itemIssuance.getBigDecimal("cancelQuantity");
                if (cancelQty == null) {
                    cancelQty = BigDecimal.ZERO;
                }
                billingQuantity = billingQuantity.subtract(cancelQty).setScale(DECIMALS, ROUNDING);
            } else if (shipmentReceipt != null) {
                billingQuantity = shipmentReceipt.getBigDecimal("quantityAccepted");
            } else {
                BigDecimal orderedQuantity = OrderReadHelper.getOrderItemQuantity(orderItem);
                BigDecimal invoicedQuantity = OrderReadHelper.getOrderItemInvoicedQuantity(orderItem);
                billingQuantity = orderedQuantity.subtract(invoicedQuantity);
                if (billingQuantity.compareTo(BigDecimal.ZERO) < 0) {
                    billingQuantity = BigDecimal.ZERO;
                }
            }
            if (billingQuantity == null) {
                billingQuantity = BigDecimal.ZERO;
            }
            // check if shipping applies to this item.  Shipping is calculated for sales invoices, not purchase invoices.
            boolean shippingApplies = false;
            if ((product != null) && (ProductWorker.shippingApplies(product)) && ("SALES_INVOICE".equals(invoiceType))) {
                shippingApplies = true;
            }
            BigDecimal billingAmount = BigDecimal.ZERO;
            GenericValue OrderAdjustment = EntityUtil.getFirst(orderItem.getRelated("OrderAdjustment", UtilMisc.toMap("orderAdjustmentTypeId", "VAT_TAX"), null, false));
            /* Apply formula to get actual product price to set amount in invoice item
                    Formula is: productPrice = (productPriceWithTax.multiply(100)) / (orderAdj sourcePercentage + 100))
                    product price = (43*100) / (20+100) = 35.83 (Here product price is 43 with VAT)
                 */
            if (UtilValidate.isNotEmpty(OrderAdjustment) && (OrderAdjustment.getBigDecimal("amount").signum() == 0) && UtilValidate.isNotEmpty(OrderAdjustment.getBigDecimal("amountAlreadyIncluded")) && OrderAdjustment.getBigDecimal("amountAlreadyIncluded").signum() != 0) {
                BigDecimal sourcePercentageTotal = OrderAdjustment.getBigDecimal("sourcePercentage").add(new BigDecimal(100));
                billingAmount = orderItem.getBigDecimal("unitPrice").divide(sourcePercentageTotal, 100, ROUNDING).multiply(new BigDecimal(100)).setScale(invoiceTypeDecimals, ROUNDING);
            } else {
                billingAmount = orderItem.getBigDecimal("unitPrice").setScale(invoiceTypeDecimals, ROUNDING);
            }
            Map<String, Object> createInvoiceItemContext = new HashMap<>();
            createInvoiceItemContext.put("invoiceId", invoiceId);
            createInvoiceItemContext.put("invoiceItemSeqId", invoiceItemSeqId);
            createInvoiceItemContext.put("invoiceItemTypeId", getInvoiceItemType(delegator, (orderItem.getString("orderItemTypeId")), (product == null ? null : product.getString("productTypeId")), invoiceType, "INV_FPROD_ITEM"));
            createInvoiceItemContext.put("description", orderItem.get("itemDescription"));
            createInvoiceItemContext.put("quantity", billingQuantity);
            createInvoiceItemContext.put("amount", billingAmount);
            createInvoiceItemContext.put("productId", orderItem.get("productId"));
            createInvoiceItemContext.put("productFeatureId", orderItem.get("productFeatureId"));
            createInvoiceItemContext.put("overrideGlAccountId", orderItem.get("overrideGlAccountId"));
            createInvoiceItemContext.put("userLogin", userLogin);
            String itemIssuanceId = null;
            if (itemIssuance != null && itemIssuance.get("inventoryItemId") != null) {
                itemIssuanceId = itemIssuance.getString("itemIssuanceId");
                createInvoiceItemContext.put("inventoryItemId", itemIssuance.get("inventoryItemId"));
            }
            // similarly, tax only for purchase invoices
            if ((product != null) && ("SALES_INVOICE".equals(invoiceType))) {
                createInvoiceItemContext.put("taxableFlag", product.get("taxable"));
            }
            Map<String, Object> createInvoiceItemResult = dispatcher.runSync("createInvoiceItem", createInvoiceItemContext);
            if (ServiceUtil.isError(createInvoiceItemResult)) {
                return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingErrorCreatingInvoiceItemFromOrder", locale), null, null, createInvoiceItemResult);
            }
            // this item total
            BigDecimal thisAmount = billingAmount.multiply(billingQuantity).setScale(invoiceTypeDecimals, ROUNDING);
            // add to the ship amount only if it applies to this item
            if (shippingApplies) {
                invoiceShipProRateAmount = invoiceShipProRateAmount.add(thisAmount).setScale(invoiceTypeDecimals, ROUNDING);
                invoiceShippableQuantity = invoiceQuantity.add(billingQuantity).setScale(invoiceTypeDecimals, ROUNDING);
            }
            // increment the invoice subtotal
            invoiceSubTotal = invoiceSubTotal.add(thisAmount).setScale(100, ROUNDING);
            // increment the invoice quantity
            invoiceQuantity = invoiceQuantity.add(billingQuantity).setScale(invoiceTypeDecimals, ROUNDING);
            // create the OrderItemBilling record
            Map<String, Object> createOrderItemBillingContext = new HashMap<>();
            createOrderItemBillingContext.put("invoiceId", invoiceId);
            createOrderItemBillingContext.put("invoiceItemSeqId", invoiceItemSeqId);
            createOrderItemBillingContext.put("orderId", orderItem.get("orderId"));
            createOrderItemBillingContext.put("orderItemSeqId", orderItem.get("orderItemSeqId"));
            createOrderItemBillingContext.put("itemIssuanceId", itemIssuanceId);
            createOrderItemBillingContext.put("quantity", billingQuantity);
            createOrderItemBillingContext.put("amount", billingAmount);
            createOrderItemBillingContext.put("userLogin", userLogin);
            if ((shipmentReceipt != null) && (shipmentReceipt.getString("receiptId") != null)) {
                createOrderItemBillingContext.put("shipmentReceiptId", shipmentReceipt.getString("receiptId"));
            }
            Map<String, Object> createOrderItemBillingResult = dispatcher.runSync("createOrderItemBilling", createOrderItemBillingContext);
            if (ServiceUtil.isError(createOrderItemBillingResult)) {
                return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingErrorCreatingOrderItemBillingFromOrder", locale), null, null, createOrderItemBillingResult);
            }
            if ("ItemIssuance".equals(currentValue.getEntityName())) {
                /* Find ShipmentItemBilling based on shipmentId, shipmentItemSeqId, invoiceId, invoiceItemSeqId as
                       because if any order item has multiple quantity and reserved by multiple inventories then there will be multiple invoice items.
                       In that case ShipmentItemBilling was creating only for one invoice item. Fixed under OFBIZ-6806.
                    */
                List<GenericValue> shipmentItemBillings = EntityQuery.use(delegator).from("ShipmentItemBilling").where("shipmentId", currentValue.get("shipmentId"), "shipmentItemSeqId", currentValue.get("shipmentItemSeqId"), "invoiceId", invoiceId, "invoiceItemSeqId", invoiceItemSeqId).queryList();
                if (UtilValidate.isEmpty(shipmentItemBillings)) {
                    // create the ShipmentItemBilling record
                    Map<String, Object> shipmentItemBillingCtx = new HashMap<>();
                    shipmentItemBillingCtx.put("invoiceId", invoiceId);
                    shipmentItemBillingCtx.put("invoiceItemSeqId", invoiceItemSeqId);
                    shipmentItemBillingCtx.put("shipmentId", currentValue.get("shipmentId"));
                    shipmentItemBillingCtx.put("shipmentItemSeqId", currentValue.get("shipmentItemSeqId"));
                    shipmentItemBillingCtx.put("userLogin", userLogin);
                    Map<String, Object> result = dispatcher.runSync("createShipmentItemBilling", shipmentItemBillingCtx);
                    if (ServiceUtil.isError(result)) {
                        return ServiceUtil.returnError(ServiceUtil.getErrorMessage(result));
                    }
                }
            }
            String parentInvoiceItemSeqId = invoiceItemSeqId;
            // increment the counter
            invoiceItemSeqNum++;
            invoiceItemSeqId = UtilFormatOut.formatPaddedNumber(invoiceItemSeqNum, INVOICE_ITEM_SEQUENCE_ID_DIGITS);
            // Get the original order item from the DB, in case the quantity has been overridden
            GenericValue originalOrderItem = EntityQuery.use(delegator).from("OrderItem").where("orderId", orderId, "orderItemSeqId", orderItem.get("orderItemSeqId")).queryOne();
            // create the item adjustment as line items
            List<GenericValue> itemAdjustments = OrderReadHelper.getOrderItemAdjustmentList(orderItem, orh.getAdjustments());
            for (GenericValue adj : itemAdjustments) {
                // Check against OrderAdjustmentBilling to see how much of this adjustment has already been invoiced
                BigDecimal adjAlreadyInvoicedAmount = null;
                try {
                    Map<String, Object> checkResult = dispatcher.runSync("calculateInvoicedAdjustmentTotal", UtilMisc.toMap("orderAdjustment", adj));
                    if (ServiceUtil.isError(checkResult)) {
                        Debug.logError("Accounting trouble calling calculateInvoicedAdjustmentTotal service", module);
                        return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingTroubleCallingCalculateInvoicedAdjustmentTotalService", locale));
                    }
                    adjAlreadyInvoicedAmount = (BigDecimal) checkResult.get("invoicedTotal");
                } catch (GenericServiceException e) {
                    Debug.logError(e, "Accounting trouble calling calculateInvoicedAdjustmentTotal service", module);
                    return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingTroubleCallingCalculateInvoicedAdjustmentTotalService", locale));
                }
                // Set adjustment amount as amountAlreadyIncluded to continue invoice item creation process
                Boolean isTaxIncludedInPrice = "VAT_TAX".equals(adj.getString("orderAdjustmentTypeId")) && UtilValidate.isNotEmpty(adj.getBigDecimal("amountAlreadyIncluded")) && adj.getBigDecimal("amountAlreadyIncluded").signum() != 0;
                if ((adj.getBigDecimal("amount").signum() == 0) && isTaxIncludedInPrice) {
                    adj.set("amount", adj.getBigDecimal("amountAlreadyIncluded"));
                }
                // If the absolute invoiced amount >= the abs of the adjustment amount, the full amount has already been invoiced, so skip this adjustment
                if (adjAlreadyInvoicedAmount.abs().compareTo(adj.getBigDecimal("amount").setScale(invoiceTypeDecimals, ROUNDING).abs()) > 0) {
                    continue;
                }
                BigDecimal originalOrderItemQuantity = OrderReadHelper.getOrderItemQuantity(originalOrderItem);
                BigDecimal amount = BigDecimal.ZERO;
                if (originalOrderItemQuantity.signum() != 0) {
                    if (adj.get("amount") != null) {
                        if ("PROMOTION_ADJUSTMENT".equals(adj.getString("orderAdjustmentTypeId")) && adj.get("productPromoId") != null) {
                            /* Find negative amountAlreadyIncluded in OrderAdjustment to subtract it from discounted amount.
                                                                          As we stored negative sales tax amount in order adjustment for discounted item.
                                     */
                            List<EntityExpr> exprs = UtilMisc.toList(EntityCondition.makeCondition("orderId", EntityOperator.EQUALS, orderItem.getString("orderId")), EntityCondition.makeCondition("orderItemSeqId", EntityOperator.EQUALS, orderItem.getString("orderItemSeqId")), EntityCondition.makeCondition("orderAdjustmentTypeId", EntityOperator.EQUALS, "VAT_TAX"), EntityCondition.makeCondition("amountAlreadyIncluded", EntityOperator.LESS_THAN, BigDecimal.ZERO));
                            EntityCondition andCondition = EntityCondition.makeCondition(exprs, EntityOperator.AND);
                            GenericValue orderAdjustment = EntityUtil.getFirst(delegator.findList("OrderAdjustment", andCondition, null, null, null, false));
                            if (UtilValidate.isNotEmpty(orderAdjustment)) {
                                amount = adj.getBigDecimal("amount").subtract(orderAdjustment.getBigDecimal("amountAlreadyIncluded")).setScale(100, ROUNDING);
                            } else {
                                amount = adj.getBigDecimal("amount");
                            }
                        } else {
                            // set decimals = 100 means we don't round this intermediate value, which is very important
                            if (isTaxIncludedInPrice) {
                                BigDecimal priceWithTax = originalOrderItem.getBigDecimal("unitPrice");
                                // Get tax included in item price
                                amount = priceWithTax.subtract(billingAmount);
                                amount = amount.multiply(billingQuantity);
                                // get adjustment amount
                                /* Get tax amount of other invoice and calculate remaining amount need to store in invoice item(Handle case of of partial shipment and promotional item)
                                                                              to adjust tax amount in invoice item.
                                         */
                                BigDecimal otherInvoiceTaxAmount = BigDecimal.ZERO;
                                GenericValue orderAdjBilling = EntityQuery.use(delegator).from("OrderAdjustmentBilling").where("orderAdjustmentId", adj.getString("orderAdjustmentId")).queryFirst();
                                if (UtilValidate.isNotEmpty(orderAdjBilling)) {
                                    // FIXME: Need to check here isTaxIncludedInPrice pass to use cache
                                    List<GenericValue> invoiceItems = EntityQuery.use(delegator).from("InvoiceItem").where("invoiceId", orderAdjBilling.getString("invoiceId"), "invoiceItemTypeId", "ITM_SALES_TAX", "productId", originalOrderItem.getString("productId")).cache(isTaxIncludedInPrice).queryList();
                                    for (GenericValue invoiceItem : invoiceItems) {
                                        otherInvoiceTaxAmount = otherInvoiceTaxAmount.add(invoiceItem.getBigDecimal("amount"));
                                    }
                                    if (otherInvoiceTaxAmount.compareTo(BigDecimal.ZERO) > 0) {
                                        BigDecimal remainingAmount = adj.getBigDecimal("amountAlreadyIncluded").subtract(otherInvoiceTaxAmount);
                                        amount = amount.min(remainingAmount);
                                    }
                                }
                                amount = amount.min(adj.getBigDecimal("amountAlreadyIncluded")).setScale(100, ROUNDING);
                            } else {
                                amount = adj.getBigDecimal("amount").divide(originalOrderItemQuantity, 100, ROUNDING);
                                amount = amount.multiply(billingQuantity);
                            }
                        }
                        // Tax needs to be rounded differently from other order adjustments
                        if ("SALES_TAX".equals(adj.getString("orderAdjustmentTypeId"))) {
                            amount = amount.setScale(TAX_DECIMALS, TAX_ROUNDING);
                        } else {
                            amount = amount.setScale(invoiceTypeDecimals, ROUNDING);
                        }
                    } else if (adj.get("sourcePercentage") != null) {
                        // pro-rate the amount
                        // set decimals = 100 means we don't round this intermediate value, which is very important
                        BigDecimal percent = adj.getBigDecimal("sourcePercentage");
                        percent = percent.divide(new BigDecimal(100), 100, ROUNDING);
                        amount = billingAmount.multiply(percent);
                        amount = amount.divide(originalOrderItemQuantity, 100, ROUNDING);
                        amount = amount.multiply(billingQuantity);
                        amount = amount.setScale(invoiceTypeDecimals, ROUNDING);
                    }
                }
                if (amount.signum() != 0) {
                    Map<String, Object> createInvoiceItemAdjContext = new HashMap<>();
                    createInvoiceItemAdjContext.put("invoiceId", invoiceId);
                    createInvoiceItemAdjContext.put("invoiceItemSeqId", invoiceItemSeqId);
                    createInvoiceItemAdjContext.put("invoiceItemTypeId", getInvoiceItemType(delegator, adj.getString("orderAdjustmentTypeId"), null, invoiceType, "INVOICE_ITM_ADJ"));
                    createInvoiceItemAdjContext.put("quantity", BigDecimal.ONE);
                    createInvoiceItemAdjContext.put("amount", amount);
                    createInvoiceItemAdjContext.put("productId", orderItem.get("productId"));
                    createInvoiceItemAdjContext.put("productFeatureId", orderItem.get("productFeatureId"));
                    createInvoiceItemAdjContext.put("overrideGlAccountId", adj.get("overrideGlAccountId"));
                    createInvoiceItemAdjContext.put("parentInvoiceId", invoiceId);
                    createInvoiceItemAdjContext.put("parentInvoiceItemSeqId", parentInvoiceItemSeqId);
                    createInvoiceItemAdjContext.put("userLogin", userLogin);
                    createInvoiceItemAdjContext.put("taxAuthPartyId", adj.get("taxAuthPartyId"));
                    createInvoiceItemAdjContext.put("taxAuthGeoId", adj.get("taxAuthGeoId"));
                    createInvoiceItemAdjContext.put("taxAuthorityRateSeqId", adj.get("taxAuthorityRateSeqId"));
                    // some adjustments fill out the comments field instead
                    String description = (UtilValidate.isEmpty(adj.getString("description")) ? adj.getString("comments") : adj.getString("description"));
                    createInvoiceItemAdjContext.put("description", description);
                    // TODO: This is not an ideal solution. Instead, we need to use OrderAdjustment.includeInTax when it is implemented
                    if (!("SALES_TAX".equals(adj.getString("orderAdjustmentTypeId")))) {
                        createInvoiceItemAdjContext.put("taxableFlag", product.get("taxable"));
                    }
                    // represent an organization override for the payToPartyId
                    if (UtilValidate.isNotEmpty(adj.getString("productPromoId"))) {
                        try {
                            GenericValue productPromo = adj.getRelatedOne("ProductPromo", false);
                            if (UtilValidate.isNotEmpty(productPromo.getString("overrideOrgPartyId"))) {
                                createInvoiceItemAdjContext.put("overrideOrgPartyId", productPromo.getString("overrideOrgPartyId"));
                            }
                        } catch (GenericEntityException e) {
                            Debug.logError(e, "Error looking up ProductPromo with id [" + adj.getString("productPromoId") + "]", module);
                        }
                    }
                    Map<String, Object> createInvoiceItemAdjResult = dispatcher.runSync("createInvoiceItem", createInvoiceItemAdjContext);
                    if (ServiceUtil.isError(createInvoiceItemAdjResult)) {
                        return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingErrorCreatingInvoiceItemFromOrder", locale), null, null, createInvoiceItemAdjResult);
                    }
                    // Create the OrderAdjustmentBilling record
                    Map<String, Object> createOrderAdjustmentBillingContext = new HashMap<>();
                    createOrderAdjustmentBillingContext.put("orderAdjustmentId", adj.getString("orderAdjustmentId"));
                    createOrderAdjustmentBillingContext.put("invoiceId", invoiceId);
                    createOrderAdjustmentBillingContext.put("invoiceItemSeqId", invoiceItemSeqId);
                    createOrderAdjustmentBillingContext.put("amount", amount);
                    createOrderAdjustmentBillingContext.put("userLogin", userLogin);
                    Map<String, Object> createOrderAdjustmentBillingResult = dispatcher.runSync("createOrderAdjustmentBilling", createOrderAdjustmentBillingContext);
                    if (ServiceUtil.isError(createOrderAdjustmentBillingResult)) {
                        return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingErrorCreatingOrderAdjustmentBillingFromOrder", locale), null, null, createOrderAdjustmentBillingContext);
                    }
                    // this adjustment amount
                    BigDecimal thisAdjAmount = amount;
                    // adjustments only apply to totals when they are not tax or shipping adjustments
                    if (!"SALES_TAX".equals(adj.getString("orderAdjustmentTypeId")) && !"SHIPPING_ADJUSTMENT".equals(adj.getString("orderAdjustmentTypeId"))) {
                        // increment the invoice subtotal
                        invoiceSubTotal = invoiceSubTotal.add(thisAdjAmount).setScale(100, ROUNDING);
                        // add to the ship amount only if it applies to this item
                        if (shippingApplies) {
                            invoiceShipProRateAmount = invoiceShipProRateAmount.add(thisAdjAmount).setScale(invoiceTypeDecimals, ROUNDING);
                        }
                    }
                    // increment the counter
                    invoiceItemSeqNum++;
                    invoiceItemSeqId = UtilFormatOut.formatPaddedNumber(invoiceItemSeqNum, INVOICE_ITEM_SEQUENCE_ID_DIGITS);
                }
            }
        }
        // create header adjustments as line items -- always to tax/shipping last
        Map<GenericValue, BigDecimal> shipAdjustments = new HashMap<>();
        Map<GenericValue, BigDecimal> taxAdjustments = new HashMap<>();
        List<GenericValue> headerAdjustments = orh.getOrderHeaderAdjustments();
        for (GenericValue adj : headerAdjustments) {
            // Check against OrderAdjustmentBilling to see how much of this adjustment has already been invoiced
            BigDecimal adjAlreadyInvoicedAmount = null;
            try {
                Map<String, Object> checkResult = dispatcher.runSync("calculateInvoicedAdjustmentTotal", UtilMisc.toMap("orderAdjustment", adj));
                if (ServiceUtil.isError(checkResult)) {
                    return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingTroubleCallingCalculateInvoicedAdjustmentTotalService", locale));
                }
                adjAlreadyInvoicedAmount = ((BigDecimal) checkResult.get("invoicedTotal")).setScale(invoiceTypeDecimals, ROUNDING);
            } catch (GenericServiceException e) {
                Debug.logError(e, "Accounting trouble calling calculateInvoicedAdjustmentTotal service", module);
                return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingTroubleCallingCalculateInvoicedAdjustmentTotalService", locale));
            }
            // If the absolute invoiced amount >= the abs of the adjustment amount, the full amount has already been invoiced, so skip this adjustment
            if (adjAlreadyInvoicedAmount.abs().compareTo(adj.getBigDecimal("amount").setScale(invoiceTypeDecimals, ROUNDING).abs()) >= 0) {
                continue;
            }
            if ("SHIPPING_CHARGES".equals(adj.getString("orderAdjustmentTypeId"))) {
                shipAdjustments.put(adj, adjAlreadyInvoicedAmount);
            } else if ("SALES_TAX".equals(adj.getString("orderAdjustmentTypeId"))) {
                taxAdjustments.put(adj, adjAlreadyInvoicedAmount);
            } else {
                // these will effect the shipping pro-rate (unless commented)
                // other adjustment type
                BigDecimal divisor = orderSubTotal;
                BigDecimal multiplier = invoiceSubTotal;
                if (BigDecimal.ZERO.compareTo(multiplier) == 0 && BigDecimal.ZERO.compareTo(divisor) == 0) {
                    // if multiplier and divisor are equal to zero then use the quantities instead of the amounts
                    // this is useful when the order has free items and misc charges
                    divisor = orderQuantity;
                    multiplier = invoiceQuantity;
                }
                calcHeaderAdj(delegator, adj, invoiceType, invoiceId, invoiceItemSeqId, divisor, multiplier, adj.getBigDecimal("amount").setScale(invoiceTypeDecimals, ROUNDING), invoiceTypeDecimals, ROUNDING, userLogin, dispatcher, locale);
                // invoiceShipProRateAmount += adjAmount;
                // do adjustments compound or are they based off subtotal? Here we will (unless commented)
                // invoiceSubTotal += adjAmount;
                // increment the counter
                invoiceItemSeqNum++;
                invoiceItemSeqId = UtilFormatOut.formatPaddedNumber(invoiceItemSeqNum, INVOICE_ITEM_SEQUENCE_ID_DIGITS);
            }
        }
        // numerator/denominator problems when the shipping is not pro-rated but rather charged all on the first invoice
        for (Map.Entry<GenericValue, BigDecimal> set : shipAdjustments.entrySet()) {
            BigDecimal adjAlreadyInvoicedAmount = set.getValue();
            GenericValue adj = set.getKey();
            if ("N".equalsIgnoreCase(prorateShipping)) {
                // Set the divisor and multiplier to 1 to avoid prorating
                BigDecimal divisor = BigDecimal.ONE;
                BigDecimal multiplier = BigDecimal.ONE;
                // The base amount in this case is the adjustment amount minus the total already invoiced for that adjustment, since
                // it won't be prorated
                BigDecimal baseAmount = adj.getBigDecimal("amount").setScale(invoiceTypeDecimals, ROUNDING).subtract(adjAlreadyInvoicedAmount);
                calcHeaderAdj(delegator, adj, invoiceType, invoiceId, invoiceItemSeqId, divisor, multiplier, baseAmount, invoiceTypeDecimals, ROUNDING, userLogin, dispatcher, locale);
            } else {
                // Pro-rate the shipping amount based on shippable information
                BigDecimal divisor = shippableAmount;
                BigDecimal multiplier = invoiceShipProRateAmount;
                if (BigDecimal.ZERO.compareTo(multiplier) == 0 && BigDecimal.ZERO.compareTo(divisor) == 0) {
                    // if multiplier and divisor are equal to zero then use the quantities instead of the amounts
                    // this is useful when the order has free items and shipping charges
                    divisor = shippableQuantity;
                    multiplier = invoiceShippableQuantity;
                }
                // The base amount in this case is the adjustment amount, since we want to prorate based on the full amount
                BigDecimal baseAmount = adj.getBigDecimal("amount").setScale(invoiceTypeDecimals, ROUNDING);
                calcHeaderAdj(delegator, adj, invoiceType, invoiceId, invoiceItemSeqId, divisor, multiplier, baseAmount, invoiceTypeDecimals, ROUNDING, userLogin, dispatcher, locale);
            }
            // Increment the counter
            invoiceItemSeqNum++;
            invoiceItemSeqId = UtilFormatOut.formatPaddedNumber(invoiceItemSeqNum, INVOICE_ITEM_SEQUENCE_ID_DIGITS);
        }
        // last do the tax adjustments
        String prorateTaxes = productStore != null ? productStore.getString("prorateTaxes") : "Y";
        if (prorateTaxes == null) {
            prorateTaxes = "Y";
        }
        for (Map.Entry<GenericValue, BigDecimal> entry : taxAdjustments.entrySet()) {
            GenericValue adj = entry.getKey();
            BigDecimal adjAlreadyInvoicedAmount = entry.getValue();
            BigDecimal adjAmount = null;
            if ("N".equalsIgnoreCase(prorateTaxes)) {
                // Set the divisor and multiplier to 1 to avoid prorating
                BigDecimal divisor = BigDecimal.ONE;
                BigDecimal multiplier = BigDecimal.ONE;
                // The base amount in this case is the adjustment amount minus the total already invoiced for that adjustment, since
                // it won't be prorated
                BigDecimal baseAmount = adj.getBigDecimal("amount").setScale(TAX_DECIMALS, TAX_ROUNDING).subtract(adjAlreadyInvoicedAmount);
                adjAmount = calcHeaderAdj(delegator, adj, invoiceType, invoiceId, invoiceItemSeqId, divisor, multiplier, baseAmount, TAX_DECIMALS, TAX_ROUNDING, userLogin, dispatcher, locale);
            } else {
                // Pro-rate the tax amount based on shippable information
                BigDecimal divisor = orderSubTotal;
                BigDecimal multiplier = invoiceSubTotal;
                // The base amount in this case is the adjustment amount, since we want to prorate based on the full amount
                BigDecimal baseAmount = adj.getBigDecimal("amount");
                adjAmount = calcHeaderAdj(delegator, adj, invoiceType, invoiceId, invoiceItemSeqId, divisor, multiplier, baseAmount, TAX_DECIMALS, TAX_ROUNDING, userLogin, dispatcher, locale);
            }
            invoiceSubTotal = invoiceSubTotal.add(adjAmount).setScale(invoiceTypeDecimals, ROUNDING);
            // Increment the counter
            invoiceItemSeqNum++;
            invoiceItemSeqId = UtilFormatOut.formatPaddedNumber(invoiceItemSeqNum, INVOICE_ITEM_SEQUENCE_ID_DIGITS);
        }
        // check for previous order payments
        List<GenericValue> orderPaymentPrefs = EntityQuery.use(delegator).from("OrderPaymentPreference").where(EntityCondition.makeCondition("orderId", EntityOperator.EQUALS, orderId), EntityCondition.makeCondition("statusId", EntityOperator.NOT_EQUAL, "PAYMENT_CANCELLED")).queryList();
        List<GenericValue> currentPayments = new LinkedList<>();
        for (GenericValue paymentPref : orderPaymentPrefs) {
            List<GenericValue> payments = paymentPref.getRelated("Payment", null, null, false);
            currentPayments.addAll(payments);
        }
        // apply these payments to the invoice if they have any remaining amount to apply
        for (GenericValue payment : currentPayments) {
            if ("PMNT_VOID".equals(payment.getString("statusId")) || "PMNT_CANCELLED".equals(payment.getString("statusId"))) {
                continue;
            }
            BigDecimal notApplied = PaymentWorker.getPaymentNotApplied(payment);
            if (notApplied.signum() > 0) {
                Map<String, Object> appl = new HashMap<>();
                appl.put("paymentId", payment.get("paymentId"));
                appl.put("invoiceId", invoiceId);
                appl.put("billingAccountId", billingAccountId);
                appl.put("amountApplied", notApplied);
                appl.put("userLogin", userLogin);
                Map<String, Object> createPayApplResult = dispatcher.runSync("createPaymentApplication", appl);
                if (ServiceUtil.isError(createPayApplResult)) {
                    return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingErrorCreatingInvoiceFromOrder", locale), null, null, createPayApplResult);
                }
            }
        }
        // Should all be in place now. Depending on the ProductStore.autoApproveInvoice setting, set status to INVOICE_READY (unless it's a purchase invoice, which we set to INVOICE_IN_PROCESS)
        String autoApproveInvoice = productStore != null ? productStore.getString("autoApproveInvoice") : "Y";
        if (!"N".equals(autoApproveInvoice)) {
            String nextStatusId = "PURCHASE_INVOICE".equals(invoiceType) ? "INVOICE_IN_PROCESS" : "INVOICE_READY";
            Map<String, Object> setInvoiceStatusResult = dispatcher.runSync("setInvoiceStatus", UtilMisc.<String, Object>toMap("invoiceId", invoiceId, "statusId", nextStatusId, "userLogin", userLogin));
            if (ServiceUtil.isError(setInvoiceStatusResult)) {
                return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingErrorCreatingInvoiceFromOrder", locale), null, null, setInvoiceStatusResult);
            }
        }
        Map<String, Object> resp = ServiceUtil.returnSuccess();
        resp.put("invoiceId", invoiceId);
        resp.put("invoiceTypeId", invoiceType);
        return resp;
    } catch (GenericEntityException e) {
        Debug.logError(e, "Entity/data problem creating invoice from order items: " + e.toString(), module);
        return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingEntityDataProblemCreatingInvoiceFromOrderItems", UtilMisc.toMap("reason", e.toString()), locale));
    } catch (GenericServiceException e) {
        Debug.logError(e, "Service/other problem creating invoice from order items: " + e.toString(), module);
        return ServiceUtil.returnError(UtilProperties.getMessage(resource, "AccountingServiceOtherProblemCreatingInvoiceFromOrderItems", UtilMisc.toMap("reason", e.toString()), locale));
    }
}
Also used : Locale(java.util.Locale) LocalDispatcher(org.apache.ofbiz.service.LocalDispatcher) HashMap(java.util.HashMap) EntityCondition(org.apache.ofbiz.entity.condition.EntityCondition) Timestamp(java.sql.Timestamp) OrderReadHelper(org.apache.ofbiz.order.order.OrderReadHelper) GenericValue(org.apache.ofbiz.entity.GenericValue) BigDecimal(java.math.BigDecimal) LinkedList(java.util.LinkedList) Delegator(org.apache.ofbiz.entity.Delegator) GenericEntityException(org.apache.ofbiz.entity.GenericEntityException) GenericServiceException(org.apache.ofbiz.service.GenericServiceException) HashMap(java.util.HashMap) Map(java.util.Map) EntityExpr(org.apache.ofbiz.entity.condition.EntityExpr)

Example 45 with LocalDispatcher

use of org.apache.ofbiz.service.LocalDispatcher in project ofbiz-framework by apache.

the class GiftCertificateServices method giftCertificateProcessor.

// Fullfilment Services
public static Map<String, Object> giftCertificateProcessor(DispatchContext dctx, Map<String, ? extends Object> context) {
    LocalDispatcher dispatcher = dctx.getDispatcher();
    Delegator delegator = dctx.getDelegator();
    GenericValue userLogin = (GenericValue) context.get("userLogin");
    Locale locale = (Locale) context.get("locale");
    BigDecimal amount = (BigDecimal) context.get("processAmount");
    String currency = (String) context.get("currency");
    // make sure we have a currency
    if (currency == null) {
        currency = EntityUtilProperties.getPropertyValue("general", "currency.uom.id.default", "USD", delegator);
    }
    // get the authorizations
    GenericValue orderPaymentPreference = (GenericValue) context.get("orderPaymentPreference");
    GenericValue authTransaction = (GenericValue) context.get("authTrans");
    if (authTransaction == null) {
        authTransaction = PaymentGatewayServices.getAuthTransaction(orderPaymentPreference);
    }
    if (authTransaction == null) {
        return ServiceUtil.returnError(UtilProperties.getMessage(resourceError, "AccountingFinAccountCannotCapture", locale));
    }
    // get the gift certificate and its authorization from the authorization
    String finAccountAuthId = authTransaction.getString("referenceNum");
    try {
        GenericValue finAccountAuth = EntityQuery.use(delegator).from("FinAccountAuth").where("finAccountAuthId", finAccountAuthId).queryOne();
        GenericValue giftCard = finAccountAuth.getRelatedOne("FinAccount", false);
        // make sure authorization has not expired
        Timestamp authExpiration = finAccountAuth.getTimestamp("thruDate");
        if ((authExpiration != null) && (authExpiration.before(UtilDateTime.nowTimestamp()))) {
            return ServiceUtil.returnError(UtilProperties.getMessage(resourceError, "AccountingFinAccountAuthorizationExpired", UtilMisc.toMap("paymentGatewayResponseId", authTransaction.getString("paymentGatewayResponseId"), "authExpiration", authExpiration), locale));
        }
        // make sure the fin account itself has not expired
        if ((giftCard.getTimestamp("thruDate") != null) && (giftCard.getTimestamp("thruDate").before(UtilDateTime.nowTimestamp()))) {
            return ServiceUtil.returnError(UtilProperties.getMessage(resourceError, "AccountingGiftCerticateNumberExpired", UtilMisc.toMap("thruDate", giftCard.getTimestamp("thruDate")), locale));
        }
        // obtain the order information
        OrderReadHelper orh = new OrderReadHelper(delegator, orderPaymentPreference.getString("orderId"));
        Map<String, Object> redeemCtx = new HashMap<>();
        redeemCtx.put("userLogin", userLogin);
        redeemCtx.put("productStoreId", orh.getProductStoreId());
        redeemCtx.put("cardNumber", giftCard.get("finAccountId"));
        redeemCtx.put("pinNumber", giftCard.get("finAccountCode"));
        redeemCtx.put("currency", currency);
        if (orh.getBillToParty() != null) {
            redeemCtx.put("partyId", orh.getBillToParty().get("partyId"));
        }
        redeemCtx.put("amount", amount);
        // invoke the redeem service
        Map<String, Object> redeemResult = null;
        redeemResult = dispatcher.runSync("redeemGiftCertificate", redeemCtx);
        if (ServiceUtil.isError(redeemResult)) {
            return ServiceUtil.returnError(ServiceUtil.getErrorMessage(redeemResult));
        }
        // now release the authorization should this use the gift card release service?
        Map<String, Object> releaseResult = dispatcher.runSync("expireFinAccountAuth", UtilMisc.<String, Object>toMap("userLogin", userLogin, "finAccountAuthId", finAccountAuthId));
        if (ServiceUtil.isError(releaseResult)) {
            return ServiceUtil.returnError(ServiceUtil.getErrorMessage(releaseResult));
        }
        String authRefNum = authTransaction.getString("referenceNum");
        Map<String, Object> result = ServiceUtil.returnSuccess();
        if (redeemResult != null) {
            Boolean processResult = (Boolean) redeemResult.get("processResult");
            result.put("processAmount", amount);
            result.put("captureResult", processResult);
            result.put("captureCode", "C");
            result.put("captureRefNum", redeemResult.get("referenceNum"));
            result.put("authRefNum", authRefNum);
        }
        return result;
    } catch (GenericEntityException | GenericServiceException ex) {
        return ServiceUtil.returnError(UtilProperties.getMessage(resourceError, "AccountingGiftCerticateNumberCannotProcess", UtilMisc.toMap("errorString", ex.getMessage()), locale));
    }
}
Also used : Locale(java.util.Locale) GenericValue(org.apache.ofbiz.entity.GenericValue) LocalDispatcher(org.apache.ofbiz.service.LocalDispatcher) HashMap(java.util.HashMap) Timestamp(java.sql.Timestamp) BigDecimal(java.math.BigDecimal) OrderReadHelper(org.apache.ofbiz.order.order.OrderReadHelper) Delegator(org.apache.ofbiz.entity.Delegator) GenericEntityException(org.apache.ofbiz.entity.GenericEntityException) GenericServiceException(org.apache.ofbiz.service.GenericServiceException)

Aggregations

LocalDispatcher (org.apache.ofbiz.service.LocalDispatcher)427 GenericValue (org.apache.ofbiz.entity.GenericValue)356 Delegator (org.apache.ofbiz.entity.Delegator)324 GenericServiceException (org.apache.ofbiz.service.GenericServiceException)321 Locale (java.util.Locale)296 GenericEntityException (org.apache.ofbiz.entity.GenericEntityException)270 HashMap (java.util.HashMap)214 BigDecimal (java.math.BigDecimal)135 GeneralException (org.apache.ofbiz.base.util.GeneralException)87 Timestamp (java.sql.Timestamp)81 LinkedList (java.util.LinkedList)79 IOException (java.io.IOException)59 Map (java.util.Map)51 HttpSession (javax.servlet.http.HttpSession)49 OrderReadHelper (org.apache.ofbiz.order.order.OrderReadHelper)28 ModelService (org.apache.ofbiz.service.ModelService)28 EntityCondition (org.apache.ofbiz.entity.condition.EntityCondition)24 ShoppingCart (org.apache.ofbiz.order.shoppingcart.ShoppingCart)23 Security (org.apache.ofbiz.security.Security)20 ByteBuffer (java.nio.ByteBuffer)19