Author: apatel
Date: Wed Jul 29 10:54:02 2009 New Revision: 798854 URL: http://svn.apache.org/viewvc?rev=798854&view=rev Log: Adding ability to create Commission invoice from sales invoices. Patch from OFBIZ-2713. Thanks Rishi, Sumit, Awdesh, Amit for working on it. Added: ofbiz/trunk/applications/accounting/script/org/ofbiz/accounting/test/AutoInvoiceTests.xml (with props) ofbiz/trunk/applications/accounting/testdef/invoicetests.xml (with props) Modified: ofbiz/trunk/applications/accounting/data/PaymentsInvoices.xml ofbiz/trunk/applications/accounting/ofbiz-component.xml ofbiz/trunk/applications/accounting/servicedef/secas.xml ofbiz/trunk/applications/accounting/servicedef/services_agreement.xml ofbiz/trunk/applications/accounting/servicedef/services_invoice.xml ofbiz/trunk/applications/accounting/src/org/ofbiz/accounting/agreement/AgreementServices.java ofbiz/trunk/applications/accounting/src/org/ofbiz/accounting/invoice/InvoiceServices.java ofbiz/trunk/applications/accounting/webapp/ap/WEB-INF/controller.xml ofbiz/trunk/applications/accounting/webapp/ap/invoices/CommissionRun.ftl ofbiz/trunk/applications/accounting/widget/ap/InvoiceScreens.xml Modified: ofbiz/trunk/applications/accounting/data/PaymentsInvoices.xml URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/accounting/data/PaymentsInvoices.xml?rev=798854&r1=798853&r2=798854&view=diff ============================================================================== --- ofbiz/trunk/applications/accounting/data/PaymentsInvoices.xml (original) +++ ofbiz/trunk/applications/accounting/data/PaymentsInvoices.xml Wed Jul 29 10:54:02 2009 @@ -158,4 +158,25 @@ <GlAccountOrganization glAccountId="140000" organizationPartyId="Company" fromDate="2001-01-01 00:00:00.0" postedBalance="101.97"/> <GlAccountOrganization glAccountId="210000" organizationPartyId="Company" fromDate="2001-01-01 00:00:00.0" postedBalance="0.00"/> <GlAccountOrganization glAccountId="516100" organizationPartyId="Company" fromDate="2001-01-01 00:00:00.0" postedBalance="14.88"/> + + <!-- Demo invoice data for sales invoice with INVOICE_PAID status --> + <Party partyId="DemoCustAgent"/> + <PartyRole partyId="DemoCustAgent" roleTypeId="SALES_REP"/> + <Product productId="WG-9943"/> + <Product productId="WG-9943-B3"/> + + <Agreement agreementId="8000" partyIdFrom="Company" partyIdTo="DemoCustAgent" agreementTypeId="COMMISSION_AGREEMENT" description="Commission Agreement with DemoCustAgent"/> + <AgreementItem agreementId="8000" agreementItemSeqId="0001" agreementItemTypeId="AGREEMENT_COMMISSION" currencyUomId="USD" agreementText="Commission in USD"/> + <AgreementTerm agreementTermId="8000" termTypeId="FIN_COMM_FIXED" agreementId="8000" agreementItemSeqId="0001" invoiceItemTypeId="INV_FPROD_ITEM" termValue="1.000" termDays="30"/> + <AgreementTerm agreementTermId="8001" termTypeId="FIN_COMM_VARIABLE" agreementId="8000" agreementItemSeqId="0001" invoiceItemTypeId="INV_FPROD_ITEM" termValue="3.000" termDays="30"/> + <AgreementTerm agreementTermId="8002" termTypeId="FIN_COMM_MIN" agreementId="8000" agreementItemSeqId="0001" invoiceItemTypeId="INV_FPROD_ITEM" termValue="0.000" termDays="30"/> + <AgreementProductAppl agreementId="8000" agreementItemSeqId="0001" productId="WG-9943"/> + + <Invoice invoiceId="8100" invoiceTypeId="SALES_INVOICE" partyIdFrom="Company" partyId="admin" statusId="INVOICE_PAID" invoiceDate="2009-07-08 11:54:00.765" paidDate="2009-07-08 11:54:03.246" currencyUomId="USD"/> + <InvoiceStatus statusId="INVOICE_IN_PROCESS" invoiceId="8100" statusDate="2009-07-08 11:54:00.845"/> + <InvoiceStatus statusId="INVOICE_PAID" invoiceId="8100" statusDate="2009-07-08 11:54:03.8"/> + <InvoiceStatus statusId="INVOICE_READY" invoiceId="8100" statusDate="2009-07-08 11:54:01.863"/> + <InvoiceItem invoiceId="8100" invoiceItemSeqId="00002" invoiceItemTypeId="INV_FPROD_ITEM" productId="WG-9943-B3" quantity="3.000000" amount="440.000" description="Giant Widget B3"/> + <InvoiceRole invoiceId="8100" partyId="Company" roleTypeId="BILL_FROM_VENDOR" datetimePerformed="2009-07-08 11:54:00.979"/> + <InvoiceRole invoiceId="8100" partyId="DemoCustAgent" roleTypeId="SALES_REP" datetimePerformed="2009-07-08 11:54:00.979"/> </entity-engine-xml> Modified: ofbiz/trunk/applications/accounting/ofbiz-component.xml URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/accounting/ofbiz-component.xml?rev=798854&r1=798853&r2=798854&view=diff ============================================================================== --- ofbiz/trunk/applications/accounting/ofbiz-component.xml (original) +++ ofbiz/trunk/applications/accounting/ofbiz-component.xml Wed Jul 29 10:54:02 2009 @@ -80,6 +80,7 @@ <test-suite loader="main" location="testdef/accountingtests.xml"/> <test-suite loader="main" location="testdef/paymenttests.xml"/> + <test-suite loader="main" location="testdef/invoicetests.xml"/> <webapp name="accounting" title="Accounting" Added: ofbiz/trunk/applications/accounting/script/org/ofbiz/accounting/test/AutoInvoiceTests.xml URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/accounting/script/org/ofbiz/accounting/test/AutoInvoiceTests.xml?rev=798854&view=auto ============================================================================== --- ofbiz/trunk/applications/accounting/script/org/ofbiz/accounting/test/AutoInvoiceTests.xml (added) +++ ofbiz/trunk/applications/accounting/script/org/ofbiz/accounting/test/AutoInvoiceTests.xml Wed Jul 29 10:54:02 2009 @@ -0,0 +1,74 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +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. +--> + +<simple-methods xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="http://ofbiz.apache.org/dtds/simple-methods.xsd"> + <!-- Test case for Commission Run --> + <simple-method method-name="testCommissionRun" short-description="Test case for Commission Run" login-required="false"> + + <!-- Precondition : For Creating Commission invoice following data should be there : + 1 ) Sales invoices with paid status.(invoiceId = "8100") + 2 ) Sales Representative with agreement to Company on product (DemoCustAgent and DemoRepAll). --> + <!-- Postcondition : + 1 ) Two commission will be creating for the parties DemoCustAgent and DemoRepAll (like 10000 and 10001 invoiceId). + 2 ) Its amountTotal will be same as commission cost of associated products. --> + + <set field="invoiceId" value="8100"/> + <set field="invoiceIds[]" from-field="invoiceId"/> + <set field="partyIds[]" value="DemoRepAll"/> + <set field="partyIds[]" value="DemoCustAgent"/> + <set field="partyIds[]" value="DemoRepStore"/> + <set field="invoiceTotal" type="BigDecimal" value="0"/> + <entity-one entity-name="UserLogin" value-field="userLogin"> + <field-map field-name="userLoginId" value="system"/> + </entity-one> + <entity-and entity-name="InvoiceItem" list="invoiceItems"> + <field-map field-name="invoiceId" from-field="invoiceId"/> + </entity-and> + <iterate list="invoiceItems" entry="invoiceItem"> + <if-compare field="invoiceItem.productId" operator="equals" value="WG-9943-B3"> + <set field="invoiceTotal" value="${invoiceTotal + (invoiceItem.quantity * (invoiceItem.amount * 3 / 100 + 1))}" type="BigDecimal"/> + </if-compare> + </iterate> + <set field="createCommissionInvoicesMap.invoiceIds" from-field="invoiceIds"/> + <set field="createCommissionInvoicesMap.partyIds" from-field="partyIds"/> + <set field="createCommissionInvoicesMap.userLogin" from-field="userLogin"/> + <call-service service-name="createCommissionInvoices" in-map-name="createCommissionInvoicesMap"> + <result-to-field result-name="invoicesCreated"/> + </call-service> + <assert> + <not><if-empty field="invoicesCreated"/></not> + </assert> + <check-errors/> + <iterate list="invoicesCreated" entry="invoice"> + <call-class-method method-name="getInvoiceTotal" ret-field="amountTotal" class-name="org.ofbiz.accounting.invoice.InvoiceWorker"> + <field field="delegator" type="org.ofbiz.entity.GenericDelegator"/> + <field field="invoice" type="String"/> + </call-class-method> + <assert> + <or> + <if-compare-field field="amountTotal" operator="equals" to-field="invoiceTotal"/> + </or> + </assert> + <check-errors/> + </iterate> + <log level="info" message="Commission Invoice ${invoicesCreated} is created successfully for ${partyIds}"/> + </simple-method> +</simple-methods> Propchange: ofbiz/trunk/applications/accounting/script/org/ofbiz/accounting/test/AutoInvoiceTests.xml ------------------------------------------------------------------------------ svn:eol-style = native Propchange: ofbiz/trunk/applications/accounting/script/org/ofbiz/accounting/test/AutoInvoiceTests.xml ------------------------------------------------------------------------------ svn:keywords = Date Rev Author URL Id Propchange: ofbiz/trunk/applications/accounting/script/org/ofbiz/accounting/test/AutoInvoiceTests.xml ------------------------------------------------------------------------------ svn:mime-type = text/xml Modified: ofbiz/trunk/applications/accounting/servicedef/secas.xml URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/accounting/servicedef/secas.xml?rev=798854&r1=798853&r2=798854&view=diff ============================================================================== --- ofbiz/trunk/applications/accounting/servicedef/secas.xml (original) +++ ofbiz/trunk/applications/accounting/servicedef/secas.xml Wed Jul 29 10:54:02 2009 @@ -24,7 +24,6 @@ <eca service="createPaymentApplication" event="commit"> <condition field-name="invoiceId" operator="is-not-empty"/> <action service="checkInvoicePaymentApplications" mode="sync"/> - <action service="createCommissionInvoices" mode="sync" run-as-user="system"/> </eca> <!-- Uncomment this if you want commission invoices to be set to READY automatically, which means they are official Modified: ofbiz/trunk/applications/accounting/servicedef/services_agreement.xml URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/accounting/servicedef/services_agreement.xml?rev=798854&r1=798853&r2=798854&view=diff ============================================================================== --- ofbiz/trunk/applications/accounting/servicedef/services_agreement.xml (original) +++ ofbiz/trunk/applications/accounting/servicedef/services_agreement.xml Wed Jul 29 10:54:02 2009 @@ -227,6 +227,8 @@ </description> <permission-service service-name="acctgCommissionPermissionCheck" main-action="VIEW"/> <attribute name="productId" type="String" mode="IN" optional="false"/> + <attribute name="invoiceId" type="String" mode="IN" optional="true"/> + <attribute name="invoiceItemSeqId" type="String" mode="IN" optional="true"/> <attribute name="invoiceItemTypeId" type="String" mode="IN" optional="false"/> <attribute name="amount" type="BigDecimal" mode="IN" optional="false"/> <attribute name="quantity" type="BigDecimal" mode="IN" optional="true"/> Modified: ofbiz/trunk/applications/accounting/servicedef/services_invoice.xml URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/accounting/servicedef/services_invoice.xml?rev=798854&r1=798853&r2=798854&view=diff ============================================================================== --- ofbiz/trunk/applications/accounting/servicedef/services_invoice.xml (original) +++ ofbiz/trunk/applications/accounting/servicedef/services_invoice.xml Wed Jul 29 10:54:02 2009 @@ -168,14 +168,9 @@ </service> <service name="createCommissionInvoices" engine="java" location="org.ofbiz.accounting.invoice.InvoiceServices" invoke="createCommissionInvoices"> - <description> - Create commission invoices from an order or return invoice. It - does not create any OrderItemBilling or ReturnItemBilling entities for partial commissions. Therefore, correct value - of the amountApplied needs to be supplied. - </description> - <attribute name="invoiceId" type="String" mode="IN" optional="false"/> - <attribute name="invoiceItemSeqId" type="String" mode="IN" optional="true"/> - <attribute name="amountApplied" type="BigDecimal" mode="IN" optional="false"/> + <description>Create commission invoice for the list of sales invoices</description> + <attribute name="partyIds" type="List" mode="IN" optional="true"/> + <attribute name="invoiceIds" type="List" mode="IN" optional="false"/> <attribute name="invoicesCreated" type="List" mode="OUT" optional="true"/> </service> <service name="sampleInvoiceAffiliateCommission" engine="simple" Modified: ofbiz/trunk/applications/accounting/src/org/ofbiz/accounting/agreement/AgreementServices.java URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/accounting/src/org/ofbiz/accounting/agreement/AgreementServices.java?rev=798854&r1=798853&r2=798854&view=diff ============================================================================== --- ofbiz/trunk/applications/accounting/src/org/ofbiz/accounting/agreement/AgreementServices.java (original) +++ ofbiz/trunk/applications/accounting/src/org/ofbiz/accounting/agreement/AgreementServices.java Wed Jul 29 10:54:02 2009 @@ -92,6 +92,8 @@ quantity = quantity.abs(); String productId = (String) context.get("productId"); String invoiceItemTypeId = (String) context.get("invoiceItemTypeId"); + String invoiceItemSeqId = (String) context.get("invoiceItemSeqId"); + String invoiceId = (String) context.get("invoiceId"); // Collect agreementItems applicable to this orderItem/returnItem // TODO: partyIds should be part of this query! @@ -165,6 +167,8 @@ Map<String, Object> partyCommissionResult = UtilMisc.toMap( "partyIdFrom", agreementItem.getString("partyIdFrom"), "partyIdTo", agreementItem.getString("partyIdTo"), + "invoiceItemSeqId", invoiceItemSeqId, + "invoiceId", invoiceId, "commission", commission, "quantity", quantity, "currencyUomId", agreementItem.getString("currencyUomId"), @@ -172,7 +176,9 @@ if (days >= 0) { partyCommissionResult.put("days", Long.valueOf(days)); } - commissions.add(partyCommissionResult); + if(!commissions.contains(partyCommissionResult)) { + commissions.add(partyCommissionResult); + } } } } catch (GenericEntityException e) { Modified: ofbiz/trunk/applications/accounting/src/org/ofbiz/accounting/invoice/InvoiceServices.java URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/accounting/src/org/ofbiz/accounting/invoice/InvoiceServices.java?rev=798854&r1=798853&r2=798854&view=diff ============================================================================== --- ofbiz/trunk/applications/accounting/src/org/ofbiz/accounting/invoice/InvoiceServices.java (original) +++ ofbiz/trunk/applications/accounting/src/org/ofbiz/accounting/invoice/InvoiceServices.java Wed Jul 29 10:54:02 2009 @@ -32,6 +32,7 @@ import javolution.util.FastMap; +import org.apache.commons.collections.CollectionUtils; import org.ofbiz.accounting.payment.BillingAccountWorker; import org.ofbiz.accounting.payment.PaymentWorker; import org.ofbiz.accounting.payment.PaymentGatewayServices; @@ -832,187 +833,225 @@ LocalDispatcher dispatcher = dctx.getDispatcher(); GenericValue userLogin = (GenericValue) context.get("userLogin"); Locale locale = (Locale) context.get("locale"); - List invoicesCreated = FastList.newInstance(); - - String invoiceIdIn = (String) context.get("invoiceId"); - String invoiceItemSeqIdIn = (String) context.get("invoiceItemSeqId"); - BigDecimal amountTotal = InvoiceWorker.getInvoiceTotal(delegator, invoiceIdIn); - // never use equals for BigDecimal - use either signum or compareTo - if (amountTotal.signum() == 0) { - Debug.logWarning("Invoice [" + invoiceIdIn + "] has an amount total of [" + amountTotal + "], so no commission invoice will be created", module); - return ServiceUtil.returnSuccess(UtilProperties.getMessage(resource,"AccountingInvoiceCommissionZeroInvoiceAmount",locale)); - } - - try { - List billFromVendorInvoiceRoles = EntityUtil.getFieldListFromEntityList(delegator.findByAnd("InvoiceRole", UtilMisc.<String, Object>toMap("invoiceId", invoiceIdIn, "roleTypeId", "BILL_FROM_VENDOR")), "partyId", true); - List salesRepInvoiceRoles = EntityUtil.getFieldListFromEntityList(delegator.findByAnd("InvoiceRole", UtilMisc.<String, Object>toMap("invoiceId", invoiceIdIn, "roleTypeId", "SALES_REP")), "partyId", true); - if (UtilValidate.isEmpty(billFromVendorInvoiceRoles) || UtilValidate.isEmpty(salesRepInvoiceRoles)) { - return ServiceUtil.returnSuccess(); + List<String> salesInvoiceIds = (List) context.get("invoiceIds"); + List<Map> invoicesCreated = FastList.newInstance(); + Map commissionParties = FastMap.newInstance(); + for (String salesInvoiceId : salesInvoiceIds) { + List<String> salesRepPartyIds = (List) context.get("partyIds"); + BigDecimal amountTotal = InvoiceWorker.getInvoiceTotal(delegator, salesInvoiceId); + if (amountTotal.signum() == 0) { + Debug.logWarning("Invoice [" + salesInvoiceId + "] has an amount total of [" + amountTotal + "], so no commission invoice will be created", module); + return ServiceUtil.returnError(UtilProperties.getMessage(resource,"AccountingInvoiceCommissionZeroInvoiceAmount",locale)); } - // Change this when amountApplied is BigDecimal, 18 digit scale to keep all the precision - BigDecimal appliedFraction = ((BigDecimal)context.get("amountApplied")).divide(amountTotal, 12, rounding); - Map inMap = UtilMisc.toMap("invoiceId", invoiceIdIn); - GenericValue invoice = delegator.findByPrimaryKey("Invoice", inMap); - String invoiceTypeId = invoice.getString("invoiceTypeId"); - - // Determine sales or return + BigDecimal appliedFraction = amountTotal.divide(amountTotal, 12, rounding); + GenericValue invoice = null; boolean isReturn = false; - if ("SALES_INVOICE".equals(invoiceTypeId)) { - isReturn = false; - } else if ("CUST_RTN_INVOICE".equals(invoiceTypeId)) { - isReturn = true; - } else { - Debug.logWarning("This type of invoice has no commission; returning success", module); - return ServiceUtil.returnSuccess(UtilProperties.getMessage(resource,"AccountingInvoiceCommissionInvalid",locale)); - } - - if (invoiceItemSeqIdIn != null) { - inMap.put("invoiceItemSeqId", invoiceItemSeqIdIn); + List<String> billFromVendorInvoiceRoles = new ArrayList<String>(); + List<GenericValue> invoiceItems = new ArrayList<GenericValue>(); + try { + List invoiceRoleConds = UtilMisc.toList( + EntityCondition.makeCondition("invoiceId", EntityOperator.EQUALS, salesInvoiceId), + EntityCondition.makeCondition("roleTypeId", EntityOperator.EQUALS, "BILL_FROM_VENDOR")); + billFromVendorInvoiceRoles = EntityUtil.getFieldListFromEntityList(delegator.findList("InvoiceRole", EntityCondition.makeCondition(invoiceRoleConds, EntityOperator.AND), null, null, null, false), "partyId", true); + invoiceRoleConds = UtilMisc.toList( + EntityCondition.makeCondition("invoiceId", EntityOperator.EQUALS, salesInvoiceId), + EntityCondition.makeCondition("roleTypeId", EntityOperator.EQUALS, "SALES_REP")); + // if the receiving parties is empty then we will create commission invoices for all sales agent associated to sales invoice. + if (UtilValidate.isEmpty(salesRepPartyIds)) { + salesRepPartyIds = EntityUtil.getFieldListFromEntityList(delegator.findList("InvoiceRole", EntityCondition.makeCondition(invoiceRoleConds, EntityOperator.AND), null, null, null, false), "partyId", true); + if (UtilValidate.isEmpty(salesRepPartyIds)) { + return ServiceUtil.returnError(UtilProperties.getMessage(resource,"No party found with role sales representative for sales invoice "+ salesInvoiceId,locale)); + } + } else { + List<String> salesInvoiceRolePartyIds = EntityUtil.getFieldListFromEntityList(delegator.findList("InvoiceRole", EntityCondition.makeCondition(invoiceRoleConds, EntityOperator.AND), null, null, null, false), "partyId", true); + if (UtilValidate.isNotEmpty(salesInvoiceRolePartyIds)) { + salesRepPartyIds = (List) CollectionUtils.intersection(salesRepPartyIds, salesInvoiceRolePartyIds); + } + } + invoice = delegator.findOne("Invoice", UtilMisc.toMap("invoiceId", salesInvoiceId), false); + String invoiceTypeId = invoice.getString("invoiceTypeId"); + if ("CUST_RTN_INVOICE".equals(invoiceTypeId)) { + isReturn = true; + } else if (!"SALES_INVOICE".equals(invoiceTypeId)) { + Debug.logWarning("This type of invoice has no commission; returning success", module); + return ServiceUtil.returnError(UtilProperties.getMessage(resource,"AccountingInvoiceCommissionInvalid",locale)); + } + invoiceItems = delegator.findList("InvoiceItem", EntityCondition.makeCondition("invoiceId", EntityOperator.EQUALS, salesInvoiceId), null, null, null, false); + } catch (GenericEntityException e) { + return ServiceUtil.returnError(e.getMessage()); } - List invoiceItems = delegator.findByAnd("InvoiceItem", inMap); - - // Map of commission Lists (of Maps) for each party - Map commissionParties = FastMap.newInstance(); - // Determine commissions for various parties - Iterator itemIter = invoiceItems.iterator(); - while (itemIter.hasNext()) { - GenericValue invoiceItem = (GenericValue) itemIter.next(); + // Map of commission Lists (of Maps) for each party. + // Determine commissions for various parties. + for (GenericValue invoiceItem : invoiceItems) { BigDecimal amount = ZERO; BigDecimal quantity = ZERO; quantity = invoiceItem.getBigDecimal("quantity"); amount = invoiceItem.getBigDecimal("amount"); amount = isReturn ? amount.negate() : amount; String productId = invoiceItem.getString("productId"); - + String invoiceItemSeqId = invoiceItem.getString("invoiceItemSeqId"); + String invoiceId = invoiceItem.getString("invoiceId"); // Determine commission parties for this invoiceItem if (productId != null && productId.length() > 0) { - Map outMap = dispatcher.runSync("getCommissionForProduct", UtilMisc.<String, Object>toMap( - "productId", productId, - "invoiceItemTypeId", invoiceItem.getString("invoiceItemTypeId"), - "amount", amount, - "quantity", quantity, - "userLogin", userLogin)); - if (ServiceUtil.isError(outMap)) { - return outMap; + Map resultMap = null; + try{ + resultMap = dispatcher.runSync("getCommissionForProduct", UtilMisc.<String, Object>toMap( + "productId", productId, + "invoiceId", invoiceId, + "invoiceItemSeqId", invoiceItemSeqId, + "invoiceItemTypeId", invoiceItem.getString("invoiceItemTypeId"), + "amount", amount, + "quantity", quantity, + "userLogin", userLogin)); + } catch (GenericServiceException e) { + return ServiceUtil.returnError(e.getMessage()); } - // build a Map of partyIds (both to and from) in a commission and the amounts // Note that getCommissionForProduct returns a List of Maps with a lot values. See services.xml definition for reference. - List itemComms = (List) outMap.get("commissions"); - if (UtilValidate.isNotEmpty(itemComms)) { - Iterator it = itemComms.iterator(); - while (it.hasNext()) { - Map commMap = (Map)it.next(); - if (!billFromVendorInvoiceRoles.contains(commMap.get("partyIdFrom")) || !salesRepInvoiceRoles.contains(commMap.get("partyIdTo"))) { + List<Map> itemCommissions = (List) resultMap.get("commissions"); + if (UtilValidate.isNotEmpty(itemCommissions)) { + for (Map commissionMap : itemCommissions) { + commissionMap.put("invoice", invoice); + commissionMap.put("appliedFraction", appliedFraction); + if (!billFromVendorInvoiceRoles.contains(commissionMap.get("partyIdFrom")) || !salesRepPartyIds.contains(commissionMap.get("partyIdTo"))) { continue; } - String partyIdFromTo = (String) commMap.get("partyIdFrom") + (String) commMap.get("partyIdTo"); + String partyIdFromTo = (String) commissionMap.get("partyIdFrom") + (String) commissionMap.get("partyIdTo"); if (!commissionParties.containsKey(partyIdFromTo)) { - commissionParties.put(partyIdFromTo, UtilMisc.toList(commMap)); + commissionParties.put(partyIdFromTo, UtilMisc.toList(commissionMap)); } else { - ((List)commissionParties.get(partyIdFromTo)).add(commMap); + ((List)commissionParties.get(partyIdFromTo)).add(commissionMap); } } } } } - - String invoiceType = "COMMISSION_INVOICE"; - Timestamp now = UtilDateTime.nowTimestamp(); - - // Create invoice for each commission receiving party - Iterator it = commissionParties.entrySet().iterator(); - while (it.hasNext()) { - Map.Entry pair = (Map.Entry)it.next(); - List toStore = FastList.newInstance(); - List commList = (List)pair.getValue(); - // get the billing parties - if (UtilValidate.isEmpty(commList)) { - continue; - } - - // From and To are reversed between commission and invoice - String partyIdBillTo = (String) ((Map)commList.get(0)).get("partyIdFrom"); - String partyIdBillFrom = (String) ((Map)commList.get(0)).get("partyIdTo"); - Long days = (Long) ((Map)commList.get(0)).get("days"); - - // create the invoice record - // To and From are in commission's sense, opposite for invoice - Map createInvoiceContext = FastMap.newInstance(); - createInvoiceContext.put("partyId", partyIdBillTo); - createInvoiceContext.put("partyIdFrom", partyIdBillFrom); - createInvoiceContext.put("invoiceDate", now); - // if there were days associated with the commission agreement, then set a dueDate for the invoice. - if (days != null) { - createInvoiceContext.put("dueDate", UtilDateTime.getDayEnd(now, days)); - } - 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", invoice.getString("currencyUomId")); - createInvoiceContext.put("userLogin", userLogin); - - // store the invoice first - Map createInvoiceResult = dispatcher.runSync("createInvoice", createInvoiceContext); - if (ServiceUtil.isError(createInvoiceResult)) { - return ServiceUtil.returnError(UtilProperties.getMessage(resource,"AccountingInvoiceCommissionError",locale), null, null, createInvoiceResult); - } - String invoiceId = (String) createInvoiceResult.get("invoiceId"); - - // create the bill-from (or pay-to) contact mech as the primary PAYMENT_LOCATION of the party from the store - List contactMechs = delegator.findByAnd("PartyContactMechPurpose", UtilMisc.toMap("partyId", partyIdBillTo, "contactMechPurposeTypeId", "BILLING_LOCATION")); - if (contactMechs.size() > 0) { - GenericValue address = (GenericValue) contactMechs.get(0); - GenericValue payToCm = delegator.makeValue("InvoiceContactMech", UtilMisc.toMap( - "invoiceId", invoiceId, - "contactMechId", address.getString("contactMechId"), - "contactMechPurposeTypeId", "BILLING_LOCATION")); - toStore.add(payToCm); - } - contactMechs = delegator.findByAnd("PartyContactMechPurpose", UtilMisc.toMap("partyId", partyIdBillFrom, "contactMechPurposeTypeId", "PAYMENT_LOCATION")); - if (contactMechs.size() > 0) { - GenericValue address = (GenericValue) contactMechs.get(0); - GenericValue payToCm = delegator.makeValue("InvoiceContactMech", UtilMisc.toMap( - "invoiceId", invoiceId, - "contactMechId", address.getString("contactMechId"), - "contactMechPurposeTypeId", "PAYMENT_LOCATION")); - toStore.add(payToCm); - } - - // create the item records - Iterator itt = commList.iterator(); - while (itt.hasNext()) { - Map elem = (Map) itt.next(); - BigDecimal elemAmount = ((BigDecimal)elem.get("commission")).multiply(appliedFraction); - BigDecimal quantity = (BigDecimal)elem.get("quantity"); - elemAmount = elemAmount.setScale(decimals, rounding); - Map resMap = dispatcher.runSync("createInvoiceItem", UtilMisc.toMap( + } + Timestamp now = UtilDateTime.nowTimestamp(); + // Create invoice for each commission receiving party + for (Object commissionParty : commissionParties.entrySet()) { + Map.Entry pair = (Map.Entry)commissionParty; + List toStore = FastList.newInstance(); + List<Map> commList = (List)pair.getValue(); + // get the billing parties + if (UtilValidate.isEmpty(commList)) { + continue; + } + // From and To are reversed between commission and invoice + String partyIdBillTo = (String) (commList.get(0)).get("partyIdFrom"); + String partyIdBillFrom = (String) (commList.get(0)).get("partyIdTo"); + GenericValue invoice = (GenericValue) (commList.get(0)).get("invoice"); + BigDecimal appliedFraction = (BigDecimal) (commList.get(0)).get("appliedFraction"); + Long days = (Long) (commList.get(0)).get("days"); + // create the invoice record + // To and From are in commission's sense, opposite for invoice + Map createInvoiceMap = FastMap.newInstance(); + createInvoiceMap.put("partyId", partyIdBillTo); + createInvoiceMap.put("partyIdFrom", partyIdBillFrom); + createInvoiceMap.put("invoiceDate", now); + // if there were days associated with the commission agreement, then set a dueDate for the invoice. + if (days != null) { + createInvoiceMap.put("dueDate", UtilDateTime.getDayEnd(now, days)); + } + createInvoiceMap.put("invoiceTypeId", "COMMISSION_INVOICE"); + // start with INVOICE_IN_PROCESS, in the INVOICE_READY we can't change the invoice (or shouldn't be able to...) + createInvoiceMap.put("statusId", "INVOICE_IN_PROCESS"); + createInvoiceMap.put("currencyUomId", invoice.getString("currencyUomId")); + createInvoiceMap.put("userLogin", userLogin); + // store the invoice first + Map createInvoiceResult = null; + try{ + createInvoiceResult = dispatcher.runSync("createInvoice", createInvoiceMap); + } catch (GenericServiceException e) { + return ServiceUtil.returnError(UtilProperties.getMessage(resource,"AccountingInvoiceCommissionError",locale), null, null, createInvoiceResult); + } + String invoiceId = (String) createInvoiceResult.get("invoiceId"); + // create the bill-from (or pay-to) contact mech as the primary PAYMENT_LOCATION of the party from the store + List partyContactMechPurposeConds = UtilMisc.toList( + EntityCondition.makeCondition("partyId", EntityOperator.EQUALS, partyIdBillTo), + EntityCondition.makeCondition("contactMechPurposeTypeId", EntityOperator.EQUALS, "BILLING_LOCATION")); + List<GenericValue> partyContactMechPurposes = new ArrayList<GenericValue>(); + try { + partyContactMechPurposes = delegator.findList("PartyContactMechPurpose", + EntityCondition.makeCondition(partyContactMechPurposeConds, EntityOperator.AND), null, null, null, false); + } catch (GenericEntityException e) { + return ServiceUtil.returnError(e.getMessage()); + } + if (partyContactMechPurposes.size() > 0) { + GenericValue address = partyContactMechPurposes.get(0); + GenericValue invoiceContactMech = delegator.makeValue("InvoiceContactMech", UtilMisc.toMap( + "invoiceId", invoiceId, + "contactMechId", address.getString("contactMechId"), + "contactMechPurposeTypeId", "BILLING_LOCATION")); + toStore.add(invoiceContactMech); + } + partyContactMechPurposeConds = UtilMisc.toList( + EntityCondition.makeCondition("partyId", EntityOperator.EQUALS, partyIdBillTo), + EntityCondition.makeCondition("contactMechPurposeTypeId", EntityOperator.EQUALS, "PAYMENT_LOCATION")); + try { + partyContactMechPurposes = delegator.findList("PartyContactMechPurpose", + EntityCondition.makeCondition(partyContactMechPurposeConds, EntityOperator.AND), null, null, null, false); + } catch (GenericEntityException e) { + return ServiceUtil.returnError(e.getMessage()); + } + if (partyContactMechPurposes.size() > 0) { + GenericValue address = partyContactMechPurposes.get(0); + GenericValue invoiceContactMech = delegator.makeValue("InvoiceContactMech", UtilMisc.toMap( + "invoiceId", invoiceId, + "contactMechId", address.getString("contactMechId"), + "contactMechPurposeTypeId", "PAYMENT_LOCATION")); + toStore.add(invoiceContactMech); + } + // create the item records + for (Map commissionMap : commList) { + BigDecimal elemAmount = ((BigDecimal)commissionMap.get("commission")).multiply(appliedFraction); + BigDecimal quantity = (BigDecimal)commissionMap.get("quantity"); + String invoiceIdFrom = (String)commissionMap.get("invoiceId"); + String invoiceItemSeqIdFrom = (String)commissionMap.get("invoiceItemSeqId"); + elemAmount = elemAmount.setScale(decimals, rounding); + Map resMap = null; + Map invoiceItemAssocResultMap = null; + try { + resMap = dispatcher.runSync("createInvoiceItem", UtilMisc.toMap( "invoiceId", invoiceId, - "productId", elem.get("productId"), + "productId", commissionMap.get("productId"), "invoiceItemTypeId", "COMM_INV_ITEM", "quantity",quantity, "amount", elemAmount, "userLogin", userLogin)); - if (ServiceUtil.isError(resMap)) { - return ServiceUtil.returnError(UtilProperties.getMessage(resource,"AccountingInvoiceCommissionErrorItem",locale), null, null, resMap); - } + invoiceItemAssocResultMap = dispatcher.runSync("createInvoiceItemAssoc", UtilMisc.toMap( + "invoiceIdFrom", invoiceIdFrom, + "invoiceItemSeqIdFrom", invoiceItemSeqIdFrom, + "invoiceIdTo", invoiceId, + "invoiceItemSeqIdTo", resMap.get("invoiceItemSeqId"), + "invoiceItemAssocTypeId", "COMMISSION_INVOICE", + "partyIdFrom", partyIdBillFrom, + "partyIdTo", partyIdBillTo, + "quantity", quantity, + "amount", elemAmount, + "userLogin", userLogin)); + } catch (GenericServiceException e) { + return ServiceUtil.returnError(e.getMessage()); } - // store value objects + GenericValue invoiceItemAssoc = null; + if (ServiceUtil.isError(resMap)) { + return ServiceUtil.returnError(UtilProperties.getMessage(resource,"AccountingInvoiceCommissionErrorItem",locale), null, null, resMap); + } + } + // store value objects + try { delegator.storeAll(toStore); - invoicesCreated.add(invoiceId); + } catch (GenericEntityException e) { + String errMsg = UtilProperties.getMessage(resource,"AccountingInvoiceCommissionEntityDataProblem",UtilMisc.toMap("reason",e.toString()),locale); + Debug.logError(e, errMsg, module); + return ServiceUtil.returnError(errMsg); } - Map resp = ServiceUtil.returnSuccess(); - resp.put("invoicesCreated", invoicesCreated); - return resp; - } catch (GenericEntityException e) { - String errMsg = UtilProperties.getMessage(resource,"AccountingInvoiceCommissionEntityDataProblem",UtilMisc.toMap("reason",e.toString()),locale); - Debug.logError(e, errMsg, module); - return ServiceUtil.returnError(errMsg); - } catch (GenericServiceException e) { - String errMsg = UtilProperties.getMessage(resource,"AccountingInvoiceCommissionEntityDataProblem",UtilMisc.toMap("reason",e.toString()),locale); - Debug.logError(e, errMsg, module); - return ServiceUtil.returnError(errMsg); + invoicesCreated.add(UtilMisc.toMap("commissionInvoiceId",invoiceId, "salesRepresentative ",partyIdBillFrom)); } + Map result = ServiceUtil.returnSuccess("Created Commission invoices for each commission receiving parties " + invoicesCreated); + Debug.logInfo("Created Commission invoices for each commission receiving parties " + invoicesCreated, module); + result.put("invoicesCreated", invoicesCreated); + return result; } public static Map readyInvoices(DispatchContext dctx, Map context) { Added: ofbiz/trunk/applications/accounting/testdef/invoicetests.xml URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/accounting/testdef/invoicetests.xml?rev=798854&view=auto ============================================================================== --- ofbiz/trunk/applications/accounting/testdef/invoicetests.xml (added) +++ ofbiz/trunk/applications/accounting/testdef/invoicetests.xml Wed Jul 29 10:54:02 2009 @@ -0,0 +1,27 @@ +<!-- + 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. + --> + +<test-suite suite-name="accountingtests" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="http://ofbiz.apache.org/dtds/test-suite.xsd"> + <!-- Test case for Commission Run --> + <test-case case-name="auto-tests-CommissionRun"> + <simple-method-test location="component://accounting/script/org/ofbiz/accounting/test/AutoInvoiceTests.xml" name="testCommissionRun"/> + </test-case> +</test-suite> Propchange: ofbiz/trunk/applications/accounting/testdef/invoicetests.xml ------------------------------------------------------------------------------ svn:eol-style = native Propchange: ofbiz/trunk/applications/accounting/testdef/invoicetests.xml ------------------------------------------------------------------------------ svn:keywords = Date Rev Author URL Id Propchange: ofbiz/trunk/applications/accounting/testdef/invoicetests.xml ------------------------------------------------------------------------------ svn:mime-type = text/xml Modified: ofbiz/trunk/applications/accounting/webapp/ap/WEB-INF/controller.xml URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/accounting/webapp/ap/WEB-INF/controller.xml?rev=798854&r1=798853&r2=798854&view=diff ============================================================================== --- ofbiz/trunk/applications/accounting/webapp/ap/WEB-INF/controller.xml (original) +++ ofbiz/trunk/applications/accounting/webapp/ap/WEB-INF/controller.xml Wed Jul 29 10:54:02 2009 @@ -54,6 +54,11 @@ <response name="success" type="request" value="PaymentGroupOverview"/> <response name="error" type="view" value="FindPurchaseInvoices"/> </request-map> + <request-map uri="processCommissionRun"> + <security https="true" auth="true"/> + <event type="service" invoke="createCommissionInvoices"/> + <response name="success" type="view" value="CommissionRun"/> + </request-map> <!-- ================ Vendor requests ================ --> <request-map uri="findVendors"> @@ -89,4 +94,4 @@ <!-- Vendor Mappings --> <view-map name="FindVendors" type="screen" page="component://accounting/widget/ap/CommonScreens.xml#FindVendors"/> <view-map name="EditVendor" type="screen" page="component://accounting/widget/ap/CommonScreens.xml#EditVendor"/> -</site-conf> \ No newline at end of file +</site-conf> Modified: ofbiz/trunk/applications/accounting/webapp/ap/invoices/CommissionRun.ftl URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/accounting/webapp/ap/invoices/CommissionRun.ftl?rev=798854&r1=798853&r2=798854&view=diff ============================================================================== --- ofbiz/trunk/applications/accounting/webapp/ap/invoices/CommissionRun.ftl (original) +++ ofbiz/trunk/applications/accounting/webapp/ap/invoices/CommissionRun.ftl Wed Jul 29 10:54:02 2009 @@ -29,31 +29,49 @@ } } } +function setServiceName(selection) { + document.listSalesInvoices.action = '<@ofbizUrl>'+selection.value+'</@ofbizUrl>'; +} +function runAction() { + var form = document.listSalesInvoices; + var invoices = form.elements.length; + for (var i = 0; i < invoices; i++) { + var element = form.elements[i]; + if (element.name == "invoiceIds") { + element.disabled = false; + } + } + form.submit(); +} --> </script> <#if invoices?has_content > - <form name="listSalesInvoices" id="listSalesInvoices"> + <form name="listSalesInvoices" id="listSalesInvoices" method="post"> + <#if parties?has_content> + <input type="hidden" name="partyIds" value="${parties?if_exists}"/> + </#if> <div align="right"> - <select name="serviceName" id="serviceName"> + <select name="serviceName" id="serviceName" onchange="javascript:setServiceName(this);"> <option value=""/> - <option value="commissionRun">${uiLabelMap.AccountingCommissionRun}</option> + <option value="processCommissionRun">${uiLabelMap.AccountingCommissionRun}</option> </select> - <a href="#" id="runAction" class="buttontext">${uiLabelMap.OrderRunAction}</a> + <a href="javascript:runAction();" id="runAction" class="buttontext">${uiLabelMap.OrderRunAction}</a> </div> <table class="basic-table hover-bar" cellspacing="0"> <#-- Header Begins --> <tr class="header-row-2"> - <td width="8%"><input type="checkbox" id="checkAllInvoices" name="checkAllInvoices" onchange="javascript:toggleInvoiceId(this);"/> ${uiLabelMap.CommonSelectAll}</td> + <td width="10%"><input type="checkbox" id="checkAllInvoices" name="checkAllInvoices" onchange="javascript:toggleInvoiceId(this);"/> ${uiLabelMap.CommonSelectAll}</td> <td width="10%">${uiLabelMap.FormFieldTitle_invoiceId}</td> - <td width="15%">${uiLabelMap.AccountingVendorParty}</td> - <td width="10%">${uiLabelMap.CommonStatus}</td> + <td width="10%">${uiLabelMap.AccountingVendorParty}</td> + <td width="8%">${uiLabelMap.CommonStatus}</td> <td width="10%">${uiLabelMap.AccountingReferenceNumber}</td> + <td width="15%">${uiLabelMap.CommonDescription}</td> <td width="10%">${uiLabelMap.AccountingInvoiceDate}</td> - <td width="10%">${uiLabelMap.AccountingDueDate}</td> - <td width="9%">${uiLabelMap.AccountingAmount}</td> - <td width="9%">${uiLabelMap.FormFieldTitle_paidAmount}</td> - <td width="9%">${uiLabelMap.FormFieldTitle_outstandingAmount}</td> + <td width="8%">${uiLabelMap.AccountingDueDate}</td> + <td width="8%">${uiLabelMap.AccountingAmount}</td> + <td width="8%">${uiLabelMap.FormFieldTitle_paidAmount}</td> + <td width="8%">${uiLabelMap.FormFieldTitle_outstandingAmount}</td> </tr> <#-- Header Ends--> <#assign alt_row = false> @@ -67,6 +85,7 @@ <td>${Static["org.ofbiz.party.party.PartyHelper"].getPartyName(delegator, invoice.partyIdFrom, false)?if_exists}</td> <td>${statusItem.get("description")?if_exists}</td> <td>${invoice.get("referenceNumber")?if_exists}</td> + <td>${invoice.get("description")?if_exists}</td> <td>${invoice.get("invoiceDate")?if_exists}</td> <td>${invoice.get("dueDate")?if_exists}</td> <td><@ofbizCurrency amount=invoicePaymentInfo.amount isoCode=defaultOrganizationPartyCurrencyUomId/></td> Modified: ofbiz/trunk/applications/accounting/widget/ap/InvoiceScreens.xml URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/accounting/widget/ap/InvoiceScreens.xml?rev=798854&r1=798853&r2=798854&view=diff ============================================================================== --- ofbiz/trunk/applications/accounting/widget/ap/InvoiceScreens.xml (original) +++ ofbiz/trunk/applications/accounting/widget/ap/InvoiceScreens.xml Wed Jul 29 10:54:02 2009 @@ -157,6 +157,7 @@ <actions> <set field="titleProperty" value="AccountingFindSalesInvoicesForCommissionRun"/> <set field="tabButtonItem" value="commissionRun"/> + <set field="parties" type="List" from-field="parameters.partyIds"/> <script location="component://accounting/webapp/ap/WEB-INF/actions/invoices/CommissionRun.groovy"/> <set field="parameters.partyIds" value=""/> </actions> |
Free forum by Nabble | Edit this page |