📄 ledgerservices.java
字号:
* is handled through specialized methods. The result is a map that includes the * offsettingGlAccountId, the postingTotal, and a list of transaction entry Maps that * are ready to be turned into GenericValues. */ private static Map processInvoiceItems( GenericDelegator delegator, LocalDispatcher dispatcher, GenericValue userLogin, GenericValue invoice, String acctgTransTypeId, String offsettingGlAccountTypeId, String organizationPartyId, String transactionPartyId, String transactionPartyRoleTypeId, String defaultDebitCreditFlag, String defaultGlAccountTypeId ) throws GenericEntityException, GenericServiceException { // get the offsetting gl account for this invoice String offsettingGlAccountId = UtilAccounting.getDefaultAccountId(offsettingGlAccountTypeId, organizationPartyId, delegator); Debug.logInfo("Posting to GL for party " + organizationPartyId + " and offsetting account " + offsettingGlAccountId, module); // get the conversion factor for converting the invoice's amounts to the currencyUomId of the organization BigDecimal conversionFactor = new BigDecimal(UtilFinancial.determineUomConversionFactor(delegator, dispatcher, organizationPartyId, invoice.getString("currencyUomId"))); // loop variables List acctgTransEntries = new ArrayList(); // a List of AcctgTransEntry entities to be posted to BigDecimal postingTotal = new BigDecimal("0.00"); List invoiceItems = invoice.getRelatedCache("InvoiceItem", UtilMisc.toList("invoiceItemSeqId")); // Now, for each line item of the invoice, figure out which account on GL to post to and add it to a List Iterator invoiceItemIterator = invoiceItems.iterator(); while (invoiceItemIterator.hasNext()) { GenericValue invoiceItem = (GenericValue) invoiceItemIterator.next(); // default null quantities to 1.0 if (invoiceItem.getDouble("quantity") == null) { invoiceItem.set("quantity", new Double(1.0)); } BigDecimal amount = invoiceItem.getBigDecimal("amount"); BigDecimal quantity = invoiceItem.getBigDecimal("quantity"); // do not post invoice items with net zero value (usually because there's no amount) if ((amount == null) || (amount.signum() == 0) || (quantity.signum() == 0)) { continue; } // GL account for posting this invoice item, default to overrideGlAccountId if present String invoiceItemPostingGlAccountId = invoiceItem.getString("overrideGlAccountId"); if (invoiceItemPostingGlAccountId == null) { // if this invoice item is for a product, check to see if it has a particular GL account defined in ProductGlAccount, // or, alternatively, in GlAccountTypeDefault String invoiceItemTypeId = invoiceItem.getString("invoiceItemTypeId"); if ((invoiceItemTypeId.equals(INVOICE_PRODUCT_ITEM_TYPE)) || (invoiceItemTypeId.equals(PURCHINV_PRODUCT_ITEM_TYPE)) || invoiceItemTypeId.equals(RETINV_PRODUCT_ITEM_TYPE)) { invoiceItemPostingGlAccountId = UtilAccounting.getProductOrgGlAccountId(invoiceItem.getString("productId"), defaultGlAccountTypeId, organizationPartyId, delegator); } } // specific invoice type processing for each invoice item // note that these methods should add acctgTransEntries and return the postingTotal in a Map Map tmpResult = null; if ("SALES_INVOICE".equals(invoice.getString("invoiceTypeId"))) { tmpResult = processSalesInvoiceItem( delegator, dispatcher, userLogin, invoice, invoiceItem, acctgTransEntries, postingTotal, conversionFactor, amount, quantity, invoiceItemPostingGlAccountId, acctgTransTypeId, offsettingGlAccountTypeId, organizationPartyId, transactionPartyId, transactionPartyRoleTypeId, defaultDebitCreditFlag, defaultGlAccountTypeId ); } else { // for both purchase and return invoices, run processPurchaseInvoiceItem tmpResult = processPurchaseInvoiceItem( delegator, dispatcher, userLogin, invoice, invoiceItem, acctgTransEntries, postingTotal, conversionFactor, amount, quantity, invoiceItemPostingGlAccountId, acctgTransTypeId, offsettingGlAccountTypeId, organizationPartyId, transactionPartyId, transactionPartyRoleTypeId, defaultDebitCreditFlag, defaultGlAccountTypeId ); } if (ServiceUtil.isError(tmpResult)) return tmpResult; if (Debug.verboseOn()) { Debug.logInfo("invoiceItem " + invoiceItem.getString("invoiceId") + ", " + invoiceItem.getString("invoiceItemSeqId") + ": gl account = " + invoiceItemPostingGlAccountId + ", amount = " + invoiceItem.getDouble("amount") + ", quantity = " + invoiceItem.getDouble("quantity") + ", default debit/credit flag = " + defaultDebitCreditFlag, module); } postingTotal = (BigDecimal) tmpResult.get("postingTotal"); } return UtilMisc.toMap("offsettingGlAccountId", offsettingGlAccountId, "acctgTransEntries", acctgTransEntries, "postingTotal", new Double(postingTotal.doubleValue())); } /** * For purchase invoices, this method creates accounting transaction entries first * for uninvoiced shipment receipts. Then it reconciles the aggregate value of all order items * related to the invoice item with the actual value of the invoice item and posts it as a * purchase price variance. Note that this method is also used for return invoices. */ private static Map processPurchaseInvoiceItem( GenericDelegator delegator, LocalDispatcher dispatcher, GenericValue userLogin, GenericValue invoice, GenericValue invoiceItem, List acctgTransEntries, BigDecimal postingTotal, BigDecimal conversionFactor, BigDecimal amount, BigDecimal quantity, String invoiceItemPostingGlAccountId, String acctgTransTypeId, String offsettingGlAccountTypeId, String organizationPartyId, String transactionPartyId, String transactionPartyRoleTypeId, String defaultDebitCreditFlag, String defaultGlAccountTypeId ) throws GenericServiceException, GenericEntityException { int decimals = UtilNumber.getBigDecimalScale("invoice.decimals"); int rounding = UtilNumber.getBigDecimalRoundingMode("invoice.rounding"); // First, determine the amount to credit and update postingTotal BigDecimal postingAmount = conversionFactor.multiply(amount).multiply(quantity).setScale(decimals, rounding); postingTotal = postingTotal.add(postingAmount); // Next, determine the amount which has already been debit to uninvoiced receipts, so we can reconcile the difference // between invoice amounts and original order amounts in Uninvoiced Item Receipt BigDecimal orderAmount = new BigDecimal("0.00"); List orderItemBillings = invoiceItem.getRelated("OrderItemBilling"); for (Iterator iter = orderItemBillings.iterator(); iter.hasNext(); ) { GenericValue orderItemBilling = (GenericValue) iter.next(); GenericValue orderItem = orderItemBilling.getRelatedOne("OrderItem"); orderAmount = orderAmount.add(conversionFactor .multiply(orderItem.getBigDecimal("unitPrice")) .multiply(orderItem.getBigDecimal("quantity")) .setScale(decimals, rounding)); } // if there were no associated order items, post full amount to a default account if (orderItemBillings.size() == 0) { invoiceItemPostingGlAccountId = getDefaultGlAccount(invoiceItem, organizationPartyId); orderAmount = postingAmount; } // if still no GL account, then it is an error if ((invoiceItemPostingGlAccountId == null) || (invoiceItemPostingGlAccountId.equals(""))) { Debug.logError("Canot find GL account to post for this invoice item " + invoiceItem, module); return ServiceUtil.returnError("Cannot find posting GL account for invoice " + invoiceItem.getString("invoiceId") + ", item " + invoiceItem.getString("invoiceItemSeqId")); } // create the transaction entry for uninvoiced receipts or the default account Map acctgTransEntry = new HashMap(); acctgTransEntry.put("glAccountId", invoiceItemPostingGlAccountId); acctgTransEntry.put("debitCreditFlag", defaultDebitCreditFlag); acctgTransEntry.put("organizationPartyId", organizationPartyId); acctgTransEntry.put("partyId", transactionPartyId); acctgTransEntry.put("roleTypeId", transactionPartyRoleTypeId); acctgTransEntry.put("acctgTransEntryTypeId", "_NA_"); acctgTransEntry.put("productId", invoiceItem.getString("productId")); acctgTransEntry.put("amount", new Double(orderAmount.doubleValue())); acctgTransEntries.add(acctgTransEntry); // compute the variance BigDecimal varianceAmount = postingAmount.subtract(orderAmount); if (Debug.verboseOn()) { Debug.logVerbose("Purchase InvoiceItem: orderAmount ["+orderAmount+"], postingAmount ["+postingAmount+"], varianceAmount["+varianceAmount+"]", module); } // if there's a variance if (varianceAmount.signum() != 0) { // get the inventory price variance gl account String varianceGlAccountId = UtilAccounting.getDefaultAccountId("PURCHASE_PRICE_VAR", organizationPartyId, delegator); // and debit the variance account acctgTransEntry = new HashMap(acctgTransEntry); acctgTransEntry.put("glAccountId", varianceGlAccountId); acctgTransEntry.put("amount", new Double(varianceAmount.doubleValue())); acctgTransEntries.add(acctgTransEntry); } return UtilMisc.toMap("postingTotal", postingTotal); } /** * For sales invoices, if an invoice item is a product, then we will need to post COGS and INVENTORY as well. * TODO: maybe do COGS/INVENTORY posting for other invoiceItemTypeId (easier now with this refactored method) */ private static Map processSalesInvoiceItem( GenericDelegator delegator, LocalDispatcher dispatcher, GenericValue userLogin, GenericValue invoice, GenericValue invoiceItem, List acctgTransEntries, BigDecimal postingTotal, BigDecimal conversionFactor, BigDecimal amount, BigDecimal quantity, String invoiceItemPostingGlAccountId, String acctgTransTypeId, String offsettingGlAccountTypeId, String organizationPartyId, String transactionPartyId, String transactionPartyRoleTypeId, String defaultDebitCreditFlag, String defaultGlAccountTypeId ) throws GenericServiceException, GenericEntityException { int decimals = UtilNumber.getBigDecimalScale("invoice.decimals"); int rounding = UtilNumber.getBigDecimalRoundingMode("invoice.rounding"); // If there is still no account, try getting a default account if ((invoiceItemPostingGlAccountId == null) || invoiceItemPostingGlAccountId.equals("")) { invoiceItemPostingGlAccountId = getDefaultGlAccount(invoiceItem, organizationPartyId); } // if still no GL account, then it is an error if ((invoiceItemPostingGlAccountId == null) || (invoiceItemPostingGlAccountId.equals(""))) { Debug.logError("Canot find GL account to post for this invoice item " + invoiceItem, module); return ServiceUtil.returnError("Cannot find posting GL account for invoice " + invoiceItem.getString("invoiceId") + ", item " + invoiceItem.getString("invoiceItemSeqId")); } // prepare a transaction entry // TODO: if necessary, process theirPartyId, origAmount, dueDate, groupId, description, voucherRef fields of AcctgTransEntry Map acctgTransEntry = new HashMap(); acctgTransEntry.put("glAccountId", invoiceItemPostingGlAccountId); acctgTransEntry.put("debitCreditFlag", defaultDebitCreditFlag); acctgTransEntry.put("organizationPartyId", organizationPartyId); acctgTransEntry.put("acctgTransEntryTypeId", "_NA_"); acctgTransEntry.put("productId", invoiceItem.getString("productId")); // amount to be posted. NOTE: this conversion is rounded AFTER (conversion*amount*quantity) BigDecimal postingAmount = amount.multiply(conversionFactor).multiply(quantity).setScale(decimals, rounding); acctgTransEntry.put("amount", new Double(postingAmount.doubleValue())); // if there is a taxAuthPartyId on the invoice item, then this invoice item is a tax transaction. if (invoiceItem.getString("taxAuthPartyId") != null) { // In that case, the partyId on the accounting trans entry should be the the taxAuthPartyId on the invoice item acctgTransEntry.put("partyId", invoiceItem.getString("taxAuthPartyId")); acctgTransEntry.put("roleTypeId", "TAX_AUTHORITY"); } else { // TODO: have to determine role here acctgTransEntry.put("partyId", transactionPartyId);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -