Merge branch 'develop' into po-payment-terms
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
index bda9f41..87ab31f 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
@@ -570,4 +570,4 @@
company: function(frm) {
erpnext.accounts.dimensions.update_dimension(frm, frm.doctype);
},
-})
+})
\ No newline at end of file
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py
index eaa502f..f68d819 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.py
@@ -489,15 +489,13 @@
},
}
- if frappe.get_single("Accounts Settings").automatically_fetch_payment_terms == 1:
- fields["Payment Schedule"] = {
- "doctype": "Payment Schedule",
- "add_if_empty": True
- }
-
doc = get_mapped_doc("Purchase Order", source_name, fields,
target_doc, postprocess, ignore_permissions=ignore_permissions)
+ automatically_fetch_payment_terms = cint(frappe.db.get_single_value('Accounts Settings', 'automatically_fetch_payment_terms'))
+ if automatically_fetch_payment_terms:
+ doc.set_payment_schedule()
+
return doc
@frappe.whitelist()
diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
index 8563b97..474c9cf 100644
--- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
@@ -968,8 +968,25 @@
# To test if the PO does NOT have a Blanket Order
self.assertEqual(po_doc.items[0].blanket_order, None)
+ def test_payment_terms_are_fetched_when_creating_purchase_invoice(self):
+ from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_terms_template
+ from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
+ from erpnext.selling.doctype.sales_order.test_sales_order import automatically_fetch_payment_terms, compare_payment_schedules
+ automatically_fetch_payment_terms()
+ po = create_purchase_order(qty=10, rate=100, do_not_save=1)
+ create_payment_terms_template()
+ po.payment_terms_template = 'Test Receivable Template'
+ po.submit()
+
+ pi = make_purchase_invoice(qty=10, rate=100, do_not_save=1)
+ pi.items[0].purchase_order = po.name
+ pi.items[0].po_detail = po.items[0].name
+ pi.insert()
+
+ # self.assertEqual(po.payment_terms_template, pi.payment_terms_template)
+ compare_payment_schedules(self, po, pi)
def make_pr_against_po(po, received_qty=0):
pr = make_purchase_receipt(po)
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index bf4ab1a..c793c19 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -1110,7 +1110,14 @@
data = get_payment_terms(self.payment_terms_template, posting_date, grand_total, base_grand_total)
for item in data:
self.append("payment_schedule", item)
- else:
+
+ elif self.doctype in ["Sales Invoice", "Purchase Invoice"]:
+ po_or_so, doctype, fieldname = self.get_order_details()
+
+ if self.linked_order_has_payment_terms(po_or_so, fieldname, doctype):
+ self.fetch_payment_terms_from_order(po_or_so, doctype)
+
+ elif self.doctype not in ["Purchase Receipt"]:
data = dict(due_date=due_date, invoice_portion=100, payment_amount=grand_total, base_payment_amount=base_grand_total)
self.append("payment_schedule", data)
else:
@@ -1123,6 +1130,63 @@
d.base_payment_amount = flt(base_grand_total * self.get("conversion_rate"), d.precision('base_payment_amount'))
+ def get_order_details(self):
+ if self.doctype == "Sales Invoice":
+ po_or_so = self.get('items')[0].get('sales_order')
+ po_or_so_doctype = "Sales Order"
+ po_or_so_doctype_name = "sales_order"
+
+ else:
+ po_or_so = self.get('items')[0].get('purchase_order')
+ po_or_so_doctype = "Purchase Order"
+ po_or_so_doctype_name = "purchase_order"
+
+ return po_or_so, po_or_so_doctype, po_or_so_doctype_name
+
+ def linked_order_has_payment_terms(self, po_or_so, fieldname, doctype):
+ if po_or_so and self.all_items_have_same_po_or_so(po_or_so, fieldname):
+ if self.linked_order_has_payment_terms_template(po_or_so, doctype):
+ return True
+ elif self.linked_order_has_payment_schedule(po_or_so):
+ return True
+
+ return False
+
+ def all_items_have_same_po_or_so(self, po_or_so, fieldname):
+ for item in self.get('items'):
+ if item.get(fieldname) != po_or_so:
+ return False
+
+ return True
+
+ def linked_order_has_payment_terms_template(self, po_or_so, doctype):
+ return frappe.get_value(doctype, po_or_so, 'payment_terms_template')
+
+ def linked_order_has_payment_schedule(self, po_or_so):
+ return frappe.get_all('Payment Schedule', filters={'parent': po_or_so})
+
+ def fetch_payment_terms_from_order(self, po_or_so, po_or_so_doctype):
+ """
+ Fetch Payment Terms from Purchase/Sales Order on creating a new Purchase/Sales Invoice.
+ """
+ po_or_so = frappe.get_cached_doc(po_or_so_doctype, po_or_so)
+
+ self.payment_schedule = []
+ self.payment_terms_template = po_or_so.payment_terms_template
+
+ for schedule in po_or_so.payment_schedule:
+ payment_schedule = {
+ 'payment_term': schedule.payment_term,
+ 'due_date': schedule.due_date,
+ 'invoice_portion': schedule.invoice_portion,
+ 'discount_type': schedule.discount_type,
+ 'discount': schedule.discount,
+ 'base_payment_amount': schedule.base_payment_amount,
+ 'payment_amount': schedule.payment_amount,
+ 'outstanding': schedule.outstanding
+ }
+ self.append("payment_schedule", payment_schedule)
+
def set_due_date(self):
due_dates = [d.due_date for d in self.get("payment_schedule") if d.due_date]
if due_dates:
@@ -1807,4 +1871,4 @@
@erpnext.allow_regional
def validate_einvoice_fields(doc):
- pass
+ pass
\ No newline at end of file
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index 41f57a3..2b9d516 100755
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -693,6 +693,10 @@
}
}, target_doc, postprocess, ignore_permissions=ignore_permissions)
+ automatically_fetch_payment_terms = cint(frappe.db.get_single_value('Accounts Settings', 'automatically_fetch_payment_terms'))
+ if automatically_fetch_payment_terms:
+ doclist.set_payment_schedule()
+
return doclist
@frappe.whitelist()
diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py
index 974648d..f4a089b 100644
--- a/erpnext/selling/doctype/sales_order/test_sales_order.py
+++ b/erpnext/selling/doctype/sales_order/test_sales_order.py
@@ -1229,7 +1229,42 @@
self.assertRaises(frappe.ValidationError, so.cancel)
+ def test_payment_terms_are_fetched_when_creating_sales_invoice(self):
+ from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_terms_template
+ from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
+ automatically_fetch_payment_terms()
+
+ so = make_sales_order(uom="Nos", do_not_save=1)
+ create_payment_terms_template()
+ so.payment_terms_template = 'Test Receivable Template'
+ so.submit()
+
+ si = create_sales_invoice(qty=10, do_not_save=1)
+ si.items[0].sales_order = so.name
+ si.items[0].so_detail = so.items[0].name
+ si.insert()
+
+ self.assertEqual(so.payment_terms_template, si.payment_terms_template)
+ compare_payment_schedules(self, so, si)
+
+def automatically_fetch_payment_terms(enable=1):
+ accounts_settings = frappe.get_doc("Accounts Settings")
+ accounts_settings.automatically_fetch_payment_terms = enable
+ accounts_settings.save()
+
+def compare_payment_schedules(doc, doc1, doc2):
+ payment_schedule1 = frappe.db.sql("""select payment_term, description, due_date, mode_of_payment, invoice_portion, payment_amount
+ from `tabPayment Schedule`
+ where parenttype=%s and parent=%s
+ order by payment_term asc""", (doc1.doctype, doc1.name), as_dict=1)
+
+ payment_schedule2 = frappe.db.sql("""select payment_term, description, due_date, mode_of_payment, invoice_portion, payment_amount
+ from `tabPayment Schedule`
+ where parenttype=%s and parent=%s
+ order by payment_term asc""", (doc2.doctype, doc2.name), as_dict=1)
+
+ doc.assertEqual(payment_schedule1, payment_schedule2)
def make_sales_order(**args):
so = frappe.new_doc("Sales Order")
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py
index 4808e94..f99a01b 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.py
@@ -503,6 +503,10 @@
}
}, target_doc, set_missing_values)
+ automatically_fetch_payment_terms = cint(frappe.db.get_single_value('Accounts Settings', 'automatically_fetch_payment_terms'))
+ if automatically_fetch_payment_terms:
+ doc.set_payment_schedule()
+
return doc
@frappe.whitelist()
diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
index f981aeb..ca8d8b9 100644
--- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
@@ -17,7 +17,8 @@
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos, SerialNoWarehouseError
from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation \
import create_stock_reconciliation, set_valuation_method
-from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order, create_dn_against_so
+from erpnext.selling.doctype.sales_order.test_sales_order \
+ import make_sales_order, create_dn_against_so, automatically_fetch_payment_terms, compare_payment_schedules
from erpnext.accounts.doctype.account.test_account import get_inventory_account
from erpnext.stock.doctype.warehouse.test_warehouse import get_warehouse
from erpnext.stock.doctype.item.test_item import make_item
@@ -759,6 +760,30 @@
self.assertTrue("TESTBATCH" in dn.packed_items[0].batch_no, "Batch number not added in packed item")
+ def test_payment_terms_are_fetched_when_creating_sales_invoice(self):
+ from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_terms_template
+ from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
+
+ automatically_fetch_payment_terms()
+
+ so = make_sales_order(uom="Nos", do_not_save=1)
+ create_payment_terms_template()
+ so.payment_terms_template = 'Test Receivable Template'
+ so.submit()
+
+ dn = create_dn_against_so(so.name, delivered_qty=10)
+
+ si = create_sales_invoice(qty=10, do_not_save=1)
+ si.items[0].delivery_note= dn.name
+ si.items[0].dn_detail = dn.items[0].name
+ si.items[0].sales_order = so.name
+ si.items[0].so_detail = so.items[0].name
+
+ si.insert()
+ si.submit()
+
+ self.assertEqual(so.payment_terms_template, si.payment_terms_template)
+ compare_payment_schedules(self, so, si)
def create_delivery_note(**args):
dn = frappe.new_doc("Delivery Note")
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index 899d7e8..6b8a410 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -675,6 +675,10 @@
}
}, target_doc, set_missing_values)
+ automatically_fetch_payment_terms = cint(frappe.db.get_single_value('Accounts Settings', 'automatically_fetch_payment_terms'))
+ if automatically_fetch_payment_terms:
+ doc.set_payment_schedule()
+
return doclist
def get_invoiced_qty_map(purchase_receipt):
diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
index 82461cb..ee7fe4c 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -1082,6 +1082,31 @@
self.assertEqual(discrepancy_caused_by_exchange_rate_diff, amount)
+ def test_payment_terms_are_fetched_when_creating_purchase_invoice(self):
+ from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_terms_template
+ from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
+ from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order, make_pr_against_po
+ from erpnext.selling.doctype.sales_order.test_sales_order import automatically_fetch_payment_terms, compare_payment_schedules
+
+ automatically_fetch_payment_terms()
+
+ po = create_purchase_order(qty=10, rate=100, do_not_save=1)
+ create_payment_terms_template()
+ po.payment_terms_template = 'Test Receivable Template'
+ po.submit()
+
+ pr = make_pr_against_po(po.name, received_qty=10)
+
+ pi = make_purchase_invoice(qty=10, rate=100, do_not_save=1)
+ pi.items[0].purchase_receipt = pr.name
+ pi.items[0].pr_detail = pr.items[0].name
+ pi.items[0].purchase_order = po.name
+ pi.items[0].po_detail = po.items[0].name
+ pi.insert()
+
+ # self.assertEqual(po.payment_terms_template, pi.payment_terms_template)
+ compare_payment_schedules(self, po, pi)
+
def get_sl_entries(voucher_type, voucher_no):
return frappe.db.sql(""" select actual_qty, warehouse, stock_value_difference
from `tabStock Ledger Entry` where voucher_type=%s and voucher_no=%s