// base deposit service
  public static Map<String, Object> finAccountDeposit(
      DispatchContext dctx, Map<String, Object> context) {
    LocalDispatcher dispatcher = dctx.getDispatcher();
    Delegator delegator = dctx.getDelegator();
    Locale locale = (Locale) context.get("locale");

    GenericValue userLogin = (GenericValue) context.get("userLogin");
    String productStoreId = (String) context.get("productStoreId");
    String finAccountId = (String) context.get("finAccountId");
    String orderItemSeqId = (String) context.get("orderItemSeqId");
    String reasonEnumId = (String) context.get("reasonEnumId");
    String orderId = (String) context.get("orderId");
    Boolean isRefund = (Boolean) context.get("isRefund");
    BigDecimal amount = (BigDecimal) context.get("amount");

    final String DEPOSIT = isRefund == null || !isRefund ? "DEPOSIT" : "ADJUSTMENT";

    String partyId = (String) context.get("partyId");
    if (UtilValidate.isEmpty(partyId)) {
      partyId = "_NA_";
    }
    String currencyUom = (String) context.get("currency");
    if (UtilValidate.isEmpty(currencyUom)) {
      currencyUom =
          EntityUtilProperties.getPropertyValue(
              "general.properties", "currency.uom.id.default", "USD", delegator);
    }

    GenericValue finAccount;
    try {
      finAccount =
          EntityQuery.use(delegator)
              .from("FinAccount")
              .where("finAccountId", finAccountId)
              .queryOne();
    } catch (GenericEntityException e) {
      Debug.logError(e, module);
      return ServiceUtil.returnError(
          UtilProperties.getMessage(
              resourceError,
              "AccountingFinAccountNotFound",
              UtilMisc.toMap("finAccountId", finAccountId),
              locale));
    }

    // verify we have a financial account
    if (finAccount == null) {
      return ServiceUtil.returnError(
          UtilProperties.getMessage(
              resourceError,
              "AccountingFinAccountNotFound",
              UtilMisc.toMap("finAccountId", ""),
              locale));
    }

    // make sure the fin account itself has not expired
    if ((finAccount.getTimestamp("thruDate") != null)
        && (finAccount.getTimestamp("thruDate").before(UtilDateTime.nowTimestamp()))) {
      return ServiceUtil.returnError(
          UtilProperties.getMessage(
              resourceError,
              "AccountingFinAccountExpired",
              UtilMisc.toMap("thruDate", finAccount.getTimestamp("thruDate")),
              locale));
    }
    Debug.logInfo("Deposit into financial account #" + finAccountId + " [" + amount + "]", module);

    // get the previous balance
    BigDecimal previousBalance = finAccount.getBigDecimal("actualBalance");
    if (previousBalance == null) {
      previousBalance = FinAccountHelper.ZERO;
    }

    // create the transaction
    BigDecimal actualBalance;
    String refNum;
    try {
      refNum =
          FinAccountPaymentServices.createFinAcctPaymentTransaction(
              delegator,
              dispatcher,
              userLogin,
              amount,
              productStoreId,
              partyId,
              orderId,
              orderItemSeqId,
              currencyUom,
              DEPOSIT,
              finAccountId,
              reasonEnumId);
      finAccount.refresh();
      actualBalance = finAccount.getBigDecimal("actualBalance");
    } catch (GeneralException e) {
      Debug.logError(e, module);
      return ServiceUtil.returnError(e.getMessage());
    }

    // make sure balance is not null
    if (actualBalance == null) {
      actualBalance = FinAccountHelper.ZERO;
    } else {
      if (actualBalance.compareTo(BigDecimal.ZERO) < 0) {
        // balance went below zero, set negative pending replenishment status so that no more auths
        // or captures will go through until it is replenished
        try {
          Map<String, Object> rollbackCtx =
              UtilMisc.toMap(
                  "userLogin",
                  userLogin,
                  "finAccountId",
                  finAccountId,
                  "statusId",
                  "FNACT_NEGPENDREPL");
          dispatcher.addRollbackService("updateFinAccount", rollbackCtx, true);
        } catch (GenericServiceException e) {
          Debug.logError(e, module);
          return ServiceUtil.returnError(e.getMessage());
        }
      }
    }

    Map<String, Object> result = ServiceUtil.returnSuccess();
    result.put("previousBalance", previousBalance);
    result.put("balance", actualBalance);
    result.put("amount", amount);
    result.put("processResult", Boolean.TRUE);
    result.put("referenceNum", refNum);
    return result;
  }
  // base account transaction services
  public static Map<String, Object> finAccountWithdraw(
      DispatchContext dctx, Map<String, Object> context) {
    LocalDispatcher dispatcher = dctx.getDispatcher();
    Delegator delegator = dctx.getDelegator();
    Locale locale = (Locale) context.get("locale");

    GenericValue userLogin = (GenericValue) context.get("userLogin");
    String productStoreId = (String) context.get("productStoreId");
    String finAccountId = (String) context.get("finAccountId");
    String orderItemSeqId = (String) context.get("orderItemSeqId");
    String reasonEnumId = (String) context.get("reasonEnumId");
    String orderId = (String) context.get("orderId");
    Boolean requireBalance = (Boolean) context.get("requireBalance");
    BigDecimal amount = (BigDecimal) context.get("amount");
    if (requireBalance == null) requireBalance = Boolean.TRUE;

    final String WITHDRAWAL = "WITHDRAWAL";

    String partyId = (String) context.get("partyId");
    if (UtilValidate.isEmpty(partyId)) {
      partyId = "_NA_";
    }
    String currencyUom = (String) context.get("currency");
    if (UtilValidate.isEmpty(currencyUom)) {
      currencyUom =
          EntityUtilProperties.getPropertyValue(
              "general.properties", "currency.uom.id.default", "USD", delegator);
    }

    // validate the amount
    if (amount.compareTo(BigDecimal.ZERO) < 0) {
      return ServiceUtil.returnError(
          UtilProperties.getMessage(resourceError, "AccountingFinAccountMustBePositive", locale));
    }

    GenericValue finAccount;
    try {
      finAccount =
          EntityQuery.use(delegator)
              .from("FinAccount")
              .where("finAccountId", finAccountId)
              .queryOne();
    } catch (GenericEntityException e) {
      Debug.logError(e, module);
      return ServiceUtil.returnError(e.getMessage());
    }

    // verify we have a financial account
    if (finAccount == null) {
      return ServiceUtil.returnError(
          UtilProperties.getMessage(
              resourceError,
              "AccountingFinAccountNotFound",
              UtilMisc.toMap("finAccountId", ""),
              locale));
    }

    // make sure the fin account itself has not expired
    if ((finAccount.getTimestamp("thruDate") != null)
        && (finAccount.getTimestamp("thruDate").before(UtilDateTime.nowTimestamp()))) {
      return ServiceUtil.returnError(
          UtilProperties.getMessage(
              resourceError,
              "AccountingFinAccountExpired",
              UtilMisc.toMap("thruDate", finAccount.getTimestamp("thruDate")),
              locale));
    }

    // check the actual balance (excluding authorized amounts) and create the transaction if it is
    // sufficient
    BigDecimal previousBalance = finAccount.getBigDecimal("actualBalance");
    if (previousBalance == null) {
      previousBalance = FinAccountHelper.ZERO;
    }

    BigDecimal balance;
    String refNum;
    Boolean procResult;
    if (requireBalance && previousBalance.compareTo(amount) < 0) {
      procResult = Boolean.FALSE;
      balance = previousBalance;
      refNum = "N/A";
    } else {
      try {
        refNum =
            FinAccountPaymentServices.createFinAcctPaymentTransaction(
                delegator,
                dispatcher,
                userLogin,
                amount,
                productStoreId,
                partyId,
                orderId,
                orderItemSeqId,
                currencyUom,
                WITHDRAWAL,
                finAccountId,
                reasonEnumId);
        finAccount.refresh();
        balance = finAccount.getBigDecimal("actualBalance");
        procResult = Boolean.TRUE;
      } catch (GeneralException e) {
        Debug.logError(e, module);
        return ServiceUtil.returnError(e.getMessage());
      }
    }

    // make sure balance is not null
    if (balance == null) {
      balance = FinAccountHelper.ZERO;
    }

    Map<String, Object> result = ServiceUtil.returnSuccess();
    result.put("previousBalance", previousBalance);
    result.put("balance", balance);
    result.put("amount", amount);
    result.put("processResult", procResult);
    result.put("referenceNum", refNum);
    return result;
  }