# HG changeset patch
# User Ranjith S R <ranjith@qualiantech.com>
# Date 1501767946 -19800
#      Thu Aug 03 19:15:46 2017 +0530
# Node ID 2fab7e0cb8008b6481197a6aa37c9f58fc70426d
# Parent  9ed009c5b16d9a35b8291e87294d0bca48d6602d
Issue 35571 - 3.0RR17Q1 - Split Payment Schedule based on Payment terms lines

diff -r 9ed009c5b16d -r 2fab7e0cb800 src/org/openbravo/retail/posterminal/OrderLoader.java
--- a/src/org/openbravo/retail/posterminal/OrderLoader.java	Tue Mar 14 05:08:42 2017 +0000
+++ b/src/org/openbravo/retail/posterminal/OrderLoader.java	Thu Aug 03 19:15:46 2017 +0530
@@ -91,6 +91,7 @@
 import org.openbravo.model.financialmgmt.payment.FIN_PaymentScheduleDetail;
 import org.openbravo.model.financialmgmt.payment.Fin_OrigPaymentSchedule;
 import org.openbravo.model.financialmgmt.payment.PaymentTerm;
+import org.openbravo.model.financialmgmt.payment.PaymentTermLine;
 import org.openbravo.model.financialmgmt.tax.TaxRate;
 import org.openbravo.model.materialmgmt.onhandquantity.StockProposed;
 import org.openbravo.model.materialmgmt.transaction.MaterialTransaction;
@@ -1719,25 +1720,51 @@
     }
   }
 
-  private Date getCalculatedDueDateBasedOnPaymentTerms(Date startingDate, PaymentTerm paymentTerms) {
+  private Date getCalculatedDueDateBasedOnPaymentTerms(Date startingDate, PaymentTerm paymentTerm,
+      PaymentTermLine paymentTermLine) {
     // TODO Take into account the flag "Next business date"
     // TODO Take into account the flag "Fixed due date"
-    long daysToAdd = paymentTerms.getOverduePaymentDaysRule();
-    long MonthOffset = paymentTerms.getOffsetMonthDue();
-    String dayToPay = paymentTerms.getOverduePaymentDayRule();
     Calendar calculatedDueDate = new GregorianCalendar();
     calculatedDueDate.setTime(startingDate);
-    if (MonthOffset > 0) {
-      calculatedDueDate.add(Calendar.MONTH, (int) MonthOffset);
+    calculatedDueDate.set(Calendar.HOUR_OF_DAY, 0);
+    calculatedDueDate.set(Calendar.MINUTE, 0);
+    calculatedDueDate.set(Calendar.SECOND, 0);
+    calculatedDueDate.set(Calendar.MILLISECOND, 0);
+    long daysToAdd, monthOffset, maturityDate1 = 0, maturityDate2 = 0, maturityDate3 = 0;
+    String dayToPay;
+
+    if (paymentTerm != null) {
+      daysToAdd = paymentTerm.getOverduePaymentDaysRule();
+      monthOffset = paymentTerm.getOffsetMonthDue();
+      dayToPay = paymentTerm.getOverduePaymentDayRule();
+      if (paymentTerm.isFixedDueDate()) {
+        maturityDate1 = paymentTerm.getMaturityDate1();
+        maturityDate2 = paymentTerm.getMaturityDate2();
+        maturityDate3 = paymentTerm.getMaturityDate3();
+      }
+    } else if (paymentTermLine != null) {
+      daysToAdd = paymentTermLine.getOverduePaymentDaysRule();
+      monthOffset = paymentTermLine.getOffsetMonthDue() == null ? 0 : paymentTermLine
+          .getOffsetMonthDue();
+      dayToPay = paymentTermLine.getOverduePaymentDayRule();
+      if (paymentTermLine.isFixedDueDate()) {
+        maturityDate1 = paymentTermLine.getMaturityDate1();
+        maturityDate2 = paymentTermLine.getMaturityDate2();
+        maturityDate3 = paymentTermLine.getMaturityDate3();
+      }
+    } else {
+      return calculatedDueDate.getTime();
+    }
+    if (monthOffset > 0) {
+      calculatedDueDate.add(Calendar.MONTH, (int) monthOffset);
     }
     if (daysToAdd > 0) {
       calculatedDueDate.add(Calendar.DATE, (int) daysToAdd);
     }
     // Calculating due date based on "Fixed due date"
-    if (paymentTerms.isFixedDueDate()) {
+    if ((paymentTerm != null && paymentTerm.isFixedDueDate())
+        || (paymentTermLine != null && paymentTermLine.isFixedDueDate())) {
       long dueDateDay = calculatedDueDate.get(Calendar.DAY_OF_MONTH), finalDueDateDay = 0;
-      long maturityDate1 = paymentTerms.getMaturityDate1(), maturityDate2 = paymentTerms
-          .getMaturityDate2(), maturityDate3 = paymentTerms.getMaturityDate3();
       if (maturityDate2 < dueDateDay && maturityDate3 >= dueDateDay) {
         finalDueDateDay = maturityDate3;
       } else if (maturityDate1 < dueDateDay && maturityDate2 >= dueDateDay) {
@@ -1745,12 +1772,20 @@
       } else {
         finalDueDateDay = maturityDate1;
       }
+
+      // Due Date day should be maximum of Month's Last day
+      if (finalDueDateDay == 0) {
+        finalDueDateDay = 1;
+      }
+      if ((int) finalDueDateDay > calculatedDueDate.getActualMaximum(Calendar.DAY_OF_MONTH)) {
+        finalDueDateDay = calculatedDueDate.getActualMaximum(Calendar.DAY_OF_MONTH);
+      }
       calculatedDueDate.set(Calendar.DAY_OF_MONTH, (int) finalDueDateDay);
       if (finalDueDateDay < dueDateDay) {
         calculatedDueDate.add(Calendar.MONTH, 1);
       }
     }
-    if (dayToPay != null && !dayToPay.equals("")) {
+    if (!StringUtils.isEmpty(dayToPay)) {
       // for us: 1 -> Monday
       // for Calendar: 1 -> Sunday
       int dayOfTheWeekToPay = Integer.parseInt(dayToPay);
@@ -1796,11 +1831,12 @@
       FIN_PaymentSchedule paymentSchedule = OBProvider.getInstance().get(FIN_PaymentSchedule.class);
       int pricePrecision = order.getCurrency().getObposPosprecision() == null ? order.getCurrency()
           .getPricePrecision().intValue() : order.getCurrency().getObposPosprecision().intValue();
-      BigDecimal amountPaidWithCredit = BigDecimal.ZERO;
+      BigDecimal amountPaidWithCredit = BigDecimal.ZERO, paymentScheduleCreditAmount = BigDecimal.ZERO;
       if (wasPaidOnCredit) {
         amountPaidWithCredit = (BigDecimal.valueOf(jsonorder.getDouble("gross"))
             .subtract(BigDecimal.valueOf(jsonorder.getDouble("payment")))).setScale(pricePrecision,
             RoundingMode.HALF_UP);
+        paymentScheduleCreditAmount = amountPaidWithCredit;
       }
       if ((!newLayaway && (notpaidLayaway || creditpaidLayaway || fullypaidLayaway))
           || partialpaidLayaway || paidReceipt) {
@@ -1857,8 +1893,94 @@
           // TODO: If the payment terms is configured to work with fractionated payments, we should
           // generate several payment schedules
           if (wasPaidOnCredit) {
+            OBCriteria<PaymentTermLine> lineCriteria = OBDal.getInstance().createCriteria(
+                PaymentTermLine.class);
+            lineCriteria.add(Restrictions.eq(PaymentTermLine.PROPERTY_PAYMENTTERMS,
+                order.getPaymentTerms()));
+            lineCriteria.add(Restrictions.eq(PaymentTermLine.PROPERTY_ACTIVE, true));
+            lineCriteria.addOrderBy(PaymentTermLine.PROPERTY_LINENO, true);
+            List<PaymentTermLine> termLineList = lineCriteria.list();
+            if (termLineList.size() > 0) {
+              BigDecimal pendingCreditAmount = amountPaidWithCredit;
+              BigDecimal paidAmount = amt;
+              BigDecimal grossTotalAmount = BigDecimal.valueOf(jsonorder.getDouble("gross"));
+              for (PaymentTermLine paymentTermLine : termLineList) {
+                if (pendingCreditAmount.compareTo(BigDecimal.ZERO) <= 0) {
+                  break;
+                }
+                BigDecimal paymentAmount = BigDecimal.ZERO;
+                if (paymentTermLine.isExcludeTax()) {
+                  paymentAmount = (order.getSummedLineAmount().multiply(paymentTermLine
+                      .getPercentageDue().divide(new BigDecimal("100")))).setScale(pricePrecision,
+                      RoundingMode.HALF_UP);
+                } else if (!paymentTermLine.isRest()) {
+                  paymentAmount = (grossTotalAmount.multiply(paymentTermLine.getPercentageDue()
+                      .divide(new BigDecimal("100")))).setScale(pricePrecision,
+                      RoundingMode.HALF_UP);
+                } else {
+                  paymentAmount = (pendingCreditAmount.multiply(paymentTermLine.getPercentageDue()
+                      .divide(new BigDecimal("100")))).setScale(pricePrecision,
+                      RoundingMode.HALF_UP);
+                }
+
+                if (paidAmount.compareTo(BigDecimal.ZERO) > 0) {
+                  if (paymentAmount.compareTo(paidAmount) > 0) {
+                    paymentAmount = paymentAmount.subtract(paidAmount);
+                    paidAmount = BigDecimal.ZERO;
+                  } else if (paymentAmount.compareTo(paidAmount) == 0) {
+                    paymentAmount = BigDecimal.ZERO;
+                    paidAmount = BigDecimal.ZERO;
+                  } else if (paymentAmount.compareTo(paidAmount) < 0) {
+                    paidAmount = paidAmount.subtract(paymentAmount);
+                    paymentAmount = BigDecimal.ZERO;
+                  }
+                }
+
+                if (paymentAmount.compareTo(BigDecimal.ZERO) == 0) {
+                  continue;
+                }
+
+                if (pendingCreditAmount.compareTo(paymentAmount) < 0) {
+                  paymentAmount = pendingCreditAmount;
+                  pendingCreditAmount = BigDecimal.ZERO;
+                } else {
+                  pendingCreditAmount = pendingCreditAmount.subtract(paymentAmount);
+                }
+
+                Date dueDate = getCalculatedDueDateBasedOnPaymentTerms(order.getOrderDate(), null,
+                    paymentTermLine);
+                FIN_PaymentSchedule pymtSchedule = OBProvider.getInstance().get(
+                    FIN_PaymentSchedule.class);
+                pymtSchedule.setNewOBObject(true);
+                pymtSchedule.setCurrency(order.getCurrency());
+                pymtSchedule.setInvoice(invoice);
+                pymtSchedule.setFinPaymentmethod(order.getPaymentMethod());
+                pymtSchedule.setAmount(paymentAmount);
+                pymtSchedule.setDueDate(dueDate);
+                pymtSchedule.setExpectedDate(dueDate);
+                pymtSchedule.setOutstandingAmount(paymentAmount);
+                if (ModelProvider.getInstance().getEntity(FIN_PaymentSchedule.class)
+                    .hasProperty("origDueDate")) {
+                  pymtSchedule.set("origDueDate", dueDate);
+                }
+                pymtSchedule.setFINPaymentPriority(order.getFINPaymentPriority());
+                invoice.getFINPaymentScheduleList().add(pymtSchedule);
+                OBDal.getInstance().save(pymtSchedule);
+              }
+
+              if (pendingCreditAmount.compareTo(BigDecimal.ZERO) == 0) {
+                paymentScheduleInvoice.setAmount(BigDecimal.valueOf(jsonorder.getDouble("payment"))
+                    .setScale(pricePrecision, RoundingMode.HALF_UP));
+                paymentScheduleInvoice.setOutstandingAmount(BigDecimal.ZERO);
+                paymentScheduleCreditAmount = BigDecimal.ZERO;
+              } else if (pendingCreditAmount.compareTo(BigDecimal.ZERO) > 0) {
+                paymentScheduleInvoice.setAmount(pendingCreditAmount);
+                paymentScheduleInvoice.setOutstandingAmount(pendingCreditAmount);
+                paymentScheduleCreditAmount = pendingCreditAmount;
+              }
+            }
             paymentScheduleInvoice.setDueDate(getCalculatedDueDateBasedOnPaymentTerms(
-                order.getOrderDate(), order.getPaymentTerms()));
+                order.getOrderDate(), order.getPaymentTerms(), null));
             paymentScheduleInvoice.setExpectedDate(paymentScheduleInvoice.getDueDate());
           } else {
             paymentScheduleInvoice.setDueDate(order.getOrderDate());
@@ -1964,7 +2086,7 @@
         invoice.setDaysTillDue(FIN_Utility.getDaysToDue(paymentScheduleInvoice == null ? invoice
             .getInvoiceDate() : paymentScheduleInvoice.getDueDate()));
         if (paymentScheduleInvoice != null) {
-          paymentScheduleInvoice.setOutstandingAmount(amountPaidWithCredit);
+          paymentScheduleInvoice.setOutstandingAmount(paymentScheduleCreditAmount);
           paymentScheduleInvoice.setPaidAmount(invoice.getGrandTotalAmount().subtract(
               amountPaidWithCredit));
           invoice.getFINPaymentScheduleList().add(paymentScheduleInvoice);
