diff --git a/migration/i7.1/oracle/202002122100_IDEMPIERE-3215.sql b/migration/i7.1/oracle/202002122100_IDEMPIERE-3215.sql new file mode 100644 index 0000000000..0b2accd390 --- /dev/null +++ b/migration/i7.1/oracle/202002122100_IDEMPIERE-3215.sql @@ -0,0 +1,19 @@ +SET SQLBLANKLINES ON +SET DEFINE OFF + +-- IDEMPIERE-3215 Sales Order/Proposal BP shipper, cause inability to complete +-- Feb 12, 2020, 11:14:32 PM CET +UPDATE AD_Field SET DisplayLogic='@OrderType@=''SO'' | @DeliveryViaRule@=''S'' | @DeliveryViaRule@=''''', AD_Reference_Value_ID=NULL, AD_Val_Rule_ID=NULL, IsToolbarButton=NULL,Updated=TO_DATE('2020-02-12 23:14:32','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=1108 +; + +-- Feb 12, 2020, 8:59:02 PM CET +UPDATE AD_Field SET DisplayLogic='@DeliveryViaRule@=''S''', AD_Reference_Value_ID=NULL, AD_Val_Rule_ID=NULL, IsToolbarButton=NULL,Updated=TO_DATE('2020-02-12 20:59:02','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=1109 +; + +-- Feb 12, 2020, 8:59:28 PM CET +UPDATE AD_Field SET DisplayLogic='@DeliveryViaRule@=''S''', AD_Reference_Value_ID=NULL, AD_Val_Rule_ID=NULL, IsToolbarButton=NULL,Updated=TO_DATE('2020-02-12 20:59:28','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=56446 +; + +SELECT register_migration_script('202002122100_IDEMPIERE-3215.sql') FROM dual +; + diff --git a/migration/i7.1/postgresql/202002122100_IDEMPIERE-3215.sql b/migration/i7.1/postgresql/202002122100_IDEMPIERE-3215.sql new file mode 100644 index 0000000000..eb360d5292 --- /dev/null +++ b/migration/i7.1/postgresql/202002122100_IDEMPIERE-3215.sql @@ -0,0 +1,17 @@ +-- IDEMPIERE-3215 Sales Order/Proposal BP shipper, cause inability to complete +-- Feb 12, 2020, 8:58:51 PM CET +-- Feb 12, 2020, 11:14:32 PM CET +UPDATE AD_Field SET DisplayLogic='@OrderType@=''SO'' | @DeliveryViaRule@=''S'' | @DeliveryViaRule@=''''', AD_Reference_Value_ID=NULL, AD_Val_Rule_ID=NULL, IsToolbarButton=NULL,Updated=TO_TIMESTAMP('2020-02-12 23:14:32','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=1108 +; + +-- Feb 12, 2020, 8:59:02 PM CET +UPDATE AD_Field SET DisplayLogic='@DeliveryViaRule@=''S''', AD_Reference_Value_ID=NULL, AD_Val_Rule_ID=NULL, IsToolbarButton=NULL,Updated=TO_TIMESTAMP('2020-02-12 20:59:02','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=1109 +; + +-- Feb 12, 2020, 8:59:28 PM CET +UPDATE AD_Field SET DisplayLogic='@DeliveryViaRule@=''S''', AD_Reference_Value_ID=NULL, AD_Val_Rule_ID=NULL, IsToolbarButton=NULL,Updated=TO_TIMESTAMP('2020-02-12 20:59:28','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=56446 +; + +SELECT register_migration_script('202002122100_IDEMPIERE-3215.sql') FROM dual +; + diff --git a/org.adempiere.base/src/org/compiere/acct/Doc_AllocationHdr.java b/org.adempiere.base/src/org/compiere/acct/Doc_AllocationHdr.java index 1802da5edf..a0d7c79943 100644 --- a/org.adempiere.base/src/org/compiere/acct/Doc_AllocationHdr.java +++ b/org.adempiere.base/src/org/compiere/acct/Doc_AllocationHdr.java @@ -31,12 +31,12 @@ import org.compiere.model.MAllocationHdr; import org.compiere.model.MAllocationLine; import org.compiere.model.MCashLine; import org.compiere.model.MConversionRate; -import org.compiere.model.MCurrency; import org.compiere.model.MFactAcct; import org.compiere.model.MInvoice; import org.compiere.model.MInvoiceLine; import org.compiere.model.MOrder; import org.compiere.model.MPayment; +import org.compiere.model.MPeriod; import org.compiere.util.CLogger; import org.compiere.util.DB; import org.compiere.util.Env; @@ -75,6 +75,9 @@ public class Doc_AllocationHdr extends Doc private static final BigDecimal TOLERANCE = BigDecimal.valueOf(0.02); /** Facts */ private ArrayList m_facts = null; + BigDecimal gainLossAmt =Env.ZERO; + private BigDecimal cmGainLossAmt=Env.ZERO; + private ArrayList gainLossFactList; /** @@ -174,6 +177,12 @@ public class Doc_AllocationHdr extends Doc Fact fact = new Fact(this, as, Fact.POST_Actual); Fact factForRGL = new Fact(this, as, Fact.POST_Actual); // dummy fact (not posted) to calculate Realized Gain & Loss boolean isInterOrg = isInterOrg(as); + BigDecimal paymentSelectAmt = Env.ZERO; + BigDecimal totalAllocationSource = Env.ZERO; + MPayment payment = null; + int lineID = 0; + MAccount bpAcct = null; // Liability/Receivables + gainLossFactList = new ArrayList(); for (int i = 0; i < p_lines.length; i++) { @@ -199,10 +208,7 @@ public class Doc_AllocationHdr extends Doc FactLine fl = null; FactLine flForRGL = null; - MAccount bpAcct = null; // Liability/Receivables - MAccount payAcct = null; // Payment Selection // - MPayment payment = null; if (line.getC_Payment_ID() != 0) payment = new MPayment (getCtx(), line.getC_Payment_ID(), getTrxName()); MInvoice invoice = null; @@ -254,7 +260,8 @@ public class Doc_AllocationHdr extends Doc // if not using clearing accounts, then don't post amtsource // change the allocationsource to be writeoff + discount allocationSource = line.getDiscountAmt().add(line.getWriteOffAmt()); - payAcct = getPaymentAcct(as, line.getC_Payment_ID()); + + } else { // Normal behavior -- unchanged if using clearing accounts @@ -262,11 +269,14 @@ public class Doc_AllocationHdr extends Doc // Payment/Cash DR if (line.getC_Payment_ID() != 0) { - payAcct = getPaymentAcct(as, line.getC_Payment_ID()); - fl = fact.createLine (line, payAcct, + fl = fact.createLine (line, getPaymentAcct(as, line.getC_Payment_ID()), getC_Currency_ID(), line.getAmtSource(), null); if (fl != null && payment != null) fl.setAD_Org_ID(payment.getAD_Org_ID()); + if (payment.getReversal_ID() > 0 ) + paymentSelectAmt= paymentSelectAmt.add(fl.getAcctBalance().negate()); + else + paymentSelectAmt= paymentSelectAmt.add(fl.getAcctBalance()); } else if (line.getC_CashLine_ID() != 0) { @@ -393,11 +403,11 @@ public class Doc_AllocationHdr extends Doc // Payment/Cash CR if (isUsingClearing && line.getC_Payment_ID() != 0) // Avoid usage of clearing accounts { - payAcct = getPaymentAcct(as, line.getC_Payment_ID()); - fl = fact.createLine (line, payAcct, + fl = fact.createLine (line, getPaymentAcct(as, line.getC_Payment_ID()), getC_Currency_ID(), null, line.getAmtSource().negate()); if (fl != null && payment != null) fl.setAD_Org_ID(payment.getAD_Org_ID()); + paymentSelectAmt= paymentSelectAmt.add(fl.getAcctBalance().negate()); } else if (isUsingClearing && line.getC_CashLine_ID() != 0) // Avoid usage of clearing accounts { @@ -435,14 +445,31 @@ public class Doc_AllocationHdr extends Doc && (getC_Currency_ID() != as.getC_Currency_ID() // payment allocation in foreign currency || getC_Currency_ID() != line.getInvoiceC_Currency_ID())) // allocation <> invoice currency { - p_Error = createRealizedGainLoss (line, as, fact, bpAcct, invoice, payAcct, payment, - allocationSourceForRGL, allocationAccounted); + p_Error = createRealizedGainLoss (line, as, fact, bpAcct, invoice, + allocationSource, allocationAccounted); if (p_Error != null) return null; - } - + } + totalAllocationSource = totalAllocationSource.add(line.getAmtSource()); + lineID = line.get_ID(); } // for all lines + // rounding correction + if (payment != null && getC_Currency_ID() != as.getC_Currency_ID() ) // payment allocation in foreign currency + { + p_Error = createPaymentGainLoss (as, fact, getPaymentAcct(as, payment.get_ID()), payment, + totalAllocationSource, paymentSelectAmt, lineID); + if (p_Error != null) + return null; + } + + if (getC_Currency_ID() != as.getC_Currency_ID()) + { + p_Error = createInvoiceRounding (as, fact, bpAcct); + if (p_Error != null) + return null; + } + // FR [ 1840016 ] Avoid usage of clearing accounts - subject to C_AcctSchema.IsPostIfClearingEqual if ( (!as.isPostIfClearingEqual()) && p_lines.length > 0 && (!isInterOrg)) { boolean allEquals = true; @@ -652,7 +679,7 @@ public class Doc_AllocationHdr extends Doc } catch (Exception e) { - log.log(Level.SEVERE, sql, e); + throw new RuntimeException(e.getLocalizedMessage(), e); } finally { @@ -697,21 +724,17 @@ public class Doc_AllocationHdr extends Doc * Accounted Amount of the Allocation * @param as accounting schema * @param fact fact - * @param invAcct invoice account + * @param acct account * @param invoice invoice - * @param payAcct payment account - * @param payment payment * @param allocationSource source amt * @param allocationAccounted acct amt * @return Error Message or null if OK */ - private String createRealizedGainLoss (DocLine line, MAcctSchema as, Fact fact, MAccount invAcct, - MInvoice invoice, MAccount payAcct, MPayment payment, BigDecimal allocationSource, BigDecimal allocationAccounted) + private String createRealizedGainLoss (DocLine line, MAcctSchema as, Fact fact, MAccount acct, + MInvoice invoice, BigDecimal allocationSource, BigDecimal allocationAccounted) { BigDecimal invoiceSource = null; BigDecimal invoiceAccounted = null; - BigDecimal paymentSource = null; - BigDecimal paymentAccounted = null; // StringBuilder sql = new StringBuilder() .append("SELECT SUM(AmtSourceDr), SUM(AmtAcctDr), SUM(AmtSourceCr), SUM(AmtAcctCr)") @@ -732,129 +755,66 @@ public class Doc_AllocationHdr extends Doc invoiceAccounted = (BigDecimal) valuesInv.get(3); // AmtAcctCr } } + // Requires that Invoice is Posted if (invoiceSource == null || invoiceAccounted == null) return "Gain/Loss - Invoice not posted yet"; // - String invoiceCur = MCurrency.get(getCtx(), invoice.getC_Currency_ID()).getISO_Code(); - String allocCur = MCurrency.get(getCtx(), getC_Currency_ID()).getISO_Code(); - StringBuilder descriptionInv = new StringBuilder("Invoice=(").append(invoiceCur).append(")").append(invoiceSource).append("/").append(invoiceAccounted) - .append(" - Allocation=(").append(allocCur).append(")").append(allocationSource).append("/").append(allocationAccounted); - if (log.isLoggable(Level.FINE)) log.fine(descriptionInv.toString()); + StringBuilder description = new StringBuilder("Invoice=(").append(invoice.getC_Currency_ID()).append(")").append(invoiceSource).append("/").append(invoiceAccounted) + .append(" - Allocation=(").append(getC_Currency_ID()).append(")").append(allocationSource).append("/").append(allocationAccounted); + if (log.isLoggable(Level.FINE)) log.fine(description.toString()); // Allocation not Invoice Currency - BigDecimal allocationInvoiceSource = allocationSource; if (getC_Currency_ID() != invoice.getC_Currency_ID()) { - allocationInvoiceSource = MConversionRate.convert(getCtx(), + BigDecimal allocationSourceNew = MConversionRate.convert(getCtx(), allocationSource, getC_Currency_ID(), invoice.getC_Currency_ID(), getDateAcct(), invoice.getC_ConversionType_ID(), invoice.getAD_Client_ID(), invoice.getAD_Org_ID()); - if (allocationInvoiceSource == null) + if (allocationSourceNew == null) return "Gain/Loss - No Conversion from Allocation->Invoice"; - StringBuilder d2 = new StringBuilder("Allocation=(").append(allocCur).append(")").append(allocationSource) - .append("->(").append(invoiceCur).append(")").append(allocationInvoiceSource); + StringBuilder d2 = new StringBuilder("Allocation=(").append(getC_Currency_ID()).append(")").append(allocationSource) + .append("->(").append(invoice.getC_Currency_ID()).append(")").append(allocationSourceNew); if (log.isLoggable(Level.FINE)) log.fine(d2.toString()); - descriptionInv.append(" - ").append(d2); + description.append(" - ").append(d2); + allocationSource = allocationSourceNew; } - BigDecimal invoiceDifference = null; // gain is negative - // Full Invoice in currency - if (allocationInvoiceSource.compareTo(invoiceSource) == 0) + BigDecimal acctDifference = null; // gain is negative + //reversal entry + if (allocationSource.signum() > 0 ) { - invoiceDifference = invoiceAccounted.subtract(allocationAccounted); // gain is negative - StringBuilder d2 = new StringBuilder("(full) = ").append(invoiceDifference); + acctDifference = invoiceAccounted.subtract(allocationAccounted.abs()); + } + // Full Payment in currency + if (allocationSource.compareTo(invoiceSource) == 0) + { + acctDifference = invoiceAccounted.subtract(allocationAccounted.abs()); // gain is negative + + StringBuilder d2 = new StringBuilder("(full) = ").append(acctDifference); if (log.isLoggable(Level.FINE)) log.fine(d2.toString()); - descriptionInv.append(" - ").append(d2); + description.append(" - ").append(d2); } else // partial or MC { // percent of total payment - double multiplier = allocationInvoiceSource.doubleValue() / invoiceSource.doubleValue(); + double multiplier = allocationSource.doubleValue() / invoiceSource.doubleValue(); // Reduce Orig Invoice Accounted invoiceAccounted = invoiceAccounted.multiply(BigDecimal.valueOf(multiplier)); // Difference based on percentage of Orig Invoice - invoiceDifference = invoiceAccounted.subtract(allocationAccounted); // gain is negative + acctDifference = invoiceAccounted.subtract(allocationAccounted); // gain is negative // ignore Tolerance - if (invoiceDifference.abs().compareTo(TOLERANCE) < 0) - invoiceDifference = Env.ZERO; + if (acctDifference.abs().compareTo(TOLERANCE) < 0) + acctDifference = Env.ZERO; // Round int precision = as.getStdPrecision(); - if (invoiceDifference.scale() > precision) - invoiceDifference = invoiceDifference.setScale(precision, RoundingMode.HALF_UP); - StringBuilder d2 = new StringBuilder("(partial) = ").append(invoiceDifference).append(" - Multiplier=").append(multiplier); + if (acctDifference.scale() > precision) + acctDifference = acctDifference.setScale(precision, RoundingMode.HALF_UP); + StringBuilder d2 = new StringBuilder("(partial) = ").append(acctDifference).append(" - Multiplier=").append(multiplier); if (log.isLoggable(Level.FINE)) log.fine(d2.toString()); - descriptionInv.append(" - ").append(d2); + description.append(" - ").append(d2); } - // For Payment - BigDecimal paymentDifference = Env.ZERO; - StringBuilder descriptionPay = null; - if (payment != null && payment.getC_Payment_ID() > 0) { - List valuesPay = DB.getSQLValueObjectsEx(getTrxName(), sql.toString(), - MPayment.Table_ID, payment.getC_Payment_ID(), as.getC_AcctSchema_ID()); - if (valuesPay != null) { - if (invoice.isSOTrx()) { - paymentSource = (BigDecimal) valuesPay.get(2); // AmtSourceCr - paymentAccounted = (BigDecimal) valuesPay.get(3); // AmtAcctCr - } else { - paymentSource = (BigDecimal) valuesPay.get(0); // AmtSourceDr - paymentAccounted = (BigDecimal) valuesPay.get(1); // AmtAcctDr - } - } - // Requires that Payment is Posted - if (paymentSource == null || paymentAccounted == null) - return "Gain/Loss - Payment not posted yet"; - // - String paymentCur = MCurrency.get(getCtx(), payment.getC_Currency_ID()).getISO_Code(); - descriptionPay = new StringBuilder("Payment=(").append(paymentCur).append(")").append(paymentSource).append("/").append(paymentAccounted) - .append(" - Allocation=(").append(allocCur).append(")").append(allocationSource).append("/").append(allocationAccounted); - if (log.isLoggable(Level.FINE)) log.fine(descriptionPay.toString()); - // Allocation not Payment Currency - BigDecimal allocationPaymentSource = allocationSource; - if (getC_Currency_ID() != payment.getC_Currency_ID()) - { - allocationPaymentSource = MConversionRate.convert(getCtx(), - allocationSource, getC_Currency_ID(), - payment.getC_Currency_ID(), getDateAcct(), - payment.getC_ConversionType_ID(), payment.getAD_Client_ID(), payment.getAD_Org_ID()); - if (allocationPaymentSource == null) - return "Gain/Loss - No Conversion from Allocation->Payment"; - StringBuilder d2 = new StringBuilder("Allocation=(").append(allocCur).append(")").append(allocationSource) - .append("->(").append(paymentCur).append(")").append(allocationPaymentSource); - if (log.isLoggable(Level.FINE)) log.fine(d2.toString()); - descriptionPay.append(" - ").append(d2); - } - - // Full Payment in currency - if (allocationPaymentSource.compareTo(paymentSource) == 0) - { - paymentDifference = paymentAccounted.subtract(allocationAccounted); // gain is negative - StringBuilder d2 = new StringBuilder("(full) = ").append(paymentDifference); - if (log.isLoggable(Level.FINE)) log.fine(d2.toString()); - descriptionPay.append(" - ").append(d2); - } - else // partial or MC - { - // percent of total payment - double multiplier = allocationPaymentSource.doubleValue() / paymentSource.doubleValue(); - // Reduce Orig Payment Accounted - paymentAccounted = paymentAccounted.multiply(BigDecimal.valueOf(multiplier)); - // Difference based on percentage of Orig Payment - paymentDifference = paymentAccounted.subtract(allocationAccounted); // gain is negative - // ignore Tolerance - if (paymentDifference.abs().compareTo(TOLERANCE) < 0) - paymentDifference = Env.ZERO; - // Round - int precision = as.getStdPrecision(); - if (paymentDifference.scale() > precision) - paymentDifference = paymentDifference.setScale(precision, RoundingMode.HALF_UP); - StringBuilder d2 = new StringBuilder("(partial) = ").append(paymentDifference).append(" - Multiplier=").append(multiplier); - if (log.isLoggable(Level.FINE)) log.fine(d2.toString()); - descriptionPay.append(" - ").append(d2); - } - } - - if (invoiceDifference.signum() == 0 && paymentDifference.signum() == 0) + if (acctDifference.signum() == 0) { log.fine("No Difference"); return null; @@ -862,51 +822,28 @@ public class Doc_AllocationHdr extends Doc MAccount gain = MAccount.get (as.getCtx(), as.getAcctSchemaDefault().getRealizedGain_Acct()); MAccount loss = MAccount.get (as.getCtx(), as.getAcctSchemaDefault().getRealizedLoss_Acct()); + if (invoice.isCreditMemo() || invoice.getReversal_ID() > 0) + cmGainLossAmt = cmGainLossAmt.add(acctDifference); + else + gainLossAmt = gainLossAmt.add(acctDifference); // - BigDecimal acctDifference = invoiceDifference.subtract(paymentDifference); + if (invoice.isSOTrx()) { - if (acctDifference.signum() != 0) { - FactLine fl = fact.createLine (line, loss, gain, as.getC_Currency_ID(), acctDifference); - StringBuilder description = new StringBuilder(descriptionInv); - if (paymentDifference.signum() != 0 && descriptionPay != null) { - description.append(" / ").append(descriptionPay); - } - fl.setDescription(description.toString()); - fl.setAD_Org_ID(invoice.getAD_Org_ID()); - } - if (invoiceDifference.signum() != 0) { - FactLine fl = fact.createLine (line, invAcct, as.getC_Currency_ID(), invoiceDifference.negate()); - fl.setDescription(descriptionInv.toString()); - fl.setAD_Org_ID(invoice.getAD_Org_ID()); - } - if (paymentDifference.signum() != 0) { - FactLine fl = fact.createLine (line, payAcct, as.getC_Currency_ID(), paymentDifference); - fl.setDescription(descriptionPay.toString()); - fl.setAD_Org_ID(invoice.getAD_Org_ID()); - } + FactLine fl = fact.createLine (line, loss, gain, as.getC_Currency_ID(), acctDifference); + fl.setDescription(description.toString()); + fl = fact.createLine (line, acct, as.getC_Currency_ID(), acctDifference.negate()); + gainLossFactList.add(fl); } else { - if (invoiceDifference.signum() != 0) { - FactLine fl = fact.createLine (line, invAcct, as.getC_Currency_ID(), invoiceDifference); - fl.setDescription(descriptionInv.toString()); - fl.setAD_Org_ID(invoice.getAD_Org_ID()); - } - if (paymentDifference.signum() != 0) { - FactLine fl = fact.createLine (line, payAcct, as.getC_Currency_ID(), paymentDifference.negate()); - fl.setDescription(descriptionPay.toString()); - fl.setAD_Org_ID(invoice.getAD_Org_ID()); - } - if (acctDifference.signum() != 0) { - FactLine fl = fact.createLine (line, loss, gain, as.getC_Currency_ID(), acctDifference.negate()); - StringBuilder description = new StringBuilder(descriptionInv); - if (paymentDifference.signum() != 0 && descriptionPay != null) { - description.append(" / ").append(descriptionPay); - } - fl.setDescription(description.toString()); - fl.setAD_Org_ID(invoice.getAD_Org_ID()); - } + FactLine fl = fact.createLine (line, acct, + as.getC_Currency_ID(), acctDifference); + gainLossFactList.add(fl); + fl = fact.createLine (line, loss, gain, + as.getC_Currency_ID(), acctDifference.negate()); + fl.setDescription(description.toString()); + } return null; } // createRealizedGainLoss @@ -947,7 +884,7 @@ public class Doc_AllocationHdr extends Doc // Get Source Amounts with account String sql = "SELECT * " + "FROM Fact_Acct " - + "WHERE AD_Table_ID=318 AND Record_ID=?" // Invoice + + "WHERE AD_Table_ID=? AND Record_ID=?" // Invoice + " AND C_AcctSchema_ID=?" + " AND Line_ID IS NULL"; // header lines like tax or total PreparedStatement pstmt = null; @@ -955,15 +892,16 @@ public class Doc_AllocationHdr extends Doc try { pstmt = DB.prepareStatement(sql, getTrxName()); - pstmt.setInt(1, line.getC_Invoice_ID()); - pstmt.setInt(2, as.getC_AcctSchema_ID()); + pstmt.setInt(1, MInvoice.Table_ID); + pstmt.setInt(2, line.getC_Invoice_ID()); + pstmt.setInt(3, as.getC_AcctSchema_ID()); rs = pstmt.executeQuery(); while (rs.next()) tax.addInvoiceFact (new MFactAcct(getCtx(), rs, fact.get_TrxName())); } catch (Exception e) { - log.log(Level.SEVERE, sql, e); + throw new RuntimeException(e.getLocalizedMessage(), e); } finally { DB.close(rs, pstmt); @@ -982,6 +920,430 @@ public class Doc_AllocationHdr extends Doc } // createTaxCorrection + /************************************************************************** + * Create Rounding Correction. + * Compares the Accounted Amount of the Payment to the + * Accounted Amount of the Allocation + * @param as accounting schema + * @param fact fact + * @param acct account + * @param payment payment + * @param paymentSource source amt + * @param paymentAccounted acct amt + * @return Error Message or null if OK + */ + private String createPaymentGainLoss (MAcctSchema as, Fact fact, MAccount acct, + MPayment payment, BigDecimal allocationSource, BigDecimal totalAllocationAccounted, int lineID) + { + BigDecimal paymentSource = null; + BigDecimal paymentAccounted = null; + // + StringBuilder sql = new StringBuilder() + .append("SELECT SUM(AmtSourceDr), SUM(AmtAcctDr), SUM(AmtSourceCr), SUM(AmtAcctCr)") + .append(" FROM Fact_Acct ") + .append("WHERE AD_Table_ID=? AND Record_ID=?") + .append(" AND C_AcctSchema_ID=?") + .append(" AND Account_ID = ? ") + .append(" AND PostingType='A'"); + + // For Payment + List valuesPay = DB.getSQLValueObjectsEx(getTrxName(), sql.toString(), + MPayment.Table_ID, payment.getC_Payment_ID(), as.getC_AcctSchema_ID(), acct.getAccount_ID()); + if (valuesPay != null) { + if (payment.isReceipt()) { + paymentSource = (BigDecimal) valuesPay.get(2); // AmtSourceCr + paymentAccounted = (BigDecimal) valuesPay.get(3); // AmtAcctCr + } else { + paymentSource = (BigDecimal) valuesPay.get(0); // AmtSourceDr + paymentAccounted = (BigDecimal) valuesPay.get(1); // AmtAcctDr + } + } + + // Requires that Allocation is Posted + if (paymentSource == null || paymentAccounted == null) + return null; //"Gain/Loss - Payment not posted yet"; + // + StringBuilder description = new StringBuilder("Payment=(").append(payment.getC_Currency_ID()).append(")").append(paymentSource).append("/").append(paymentAccounted) + .append(" - Allocation=(").append(getC_Currency_ID()).append(")").append(allocationSource).append("/").append(totalAllocationAccounted); + if (log.isLoggable(Level.FINE)) log.fine(description.toString()); + + boolean isSameSourceDiffPeriod = false; + BigDecimal acctDifference = null; // gain is negative + // Full Payment in currency + if (allocationSource.abs().compareTo(paymentSource.abs()) == 0) + { + acctDifference = totalAllocationAccounted.subtract(paymentAccounted.abs()); // gain is negative + StringBuilder d2 = new StringBuilder("(full) = ").append(acctDifference); + if (log.isLoggable(Level.FINE)) log.fine(d2.toString()); + description.append(" - ").append(d2); + + // Different period + if (MPeriod.getC_Period_ID(getCtx(), payment.getDateAcct(), payment.getAD_Org_ID()) != + MPeriod.getC_Period_ID(getCtx(), getDateAcct(), getAD_Org_ID())) + { + BigDecimal allocationAccounted0 = MConversionRate.convert(getCtx(), + allocationSource, getC_Currency_ID(), + as.getC_Currency_ID(), payment.getDateAcct(), + payment.getC_ConversionType_ID(), payment.getAD_Client_ID(), payment.getAD_Org_ID()); + BigDecimal paymentAccounted0 = MConversionRate.convert(getCtx(), + paymentSource, getC_Currency_ID(), + as.getC_Currency_ID(), getDateAcct(), + getC_ConversionType_ID(), getAD_Client_ID(), getAD_Org_ID()); + isSameSourceDiffPeriod = allocationAccounted0.abs().compareTo(paymentAccounted.abs()) == 0 && + paymentAccounted0.abs().compareTo(totalAllocationAccounted.abs()) == 0; + } + } + + if (acctDifference == null || acctDifference.signum() == 0) + { + log.fine("No Difference"); + return null; + } + + MAccount gain = MAccount.get (as.getCtx(), as.getAcctSchemaDefault().getRealizedGain_Acct()); + MAccount loss = MAccount.get (as.getCtx(), as.getAcctSchemaDefault().getRealizedLoss_Acct()); + // + if (payment.isReceipt()) + { + FactLine fl = fact.createLine (null, acct,as.getC_Currency_ID(), acctDifference.negate()); + fl.setDescription(description.toString()); + fl.setLine_ID(lineID); + if (!fact.isAcctBalanced()) + { + if (!isSameSourceDiffPeriod && as.isCurrencyBalancing() && as.getC_Currency_ID() != payment.getC_Currency_ID() ) + { + fact.createLine (null, as.getCurrencyBalancing_Acct(),as.getC_Currency_ID(), acctDifference); + } else + { + fl = fact.createLine (null, loss, gain,as.getC_Currency_ID(), acctDifference); + + } + } + } + else + { + FactLine fl = fact.createLine (null, acct,as.getC_Currency_ID(), acctDifference); + fl.setDescription(description.toString()); + fl.setLine_ID(lineID); + if (!fact.isAcctBalanced()) + { + if (!isSameSourceDiffPeriod && as.isCurrencyBalancing() && as.getC_Currency_ID() != payment.getC_Currency_ID() ) + { + fact.createLine (null, as.getCurrencyBalancing_Acct(),as.getC_Currency_ID(), acctDifference.negate()); + } else { + fact.createLine (null, loss, gain, as.getC_Currency_ID(), acctDifference.negate()); + } + } + + } + return null; + } + + /************************************************************************** + * Create Rounding Correction. + * Compares the Accounted Amount of the AR/AP Invoice to the + * Accounted Amount of the AR/AP Allocation + * @param as accounting schema + * @param fact fact + * @param bpAcct account + * @param payment payment + * @return Error Message or null if OK + */ + private String createInvoiceRounding(MAcctSchema as, Fact fact, MAccount bpAcct) { + PreparedStatement pstmt = null; + ResultSet rs = null; + // Invoice AR/AP + BigDecimal totalInvoiceSource = BigDecimal.ZERO; + BigDecimal totalInvoiceAccounted = BigDecimal.ZERO; + boolean isCMReversal =false ; + MInvoice invoice = null; + MPayment payment = null; + ArrayList invList = new ArrayList(); + for (int i = 0; i < p_lines.length; i++) + { + DocLine_Allocation line = (DocLine_Allocation)p_lines[i]; + if (line.getC_Invoice_ID() != 0) + invoice = new MInvoice (getCtx(), line.getC_Invoice_ID(), getTrxName()); + if (line.getC_Payment_ID() != 0) + payment = new MPayment (getCtx(), line.getC_Payment_ID(), getTrxName()); + + if (invoice != null ) + { + boolean isDebit = false; + // to cater for invoice reverse-accrual. + if (invoice.isSOTrx() && !invoice.isCreditMemo()) + isDebit = true; + else if (!invoice.isSOTrx() && invoice.isCreditMemo() && invoice.getReversal_ID() > 0 ) + isDebit = true; + else if (invoice.isSOTrx() && invoice.isCreditMemo() && invoice.getReversal_ID() == 0) + isDebit = true; + // + StringBuilder sql = new StringBuilder("SELECT ") + .append((isDebit ) + ? "SUM(AmtSourceDr), SUM(AmtAcctDr)" // so + : "SUM(AmtSourceCr), SUM(AmtAcctCr)") // po + .append(" FROM Fact_Acct ") + .append("WHERE AD_Table_ID=? AND Record_ID=?") // Invoice + .append(" AND C_AcctSchema_ID=?") + .append(" AND PostingType='A'") + .append(" AND Account_ID= ? "); + pstmt = null; + rs = null; + try + { + pstmt = DB.prepareStatement(sql.toString(), getTrxName()); + pstmt.setInt(1, MInvoice.Table_ID); + pstmt.setInt(2, invoice.getC_Invoice_ID()); + pstmt.setInt(3, as.getC_AcctSchema_ID()); + pstmt.setInt(4, bpAcct.getAccount_ID()); + rs = pstmt.executeQuery(); + if (rs.next()) + { + BigDecimal invoiceSource = rs.getBigDecimal(1); + BigDecimal invoiceAccounted = rs.getBigDecimal(2); + if ( !invList.contains(invoice)) + { + totalInvoiceSource =totalInvoiceSource.add(invoiceSource); + totalInvoiceAccounted =totalInvoiceAccounted.add(invoiceAccounted); + } + + + invList.add(invoice); + } + } + catch (Exception e) + { + throw new RuntimeException(e.getLocalizedMessage(), e); + } + finally { + DB.close(rs, pstmt); + rs = null; pstmt = null; + } + } + } + BigDecimal allocInvoiceSource = BigDecimal.ZERO; + BigDecimal allocInvoiceAccounted = BigDecimal.ZERO; + MAllocationLine allocationLine = null; + FactLine[] factlines = fact.getLines(); + boolean isExcludeCMGainLoss = false; + for (FactLine factLine : factlines) { + if (bpAcct != null) { + if (factLine.getAccount_ID() == bpAcct.getAccount_ID() ) + { + if (factLine.getLine_ID() != 0 ) + allocationLine = new MAllocationLine(getCtx(), factLine.getLine_ID(), getTrxName()); + + if (allocationLine != null) + invoice = allocationLine.getInvoice(); + + if (invoice.isSOTrx()) + { + if (factLine.getC_Currency_ID() != as.getC_Currency_ID()) + allocInvoiceSource = allocInvoiceSource.add(factLine.getAmtSourceCr()); + if (!gainLossFactList.contains(factLine) && !invoice.isCreditMemo()) + allocInvoiceAccounted = allocInvoiceAccounted.add(factLine.getAmtAcctCr()); + + if (!gainLossFactList.contains(factLine) && invoice.isCreditMemo() && invoice.getReversal_ID() > 0 ) { + allocInvoiceAccounted = allocInvoiceAccounted.add(factLine.getAmtAcctDr()); + if (!invoice.getDateAcct().equals(getDateAcct())) + isCMReversal =true; + } + if (invoice!=null) + { + if (invoice.isCreditMemo() || invoice.getReversal_ID() > 0 ) { + allocInvoiceAccounted = allocInvoiceAccounted.add(cmGainLossAmt.abs()); + cmGainLossAmt = Env.ZERO; + } + if (gainLossFactList.contains(factLine)) { + isExcludeCMGainLoss = true; + } + } + + if (payment != null && payment.getReversal_ID() > 0 && !gainLossFactList.contains(factLine)) + { + allocInvoiceSource = allocInvoiceSource.add(factLine.getAmtSourceDr()); + allocInvoiceAccounted = allocInvoiceAccounted.add(factLine.getAmtAcctDr()); + } + + } else + { + if (as.getC_Currency_ID() != factLine.getC_Currency_ID()) + allocInvoiceSource = allocInvoiceSource.add(factLine.getAmtSourceDr()); + + if (!gainLossFactList.contains(factLine) && !invoice.isCreditMemo()) + allocInvoiceAccounted = allocInvoiceAccounted.add(factLine.getAmtAcctDr()); + + if (!gainLossFactList.contains(factLine) && invoice.isCreditMemo() && invoice.getReversal_ID() > 0 ) { + allocInvoiceAccounted = allocInvoiceAccounted.add(factLine.getAmtAcctCr()); + // this is to cater for reverse-accrual. + if (!invoice.getDateAcct().equals(getDateAcct())) + isCMReversal =true; + } + + if (invoice!=null ) + { + if (invoice.isCreditMemo() || invoice.getReversal_ID() > 0 ) { + allocInvoiceAccounted = allocInvoiceAccounted.add(cmGainLossAmt.abs()); + cmGainLossAmt = Env.ZERO; + } + if (gainLossFactList.contains(factLine)) { + isExcludeCMGainLoss = true; + } + + } + + } + + } + } + } + + + BigDecimal acctDifference = null; // gain is negative + // + StringBuilder description = new StringBuilder("Invoice=(").append(getC_Currency_ID()).append(")").append(allocInvoiceSource).append("/").append(allocInvoiceAccounted); + if (log.isLoggable(Level.FINE)) log.fine(description.toString()); + boolean isBPartnerAdjust = true; + if (allocInvoiceSource.abs().compareTo(totalInvoiceSource.abs()) == 0) + { + if (isExcludeCMGainLoss) + allocInvoiceAccounted = allocInvoiceAccounted.add(cmGainLossAmt); + if (payment != null && payment.getReversal_ID() > 0 ) + allocInvoiceAccounted = allocInvoiceAccounted.subtract(gainLossAmt); + else + allocInvoiceAccounted = allocInvoiceAccounted.add(gainLossAmt); + if (isCMReversal) + acctDifference = totalInvoiceAccounted.subtract(allocInvoiceAccounted.abs()); + else + acctDifference = allocInvoiceAccounted.subtract(totalInvoiceAccounted.abs()); // gain is positive for receipt + + StringBuilder d2 = new StringBuilder("(full) = ").append(acctDifference); + if (log.isLoggable(Level.FINE)) log.fine(d2.toString()); + description.append(" - ").append(d2); + } else{ + MAllocationHdr[] allocations = MAllocationHdr.getOfInvoice(getCtx(), invoice.get_ID(), getTrxName()); + for (MAllocationHdr alloc : allocations) + { + StringBuilder sql = new StringBuilder("SELECT ") + .append(invoice.isSOTrx() + ? "SUM(AmtSourceCr), SUM(AmtAcctCr), SUM(AmtAcctDr)" // so + : "SUM(AmtSourceDr), SUM(AmtAcctDr), SUM(AmtAcctCr)") // po + .append(" FROM Fact_Acct ") + .append("WHERE AD_Table_ID=? AND Record_ID=?") // allocation + .append(" AND C_AcctSchema_ID=?") + .append(" AND PostingType='A'") + .append(" AND Account_ID= ? "); + pstmt = null; + rs = null; + try + { + pstmt = DB.prepareStatement(sql.toString(), getTrxName()); + pstmt.setInt(1, MAllocationHdr.Table_ID); + pstmt.setInt(2, alloc.get_ID()); + pstmt.setInt(3, as.getC_AcctSchema_ID()); + pstmt.setInt(4, bpAcct.getAccount_ID()); + rs = pstmt.executeQuery(); + if (rs.next()) + { + BigDecimal allocateSource = rs.getBigDecimal(1); + BigDecimal allocateAccounted = rs.getBigDecimal(2); + BigDecimal allocateCredit = rs.getBigDecimal(3); + allocInvoiceSource =allocInvoiceSource.add(allocateSource != null ? allocateSource: BigDecimal.ZERO); + allocInvoiceAccounted =allocInvoiceAccounted.add(allocateAccounted != null ? allocateAccounted : BigDecimal.ZERO); + allocInvoiceAccounted= allocInvoiceAccounted.subtract(allocateCredit != null ? allocateCredit : BigDecimal.ZERO); + } + + } + catch (Exception e) + { + throw new RuntimeException(e.getLocalizedMessage(), e); + } + finally { + DB.close(rs, pstmt); + rs = null; pstmt = null; + } + } + double multiplier = allocInvoiceSource.doubleValue() / totalInvoiceSource.doubleValue(); + + // Reduce Orig Invoice Accounted + BigDecimal reduceOrigAccounted = totalInvoiceAccounted.multiply(BigDecimal.valueOf(multiplier)); + if (reduceOrigAccounted.compareTo(totalInvoiceAccounted) < 0 ) + totalInvoiceAccounted = reduceOrigAccounted; + if (isExcludeCMGainLoss) + allocInvoiceAccounted = allocInvoiceAccounted.add(cmGainLossAmt); + + allocInvoiceAccounted = allocInvoiceAccounted.add(gainLossAmt); + + // Difference based on percentage of Orig Invoice + acctDifference = allocInvoiceAccounted.subtract(totalInvoiceAccounted); + // ignore Tolerance + if (acctDifference.abs().compareTo(BigDecimal.valueOf(0.01)) < 0) + acctDifference = Env.ZERO; + + // Round + int precision = as.getStdPrecision(); + if (acctDifference.scale() > precision) + acctDifference = acctDifference.setScale(precision, RoundingMode.HALF_UP); + StringBuilder d2 = new StringBuilder("(partial) = ").append(acctDifference).append(" - Multiplier=").append(multiplier); + if (log.isLoggable(Level.FINE)) log.fine(d2.toString()); + description.append(" - ").append(d2); + } + + if (acctDifference.signum() == 0) + { + log.fine("No Difference"); + return null; + } + + MAccount gain = MAccount.get (as.getCtx(), as.getAcctSchemaDefault().getRealizedGain_Acct()); + MAccount loss = MAccount.get (as.getCtx(), as.getAcctSchemaDefault().getRealizedLoss_Acct()); + // + if (acctDifference.abs().compareTo(TOLERANCE) <= 0) + { + if (invoice.isSOTrx()) + { + FactLine fl = null; + if (!isBPartnerAdjust) + fl = fact.createLine (null, as.getCurrencyBalancing_Acct(),as.getC_Currency_ID(), acctDifference); + else + fl = fact.createLine (null, bpAcct,as.getC_Currency_ID(), acctDifference); + fl.setDescription(description.toString()); + + if (!fact.isAcctBalanced()) + { + if (as.isCurrencyBalancing() && as.getC_Currency_ID() != invoice.getC_Currency_ID() ) + { + fact.createLine (null, as.getCurrencyBalancing_Acct(),as.getC_Currency_ID(), acctDifference.negate()); + } else + { + fl = fact.createLine (null, loss, gain,as.getC_Currency_ID(), acctDifference.negate()); + + } + } + }else + { + FactLine fl = null; + if (!isBPartnerAdjust) + fl = fact.createLine (null, as.getCurrencyBalancing_Acct(),as.getC_Currency_ID(), acctDifference.negate()); + else + fl = fact.createLine (null, bpAcct,as.getC_Currency_ID(), acctDifference.negate()); + + fl.setDescription(description.toString()); + if (!fact.isAcctBalanced()) + { + if (as.isCurrencyBalancing() && as.getC_Currency_ID() != invoice.getC_Currency_ID() ) + { + fact.createLine (null, as.getCurrencyBalancing_Acct(),as.getC_Currency_ID(), acctDifference); + } else { + fact.createLine (null, loss, gain, as.getC_Currency_ID(), acctDifference); + } + } + } + } + return null; + + } // createInvoiceRounding + } // Doc_Allocation /** diff --git a/org.adempiere.base/src/org/compiere/acct/Doc_MatchInv.java b/org.adempiere.base/src/org/compiere/acct/Doc_MatchInv.java index 6dfa0467e1..bb1354956f 100644 --- a/org.adempiere.base/src/org/compiere/acct/Doc_MatchInv.java +++ b/org.adempiere.base/src/org/compiere/acct/Doc_MatchInv.java @@ -24,6 +24,7 @@ import java.sql.Savepoint; import java.sql.Timestamp; import java.util.ArrayList; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import java.util.logging.Level; @@ -43,6 +44,7 @@ import org.compiere.model.MMatchInv; import org.compiere.model.MOrderLandedCostAllocation; import org.compiere.model.ProductCost; import org.compiere.model.X_M_Cost; +import org.compiere.util.DB; import org.compiere.util.Env; import org.compiere.util.Trx; @@ -286,6 +288,21 @@ public class Doc_MatchInv extends Doc cr.setQty(getQty().multiply(multiplier).negate()); } } + + // Rounding correction + if (m_receiptLine != null && m_invoiceLine.getParent().getC_Currency_ID() != as.getC_Currency_ID()) // in foreign currency + { + p_Error = createReceiptGainLoss(as, fact, getAccount(Doc.ACCTTYPE_NotInvoicedReceipts, as), m_receiptLine.getParent(), dr.getAmtSourceDr(), dr.getAmtAcctDr()); + if (p_Error != null) + return null; + } + if (m_invoiceLine != null && m_invoiceLine.getParent().getC_Currency_ID() != as.getC_Currency_ID()) // in foreign currency + { + p_Error = createInvoiceGainLoss(as, fact, expense, m_invoiceLine.getParent(), cr.getAmtSourceCr(), cr.getAmtAcctCr()); + if (p_Error != null) + return null; + } + if (m_matchInv.getReversal_ID() == 0) { cr.setC_Activity_ID(m_invoiceLine.getC_Activity_ID()); @@ -831,4 +848,361 @@ public class Doc_MatchInv extends Doc factLine.setM_Product_ID(m_invoiceLine.getM_Product_ID()); factLine.setQty(getQty()); } + + private String createInvoiceGainLoss(MAcctSchema as, Fact fact, MAccount acct, + MInvoice invoice, BigDecimal matchInvSource, BigDecimal matchInvAccounted) + { + BigDecimal invoiceSource = null; + BigDecimal invoiceAccounted = null; + // + StringBuilder sql = new StringBuilder() + .append("SELECT SUM(AmtSourceDr), SUM(AmtAcctDr), SUM(AmtSourceCr), SUM(AmtAcctCr)") + .append(" FROM Fact_Acct ") + .append("WHERE AD_Table_ID=? AND Record_ID=?") + .append(" AND C_AcctSchema_ID=?") + .append(" AND Account_ID=?") + .append(" AND PostingType='A'"); + + // For Invoice + List valuesInv = DB.getSQLValueObjectsEx(getTrxName(), sql.toString(), + MInvoice.Table_ID, invoice.getC_Invoice_ID(), as.getC_AcctSchema_ID(), acct.getAccount_ID()); + if (valuesInv != null) { + invoiceSource = (BigDecimal) valuesInv.get(0); // AmtSourceDr + invoiceAccounted = (BigDecimal) valuesInv.get(1); // AmtAcctDr + if (invoiceSource.signum() == 0 && invoiceAccounted.signum() == 0) { + invoiceSource = (BigDecimal) valuesInv.get(2); // AmtSourceCr + invoiceAccounted = (BigDecimal) valuesInv.get(3); // AmtAcctCr + } + } + + // Requires that Invoice is Posted + if (invoiceSource == null || invoiceAccounted == null) + return null; + // + + if (m_matchInv.getReversal_ID() == 0 || m_matchInv.get_ID() < m_matchInv.getReversal_ID()) + { + String matchInvLineSql = "SELECT M_MatchInv_ID FROM M_MatchInv " + + "WHERE C_InvoiceLine_ID IN (SELECT C_InvoiceLine_ID FROM C_InvoiceLine WHERE C_Invoice_ID=?) " + + "AND COALESCE(Reversal_ID,0)=0"; + List> list = DB.getSQLArrayObjectsEx(getTrxName(), matchInvLineSql, invoice.get_ID()); + StringBuffer s = new StringBuffer(); + + if (list == null) + return null; + + for (int index=0; index < list.size(); index++) + { + List l = list.get(index); + s.append(l.get(0)); + if (index != list.size()-1) + s.append(","); + } + + sql = new StringBuilder() + .append("SELECT SUM(AmtSourceDr), SUM(AmtAcctDr), SUM(AmtSourceCr), SUM(AmtAcctCr)") + .append(" FROM Fact_Acct ") + .append("WHERE AD_Table_ID=? AND Record_ID IN (").append(s).append(")") + .append(" AND Record_ID <> ?") + .append(" AND C_AcctSchema_ID=?") + .append(" AND Account_ID=?") + .append(" AND PostingType='A'"); + } + else + { + sql = new StringBuilder() + .append("SELECT SUM(AmtSourceDr), SUM(AmtAcctDr), SUM(AmtSourceCr), SUM(AmtAcctCr)") + .append(" FROM Fact_Acct ") + .append("WHERE AD_Table_ID=? AND Record_ID IN (").append(m_matchInv.getReversal_ID()).append(")") + .append(" AND Record_ID <> ?") + .append(" AND C_AcctSchema_ID=?") + .append(" AND Account_ID=?") + .append(" AND PostingType='A'"); + } + + BigDecimal acctDifference = null; // gain is negative + // For Match Invoice + valuesInv = DB.getSQLValueObjectsEx(getTrxName(), sql.toString(), + MMatchInv.Table_ID, get_ID(), as.getC_AcctSchema_ID(), acct.getAccount_ID()); + if (valuesInv != null) + { + BigDecimal totalAmtSourceDr = (BigDecimal) valuesInv.get(0); + if (totalAmtSourceDr == null) + totalAmtSourceDr = Env.ZERO; + BigDecimal totalAmtAcctDr = (BigDecimal) valuesInv.get(1); + if (totalAmtAcctDr == null) + totalAmtAcctDr = Env.ZERO; + BigDecimal totalAmtSourceCr = (BigDecimal) valuesInv.get(2); + if (totalAmtSourceCr == null) + totalAmtSourceCr = Env.ZERO; + BigDecimal totalAmtAcctCr = (BigDecimal) valuesInv.get(3); + if (totalAmtAcctCr == null) + totalAmtAcctCr = Env.ZERO; + + if (totalAmtSourceDr.signum() == 0 && totalAmtAcctDr.signum() == 0) + { + matchInvSource = matchInvSource.add(totalAmtSourceCr); + matchInvAccounted = matchInvAccounted.add(totalAmtAcctCr); + } + else if (totalAmtSourceCr.signum() == 0 && totalAmtAcctCr.signum() == 0) + { + matchInvSource = matchInvSource.add(totalAmtSourceDr); + matchInvAccounted = matchInvAccounted.add(totalAmtAcctDr); + } + else + { + if (totalAmtAcctDr.compareTo(totalAmtAcctCr) > 0) + { + matchInvSource = matchInvSource.add(totalAmtSourceDr); + matchInvAccounted = matchInvAccounted.add(totalAmtAcctDr); + acctDifference = totalAmtAcctCr.negate(); + } + else + { + matchInvSource = matchInvSource.add(totalAmtSourceCr); + matchInvAccounted = matchInvAccounted.add(totalAmtAcctCr); + acctDifference = totalAmtSourceDr.negate(); + } + } + } + + StringBuilder description = new StringBuilder("Invoice=(").append(invoice.getC_Currency_ID()).append(")").append(invoiceSource).append("/").append(invoiceAccounted) + .append(" - MatchInv=(").append(getC_Currency_ID()).append(")").append(matchInvSource).append("/").append(matchInvAccounted); + if (log.isLoggable(Level.FINE)) log.fine(description.toString()); + // + + // Full Payment in currency + if (acctDifference == null && matchInvSource.compareTo(invoiceSource) == 0) + { + acctDifference = matchInvAccounted.subtract(invoiceAccounted.abs()); // gain is negative + StringBuilder d2 = new StringBuilder("(full) = ").append(acctDifference); + if (log.isLoggable(Level.FINE)) log.fine(d2.toString()); + description.append(" - ").append(d2); + } + + if (acctDifference == null || acctDifference.signum() == 0) + { + log.fine("No Difference"); + return null; + } + + MAccount gain = MAccount.get (as.getCtx(), as.getAcctSchemaDefault().getRealizedGain_Acct()); + MAccount loss = MAccount.get (as.getCtx(), as.getAcctSchemaDefault().getRealizedLoss_Acct()); + // + if (invoice.isSOTrx()) + { + FactLine fl = fact.createLine (null, acct, as.getC_Currency_ID(), acctDifference.negate()); + fl.setDescription(description.toString()); + updateFactLine(fl); + + if (as.isCurrencyBalancing() && as.getC_Currency_ID() != invoice.getC_Currency_ID()) { + fl = fact.createLine (null, as.getCurrencyBalancing_Acct(), as.getC_Currency_ID(), acctDifference); + } else { + fl = fact.createLine (null, loss, gain, as.getC_Currency_ID(), acctDifference); + } + fl.setDescription(description.toString()); + updateFactLine(fl); + } + else + { + FactLine fl = fact.createLine (null, acct, as.getC_Currency_ID(), acctDifference); + fl.setDescription(description.toString()); + updateFactLine(fl); + + if (as.isCurrencyBalancing() && as.getC_Currency_ID() != invoice.getC_Currency_ID()) { + fl = fact.createLine (null, as.getCurrencyBalancing_Acct(), as.getC_Currency_ID(), acctDifference.negate()); + } else { + fl = fact.createLine (null, loss, gain, as.getC_Currency_ID(), acctDifference.negate()); + } + fl.setDescription(description.toString()); + updateFactLine(fl); + } + return null; + } // createInvoiceGainLoss + + private String createReceiptGainLoss(MAcctSchema as, Fact fact, MAccount acct, + MInOut receipt, BigDecimal matchInvSource, BigDecimal matchInvAccounted) + { + BigDecimal receiptSource = null; + BigDecimal receiptAccounted = null; + // + StringBuilder sql = new StringBuilder() + .append("SELECT SUM(AmtSourceDr), SUM(AmtAcctDr), SUM(AmtSourceCr), SUM(AmtAcctCr)") + .append(" FROM Fact_Acct ") + .append("WHERE AD_Table_ID=? AND Record_ID=?") + .append(" AND C_AcctSchema_ID=?") + .append(" AND Account_ID=?") + .append(" AND PostingType='A'"); + + // For Receipt + List valuesInv = DB.getSQLValueObjectsEx(getTrxName(), sql.toString(), + MInOut.Table_ID, receipt.getM_InOut_ID(), as.getC_AcctSchema_ID(), acct.getAccount_ID()); + if (valuesInv != null) { + receiptSource = (BigDecimal) valuesInv.get(0); // AmtSourceDr + receiptAccounted = (BigDecimal) valuesInv.get(1); // AmtAcctDr + if (receiptSource.signum() == 0 && receiptAccounted.signum() == 0) { + receiptSource = (BigDecimal) valuesInv.get(2); // AmtSourceCr + receiptAccounted = (BigDecimal) valuesInv.get(3); // AmtAcctCr + } + } + + // Requires that Receipt is Posted + if (receiptSource == null || receiptAccounted == null) + return null; + // + if (m_matchInv.getReversal_ID() == 0 || m_matchInv.get_ID() < m_matchInv.getReversal_ID()) + { + String matchInvLineSql = "SELECT M_MatchInv_ID FROM M_MatchInv " + + "WHERE M_InOutLine_ID IN (SELECT M_InOutLine_ID FROM M_InOutLine WHERE M_InOut_ID=?) " + + "AND COALESCE(Reversal_ID,0)=0"; + List> list = DB.getSQLArrayObjectsEx(getTrxName(), matchInvLineSql, receipt.get_ID()); + StringBuffer s = new StringBuffer(); + + if (list == null) + return null; + + for (int index=0; index < list.size(); index++) + { + List l = list.get(index); + s.append(l.get(0)); + if (index != list.size()-1) + s.append(","); + } + + + sql = new StringBuilder() + .append("SELECT SUM(AmtSourceDr), SUM(AmtAcctDr), SUM(AmtSourceCr), SUM(AmtAcctCr)") + .append(" FROM Fact_Acct ") + .append("WHERE AD_Table_ID=? AND Record_ID IN (").append(s).append(")") + .append(" AND Record_ID <> ?") + .append(" AND C_AcctSchema_ID=?") + .append(" AND Account_ID=?") + .append(" AND PostingType='A'"); + } + else + { + sql = new StringBuilder() + .append("SELECT SUM(AmtSourceDr), SUM(AmtAcctDr), SUM(AmtSourceCr), SUM(AmtAcctCr)") + .append(" FROM Fact_Acct ") + .append("WHERE AD_Table_ID=? AND Record_ID IN (").append(m_matchInv.getReversal_ID()).append(")") + .append(" AND Record_ID <> ?") + .append(" AND C_AcctSchema_ID=?") + .append(" AND Account_ID=?") + .append(" AND PostingType='A'"); + } + + BigDecimal acctDifference = null; // gain is negative + // For Match Invoice + valuesInv = DB.getSQLValueObjectsEx(getTrxName(), sql.toString(), + MMatchInv.Table_ID, get_ID(), as.getC_AcctSchema_ID(), acct.getAccount_ID()); + if (valuesInv != null) + { + BigDecimal totalAmtSourceDr = (BigDecimal) valuesInv.get(0); + if (totalAmtSourceDr == null) + totalAmtSourceDr = Env.ZERO; + BigDecimal totalAmtAcctDr = (BigDecimal) valuesInv.get(1); + if (totalAmtAcctDr == null) + totalAmtAcctDr = Env.ZERO; + BigDecimal totalAmtSourceCr = (BigDecimal) valuesInv.get(2); + if (totalAmtSourceCr == null) + totalAmtSourceCr = Env.ZERO; + BigDecimal totalAmtAcctCr = (BigDecimal) valuesInv.get(3); + if (totalAmtAcctCr == null) + totalAmtAcctCr = Env.ZERO; + + if (totalAmtSourceDr.signum() == 0 && totalAmtAcctDr.signum() == 0) + { + matchInvSource = matchInvSource.add(totalAmtSourceCr); + matchInvAccounted = matchInvAccounted.add(totalAmtAcctCr); + } + else if (totalAmtSourceCr.signum() == 0 && totalAmtAcctCr.signum() == 0) + { + matchInvSource = matchInvSource.add(totalAmtSourceDr); + matchInvAccounted = matchInvAccounted.add(totalAmtAcctDr); + } + else + { + if (m_matchInv.getReversal_ID() == 0 || m_matchInv.get_ID() < m_matchInv.getReversal_ID()) + { + if (totalAmtAcctDr.compareTo(totalAmtAcctCr) > 0) + { + matchInvSource = matchInvSource.add(totalAmtSourceDr); + matchInvAccounted = matchInvAccounted.add(totalAmtAcctDr).subtract(totalAmtAcctCr); + } + else + { + matchInvSource = matchInvSource.add(totalAmtSourceCr); + matchInvAccounted = matchInvAccounted.add(totalAmtAcctCr).subtract(totalAmtAcctDr); + } + } + else + { + if (totalAmtAcctDr.compareTo(totalAmtAcctCr) > 0) + { + matchInvSource = matchInvSource.add(totalAmtSourceDr); + matchInvAccounted = matchInvAccounted.add(totalAmtAcctDr); + acctDifference = totalAmtAcctCr.negate(); + } + else + { + matchInvSource = matchInvSource.add(totalAmtSourceCr); + matchInvAccounted = matchInvAccounted.add(totalAmtAcctCr); + acctDifference = totalAmtAcctDr.negate(); + } + } + } + } + + StringBuilder description = new StringBuilder("InOut=(").append(m_invoiceLine.getParent().getC_Currency_ID()).append(")").append(receiptSource).append("/").append(receiptAccounted) + .append(" - MatchInv=(").append(getC_Currency_ID()).append(")").append(matchInvSource).append("/").append(matchInvAccounted); + if (log.isLoggable(Level.FINE)) log.fine(description.toString()); + + // Full Payment in currency + if (acctDifference == null && matchInvSource.compareTo(receiptSource) == 0) + { + acctDifference = matchInvAccounted.subtract(receiptAccounted.abs()); // gain is negative + StringBuilder d2 = new StringBuilder("(full) = ").append(acctDifference); + if (log.isLoggable(Level.FINE)) log.fine(d2.toString()); + description.append(" - ").append(d2); + } + + if (acctDifference == null || acctDifference.signum() == 0) + { + log.fine("No Difference"); + return null; + } + + MAccount gain = MAccount.get (as.getCtx(), as.getAcctSchemaDefault().getRealizedGain_Acct()); + MAccount loss = MAccount.get (as.getCtx(), as.getAcctSchemaDefault().getRealizedLoss_Acct()); + // + if (!receipt.isSOTrx()) + { + FactLine fl = fact.createLine (null, acct, as.getC_Currency_ID(), acctDifference.negate()); + fl.setDescription(description.toString()); + updateFactLine(fl); + + if (as.isCurrencyBalancing() && as.getC_Currency_ID() != m_invoiceLine.getParent().getC_Currency_ID()) { + fl = fact.createLine (null, as.getCurrencyBalancing_Acct(), as.getC_Currency_ID(), acctDifference); + } else { + fl = fact.createLine (null, loss, gain, as.getC_Currency_ID(), acctDifference); + } + fl.setDescription(description.toString()); + updateFactLine(fl); + } + else + { + FactLine fl = fact.createLine (null, acct, as.getC_Currency_ID(), acctDifference); + fl.setDescription(description.toString()); + updateFactLine(fl); + + if (as.isCurrencyBalancing() && as.getC_Currency_ID() != m_invoiceLine.getParent().getC_Currency_ID()) { + fl = fact.createLine (null, as.getCurrencyBalancing_Acct(), as.getC_Currency_ID(), acctDifference.negate()); + } else { + fl = fact.createLine (null, loss, gain, as.getC_Currency_ID(), acctDifference.negate()); + } + fl.setDescription(description.toString()); + updateFactLine(fl); + } + return null; + } // createReceiptGainLoss } // Doc_MatchInv diff --git a/org.adempiere.base/src/org/compiere/acct/FactLine.java b/org.adempiere.base/src/org/compiere/acct/FactLine.java index c888575670..f690e30704 100644 --- a/org.adempiere.base/src/org/compiere/acct/FactLine.java +++ b/org.adempiere.base/src/org/compiere/acct/FactLine.java @@ -1124,6 +1124,8 @@ public final class FactLine extends X_Fact_Acct if (MMovement.Table_ID == AD_Table_ID) sql.append(" AND M_Locator_ID=?"); // end MZ + sql.append(" ORDER BY Fact_Acct_ID "); + PreparedStatement pstmt = null; ResultSet rs = null; try diff --git a/org.adempiere.base/src/org/compiere/model/GridTable.java b/org.adempiere.base/src/org/compiere/model/GridTable.java index 1570fed909..f7a8da5558 100644 --- a/org.adempiere.base/src/org/compiere/model/GridTable.java +++ b/org.adempiere.base/src/org/compiere/model/GridTable.java @@ -104,7 +104,7 @@ public class GridTable extends AbstractTableModel /** * */ - private static final long serialVersionUID = -2741647620577906242L; + private static final long serialVersionUID = 817894725729408648L; public static final String DATA_REFRESH_MESSAGE = "Refreshed"; public static final String DATA_UPDATE_COPIED_MESSAGE = "UpdateCopied"; @@ -3499,7 +3499,7 @@ public class GridTable extends AbstractTableModel /** * */ - private static final long serialVersionUID = -8735217685095696892L; + private static final long serialVersionUID = -6866671239509705988L; /** * Construct Loader diff --git a/org.adempiere.base/src/org/compiere/model/MPayment.java b/org.adempiere.base/src/org/compiere/model/MPayment.java index f5958eef39..4f1422a260 100644 --- a/org.adempiere.base/src/org/compiere/model/MPayment.java +++ b/org.adempiere.base/src/org/compiere/model/MPayment.java @@ -726,10 +726,20 @@ public class MPayment extends X_C_Payment if (newRecord || is_ValueChanged("C_Charge_ID") || is_ValueChanged("C_Invoice_ID") || is_ValueChanged("C_Order_ID") || is_ValueChanged("C_Project_ID")) - setIsPrepayment (getC_Charge_ID() == 0 - && getC_BPartner_ID() != 0 - && (getC_Order_ID() != 0 - || (getC_Project_ID() != 0 && getC_Invoice_ID() == 0))); + { + if (getReversal_ID() > 0) + { + setIsPrepayment(getReversal().isPrepayment()); + } + else + { + setIsPrepayment (getC_Charge_ID() == 0 + && getC_BPartner_ID() != 0 + && (getC_Order_ID() != 0 + || (getC_Project_ID() != 0 && getC_Invoice_ID() == 0))); + } + } + if (isPrepayment()) { if (newRecord diff --git a/org.adempiere.base/src/org/compiere/util/CLogger.java b/org.adempiere.base/src/org/compiere/util/CLogger.java index 6c1837f600..a9e2278da9 100644 --- a/org.adempiere.base/src/org/compiere/util/CLogger.java +++ b/org.adempiere.base/src/org/compiere/util/CLogger.java @@ -241,6 +241,19 @@ public class CLogger extends Logger return true; } // saveWarning + /** + * Get Warning message from stack + * @param defaultMsg default message (used when there are no warnings on stack) + * @return error message, or defaultMsg if there is not error message saved + * @see #retrieveError() + */ + public static String retrieveWarningString(String defaultMsg) { + ValueNamePair vp = retrieveWarning(); + if (vp == null) + return defaultMsg; + return vp.getName(); + } + /** * Get Warning from Stack * @return AD_Message as Value and Message as String diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/ADTabpanel.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/ADTabpanel.java index 3224c1c5cc..c3ae8236d8 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/ADTabpanel.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/ADTabpanel.java @@ -1653,7 +1653,7 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer if (listPanel.isVisible()) { listPanel.refresh(gridTab); listPanel.scrollToCurrentRow(); - Clients.resize(listPanel.getListbox()); + listPanel.getListbox().invalidate(); } else { listPanel.deactivate(); } @@ -1880,7 +1880,11 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer continue; } if (found) { - if (editor.isVisible() && editor.isReadWrite()) { + if (editor.isVisible() && editor.isReadWrite() + // note, no auto focus on next button - if interested in + // focusing next button must implement to check if the button + // is just showin in toolbar, just focus on window fields must be auto focused + && ! (editor instanceof WButtonEditor)) { focusToEditor(editor, false); break; } diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/ADWindowToolbar.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/ADWindowToolbar.java index 8956ddf430..be9ee25222 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/ADWindowToolbar.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/ADWindowToolbar.java @@ -83,9 +83,11 @@ public class ADWindowToolbar extends FToolbar implements EventListener /** * */ - private static final long serialVersionUID = -9183846974546235806L; + private static final long serialVersionUID = 2945672260455902597L; public static final String BTNPREFIX = "Btn"; + + public static final String MNITMPREFIX = "Mnitm"; private static final CLogger log = CLogger.getCLogger(ADWindowToolbar.class); @@ -274,7 +276,6 @@ public class ADWindowToolbar extends FToolbar implements EventListener tooltipKey = null; } ToolBarButton btn = createButton(button.getComponentName(), null, tooltipKey); - this.appendChild(btn); btn.removeEventListener(Events.ON_CLICK, this); btn.setId(button.getName()); btn.setDisabled(false); @@ -291,9 +292,7 @@ public class ADWindowToolbar extends FToolbar implements EventListener if (ClientInfo.isMobile() && button.isShowMore()) mobileShowMoreButtons.add(btn); - else if (button.isShowMore()) - createMenuitem(btn); - else { + else if (!button.isShowMore()) { this.appendChild(btn); action.decorate(btn); } @@ -371,12 +370,15 @@ public class ADWindowToolbar extends FToolbar implements EventListener Menuitem item = new Menuitem(button.getTooltiptext()); if (button.getImage() != null) item.setImage(button.getImage()); + else if (button.getImageContent() != null) + item.setImageContent(button.getImageContent()); else if (ThemeManager.isUseFontIconForImage()) { item.setIconSclass(button.getIconSclass()); LayoutUtils.addSclass("font-icon-toolbar-button", item); } + item.setId(MNITMPREFIX+button.getName()); item.setValue(button.getName()); - item.addEventListener(Events.ON_CLICK, evt -> doOnClick(new Event(Events.ON_CLICK, button))); + item.addEventListener(Events.ON_CLICK, evt -> Events.sendEvent(new Event(Events.ON_CLICK, button))); menupopup.appendChild(item); menuItems.put(button, item); return item; @@ -905,8 +907,10 @@ public class ADWindowToolbar extends FToolbar implements EventListener public void dynamicDisplay() { List customButtons = new ArrayList(); for(ToolbarCustomButton toolbarCustomBtn : toolbarCustomButtons) { - toolbarCustomBtn.dynamicDisplay(); + toolbarCustomBtn.dynamicDisplay(menuItems.get(toolbarCustomBtn.getToolbarbutton()) != null); customButtons.add(toolbarCustomBtn.getToolbarbutton()); + if (menuItems.get(toolbarCustomBtn.getToolbarbutton()) != null) + menuItems.get(toolbarCustomBtn.getToolbarbutton()).setVisible(toolbarCustomBtn.getToolbarbutton().isVisible()); } ADWindow adwindow = ADWindow.findADWindow(this); diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/AbstractADWindowContent.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/AbstractADWindowContent.java index ea38b7dbdc..d8b5d1c96e 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/AbstractADWindowContent.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/AbstractADWindowContent.java @@ -2016,16 +2016,20 @@ public abstract class AbstractADWindowContent extends AbstractUIPart implements return; clearTitleRelatedContext(); - - onSave(false, false, new Callback() { - - @Override - public void onCallback(Boolean result) { - if (result) { - doOnFind(); - } - } - }); + + // The record was not changed locally + if (adTabbox.getDirtyADTabpanel() == null) { + doOnFind(); + } else { + onSave(false, false, new Callback() { + @Override + public void onCallback(Boolean result) { + if (result) { + doOnFind(); + } + } + }); + } } private void doOnFind() { @@ -2156,11 +2160,15 @@ public abstract class AbstractADWindowContent extends AbstractUIPart implements if (dirtyTabpanel != null) { focusToTabpanel(dirtyTabpanel); //ensure row indicator is not lost - RowRenderer renderer = dirtyTabpanel.getGridView().getListbox().getRowRenderer(); - GridTabRowRenderer gtr = (GridTabRowRenderer)renderer; - org.zkoss.zul.Row row = gtr.getCurrentRow(); - if (row != null) - gtr.setCurrentRow(row); + if (dirtyTabpanel.getGridView() != null && + dirtyTabpanel.getGridView().getListbox() != null && + dirtyTabpanel.getGridView().getListbox().getRowRenderer() != null) { + RowRenderer renderer = dirtyTabpanel.getGridView().getListbox().getRowRenderer(); + GridTabRowRenderer gtr = (GridTabRowRenderer)renderer; + org.zkoss.zul.Row row = gtr.getCurrentRow(); + if (row != null) + gtr.setCurrentRow(row); + } } else focusToActivePanel(); @@ -2379,6 +2387,14 @@ public abstract class AbstractADWindowContent extends AbstractUIPart implements //other error will be catch in the dataStatusChanged event } + private void showLastWarning() { + String msg = CLogger.retrieveWarningString(null); + if (msg != null) + { + statusBar.setStatusLine(Msg.getMsg(Env.getCtx(), msg), true); + } + } + /** * @see ToolbarListener#onSaveCreate() */ @@ -2458,9 +2474,11 @@ public abstract class AbstractADWindowContent extends AbstractUIPart implements if (result) { //error will be catch in the dataStatusChanged event - adTabbox.getSelectedGridTab().dataDelete(); + boolean success = adTabbox.getSelectedGridTab().dataDelete(); adTabbox.getSelectedGridTab().dataRefreshAll(true, true); adTabbox.getSelectedGridTab().refreshParentTabs(); + if (!success) + showLastWarning(); adTabbox.getSelectedTabpanel().dynamicDisplay(0); focusToActivePanel(); diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/GridView.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/GridView.java index 5d1671a4c5..658176a51b 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/GridView.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/GridView.java @@ -356,7 +356,7 @@ public class GridView extends Vlayout implements EventListener, IdSpace, showRecordsCount(); } if (this.isVisible()) - Clients.resize(listbox); + listbox.invalidate(); } /** @@ -729,7 +729,7 @@ public class GridView extends Vlayout implements EventListener, IdSpace, listModel.setPage(pgNo); onSelectedRowChange(0); gridTab.clearSelection(); - Clients.resize(listbox); + listbox.invalidate(); } } else if (event.getTarget() == selectAll) @@ -1121,7 +1121,7 @@ public class GridView extends Vlayout implements EventListener, IdSpace, refresh(gridTab); scrollToCurrentRow(); - Clients.resize(listbox); + listbox.invalidate(); } /** diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/ToolbarCustomButton.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/ToolbarCustomButton.java index 67ff617a1a..bcefed242a 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/ToolbarCustomButton.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/ToolbarCustomButton.java @@ -70,7 +70,11 @@ public class ToolbarCustomButton implements EventListener, Evaluatee { } public void dynamicDisplay() { - if (toolbarButton.getParent() == null) + dynamicDisplay(false); + } + + public void dynamicDisplay(boolean forceValidation) { + if (toolbarButton.getParent() == null && !forceValidation) return; String displayLogic = mToolbarButton.getDisplayLogic(); diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/editor/WEditor.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/editor/WEditor.java index c166a23aeb..986861ec66 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/editor/WEditor.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/editor/WEditor.java @@ -26,6 +26,7 @@ import java.util.Properties; import org.adempiere.webui.AdempiereWebUI; import org.adempiere.webui.ClientInfo; import org.adempiere.webui.LayoutUtils; +import org.adempiere.webui.adwindow.IFieldEditorContainer; import org.adempiere.webui.component.Bandbox; import org.adempiere.webui.component.Button; import org.adempiere.webui.component.Datebox; @@ -813,6 +814,18 @@ public abstract class WEditor implements EventListener, PropertyChangeLis return null; } + protected void focusNext() { + Component comp = getComponent(); + Component parent = comp.getParent(); + while (parent != null) { + if (parent instanceof IFieldEditorContainer) { + ((IFieldEditorContainer) parent).focusToNextEditor(this); + break; + } + parent = parent.getParent(); + } + } + protected Evaluatee getStyleEvaluatee() { return new EvaluateeWrapper(this, gridField, tableEditor); } diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/editor/WPaymentEditor.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/editor/WPaymentEditor.java index 46eea1504e..92f929a264 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/editor/WPaymentEditor.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/editor/WPaymentEditor.java @@ -278,6 +278,8 @@ public class WPaymentEditor extends WEditor implements ListDataListener, Context super.fireValueChange(changeEvent); oldValue = newValue; } + if (newValue != null) + focusNext(); } else if (Events.ON_BLUR.equalsIgnoreCase(event.getName())) { diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/editor/WSearchEditor.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/editor/WSearchEditor.java index 9dea1829aa..ff6b9d087b 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/editor/WSearchEditor.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/editor/WSearchEditor.java @@ -29,7 +29,6 @@ import org.adempiere.webui.LayoutUtils; import org.adempiere.webui.ValuePreference; import org.adempiere.webui.adwindow.ADWindow; import org.adempiere.webui.adwindow.ADWindowContent; -import org.adempiere.webui.adwindow.IFieldEditorContainer; import org.adempiere.webui.apps.AEnv; import org.adempiere.webui.component.Searchbox; import org.adempiere.webui.event.ContextMenuEvent; @@ -390,17 +389,8 @@ public class WSearchEditor extends WEditor implements ContextMenuListener, Value log.fine(getColumnName() + " - Unique ID=" + id); actionCombo(Integer.valueOf(id)); // data binding - - Searchbox comp = getComponent(); - Component parent = comp.getParent(); - while (parent != null) { - if (parent instanceof IFieldEditorContainer) { - ((IFieldEditorContainer) parent).focusToNextEditor(this); - break; - } - parent = parent.getParent(); - } - + focusNext(); + //safety check: if focus is going no where, focus back to self String uid = getComponent().getTextbox().getUuid(); String script = "setTimeout(function(){try{var e = zk.Widget.$('#" + uid + @@ -599,6 +589,7 @@ public class WSearchEditor extends WEditor implements ContextMenuListener, Value actionCombo (result); else actionCombo (result[0]); + focusNext(); } else if (cancelled) { @@ -612,13 +603,14 @@ public class WSearchEditor extends WEditor implements ContextMenuListener, Value }else{ getComponent().setText(""); actionCombo(null); - } + } + getComponent().getTextbox().focus(); } else { if (log.isLoggable(Level.CONFIG)) log.config(getColumnName() + " - Result = null (not cancelled)"); + getComponent().getTextbox().focus(); } - getComponent().getTextbox().focus(); } }); ip.setId(ip.getTitle()+"_"+ip.getWindowNo()); diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/editor/WTableDirEditor.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/editor/WTableDirEditor.java index ca27170704..cd2cd803c4 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/editor/WTableDirEditor.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/editor/WTableDirEditor.java @@ -507,6 +507,8 @@ ContextMenuListener, IZoomableEditor gridField.setLookupEditorSettingValue(false); } } + if (newValue != null) + focusNext(); } finally { onselecting = false; }