blob: dac3d87d49f0c2cb61b69bb4c03e5ac64505a6c7 [file] [log] [blame]
/*******************************************************************************
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*******************************************************************************/
package org.ofbiz.accounting.payment;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import javolution.util.FastList;
import org.ofbiz.base.util.Debug;
import org.ofbiz.base.util.GeneralException;
import org.ofbiz.base.util.UtilDateTime;
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.entity.Delegator;
import org.ofbiz.entity.GenericEntityException;
import org.ofbiz.entity.GenericValue;
import org.ofbiz.entity.condition.EntityCondition;
import org.ofbiz.entity.condition.EntityOperator;
import org.ofbiz.entity.util.EntityQuery;
import org.ofbiz.order.order.OrderReadHelper;
import org.ofbiz.service.DispatchContext;
import org.ofbiz.service.LocalDispatcher;
import org.ofbiz.service.ServiceUtil;
/**
* Worker methods for BillingAccounts
*/
public class BillingAccountWorker {
public static final String module = BillingAccountWorker.class.getName();
public static final String resourceError = "AccountingUiLabels";
private static BigDecimal ZERO = BigDecimal.ZERO;
private static int decimals = -1;
private static int rounding = -1;
static {
decimals = UtilNumber.getBigDecimalScale("order.decimals");
rounding = UtilNumber.getBigDecimalRoundingMode("order.rounding");
// set zero to the proper scale
if (decimals != -1) ZERO = ZERO.setScale(decimals);
}
public static List<Map<String, Object>> makePartyBillingAccountList(GenericValue userLogin, String currencyUomId, String partyId, Delegator delegator, LocalDispatcher dispatcher) throws GeneralException {
List<Map<String, Object>> billingAccountList = FastList.newInstance();
Map<String, Object> agentResult = dispatcher.runSync("getRelatedParties", UtilMisc.<String, Object>toMap("userLogin", userLogin, "partyIdFrom", partyId,
"roleTypeIdFrom", "AGENT", "roleTypeIdTo", "CUSTOMER", "partyRelationshipTypeId", "AGENT", "includeFromToSwitched", "Y"));
if (ServiceUtil.isError(agentResult)) {
throw new GeneralException("Error while finding party BillingAccounts when getting Customers that this party is an agent of: " + ServiceUtil.getErrorMessage(agentResult));
}
List<String> relatedPartyIdList = UtilGenerics.checkList(agentResult.get("relatedPartyIdList"));
List<GenericValue> billingAccountRoleList = EntityQuery.use(delegator).from("BillingAccountRole")
.where(EntityCondition.makeCondition("partyId", EntityOperator.IN, relatedPartyIdList),
EntityCondition.makeCondition("roleTypeId", EntityOperator.EQUALS, "BILL_TO_CUSTOMER")
).filterByDate().queryList();
if (billingAccountRoleList.size() > 0) {
BigDecimal totalAvailable = BigDecimal.ZERO;
for (GenericValue billingAccountRole : billingAccountRoleList) {
GenericValue billingAccountVO = billingAccountRole.getRelatedOne("BillingAccount", false);
// skip accounts that have thruDate < nowTimestamp
java.sql.Timestamp thruDate = billingAccountVO.getTimestamp("thruDate");
if ((thruDate != null) && UtilDateTime.nowTimestamp().after(thruDate)) continue;
if (currencyUomId.equals(billingAccountVO.getString("accountCurrencyUomId"))) {
BigDecimal accountBalance = OrderReadHelper.getBillingAccountBalance(billingAccountVO);
Map<String, Object> billingAccount = new HashMap<String, Object>(billingAccountVO);
BigDecimal accountLimit = OrderReadHelper.getAccountLimit(billingAccountVO);
billingAccount.put("accountBalance", accountBalance);
BigDecimal accountAvailable = accountLimit.subtract(accountBalance);
totalAvailable = totalAvailable.add(accountAvailable);
billingAccountList.add(billingAccount);
}
}
Collections.sort(billingAccountList, new BillingAccountComparator());
}
return billingAccountList;
}
/**
* Returns list of orders which are currently open against a billing account
*/
public static List<GenericValue> getBillingAccountOpenOrders(Delegator delegator, String billingAccountId) throws GenericEntityException {
return EntityQuery.use(delegator).from("OrderHeader")
.where(EntityCondition.makeCondition("billingAccountId", EntityOperator.EQUALS, billingAccountId),
EntityCondition.makeCondition("statusId", EntityOperator.NOT_EQUAL, "ORDER_REJECTED"),
EntityCondition.makeCondition("statusId", EntityOperator.NOT_EQUAL, "ORDER_CANCELLED"),
EntityCondition.makeCondition("statusId", EntityOperator.NOT_EQUAL, "ORDER_COMPLETED")
).queryList();
}
/**
* Returns the amount which could be charged to a billing account, which is defined as the accountLimit minus account balance and minus the balance of outstanding orders
* When trying to figure out how much of a billing account can be used to pay for an outstanding order, use this method
* @param billingAccount GenericValue object of the billing account
* @return returns the amount which could be charged to a billing account
* @throws GenericEntityException
*/
public static BigDecimal getBillingAccountAvailableBalance(GenericValue billingAccount) throws GenericEntityException {
if ((billingAccount != null) && (billingAccount.get("accountLimit") != null)) {
BigDecimal accountLimit = billingAccount.getBigDecimal("accountLimit");
BigDecimal availableBalance = accountLimit.subtract(OrderReadHelper.getBillingAccountBalance(billingAccount)).setScale(decimals, rounding);
return availableBalance;
} else {
Debug.logWarning("Available balance requested for null billing account, returning zero", module);
return ZERO;
}
}
public static BigDecimal getBillingAccountAvailableBalance(Delegator delegator, String billingAccountId) throws GenericEntityException {
GenericValue billingAccount = EntityQuery.use(delegator).from("BillingAccount").where("billingAccountId", billingAccountId).queryOne();
return getBillingAccountAvailableBalance(billingAccount);
}
/**
* Calculates the net balance of a billing account, which is sum of all amounts applied to invoices minus sum of all amounts applied from payments.
* When charging or capturing an invoice to a billing account, use this method
* @param delegator the delegator
* @param billingAccountId the billing account id
* @return the amount of the billing account which could be captured
* @throws GenericEntityException
*/
public static BigDecimal getBillingAccountNetBalance(Delegator delegator, String billingAccountId) throws GenericEntityException {
BigDecimal balance = ZERO;
// search through all PaymentApplications and add the amount that was applied to invoice and subtract the amount applied from payments
List<GenericValue> paymentAppls = EntityQuery.use(delegator).from("PaymentApplication").where("billingAccountId", billingAccountId).queryList();
for (Iterator<GenericValue> pAi = paymentAppls.iterator(); pAi.hasNext();) {
GenericValue paymentAppl = pAi.next();
BigDecimal amountApplied = paymentAppl.getBigDecimal("amountApplied");
GenericValue invoice = paymentAppl.getRelatedOne("Invoice", false);
if (invoice != null) {
// make sure the invoice has not been canceled and it is not a "Customer return invoice"
if (!"CUST_RTN_INVOICE".equals(invoice.getString("invoiceTypeId")) && !"INVOICE_CANCELLED".equals(invoice.getString("statusId"))) {
balance = balance.add(amountApplied);
}
} else {
balance = balance.subtract(amountApplied);
}
}
balance = balance.setScale(decimals, rounding);
return balance;
}
/**
* Returns the amount of the billing account which could be captured, which is BillingAccount.accountLimit - net balance
* @param billingAccount GenericValue object of the billing account
* @return the amount of the billing account which could be captured
* @throws GenericEntityException
*/
public static BigDecimal availableToCapture(GenericValue billingAccount) throws GenericEntityException {
BigDecimal netBalance = getBillingAccountNetBalance(billingAccount.getDelegator(), billingAccount.getString("billingAccountId"));
BigDecimal accountLimit = billingAccount.getBigDecimal("accountLimit");
return accountLimit.subtract(netBalance).setScale(decimals, rounding);
}
public static Map<String, Object> calcBillingAccountBalance(DispatchContext dctx, Map<String, ? extends Object> context) {
Delegator delegator = dctx.getDelegator();
String billingAccountId = (String) context.get("billingAccountId");
Locale locale = (Locale) context.get("locale");
Map<String, Object> result = ServiceUtil.returnSuccess();
try {
GenericValue billingAccount = EntityQuery.use(delegator).from("BillingAccount").where("billingAccountId", billingAccountId).queryOne();
if (billingAccount == null) {
return ServiceUtil.returnError(UtilProperties.getMessage(resourceError,
"AccountingBillingAccountNotFound",
UtilMisc.toMap("billingAccountId", billingAccountId), locale));
}
result.put("billingAccount", billingAccount);
result.put("accountBalance", OrderReadHelper.getBillingAccountBalance(billingAccount));
result.put("netAccountBalance", getBillingAccountNetBalance(delegator, billingAccountId));
result.put("availableBalance", getBillingAccountAvailableBalance(billingAccount));
result.put("availableToCapture", availableToCapture(billingAccount));
return result;
} catch (GenericEntityException e) {
Debug.logError(e, module);
return ServiceUtil.returnError(UtilProperties.getMessage(resourceError,
"AccountingBillingAccountNotFound",
UtilMisc.toMap("billingAccountId", billingAccountId), locale));
}
}
protected static class BillingAccountComparator implements Comparator<Map<String, Object>> {
public int compare(Map<String, Object> billingAccount1, Map<String, Object> billingAccount2) {
return ((BigDecimal)billingAccount1.get("accountBalance")).compareTo((BigDecimal)billingAccount2.get("accountBalance"));
}
}
}