Merge branch 'develop' into payment-terms
diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.json b/erpnext/accounts/doctype/gl_entry/gl_entry.json
index 76e66d0..e27eaab 100644
--- a/erpnext/accounts/doctype/gl_entry/gl_entry.json
+++ b/erpnext/accounts/doctype/gl_entry/gl_entry.json
@@ -80,6 +80,36 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fieldname": "due_date",
+ "fieldtype": "Date",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Due Date",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
"fieldname": "account",
"fieldtype": "Link",
"hidden": 0,
@@ -718,7 +748,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2017-08-03 12:40:09.611951",
+ "modified": "2017-08-10 18:06:44.904081",
"modified_by": "Administrator",
"module": "Accounts",
"name": "GL Entry",
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js
index 9047a4e..4ce6886 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.js
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js
@@ -185,8 +185,39 @@
})
},
+ due_date_options_cache: {},
+
reference_name: function(doc, cdt, cdn) {
var d = frappe.get_doc(cdt, cdn);
+ var me = this;
+
+ const get_invoice_due_dates = invoice_name => {
+ const options = this.due_date_options_cache[invoice_name];
+ const input = $(cur_frm.fields_dict["accounts"].wrapper).find("select[data-fieldname=reference_due_date]");
+
+ if (options) {
+ input.empty();
+ input.add_options(options);
+ frappe.model.set_value(cdt, cdn, "reference_due_date", options[0]);
+ }
+ else {
+ frappe.call({
+ method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_invoice_due_dates",
+ args: {name: invoice_name},
+ callback: function(r) {
+ const options = [];
+ $.each(r.message, function(key, value) {
+ options.push(value.due_date);
+ });
+ input.empty();
+ input.add_options(options);
+ frappe.model.set_value(cdt, cdn, "reference_due_date", options[0]);
+ me.due_date_options_cache[d.reference_name] = options;
+ }
+ });
+ }
+ }
+
if(d.reference_name) {
if (d.reference_type==="Purchase Invoice" && !flt(d.debit)) {
this.get_outstanding('Purchase Invoice', d.reference_name, doc.company, d);
@@ -197,6 +228,9 @@
if (d.reference_type==="Journal Entry" && !flt(d.credit) && !flt(d.debit)) {
this.get_outstanding('Journal Entry', d.reference_name, doc.company, d);
}
+ if( in_list(["Sales Invoice", "Purchase Invoice"]), d.reference_type) {
+ get_invoice_due_dates(d.reference_name);
+ }
}
},
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py
index 375d85d..f7e4023 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.py
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py
@@ -436,7 +436,8 @@
"against_voucher": d.reference_name,
"remarks": self.remark,
"cost_center": d.cost_center,
- "project": d.project
+ "project": d.project,
+ "due_date": d.reference_due_date
})
)
@@ -889,3 +890,14 @@
exchange_rate = bank_balance_in_company_currency / bank_balance_in_account_currency
return exchange_rate
+
+
+@frappe.whitelist()
+def get_invoice_due_dates(name):
+ result = frappe.get_list(
+ doctype='GL Entry', group_by='name, due_date',
+ filters={'voucher_no': name, "ifnull(due_date, '')": ('!=', '')},
+ fields=['due_date'], distinct=True
+ )
+
+ return result
diff --git a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json
index 54af579..48d5ed2 100644
--- a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json
+++ b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json
@@ -1,5 +1,6 @@
{
"allow_copy": 0,
+ "allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "hash",
@@ -13,6 +14,7 @@
"engine": "InnoDB",
"fields": [
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 1,
"collapsible": 0,
@@ -46,6 +48,7 @@
"width": "250px"
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -75,6 +78,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -106,6 +110,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -141,6 +146,7 @@
"width": "180px"
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -168,6 +174,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -197,6 +204,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -226,6 +234,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -256,6 +265,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -287,6 +297,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -317,6 +328,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -345,6 +357,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -374,6 +387,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -402,6 +416,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 1,
"collapsible": 0,
@@ -432,6 +447,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 1,
"collapsible": 0,
@@ -464,6 +480,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -491,6 +508,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 1,
"collapsible": 0,
@@ -521,6 +539,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 1,
"collapsible": 0,
@@ -553,6 +572,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -581,6 +601,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -611,6 +632,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -641,6 +663,39 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "eval:doc.reference_type&&!in_list(doc.reference_type, ['Expense Claim', 'Asset', 'Employee Loan'])",
+ "fieldname": "reference_due_date",
+ "fieldtype": "Select",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Reference Due Date",
+ "length": 0,
+ "no_copy": 0,
+ "options": "",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -671,6 +726,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -698,6 +754,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -729,6 +786,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -759,17 +817,17 @@
"unique": 0
}
],
+ "has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 1,
"image_view": 0,
"in_create": 0,
- "in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
- "modified": "2017-03-02 05:02:10.102039",
+ "modified": "2017-08-30 08:44:54.295493",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Journal Entry Account",
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py
index 9832c05..c6353d5 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -410,7 +410,8 @@
gle = party_gl_dict.copy()
gle.update({
"against_voucher_type": d.reference_doctype,
- "against_voucher": d.reference_name
+ "against_voucher": d.reference_name,
+ "due_date": d.due_date
})
allocated_amount_in_company_currency = flt(flt(d.allocated_amount) * flt(d.exchange_rate),
@@ -742,18 +743,53 @@
pe.allocate_payment_amount = 1
pe.letter_head = doc.get("letter_head")
- pe.append("references", {
- "reference_doctype": dt,
- "reference_name": dn,
- "due_date": doc.get("due_date"),
- "total_amount": grand_total,
- "outstanding_amount": outstanding_amount,
- "allocated_amount": outstanding_amount
- })
+ if doc.get("payment_schedule"):
+ for d in doc.get("payment_schedule"):
+ invoice_amount = d.payment_amount * doc.conversion_rate \
+ if party_account_currency == doc.company_currency else d.payment_amount
+ paid_amount = get_paid_amount(dt, dn, party_type, pe.party, party_account, d.due_date)
+ outstanding_amount = invoice_amount - paid_amount
+ pe.append("references", {
+ "reference_doctype": dt,
+ "reference_name": dn,
+ "due_date": d.due_date,
+ "total_amount": invoice_amount,
+ "outstanding_amount": outstanding_amount,
+ "allocated_amount": outstanding_amount
+ })
+ else:
+ pe.append("references", {
+ "reference_doctype": dt,
+ "reference_name": dn,
+ "due_date": doc.get("due_date"),
+ "total_amount": grand_total,
+ "outstanding_amount": outstanding_amount,
+ "allocated_amount": outstanding_amount
+ })
pe.setup_party_account_field()
pe.set_missing_values()
if party_account and bank:
pe.set_exchange_rate()
pe.set_amounts()
- return pe
\ No newline at end of file
+ return pe
+
+def get_paid_amount(dt, dn, party_type, party, account, due_date):
+ if party_type=="Customer":
+ dr_or_cr = "credit_in_account_currency - debit_in_account_currency"
+ else:
+ dr_or_cr = "debit_in_account_currency - credit_in_account_currency"
+
+ paid_amount = frappe.db.sql("""
+ select ifnull(sum({dr_or_cr}), 0) as paid_amount
+ from `tabGL Entry`
+ where against_voucher_type = %s
+ and against_voucher = %s
+ and party_type = %s
+ and party = %s
+ and account = %s
+ and due_date = %s
+ and {dr_or_cr} > 0
+ """.format(dr_or_cr=dr_or_cr), (dt, dn, party_type, party, account, due_date))
+
+ return paid_amount[0][0] if paid_amount else 0
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/payment_schedule/__init__.py b/erpnext/accounts/doctype/payment_schedule/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_schedule/__init__.py
diff --git a/erpnext/accounts/doctype/payment_schedule/payment_schedule.json b/erpnext/accounts/doctype/payment_schedule/payment_schedule.json
new file mode 100644
index 0000000..45a81b0
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_schedule/payment_schedule.json
@@ -0,0 +1,197 @@
+{
+ "allow_copy": 0,
+ "allow_guest_to_view": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "autoname": "",
+ "beta": 0,
+ "creation": "2017-08-10 15:38:00.080575",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "fields": [
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 2,
+ "fieldname": "payment_term",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Payment Term",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Payment Term",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 2,
+ "fieldname": "description",
+ "fieldtype": "Small Text",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Description",
+ "length": 0,
+ "no_copy": 0,
+ "options": "payment_term.description",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 2,
+ "fieldname": "due_date",
+ "fieldtype": "Date",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Due Date",
+ "length": 0,
+ "no_copy": 0,
+ "options": "payment_term.due_date",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 2,
+ "fieldname": "invoice_portion",
+ "fieldtype": "Percent",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Invoice Portion",
+ "length": 0,
+ "no_copy": 0,
+ "options": "payment_term.invoice_portion",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 2,
+ "fieldname": "payment_amount",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Payment Amount",
+ "length": 0,
+ "no_copy": 0,
+ "options": "currency",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ }
+ ],
+ "has_web_view": 0,
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
+ "in_create": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 1,
+ "max_attachments": 0,
+ "modified": "2017-08-10 18:09:42.122027",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Payment Schedule",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "show_name_in_global_search": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1,
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/payment_schedule/payment_schedule.py b/erpnext/accounts/doctype/payment_schedule/payment_schedule.py
new file mode 100644
index 0000000..4174017
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_schedule/payment_schedule.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+from frappe.model.document import Document
+
+
+class PaymentSchedule(Document):
+ pass
diff --git a/erpnext/accounts/doctype/payment_term/__init__.py b/erpnext/accounts/doctype/payment_term/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_term/__init__.py
diff --git a/erpnext/accounts/doctype/payment_term/payment_term.js b/erpnext/accounts/doctype/payment_term/payment_term.js
new file mode 100644
index 0000000..054c2d1
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_term/payment_term.js
@@ -0,0 +1,2 @@
+// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
diff --git a/erpnext/accounts/doctype/payment_term/payment_term.json b/erpnext/accounts/doctype/payment_term/payment_term.json
new file mode 100644
index 0000000..702319b
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_term/payment_term.json
@@ -0,0 +1,344 @@
+{
+ "allow_copy": 0,
+ "allow_guest_to_view": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "autoname": "field:payment_term_name",
+ "beta": 0,
+ "creation": "2017-08-10 15:24:54.876365",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "fields": [
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 1,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "payment_term_name",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Payment Term Name",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 1,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "invoice_portion",
+ "fieldtype": "Float",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Invoice Portion",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "column_break_3",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 1,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "due_date_based_on",
+ "fieldtype": "Select",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Due Date Based On",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Day(s) after invoice date\nDay(s) after the end of the invoice month\nMonth(s) after the end of the invoice month",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 1,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "eval:in_list(['Day(s) after invoice date', 'Day(s) after the end of the invoice month'], doc.due_date_based_on)",
+ "fieldname": "credit_days",
+ "fieldtype": "Int",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Credit Days",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "eval:doc.due_date_based_on=='Month(s) after the end of the invoice month'",
+ "fieldname": "credit_months",
+ "fieldtype": "Int",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Credit Months",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "section_break_6",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 1,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "description",
+ "fieldtype": "Small Text",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Description",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ }
+ ],
+ "has_web_view": 0,
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
+ "in_create": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 0,
+ "max_attachments": 0,
+ "modified": "2017-08-10 16:26:03.581501",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Payment Term",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "if_owner": 0,
+ "import": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 0,
+ "write": 1
+ },
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "if_owner": 0,
+ "import": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Accounts Manager",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 0,
+ "write": 1
+ },
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "if_owner": 0,
+ "import": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Accounts User",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 0,
+ "write": 1
+ }
+ ],
+ "quick_entry": 1,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "show_name_in_global_search": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1,
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/payment_term/payment_term.py b/erpnext/accounts/doctype/payment_term/payment_term.py
new file mode 100644
index 0000000..5d4df05
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_term/payment_term.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+from frappe.model.document import Document
+
+
+class PaymentTerm(Document):
+ pass
diff --git a/erpnext/accounts/doctype/payment_term/test_payment_term.js b/erpnext/accounts/doctype/payment_term/test_payment_term.js
new file mode 100644
index 0000000..b26e42a
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_term/test_payment_term.js
@@ -0,0 +1,23 @@
+/* eslint-disable */
+// rename this file from _test_[name] to test_[name] to activate
+// and remove above this line
+
+QUnit.test("test: Payment Term", function (assert) {
+ let done = assert.async();
+
+ // number of asserts
+ assert.expect(1);
+
+ frappe.run_serially([
+ // insert a new Payment Term
+ () => frappe.tests.make('Payment Term', [
+ // values to be set
+ {key: 'value'}
+ ]),
+ () => {
+ assert.equal(cur_frm.doc.key, 'value');
+ },
+ () => done()
+ ]);
+
+});
diff --git a/erpnext/accounts/doctype/payment_term/test_payment_term.py b/erpnext/accounts/doctype/payment_term/test_payment_term.py
new file mode 100644
index 0000000..d9baa59
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_term/test_payment_term.py
@@ -0,0 +1,9 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+import unittest
+
+
+class TestPaymentTerm(unittest.TestCase):
+ pass
diff --git a/erpnext/accounts/doctype/payment_term/test_records.json b/erpnext/accounts/doctype/payment_term/test_records.json
new file mode 100644
index 0000000..ef6e069
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_term/test_records.json
@@ -0,0 +1,34 @@
+[
+ {
+ "doctype":"Payment Term",
+ "due_date_based_on":"Day(s) after invoice date",
+ "payment_term_name":"_Test N30",
+ "description":"_Test Net 30 Days",
+ "invoice_portion":50,
+ "credit_days":30
+ },
+ {
+ "doctype":"Payment Term",
+ "due_date_based_on":"Day(s) after invoice date",
+ "payment_term_name":"_Test COD",
+ "description":"_Test Cash on Delivery",
+ "invoice_portion":50,
+ "credit_days":0
+ },
+ {
+ "doctype":"Payment Term",
+ "due_date_based_on":"Month(s) after the end of the invoice month",
+ "payment_term_name":"_Test EONM",
+ "description":"_Test End of Next Month",
+ "invoice_portion":100,
+ "credit_months":1
+ },
+ {
+ "doctype":"Payment Term",
+ "due_date_based_on":"Day(s) after invoice date",
+ "payment_term_name":"_Test N30 1",
+ "description":"_Test Net 30 Days",
+ "invoice_portion":100,
+ "credit_days":30
+ }
+]
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/payment_terms_template/__init__.py b/erpnext/accounts/doctype/payment_terms_template/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_terms_template/__init__.py
diff --git a/erpnext/accounts/doctype/payment_terms_template/payment_terms_template.js b/erpnext/accounts/doctype/payment_terms_template/payment_terms_template.js
new file mode 100644
index 0000000..558297f
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_terms_template/payment_terms_template.js
@@ -0,0 +1,12 @@
+// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Payment Terms Template', {
+ setup: function(frm) {
+ frm.add_fetch("payment_term", "description", "description");
+ frm.add_fetch("payment_term", "invoice_portion", "invoice_portion");
+ frm.add_fetch("payment_term", "due_date_based_on", "due_date_based_on");
+ frm.add_fetch("payment_term", "credit_days", "credit_days");
+ frm.add_fetch("payment_term", "credit_months", "credit_months");
+ }
+});
diff --git a/erpnext/accounts/doctype/payment_terms_template/payment_terms_template.json b/erpnext/accounts/doctype/payment_terms_template/payment_terms_template.json
new file mode 100644
index 0000000..0959658
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_terms_template/payment_terms_template.json
@@ -0,0 +1,164 @@
+{
+ "allow_copy": 0,
+ "allow_guest_to_view": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "autoname": "field:template_name",
+ "beta": 0,
+ "creation": "2017-08-10 15:34:28.058054",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "fields": [
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "template_name",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Template Name",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "terms",
+ "fieldtype": "Table",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Payment Terms",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Payment Terms Template Detail",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ }
+ ],
+ "has_web_view": 0,
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
+ "in_create": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 0,
+ "max_attachments": 0,
+ "modified": "2017-08-10 15:46:33.877884",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Payment Terms Template",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "if_owner": 0,
+ "import": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 0,
+ "write": 1
+ },
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "if_owner": 0,
+ "import": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Accounts User",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 0,
+ "write": 1
+ },
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "if_owner": 0,
+ "import": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Accounts Manager",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 0,
+ "write": 1
+ }
+ ],
+ "quick_entry": 0,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "show_name_in_global_search": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1,
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/payment_terms_template/payment_terms_template.py b/erpnext/accounts/doctype/payment_terms_template/payment_terms_template.py
new file mode 100644
index 0000000..d2344d6
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_terms_template/payment_terms_template.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+from frappe.model.document import Document
+
+
+class PaymentTermsTemplate(Document):
+ pass
diff --git a/erpnext/accounts/doctype/payment_terms_template/test_payment_terms_template.js b/erpnext/accounts/doctype/payment_terms_template/test_payment_terms_template.js
new file mode 100644
index 0000000..494a0ed
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_terms_template/test_payment_terms_template.js
@@ -0,0 +1,23 @@
+/* eslint-disable */
+// rename this file from _test_[name] to test_[name] to activate
+// and remove above this line
+
+QUnit.test("test: Payment Terms Template", function (assert) {
+ let done = assert.async();
+
+ // number of asserts
+ assert.expect(1);
+
+ frappe.run_serially([
+ // insert a new Payment Terms Template
+ () => frappe.tests.make('Payment Terms Template', [
+ // values to be set
+ {key: 'value'}
+ ]),
+ () => {
+ assert.equal(cur_frm.doc.key, 'value');
+ },
+ () => done()
+ ]);
+
+});
diff --git a/erpnext/accounts/doctype/payment_terms_template/test_payment_terms_template.py b/erpnext/accounts/doctype/payment_terms_template/test_payment_terms_template.py
new file mode 100644
index 0000000..ec37066
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_terms_template/test_payment_terms_template.py
@@ -0,0 +1,9 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+import unittest
+
+
+class TestPaymentTermsTemplate(unittest.TestCase):
+ pass
diff --git a/erpnext/accounts/doctype/payment_terms_template/test_records.json b/erpnext/accounts/doctype/payment_terms_template/test_records.json
new file mode 100644
index 0000000..fea0b35
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_terms_template/test_records.json
@@ -0,0 +1,60 @@
+[
+ {
+ "doctype":"Payment Terms Template",
+ "terms":[
+ {
+ "doctype":"Payment Terms Template Detail",
+ "due_date_based_on":"Day(s) after invoice date",
+ "idx":1,
+ "description":"Cash on Delivery",
+ "invoice_portion":50,
+ "credit_days":0,
+ "credit_months":0,
+ "payment_term":"_Test COD"
+ },
+ {
+ "doctype":"Payment Terms Template Detail",
+ "due_date_based_on":"Day(s) after invoice date",
+ "idx":2,
+ "description":"Net 30 Days ",
+ "invoice_portion":50,
+ "credit_days":30,
+ "credit_months":0,
+ "payment_term":"_Test N30"
+ }
+ ],
+ "template_name":"_Test Payment Term Template"
+ },
+ {
+ "doctype":"Payment Terms Template",
+ "terms":[
+ {
+ "doctype":"Payment Terms Template Detail",
+ "due_date_based_on":"Month(s) after the end of the invoice month",
+ "idx":1,
+ "description":"_Test End of Next Months",
+ "invoice_portion":100,
+ "credit_days":0,
+ "credit_months":1,
+ "payment_term":"_Test EONM"
+ }
+ ],
+ "template_name":"_Test Payment Term Template 1"
+ },
+ {
+ "doctype":"Payment Terms Template",
+ "terms":[
+ {
+ "doctype":"Payment Terms Template Detail",
+ "due_date_based_on":"Day(s) after invoice date",
+ "idx":1,
+ "description":"_Test Net Within 30 days",
+ "invoice_portion":100,
+ "credit_days":30,
+ "credit_months":0,
+ "payment_term":"_Test N30 1"
+ }
+ ],
+ "template_name":"_Test Payment Term Template 3"
+ }
+]
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/payment_terms_template_detail/__init__.py b/erpnext/accounts/doctype/payment_terms_template_detail/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_terms_template_detail/__init__.py
diff --git a/erpnext/accounts/doctype/payment_terms_template_detail/payment_terms_template_detail.json b/erpnext/accounts/doctype/payment_terms_template_detail/payment_terms_template_detail.json
new file mode 100644
index 0000000..88babd5
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_terms_template_detail/payment_terms_template_detail.json
@@ -0,0 +1,229 @@
+{
+ "allow_copy": 0,
+ "allow_guest_to_view": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "autoname": "PTTD.#####",
+ "beta": 0,
+ "creation": "2017-08-10 15:34:09.409562",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "fields": [
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 2,
+ "fieldname": "payment_term",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Payment Term",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Payment Term",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 2,
+ "fieldname": "description",
+ "fieldtype": "Small Text",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Description",
+ "length": 0,
+ "no_copy": 0,
+ "options": "",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 2,
+ "fieldname": "invoice_portion",
+ "fieldtype": "Percent",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Invoice Portion",
+ "length": 0,
+ "no_copy": 0,
+ "options": "",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 2,
+ "fieldname": "due_date_based_on",
+ "fieldtype": "Select",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Due Date Based On",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Day(s) after invoice date\nDay(s) after the end of the invoice month\nMonth(s) after the end of the invoice month",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 2,
+ "depends_on": "eval:in_list(['Day(s) after invoice date', 'Day(s) after the end of the invoice month'], doc.due_date_based_on)",
+ "fieldname": "credit_days",
+ "fieldtype": "Int",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Credit Days",
+ "length": 0,
+ "no_copy": 0,
+ "options": "",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "eval:doc.due_date_based_on=='Month(s) after the end of the invoice month'",
+ "fieldname": "credit_months",
+ "fieldtype": "Int",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Credit Months",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ }
+ ],
+ "has_web_view": 0,
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
+ "in_create": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 1,
+ "max_attachments": 0,
+ "modified": "2017-08-10 16:27:31.401380",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Payment Terms Template Detail",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "show_name_in_global_search": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1,
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/payment_terms_template_detail/payment_terms_template_detail.py b/erpnext/accounts/doctype/payment_terms_template_detail/payment_terms_template_detail.py
new file mode 100644
index 0000000..54c0fda
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_terms_template_detail/payment_terms_template_detail.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+from frappe.model.document import Document
+
+
+class PaymentTermsTemplateDetail(Document):
+ pass
diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
index 82a3d65..9ec69c3 100644
--- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
+++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
@@ -252,6 +252,7 @@
self.assertEquals(so.items[0].rate, 100)
def test_pricing_rule_with_margin_and_discount(self):
+ frappe.delete_doc_if_exists('Pricing Rule', '_Test Pricing Rule')
make_pricing_rule(selling=1, margin_type="Percentage", margin_rate_or_amount=10)
si = create_sales_invoice(do_not_save=True)
si.items[0].price_list_rate = 1000
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
index ac5f5dd..36ca528 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
@@ -372,3 +372,23 @@
frm.toggle_reqd("supplier_warehouse", frm.doc.is_subcontracted==="Yes");
}
})
+
+frappe.ui.form.on("Purchase Invoice", {
+ payment_terms_template: function() {
+ cur_frm.trigger("disable_due_date");
+ },
+
+ disable_due_date: function() {
+ const disable = cur_frm.doc.payment_terms_template || (
+ cur_frm.doc.payment_schedule && cur_frm.doc.payment_schedule.length == 0);
+ cur_frm.set_df_property("due_date", "read_only", disable ? 1 : 0);
+ },
+
+});
+
+frappe.ui.form.on("Payment Schedule", {
+ payment_schedule_remove: function() {
+ cur_frm.trigger("disable_due_date");
+ },
+
+});
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
index 8ea48f6..691ee45 100755
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
@@ -2731,6 +2731,98 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
+ "columns": 0,
+ "fieldname": "payment_schedule_section",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Payment Terms Schedule",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "payment_terms_template",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Payment Terms Template",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Payment Terms Template",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "payment_schedule",
+ "fieldtype": "Table",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Payment Schedule",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Payment Schedule",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 1,
"collapsible_depends_on": "terms",
"columns": 0,
"fieldname": "terms_section_break",
@@ -3797,7 +3889,7 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
- "modified": "2017-07-19 13:53:48.673757",
+ "modified": "2017-08-16 17:10:30.248741",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice",
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index 7ab7901..06a4974 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -92,7 +92,7 @@
if not self.credit_to:
self.credit_to = get_party_account("Supplier", self.supplier, self.company)
if not self.due_date:
- self.due_date = get_due_date(self.posting_date, "Supplier", self.supplier, self.company)
+ self.due_date = get_due_date(self.posting_date, "Supplier", self.supplier)
super(PurchaseInvoice, self).set_missing_values(for_validate)
@@ -357,7 +357,27 @@
return gl_entries
def make_supplier_gl_entry(self, gl_entries):
- if self.grand_total:
+ if self.get("payment_schedule"):
+ for d in self.get("payment_schedule"):
+ payment_amount_in_company_currency = flt(d.payment_amount * self.conversion_rate,
+ d.precision("payment_amount"))
+
+ gl_entries.append(
+ self.get_gl_dict({
+ "account": self.credit_to,
+ "party_type": "Supplier",
+ "party": self.supplier,
+ "due_date": d.due_date,
+ "against": self.against_expense_account,
+ "credit": payment_amount_in_company_currency,
+ "credit_in_account_currency": payment_amount_in_company_currency \
+ if self.party_account_currency==self.company_currency else d.payment_amount,
+ "against_voucher": self.return_against if cint(self.is_return) else self.name,
+ "against_voucher_type": self.doctype
+ }, self.party_account_currency)
+ )
+
+ elif self.grand_total:
# Didnot use base_grand_total to book rounding loss gle
grand_total_in_company_currency = flt(self.grand_total * self.conversion_rate,
self.precision("grand_total"))
diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
index 3454a2e..f189bf8 100644
--- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
@@ -6,7 +6,7 @@
import unittest
import frappe, erpnext
import frappe.model
-from frappe.utils import cint, flt, today, nowdate
+from frappe.utils import cint, flt, today, nowdate, getdate, add_days
import frappe.defaults
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory, \
test_records as pr_test_records
@@ -551,6 +551,38 @@
#check outstanding after advance cancellation
self.assertEqual(flt(pi.outstanding_amount), flt(pi.grand_total + pi.total_advance))
+ def test_gl_entry_based_on_payment_schedule(self):
+ pi = make_purchase_invoice(do_not_save=True, supplier="_Test Supplier P")
+ pi.append("payment_schedule", {
+ "due_date": add_days(nowdate(), 15),
+ "payment_amount": 100
+ })
+ pi.append("payment_schedule", {
+ "due_date": add_days(nowdate(), 45),
+ "payment_amount": 150
+ })
+
+ pi.save()
+ pi.submit()
+
+ gl_entries = frappe.db.sql("""select account, debit, credit, due_date
+ from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s
+ order by account asc, debit asc""", pi.name, as_dict=1)
+ self.assertTrue(gl_entries)
+
+ expected_gl_entries = sorted([
+ [pi.credit_to, 0.0, 100.0, add_days(nowdate(), 15)],
+ [pi.credit_to, 0.0, 150.0, add_days(nowdate(), 45)],
+ ["_Test Account Cost for Goods Sold - _TC", 250.0, 0.0, None]
+ ])
+
+ for i, gle in enumerate(sorted(gl_entries, key=lambda gle: gle.account)):
+ self.assertEquals(expected_gl_entries[i][0], gle.account)
+ self.assertEquals(expected_gl_entries[i][1], gle.debit)
+ self.assertEquals(expected_gl_entries[i][2], gle.credit)
+ self.assertEquals(getdate(expected_gl_entries[i][3]), getdate(gle.due_date))
+
+
def unlink_payment_on_cancel_of_invoice(enable=1):
accounts_settings = frappe.get_doc("Accounts Settings")
accounts_settings.unlink_payment_on_cancellation_of_invoice = enable
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
index ef233c6..5432b4c 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
@@ -564,3 +564,23 @@
refresh_field('total_billing_amount')
}
+
+frappe.ui.form.on("Sales Invoice", {
+ payment_terms_template: function() {
+ cur_frm.trigger("disable_due_date");
+ },
+
+ disable_due_date: function() {
+ const disable = cur_frm.doc.payment_terms_template || (
+ cur_frm.doc.payment_schedule && cur_frm.doc.payment_schedule.length == 0);
+ cur_frm.set_df_property("due_date", "read_only", disable ? 1 : 0);
+ },
+
+});
+
+frappe.ui.form.on("Payment Schedule", {
+ payment_schedule_remove: function() {
+ cur_frm.trigger("disable_due_date");
+ },
+
+});
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
index 260c05e..0af1733 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
@@ -2689,6 +2689,101 @@
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
+ "collapsible": 1,
+ "collapsible_depends_on": "eval:(!doc.is_pos && !doc.is_return)",
+ "columns": 0,
+ "fieldname": "payment_schedule_section",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Payment Schedule",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "eval:(!doc.is_pos && !doc.is_return)",
+ "fieldname": "payment_terms_template",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Payment Terms Template",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Payment Terms Template",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "eval:(!doc.is_pos && !doc.is_return)",
+ "fieldname": "payment_schedule",
+ "fieldtype": "Table",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Payment Schedule",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Payment Schedule",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
"collapsible": 0,
"collapsible_depends_on": "",
"columns": 0,
@@ -2939,6 +3034,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "depends_on": "is_pos",
"fieldname": "base_change_amount",
"fieldtype": "Currency",
"hidden": 0,
@@ -2999,6 +3095,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "depends_on": "is_pos",
"fieldname": "change_amount",
"fieldtype": "Currency",
"hidden": 0,
@@ -3030,6 +3127,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "depends_on": "is_pos",
"fieldname": "account_for_change_amount",
"fieldtype": "Link",
"hidden": 0,
@@ -4688,7 +4786,7 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
- "modified": "2017-07-07 13:05:37.469682",
+ "modified": "2017-08-10 18:02:44.286951",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice",
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 780edd8..afed3e8 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -241,7 +241,7 @@
if not self.debit_to:
self.debit_to = get_party_account("Customer", self.customer, self.company)
if not self.due_date and self.customer:
- self.due_date = get_due_date(self.posting_date, "Customer", self.customer, self.company)
+ self.due_date = get_due_date(self.posting_date, "Customer", self.customer)
super(SalesInvoice, self).set_missing_values(for_validate)
@@ -629,7 +629,27 @@
return gl_entries
def make_customer_gl_entry(self, gl_entries):
- if self.grand_total:
+ if self.get("payment_schedule"):
+ for d in self.get("payment_schedule"):
+ payment_amount_in_company_currency = flt(d.payment_amount * self.conversion_rate,
+ d.precision("payment_amount"))
+
+ gl_entries.append(
+ self.get_gl_dict({
+ "account": self.debit_to,
+ "party_type": "Customer",
+ "party": self.customer,
+ "due_date": d.due_date,
+ "against": self.against_income_account,
+ "debit": payment_amount_in_company_currency,
+ "debit_in_account_currency": payment_amount_in_company_currency \
+ if self.party_account_currency==self.company_currency else d.payment_amount,
+ "against_voucher": self.return_against if cint(self.is_return) else self.name,
+ "against_voucher_type": self.doctype
+ }, self.party_account_currency)
+ )
+
+ elif self.grand_total:
# Didnot use base_grand_total to book rounding loss gle
grand_total_in_company_currency = flt(self.grand_total * self.conversion_rate,
self.precision("grand_total"))
diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.js
index 35b2558..f7a4488 100644
--- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.js
+++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.js
@@ -40,4 +40,3 @@
() => done()
]);
});
-
diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
index e0a453c..add640a 100644
--- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
@@ -4,7 +4,7 @@
import frappe
import unittest, copy
-from frappe.utils import nowdate, add_days, flt
+from frappe.utils import nowdate, add_days, flt, getdate
from frappe.model.dynamic_links import get_dynamic_link_map
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry, get_qty_after_transaction
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import unlink_payment_on_cancel_of_invoice
@@ -890,20 +890,6 @@
self.assertEquals(si.get("items")[0].serial_no, dn.get("items")[0].serial_no)
- def test_invoice_due_date_against_customers_credit_days(self):
- # set customer's credit days
- frappe.db.set_value("Customer", "_Test Customer", "credit_days_based_on", "Fixed Days")
- frappe.db.set_value("Customer", "_Test Customer", "credit_days", 10)
-
- si = create_sales_invoice()
- self.assertEqual(si.due_date, add_days(nowdate(), 10))
-
- # set customer's credit days is last day of the next month
- frappe.db.set_value("Customer", "_Test Customer", "credit_days_based_on", "Last Day of the Next Month")
-
- si1 = create_sales_invoice(posting_date="2015-07-05")
- self.assertEqual(si1.due_date, "2015-08-31")
-
def test_return_sales_invoice(self):
set_perpetual_inventory()
make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=50, basic_rate=100)
@@ -1271,6 +1257,38 @@
})
si.insert()
return si
+
+ def test_gl_entry_based_on_payment_schedule(self):
+ si = create_sales_invoice(do_not_save=True, customer="_Test Customer P")
+ si.append("payment_schedule", {
+ "due_date": add_days(nowdate(), 15),
+ "payment_amount": 20
+ })
+ si.append("payment_schedule", {
+ "due_date": add_days(nowdate(), 45),
+ "payment_amount": 80
+ })
+
+ si.save()
+ si.submit()
+
+ gl_entries = frappe.db.sql("""select account, debit, credit, due_date
+ from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s
+ order by account asc, debit asc""", si.name, as_dict=1)
+ self.assertTrue(gl_entries)
+
+ expected_gl_entries = sorted([
+ [si.debit_to, 20.0, 0.0, add_days(nowdate(), 15)],
+ [si.debit_to, 80.0, 0.0, add_days(nowdate(), 45)],
+ ["Sales - _TC", 0.0, 100.0, None]
+ ])
+
+ for i, gle in enumerate(sorted(gl_entries, key=lambda gle: gle.account)):
+ self.assertEquals(expected_gl_entries[i][0], gle.account)
+ self.assertEquals(expected_gl_entries[i][1], gle.debit)
+ self.assertEquals(expected_gl_entries[i][2], gle.credit)
+ self.assertEquals(getdate(expected_gl_entries[i][3]), getdate(gle.due_date))
+
def test_company_monthly_sales(self):
existing_current_month_sales = frappe.db.get_value("Company", "_Test Company", "total_monthly_sales")
diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py
index ceae61c..45d2ef2 100644
--- a/erpnext/accounts/general_ledger.py
+++ b/erpnext/accounts/general_ledger.py
@@ -3,7 +3,7 @@
from __future__ import unicode_literals
import frappe, erpnext
-from frappe.utils import flt, cstr, cint
+from frappe.utils import flt, cstr, cint, getdate
from frappe import _
from frappe.model.meta import get_field_precision
from erpnext.accounts.doctype.budget.budget import validate_expense_against_budget
@@ -75,7 +75,8 @@
and cstr(e.get('against_voucher'))==cstr(gle.get('against_voucher')) \
and cstr(e.get('against_voucher_type')) == cstr(gle.get('against_voucher_type')) \
and cstr(e.get('cost_center')) == cstr(gle.get('cost_center')) \
- and cstr(e.get('project')) == cstr(gle.get('project')):
+ and cstr(e.get('project')) == cstr(gle.get('project')) \
+ and getdate(e.get('due_date')) == getdate(gle.get('due_date')):
return e
def save_entries(gl_map, adv_adj, update_outstanding, from_repost=False):
diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py
index ba7ae32..0c790d6 100644
--- a/erpnext/accounts/party.py
+++ b/erpnext/accounts/party.py
@@ -9,7 +9,7 @@
from frappe.defaults import get_user_permissions
from frappe.model.utils import get_fetch_values
from frappe.utils import (add_days, getdate, formatdate, get_first_day, date_diff,
- add_years, get_timestamp, nowdate, flt)
+ add_years, get_timestamp, nowdate, flt, add_months, get_last_day)
from frappe.contacts.doctype.address.address import (get_address_display,
get_default_address, get_company_address)
from frappe.contacts.doctype.contact.contact import get_contact_details, get_default_contact
@@ -51,6 +51,7 @@
set_other_values(out, party, party_type)
set_price_list(out, party, party_type, price_list)
out["taxes_and_charges"] = set_taxes(party.name, party_type, posting_date, company, out.customer_group, out.supplier_type)
+ out["payment_terms_template"] = get_pyt_term_template(party.name, party_type)
if not out.get("currency"):
out["currency"] = currency
@@ -161,7 +162,7 @@
out = {
party_type.lower(): party,
account_fieldname : account,
- "due_date": get_due_date(posting_date, party_type, party, company)
+ "due_date": get_due_date(posting_date, party_type, party)
}
return out
@@ -258,51 +259,53 @@
if doc.get("default_currency") and party_account_currency and company_default_currency:
if doc.default_currency != party_account_currency and doc.default_currency != company_default_currency:
- frappe.throw(_("Billing currency must be equal to either default comapany's currency or party account currency"))
+ frappe.throw(_("Billing currency must be equal to either default company's currency or party account currency"))
+
@frappe.whitelist()
-def get_due_date(posting_date, party_type, party, company):
- """Set Due Date = Posting Date + Credit Days"""
+def get_due_date(posting_date, party_type, party):
+ """Get due date from `Payment Terms Template`"""
due_date = None
if posting_date and party:
due_date = posting_date
- credit_days_based_on, credit_days = get_credit_days(party_type, party, company)
- if credit_days_based_on == "Fixed Days" and credit_days:
- due_date = add_days(posting_date, credit_days)
- elif credit_days_based_on == "Last Day of the Next Month":
- due_date = (get_first_day(posting_date, 0, 2) + datetime.timedelta(-1)).strftime("%Y-%m-%d")
+ template_name = get_pyt_term_template(party, party_type)
+ if template_name:
+ due_date = get_due_date_from_template(template_name, posting_date).strftime("%Y-%m-%d")
+ else:
+ if party_type == "Supplier":
+ supplier_type = frappe.db.get_value(party_type, party, fieldname="supplier_type")
+ template_name = frappe.db.get_value("Supplier Type", supplier_type, fieldname="payment_terms")
+ due_date = get_due_date_from_template(template_name, posting_date).strftime("%Y-%m-%d")
return due_date
-def get_credit_days(party_type, party, company):
- credit_days = 0
- if party_type and party:
- if party_type == "Customer":
- credit_days_based_on, credit_days, customer_group = \
- frappe.db.get_value(party_type, party, ["credit_days_based_on", "credit_days", "customer_group"])
+
+def get_due_date_from_template(template_name, posting_date):
+ """
+ Inspects all `Payment Term`s from the a `Payment Terms Template` and returns the due
+ date after considering all the `Payment Term`s requirements.
+ :param template_name: Name of the `Payment Terms Template`
+ :return: String representing the calculated due date
+ """
+ due_date = getdate(posting_date)
+ template = frappe.get_doc('Payment Terms Template', template_name)
+
+ for term in template.terms:
+ if term.due_date_based_on == 'Day(s) after invoice date':
+ due_date = max(due_date, add_days(due_date, term.credit_days))
+ elif term.due_date_based_on == 'Day(s) after the end of the invoice month':
+ due_date = max(due_date, add_days(get_last_day(due_date), term.credit_days))
else:
- credit_days_based_on, credit_days, supplier_type = \
- frappe.db.get_value(party_type, party, ["credit_days_based_on", "credit_days", "supplier_type"])
+ due_date = max(due_date, add_months(get_last_day(due_date), term.credit_months))
- if not credit_days_based_on:
- if party_type == "Customer" and customer_group:
- credit_days_based_on, credit_days = \
- frappe.db.get_value("Customer Group", customer_group, ["credit_days_based_on", "credit_days"])
- elif party_type == "Supplier" and supplier_type:
- credit_days_based_on, credit_days = \
- frappe.db.get_value("Supplier Type", supplier_type, ["credit_days_based_on", "credit_days"])
+ return due_date
- if not credit_days_based_on:
- credit_days_based_on, credit_days = \
- frappe.db.get_value("Company", company, ["credit_days_based_on", "credit_days"])
-
- return credit_days_based_on, credit_days
def validate_due_date(posting_date, due_date, party_type, party, company):
if getdate(due_date) < getdate(posting_date):
frappe.throw(_("Due Date cannot be before Posting Date"))
else:
- default_due_date = get_due_date(posting_date, party_type, party, company)
+ default_due_date = get_due_date(posting_date, party_type, party)
if not default_due_date:
return
@@ -345,6 +348,16 @@
return get_tax_template(posting_date, args)
+
+@frappe.whitelist()
+def get_pyt_term_template(party_name, party_type):
+ template = None
+ if party_type in ('Customer', 'Supplier'):
+ template = frappe.db.get_value(party_type, party_name, fieldname='payment_terms')
+
+ return template
+
+
def validate_party_frozen_disabled(party_type, party_name):
if party_type and party_name:
if party_type in ("Customer", "Supplier"):
diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
index 9906893..b72a669 100644
--- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
+++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
@@ -113,7 +113,7 @@
row += [self.get_party_name(gle.party_type, gle.party)]
# get due date
- due_date = voucher_details.get(gle.voucher_no, {}).get("due_date", "")
+ due_date = gle.due_date or voucher_details.get(gle.voucher_no, {}).get("due_date", "")
row += [gle.voucher_type, gle.voucher_no, due_date]
@@ -162,8 +162,7 @@
def get_entries_till(self, report_date, party_type):
# returns a generator
- return (e for e in self.get_gl_entries(party_type)
- if getdate(e.posting_date) <= report_date)
+ return (e for e in self.get_gl_entries(party_type) if getdate(e.posting_date) <= report_date)
def is_receivable_or_payable(self, gle, dr_or_cr, future_vouchers):
return (
@@ -189,7 +188,8 @@
reverse_dr_or_cr = "credit" if dr_or_cr=="debit" else "debit"
for e in self.get_gl_entries_for(gle.party, gle.party_type, gle.voucher_type, gle.voucher_no):
- if getdate(e.posting_date) <= report_date and e.name!=gle.name:
+ if getdate(e.posting_date) <= report_date and e.name!=gle.name \
+ and (not gle.due_date or getdate(e.due_date) == getdate(gle.due_date)):
amount = flt(e.get(reverse_dr_or_cr)) - flt(e.get(dr_or_cr))
if e.voucher_no not in return_entries:
payment_amount += amount
@@ -251,11 +251,11 @@
select_fields = "sum(debit) as debit, sum(credit) as credit"
self.gl_entries = frappe.db.sql("""select name, posting_date, account, party_type, party,
- voucher_type, voucher_no, against_voucher_type, against_voucher,
+ voucher_type, voucher_no, against_voucher_type, against_voucher, due_date,
account_currency, remarks, {0}
from `tabGL Entry`
where docstatus < 2 and party_type=%s and (party is not null and party != '') {1}
- group by voucher_type, voucher_no, against_voucher_type, against_voucher, party
+ group by voucher_type, voucher_no, against_voucher_type, against_voucher, party, due_date
order by posting_date, party"""
.format(select_fields, conditions), values, as_dict=True)
diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py
index 50530f5..6d6aced 100644
--- a/erpnext/accounts/utils.py
+++ b/erpnext/accounts/utils.py
@@ -583,7 +583,7 @@
invoice = 'Sales Invoice' if party_type == 'Customer' else 'Purchase Invoice'
invoice_list = frappe.db.sql("""
select
- voucher_no, voucher_type, posting_date, ifnull(sum({dr_or_cr}), 0) as invoice_amount,
+ voucher_no, voucher_type, posting_date, ifnull(sum({dr_or_cr}), 0) as invoice_amount, due_date,
(
case when (voucher_type = 'Sales Invoice' or voucher_type = 'Purchase Invoice')
then (select due_date from `tab{invoice}` where name = voucher_no)
@@ -597,6 +597,7 @@
and payment_gl_entry.party_type = invoice_gl_entry.party_type
and payment_gl_entry.party = invoice_gl_entry.party
and payment_gl_entry.account = invoice_gl_entry.account
+ and payment_gl_entry.due_date = invoice_gl_entry.due_date
and {payment_dr_or_cr} > 0
) as payment_amount
from
@@ -608,9 +609,9 @@
and ((voucher_type = 'Journal Entry'
and (against_voucher = '' or against_voucher is null))
or (voucher_type not in ('Journal Entry', 'Payment Entry')))
- group by voucher_type, voucher_no
+ group by voucher_type, voucher_no, due_date
having (invoice_amount - payment_amount) > 0.005
- order by posting_date, name""".format(
+ order by posting_date, name, due_date""".format(
dr_or_cr = dr_or_cr,
invoice = invoice,
payment_dr_or_cr = payment_dr_or_cr,
@@ -622,6 +623,8 @@
}, as_dict=True)
for d in invoice_list:
+ due_date = d.due_date or (frappe.db.get_value(d.voucher_type, d.voucher_no,
+ "posting_date" if party_type=="Employee" else "due_date"))
outstanding_invoices.append(frappe._dict({
'voucher_no': d.voucher_no,
'voucher_type': d.voucher_type,
@@ -630,8 +633,7 @@
'invoice_amount': flt(d.invoice_amount),
'payment_amount': flt(d.payment_amount),
'outstanding_amount': flt(d.invoice_amount - d.payment_amount, precision),
- 'due_date': frappe.db.get_value(d.voucher_type, d.voucher_no,
- "posting_date" if party_type=="Employee" else "due_date"),
+ 'due_date': due_date
}))
outstanding_invoices = sorted(outstanding_invoices, key=lambda k: k['due_date'] or getdate(nowdate()))
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json
index 09c987f..16b12ea 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.json
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.json
@@ -2293,6 +2293,98 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
+ "columns": 0,
+ "fieldname": "payment_schedule_section",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Payment Terms Schedule",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "payment_terms_template",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Payment Terms Template",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Payment Terms Template",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "payment_schedule",
+ "fieldtype": "Table",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Payment Schedule",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Payment Schedule",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 1,
"collapsible_depends_on": "terms",
"columns": 0,
"fieldname": "terms_section_break",
@@ -3335,7 +3427,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2017-07-19 14:03:51.838328",
+ "modified": "2017-08-16 16:57:51.320375",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Order",
diff --git a/erpnext/buying/doctype/supplier/supplier.json b/erpnext/buying/doctype/supplier/supplier.json
index 711e05d..e6cea53 100644
--- a/erpnext/buying/doctype/supplier/supplier.json
+++ b/erpnext/buying/doctype/supplier/supplier.json
@@ -597,8 +597,9 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
- "fieldname": "credit_days_based_on",
- "fieldtype": "Select",
+ "depends_on": "",
+ "fieldname": "payment_terms",
+ "fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
@@ -606,10 +607,10 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
- "label": "Credit Days Based On",
+ "label": "Default Payment Terms Template",
"length": 0,
"no_copy": 0,
- "options": "\nFixed Days\nLast Day of the Next Month",
+ "options": "Payment Terms Template",
"permlevel": 0,
"precision": "",
"print_hide": 0,
@@ -628,36 +629,6 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
- "depends_on": "eval:doc.credit_days_based_on == 'Fixed Days'",
- "fieldname": "credit_days",
- "fieldtype": "Int",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Credit Days",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"depends_on": "eval:!doc.__islocal",
"fieldname": "address_contacts",
"fieldtype": "Section Break",
@@ -970,8 +941,8 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2017-07-06 16:40:46.935608",
- "modified_by": "Administrator",
+ "modified": "2017-08-31 16:10:44.049915",
+ "modified_by": "tundebabzy@gmail.com",
"module": "Buying",
"name": "Supplier",
"name_case": "Title Case",
diff --git a/erpnext/buying/doctype/supplier/test_records.json b/erpnext/buying/doctype/supplier/test_records.json
index d2b3995..1c78807 100644
--- a/erpnext/buying/doctype/supplier/test_records.json
+++ b/erpnext/buying/doctype/supplier/test_records.json
@@ -1,6 +1,18 @@
[
{
"doctype": "Supplier",
+ "supplier_name": "_Test Supplier With Template 1",
+ "supplier_type": "_Test Supplier Type",
+ "payment_terms": "_Test Payment Term Template 3"
+ },
+ {
+ "doctype": "Supplier",
+ "supplier_name": "_Test Supplier P",
+ "supplier_type": "_Test Supplier Type",
+ "credit_days_based_on": "Fixed Days"
+ },
+ {
+ "doctype": "Supplier",
"supplier_name": "_Test Supplier with Country",
"supplier_type": "_Test Supplier Type",
"country": "Greece"
diff --git a/erpnext/buying/doctype/supplier/test_supplier.py b/erpnext/buying/doctype/supplier/test_supplier.py
index 1d089e7..c52b9a0 100644
--- a/erpnext/buying/doctype/supplier/test_supplier.py
+++ b/erpnext/buying/doctype/supplier/test_supplier.py
@@ -5,57 +5,54 @@
import frappe, unittest
from erpnext.accounts.party import get_due_date
-from erpnext.exceptions import PartyFrozen, PartyDisabled
+from erpnext.exceptions import PartyDisabled
from frappe.test_runner import make_test_records
+from frappe.utils import add_days, add_months, get_last_day
+test_dependencies = ['Payment Term', 'Payment Terms Template']
test_records = frappe.get_test_records('Supplier')
+
class TestSupplier(unittest.TestCase):
- def test_supplier_due_date_against_supplier_credit_limit(self):
- # Set Credit Limit based on Fixed days
- frappe.db.set_value("Supplier", "_Test Supplier", "credit_days_based_on", "Fixed Days")
- frappe.db.set_value("Supplier", "_Test Supplier", "credit_days", 10)
+ def test_supplier_default_payment_terms(self):
+ # Payment Term based on Days after invoice date
+ frappe.db.set_value(
+ "Supplier", "_Test Supplier With Template 1", "payment_terms", "_Test Payment Term Template 3")
- due_date = get_due_date("2016-01-22", "Supplier", "_Test Supplier", "_Test Company")
- self.assertEqual(due_date, "2016-02-01")
+ due_date = get_due_date("2016-01-22", "Supplier", "_Test Supplier With Template 1")
+ self.assertEqual(due_date, "2016-02-21")
- # Set Credit Limit based on Last day next month
- frappe.db.set_value("Supplier", "_Test Supplier", "credit_days", 0)
- frappe.db.set_value("Supplier", "_Test Supplier", "credit_days_based_on",
- "Last Day of the Next Month")
+ due_date = get_due_date("2017-01-22", "Supplier", "_Test Supplier With Template 1")
+ self.assertEqual(due_date, "2017-02-21")
- # Leap year
- due_date = get_due_date("2016-01-22", "Supplier", "_Test Supplier", "_Test Company")
+ # Payment Term based on last day of month
+ frappe.db.set_value(
+ "Supplier", "_Test Supplier With Template 1", "payment_terms", "_Test Payment Term Template 1")
+
+ due_date = get_due_date("2016-01-22", "Supplier", "_Test Supplier With Template 1")
self.assertEqual(due_date, "2016-02-29")
- # Non Leap year
- due_date = get_due_date("2017-01-22", "Supplier", "_Test Supplier", "_Test Company")
+
+ due_date = get_due_date("2017-01-22", "Supplier", "_Test Supplier With Template 1")
self.assertEqual(due_date, "2017-02-28")
- frappe.db.set_value("Supplier", "_Test Supplier", "credit_days_based_on", "")
+ frappe.db.set_value("Supplier", "_Test Supplier With Template 1", "payment_terms", "")
# Set credit limit for the supplier type instead of supplier and evaluate the due date
- # based on Fixed days
- frappe.db.set_value("Supplier Type", "_Test Supplier Type", "credit_days_based_on",
- "Fixed Days")
- frappe.db.set_value("Supplier Type", "_Test Supplier Type", "credit_days", 10)
+ frappe.db.set_value("Supplier Type", "_Test Supplier Type", "payment_terms", "_Test Payment Term Template 3")
- due_date = get_due_date("2016-01-22", "Supplier", "_Test Supplier", "_Test Company")
- self.assertEqual(due_date, "2016-02-01")
+ due_date = get_due_date("2016-01-22", "Supplier", "_Test Supplier With Template 1")
+ self.assertEqual(due_date, "2016-02-21")
# Set credit limit for the supplier type instead of supplier and evaluate the due date
- # based on Last day of next month
- frappe.db.set_value("Supplier", "_Test Supplier Type", "credit_days", 0)
- frappe.db.set_value("Supplier Type", "_Test Supplier Type", "credit_days_based_on",
- "Last Day of the Next Month")
+ frappe.db.set_value("Supplier Type", "_Test Supplier Type", "payment_terms", "_Test Payment Term Template 1")
# Leap year
- due_date = get_due_date("2016-01-22", "Supplier", "_Test Supplier", "_Test Company")
+ due_date = get_due_date("2016-01-22", "Supplier", "_Test Supplier With Template 1")
self.assertEqual(due_date, "2016-02-29")
- # Non Leap year
- due_date = get_due_date("2017-01-22", "Supplier", "_Test Supplier", "_Test Company")
+ # # Non Leap year
+ due_date = get_due_date("2017-01-22", "Supplier", "_Test Supplier With Template 1")
self.assertEqual(due_date, "2017-02-28")
-
def test_supplier_disabled(self):
make_test_records("Item")
@@ -71,7 +68,6 @@
po.save()
-
def test_supplier_country(self):
# Test that country field exists in Supplier DocType
supplier = frappe.get_doc('Supplier', '_Test Supplier with Country')
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index b24e047..25ec863 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -4,7 +4,7 @@
from __future__ import unicode_literals
import frappe, erpnext
from frappe import _, throw
-from frappe.utils import today, flt, cint, fmt_money, formatdate, getdate
+from frappe.utils import today, flt, cint, fmt_money, formatdate, getdate, add_days, add_months, get_last_day
from erpnext.setup.utils import get_exchange_rate
from erpnext.accounts.utils import get_fiscal_years, validate_fiscal_year, get_account_currency
from erpnext.utilities.transaction_base import TransactionBase
@@ -44,6 +44,9 @@
self.set_total_in_words()
if self.doctype in ("Sales Invoice", "Purchase Invoice") and not self.is_return:
+ if self.get("payment_schedule"):
+ self.set_due_date()
+ self.validate_payment_schedule()
self.validate_due_date()
self.validate_advance_entries()
@@ -614,6 +617,35 @@
for item in duplicate_list:
self.remove(item)
+ def _set_payment_schedule(self):
+ if not self.get("payment_schedule"):
+ if self.due_date:
+ self.append("payment_schedule", {
+ "due_date": self.due_date,
+ "invoice_portion": 100,
+ "payment_amount": self.grand_total
+ })
+ else:
+ self.due_date = max([d.due_date for d in self.get("payment_schedule")])
+
+ def set_due_date(self):
+ self.due_date = max([d.due_date for d in self.get("payment_schedule")])
+
+ def validate_payment_schedule(self):
+ if self.due_date and getdate(self.due_date) < getdate(self.posting_date):
+ frappe.throw(_("Due Date cannot be before posting date"))
+
+ total = 0
+ for d in self.get("payment_schedule"):
+ if getdate(d.due_date) < getdate(self.posting_date):
+ frappe.throw(_("Row {0}: Due Date cannot be before posting date").format(d.idx))
+
+ total += flt(d.payment_amount)
+
+ if total != self.grand_total:
+ frappe.throw(_("Total Payment Amount in Payment Schedule must be equal to Grand Total"))
+
+
@frappe.whitelist()
def get_tax_rate(account_head):
return frappe.db.get_value("Account", account_head, ["tax_rate", "account_name"], as_dict=True)
@@ -782,4 +814,43 @@
where due_date < CURDATE() and docstatus = 1 and outstanding_amount > 0""")
frappe.db.sql(""" update `tabPurchase Invoice` set status = 'Overdue'
- where due_date < CURDATE() and docstatus = 1 and outstanding_amount > 0""")
\ No newline at end of file
+ where due_date < CURDATE() and docstatus = 1 and outstanding_amount > 0""")
+
+@frappe.whitelist()
+def get_payment_terms(terms_template, posting_date=None, grand_total=None):
+ if not terms_template:
+ return
+
+ terms_doc = frappe.get_doc("Payment Terms Template", terms_template)
+
+ schedule = []
+ for d in terms_doc.get("terms"):
+ term_details = get_payment_term_details(d, posting_date, grand_total)
+ schedule.append(term_details)
+
+ return schedule
+
+@frappe.whitelist()
+def get_payment_term_details(term, posting_date=None, grand_total=None):
+ term_details = frappe._dict()
+ if isinstance(term, unicode):
+ term = frappe.get_doc("Payment Term", term)
+ else:
+ term_details.payment_term = term.payment_term
+ term_details.description = term.description
+ term_details.invoice_portion = term.invoice_portion
+ term_details.payment_amount = flt(term.invoice_portion) * flt(grand_total) / 100
+ if posting_date:
+ term_details.due_date = get_due_date(posting_date, term)
+ return term_details
+
+def get_due_date(posting_date, term):
+ due_date = None
+ if term.due_date_based_on == "Day(s) after invoice date":
+ due_date = add_days(posting_date, term.credit_days)
+ elif term.due_date_based_on == "Day(s) after the end of the invoice month":
+ due_date = add_days(get_last_day(posting_date), term.credit_days)
+ elif term.due_date_based_on == "Month(s) after the end of the invoice month":
+ due_date = add_months(get_last_day(posting_date), term.credit_months)
+
+ return due_date
\ No newline at end of file
diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py
index 0074560..d609b9e 100644
--- a/erpnext/controllers/sales_and_purchase_return.py
+++ b/erpnext/controllers/sales_and_purchase_return.py
@@ -256,6 +256,9 @@
target_doc.dn_detail = source_doc.dn_detail
target_doc.expense_account = source_doc.expense_account
+ def update_terms(source_doc, target_doc, source_parent):
+ target_doc.payment_amount = -source_doc.payment_amount
+
doclist = get_mapped_doc(doctype, source_name, {
doctype: {
"doctype": doctype,
@@ -272,6 +275,10 @@
},
"postprocess": update_item
},
+ "Payment Schedule": {
+ "doctype": "Payment Schedule",
+ "postprocess": update_terms
+ }
}, target_doc, set_missing_values)
return doclist
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index c14cbab..594a965 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -437,3 +437,9 @@
erpnext.patches.v8_6.rename_bom_update_tool
erpnext.patches.v8_9.add_setup_progress_actions
erpnext.patches.v8_9.rename_company_sales_target_field
+erpnext.patches.v8_10.add_due_date_to_gle
+erpnext.patches.v8_10.update_gl_due_date_for_pi_and_si
+erpnext.patches.v8_10.add_payment_terms_field_to_supplier
+erpnext.patches.v8_10.change_default_customer_credit_days
+erpnext.patches.v8_10.add_payment_terms_field_to_supplier_type
+erpnext.patches.v8_10.change_default_supplier_type_credit_days
diff --git a/erpnext/patches/v8_10/__init__.py b/erpnext/patches/v8_10/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/patches/v8_10/__init__.py
diff --git a/erpnext/patches/v8_10/add_due_date_to_gle.py b/erpnext/patches/v8_10/add_due_date_to_gle.py
new file mode 100644
index 0000000..ec5e003
--- /dev/null
+++ b/erpnext/patches/v8_10/add_due_date_to_gle.py
@@ -0,0 +1,7 @@
+from __future__ import unicode_literals
+import frappe
+
+
+def execute():
+ if not frappe.db.has_column("GL Entry", "due_date"):
+ frappe.db.sql("ALTER TABLE `tabGL Entry` ADD COLUMN `due_date` DATE NULL")
diff --git a/erpnext/patches/v8_10/add_payment_terms_field_to_supplier.py b/erpnext/patches/v8_10/add_payment_terms_field_to_supplier.py
new file mode 100644
index 0000000..b507b2a
--- /dev/null
+++ b/erpnext/patches/v8_10/add_payment_terms_field_to_supplier.py
@@ -0,0 +1,9 @@
+from __future__ import unicode_literals
+import frappe
+
+
+def execute():
+ if not frappe.db.has_column("Customer", "payment_terms"):
+ frappe.db.sql("ALTER TABLE `tabCustomer` ADD COLUMN `payment_terms` VARCHAR(140) NULL")
+ if not frappe.db.has_column("Supplier", "payment_terms"):
+ frappe.db.sql("ALTER TABLE `tabSupplier` ADD COLUMN `payment_terms` VARCHAR(140) NULL")
diff --git a/erpnext/patches/v8_10/add_payment_terms_field_to_supplier_type.py b/erpnext/patches/v8_10/add_payment_terms_field_to_supplier_type.py
new file mode 100644
index 0000000..78b8943
--- /dev/null
+++ b/erpnext/patches/v8_10/add_payment_terms_field_to_supplier_type.py
@@ -0,0 +1,7 @@
+from __future__ import unicode_literals
+import frappe
+
+
+def execute():
+ if not frappe.db.has_column("Supplier Type", "payment_terms"):
+ frappe.db.sql("ALTER TABLE `tabSupplier Type` ADD COLUMN `payment_terms` VARCHAR(140) NULL")
diff --git a/erpnext/patches/v8_10/change_default_customer_credit_days.py b/erpnext/patches/v8_10/change_default_customer_credit_days.py
new file mode 100644
index 0000000..a919713
--- /dev/null
+++ b/erpnext/patches/v8_10/change_default_customer_credit_days.py
@@ -0,0 +1,106 @@
+from __future__ import unicode_literals
+import frappe
+
+
+def execute():
+ frappe.reload_doc("accounts", "doctype", "payment_term")
+ frappe.reload_doc("accounts", "doctype", "payment_terms_template_detail")
+ frappe.reload_doc("accounts", "doctype", "payment_terms_template")
+
+ payment_terms = []
+ customers = []
+ suppliers = []
+ credit_days = frappe.db.sql(
+ "SELECT DISTINCT `credit_days`, `credit_days_based_on`, `customer_name` from "
+ "`tabCustomer` where credit_days_based_on='Fixed Days' or "
+ "credit_days_based_on='Last Day of the Next Month'")
+
+ credit_records = ((record[0], record[1], record[2]) for record in credit_days)
+ for days, based_on, customer_name in credit_records:
+ payment_term = make_payment_term(days, based_on)
+ template = make_template(payment_term)
+ payment_terms.append('WHEN `customer_name`="%s" THEN "%s"' % (customer_name, template.template_name))
+ customers.append(customer_name)
+
+ begin_query_str = "UPDATE `tabCustomer` SET `payment_terms` = CASE "
+ value_query_str = " ".join(payment_terms)
+ cond_query_str = " ELSE `payment_terms` END WHERE "
+
+ if customers:
+ frappe.db.sql(
+ begin_query_str + value_query_str + cond_query_str + '`customer_name` IN %s',
+ (customers,)
+ )
+
+ # reset
+ payment_terms = []
+ credit_days = frappe.db.sql(
+ "SELECT DISTINCT `credit_days`, `credit_days_based_on`, `supplier_name` from "
+ "`tabSupplier` where credit_days_based_on='Fixed Days' or "
+ "credit_days_based_on='Last Day of the Next Month'")
+
+ credit_records = ((record[0], record[1], record[2]) for record in credit_days)
+ for days, based_on, supplier_name in credit_records:
+ if based_on == "Fixed Days":
+ pyt_template_name = 'Default Payment Term - N{0}'.format(days)
+ else:
+ pyt_template_name = 'Default Payment Term - EO2M'
+
+ if not frappe.db.exists("Payment Term Template", pyt_template_name):
+ payment_term = make_payment_term(days, based_on)
+ template = make_template(payment_term)
+ else:
+ template = frappe.get_doc("Payment Term Template", pyt_template_name)
+
+ payment_terms.append('WHEN `supplier_name`="%s" THEN "%s"' % (supplier_name, template.template_name))
+ suppliers.append(supplier_name)
+
+ begin_query_str = "UPDATE `tabSupplier` SET `payment_terms` = CASE "
+ value_query_str = " ".join(payment_terms)
+ cond_query_str = " ELSE `payment_terms` END WHERE "
+
+ if suppliers:
+ frappe.db.sql(
+ begin_query_str + value_query_str + cond_query_str + '`supplier_name` IN %s',
+ (suppliers,)
+ )
+
+
+def make_template(payment_term):
+ doc = frappe.new_doc('Payment Terms Template Detail')
+ doc.payment_term = payment_term.payment_term_name
+ doc.due_date_based_on = payment_term.due_date_based_on
+ doc.invoice_portion = payment_term.invoice_portion
+ doc.description = payment_term.description
+ doc.credit_days = payment_term.credit_days
+ doc.credit_months = payment_term.credit_months
+
+ template = frappe.new_doc('Payment Terms Template')
+ template.template_name = 'Default Payment Term - {0}'.format(payment_term.payment_term_name)
+ template.append('terms', doc)
+ template.save()
+
+ return template
+
+
+def make_payment_term(days, based_on):
+ based_on_map = {
+ 'Fixed Days': 'Day(s) after invoice date',
+ 'Last Day of the Next Month': 'Month(s) after the end of the invoice month'
+ }
+
+ doc = frappe.new_doc('Payment Term')
+ doc.due_date_based_on = based_on_map.get(based_on)
+ doc.invoice_portion = 100
+
+ if based_on == 'Fixed Days':
+ doc.credit_days = days
+ doc.description = 'Net payable within {0} days'.format(days)
+ doc.payment_term_name = 'N{0}'.format(days)
+ else:
+ doc.credit_months = 1
+ doc.description = 'Net payable by the end of next month'.format(days)
+ doc.payment_term_name = 'EO2M'
+
+ doc.save()
+ return doc
diff --git a/erpnext/patches/v8_10/change_default_supplier_type_credit_days.py b/erpnext/patches/v8_10/change_default_supplier_type_credit_days.py
new file mode 100644
index 0000000..448bdbe
--- /dev/null
+++ b/erpnext/patches/v8_10/change_default_supplier_type_credit_days.py
@@ -0,0 +1,39 @@
+import frappe
+from erpnext.patches.v8_10.change_default_customer_credit_days import make_payment_term, make_template
+
+
+def execute():
+ payment_terms = []
+ supplier_types = []
+
+ credit_days = frappe.db.sql(
+ "SELECT DISTINCT `credit_days`, `credit_days_based_on`, `supplier_type` from "
+ "`tabSupplier Type` where credit_days_based_on='Fixed Days' or "
+ "credit_days_based_on='Last Day of the Next Month'")
+
+ records = ((record[0], record[1], record[2]) for record in credit_days)
+
+ for days, based_on, supplier_type in records:
+ if based_on == "Fixed Days":
+ pyt_term_name = 'N{0}'.format(days)
+ else:
+ pyt_term_name = 'EO2M'
+
+ if not frappe.db.exists("Payment Term", pyt_term_name):
+ payment_term = make_payment_term(days, based_on)
+ make_template(payment_term)
+ else:
+ payment_term = frappe.get_doc("Payment Term", pyt_term_name)
+
+ payment_terms.append('WHEN `supplier_type`="%s" THEN "%s"' % (supplier_type, payment_term.payment_term_name))
+ supplier_types.append(supplier_type)
+
+ begin_query_str = "UPDATE `tabSupplier Type` SET `payment_terms` = CASE "
+ value_query_str = " ".join(payment_terms)
+ cond_query_str = " ELSE `payment_terms` END WHERE "
+
+ if supplier_types:
+ frappe.db.sql(
+ begin_query_str + value_query_str + cond_query_str + '`supplier_type` IN %s',
+ (supplier_types,)
+ )
diff --git a/erpnext/patches/v8_10/update_gl_due_date_for_pi_and_si.py b/erpnext/patches/v8_10/update_gl_due_date_for_pi_and_si.py
new file mode 100644
index 0000000..8ececeb
--- /dev/null
+++ b/erpnext/patches/v8_10/update_gl_due_date_for_pi_and_si.py
@@ -0,0 +1,136 @@
+from __future__ import unicode_literals
+import frappe
+
+"""This will update existing GL Entries by saving its linked Purchase/Sales Invoice's
+and Journal Entry's due date as the due date for the GL Entry"""
+
+
+def execute():
+ kwargs = get_query_kwargs()
+
+ for kwarg in kwargs:
+ for batch in get_result_in_batches(**kwarg):
+ voucher_num_col = kwarg.get('voucher_num_col', 'voucher_no')
+ voucher_type = kwarg.get('use_voucher_type') or kwarg.get('voucher_type')
+ conditions, names = build_conditions(batch, voucher_type, voucher_num_col)
+ if conditions and names:
+ start = 'UPDATE `tabGL Entry` SET `due_date` = CASE '
+ cond = ' '.join(conditions)
+ else_cond = ' ELSE `due_date` END WHERE '
+
+ frappe.db.sql(
+ start + cond + else_cond + voucher_num_col + ' IN %s',
+ values=(names,)
+ )
+
+
+def get_result_in_batches(**kwargs):
+ """A simple generator to yield slices of GL Entry records"""
+ while True:
+ batch = get_gle_batch(**kwargs)
+ if batch:
+ yield batch
+ else:
+ return
+
+
+def get_gle_batch(**kwargs):
+ """Returns a slice of records in GL Entry"""
+ doctype = kwargs.get('doctype')
+ fields = kwargs.get('fields')
+ limit_start = kwargs.get('limit_start')
+ limit_page_length = kwargs.get('limit_page_length')
+ filters = kwargs.get('filters')
+ or_filters = kwargs.get('or_filters')
+
+ results = frappe.get_list(
+ doctype, fields=fields, limit_start=limit_start, limit_page_length=limit_page_length,
+ filters=filters, or_filters=or_filters
+ )
+
+ return results
+
+
+def build_conditions(query_results, voucher_type, voucher_num_col):
+ """
+ builds the string to be used is sql CASE statement. Returns the a tuple of
+ the string for the CASE statement and a tuple of applicable voucher names
+ """
+ conditions = []
+ invoice_names = []
+
+ for result in query_results:
+ voucher_no = result.get(voucher_num_col)
+ if voucher_no:
+ invoice_names.append("%s" % (voucher_no,))
+
+ # get invoice details
+ invoice_details = frappe.get_list(
+ voucher_type, fields=['name', 'due_date'], filters={'name': ('in', invoice_names)}
+ )
+
+ if invoice_details:
+ for d in invoice_details:
+ conditions.append('WHEN `{voucher_no}`="{number}" THEN "{date}"'.format(
+ number=d.name, date=d.due_date, voucher_no=voucher_num_col))
+
+ return conditions, invoice_names
+
+
+def get_query_kwargs():
+ pi_kwargs = dict(
+ voucher_type='Purchase Invoice', doctype='GL Entry', fields=['voucher_no'],
+ limit_start=0, limit_page_length=5, filters={
+ "ifnull(due_date, '')": ('=', ''), "ifnull(party, '')": ('!=', ''),
+ 'voucher_type': 'Purchase Invoice', 'credit': ('!=', '0')
+ }
+ )
+
+ si_kwargs = dict(
+ voucher_type='Sales Invoice', doctype='GL Entry', fields=['voucher_no'],
+ limit_start=0, limit_page_length=5, filters={
+ "ifnull(due_date, '')": ('=', ''), "ifnull(party, '')": ('!=', ''),
+ 'voucher_type': 'Sales Invoice', 'debit': ('!=', '0')
+ }
+ )
+
+ journal_kwargs_si = dict(
+ voucher_type='Journal Entry', doctype='GL Entry', fields=['against_voucher'],
+ limit_start=0, limit_page_length=5, filters={
+ "ifnull(due_date, '')": ('=', ''), "ifnull(party, '')": ('!=', ''),
+ 'voucher_type': 'Journal Entry', 'against_voucher_type': 'Sales Invoice'
+ },
+ voucher_num_col='against_voucher', use_voucher_type='Sales Invoice',
+ )
+
+ journal_kwargs_pi = dict(
+ voucher_type='Journal Entry', doctype='GL Entry', fields=['against_voucher'],
+ limit_start=0, limit_page_length=5, filters={
+ "ifnull(due_date, '')": ('=', ''), "ifnull(party, '')": ('!=', ''),
+ 'voucher_type': 'Journal Entry', 'against_voucher_type': 'Purchase Invoice'
+ },
+ voucher_num_col='against_voucher', use_voucher_type='Purchase Invoice',
+ )
+
+ payment_entry_kwargs_pi = dict(
+ voucher_type='Payment Entry', doctype='GL Entry', fields=['against_voucher'],
+ limit_start=0, limit_page_length=5, filters={
+ "ifnull(due_date, '')": ('=', ''), "ifnull(party, '')": ('!=', ''),
+ 'voucher_type': 'Payment Entry', 'against_voucher_type': 'Purchase Invoice'
+ },
+ voucher_num_col='against_voucher', use_voucher_type='Purchase Invoice',
+ )
+
+ payment_entry_kwargs_si = dict(
+ voucher_type='Payment Entry', doctype='GL Entry', fields=['against_voucher'],
+ limit_start=0, limit_page_length=5, filters={
+ "ifnull(due_date, '')": ('=', ''), "ifnull(party, '')": ('!=', ''),
+ 'voucher_type': 'Payment Entry', 'against_voucher_type': 'Sales Invoice'
+ },
+ voucher_num_col='against_voucher', use_voucher_type='Sales Invoice',
+ )
+
+ return [
+ pi_kwargs, si_kwargs, journal_kwargs_pi, journal_kwargs_si,
+ payment_entry_kwargs_pi, payment_entry_kwargs_si
+ ]
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index be55406..91ae634 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -1111,6 +1111,46 @@
}
}
},
+
+ payment_terms_template: function() {
+ var me = this;
+ if(this.frm.doc.payment_terms_template) {
+ frappe.call({
+ method: "erpnext.controllers.accounts_controller.get_payment_terms",
+ args: {
+ terms_template: this.frm.doc.payment_terms_template,
+ posting_date: this.frm.doc.posting_date || this.frm.doc.transaction_date,
+ grand_total: this.frm.doc.grand_total
+ },
+ callback: function(r) {
+ if(r.message && !r.exc) {
+ me.frm.set_value("payment_schedule", r.message);
+ }
+ }
+ })
+ }
+ },
+
+ payment_term: function(doc, cdt, cdn) {
+ var row = locals[cdt][cdn];
+ if(row.payment_term) {
+ frappe.call({
+ method: "erpnext.controllers.accounts_controller.get_payment_term_details",
+ args: {
+ term: row.payment_term,
+ posting_date: this.frm.doc.posting_date || this.frm.doc.transaction_date,
+ grand_total: this.frm.doc.grand_total
+ },
+ callback: function(r) {
+ if(r.message && !r.exc) {
+ for (var d in r.message) {
+ frappe.model.set_value(cdt, cdn, d, r.message[d]);
+ }
+ }
+ }
+ })
+ }
+ }
});
erpnext.show_serial_batch_selector = function(frm, d) {
diff --git a/erpnext/selling/doctype/customer/customer.json b/erpnext/selling/doctype/customer/customer.json
index 52c6b6d..222d2e5 100644
--- a/erpnext/selling/doctype/customer/customer.json
+++ b/erpnext/selling/doctype/customer/customer.json
@@ -786,7 +786,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
- "collapsible_depends_on": "eval:doc.credit_days || doc.credit_limit",
+ "collapsible_depends_on": "",
"columns": 0,
"fieldname": "credit_limit_section",
"fieldtype": "Section Break",
@@ -818,69 +818,6 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
- "fieldname": "credit_days_based_on",
- "fieldtype": "Select",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Credit Days Based On",
- "length": 0,
- "no_copy": 0,
- "options": "\nFixed Days\nLast Day of the Next Month",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "depends_on": "eval:doc.credit_days_based_on=='Fixed Days'",
- "fieldname": "credit_days",
- "fieldtype": "Int",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Credit Days",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "credit_days",
- "oldfieldtype": "Int",
- "permlevel": 1,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "credit_limit",
"fieldtype": "Currency",
"hidden": 0,
@@ -911,6 +848,38 @@
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "",
+ "fieldname": "payment_terms",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Default Payment Terms Template",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Payment Terms Template",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
"collapsible": 1,
"collapsible_depends_on": "customer_details",
"columns": 0,
@@ -1202,8 +1171,8 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2017-07-24 00:55:07.445783",
- "modified_by": "Administrator",
+ "modified": "2017-08-31 15:12:18.637132",
+ "modified_by": "tundebabzy@gmail.com",
"module": "Selling",
"name": "Customer",
"name_case": "Title Case",
diff --git a/erpnext/selling/doctype/customer/test_customer.js b/erpnext/selling/doctype/customer/test_customer.js
new file mode 100644
index 0000000..65b81af
--- /dev/null
+++ b/erpnext/selling/doctype/customer/test_customer.js
@@ -0,0 +1,23 @@
+/* eslint-disable */
+// rename this file from _test_[name] to test_[name] to activate
+// and remove above this line
+
+QUnit.test("test: Customer", function (assert) {
+ let done = assert.async();
+
+ // number of asserts
+ assert.expect(1);
+
+ frappe.run_serially([
+ // insert a new Customer
+ () => frappe.tests.make('Customer', [
+ // values to be set
+ {key: 'value'}
+ ]),
+ () => {
+ assert.equal(cur_frm.doc.key, 'value');
+ },
+ () => done()
+ ]);
+
+});
diff --git a/erpnext/selling/doctype/customer/test_records.json b/erpnext/selling/doctype/customer/test_records.json
index 94f14ed..43d6beb 100644
--- a/erpnext/selling/doctype/customer/test_records.json
+++ b/erpnext/selling/doctype/customer/test_records.json
@@ -1,6 +1,14 @@
[
{
"customer_group": "_Test Customer Group",
+ "customer_name": "_Test Customer P",
+ "customer_type": "Individual",
+ "doctype": "Customer",
+ "territory": "_Test Territory",
+ "credit_days_based_on": "Fixed Days"
+ },
+ {
+ "customer_group": "_Test Customer Group",
"customer_name": "_Test Customer",
"customer_type": "Individual",
"doctype": "Customer",
diff --git a/erpnext/selling/doctype/quotation/quotation.json b/erpnext/selling/doctype/quotation/quotation.json
index 0afc5ca..bd9814b 100644
--- a/erpnext/selling/doctype/quotation/quotation.json
+++ b/erpnext/selling/doctype/quotation/quotation.json
@@ -2094,6 +2094,100 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
+ "collapsible_depends_on": "eval:!doc.__islocal",
+ "columns": 0,
+ "depends_on": "",
+ "fieldname": "payment_schedule_section",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Payment Schedule",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "payment_terms_template",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Payment Terms Template",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Payment Terms Template",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "payment_schedule",
+ "fieldtype": "Table",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Payment Schedule",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Payment Schedule",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 1,
"collapsible_depends_on": "terms",
"columns": 0,
"fieldname": "terms_section_break",
@@ -2633,7 +2727,7 @@
"istable": 0,
"max_attachments": 1,
"menu_index": 0,
- "modified": "2017-08-09 06:35:48.691648",
+ "modified": "2017-08-16 15:31:23.079739",
"modified_by": "Administrator",
"module": "Selling",
"name": "Quotation",
diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json
index b69b3fd..94d33f0 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.json
+++ b/erpnext/selling/doctype/sales_order/sales_order.json
@@ -2255,10 +2255,9 @@
"label": "Packed Items",
"length": 0,
"no_copy": 0,
- "oldfieldname": "packing_details",
- "oldfieldtype": "Table",
"options": "Packed Item",
"permlevel": 0,
+ "precision": "",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 1,
@@ -2274,6 +2273,99 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
+ "collapsible_depends_on": "eval:!doc.__islocal||doc.payment_schedule",
+ "columns": 0,
+ "fieldname": "payment_schedule_section",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Payment Terms Schedule",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "payment_terms_template",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Payment Terms Template",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Payment Terms Template",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "payment_schedule",
+ "fieldtype": "Table",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Payment Schedule",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Payment Schedule",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 1,
"collapsible_depends_on": "terms",
"columns": 0,
"fieldname": "terms_section_break",
@@ -3659,7 +3751,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2017-08-07 21:27:10.073581",
+ "modified": "2017-08-16 16:00:08.404180",
"modified_by": "Administrator",
"module": "Selling",
"name": "Sales Order",
diff --git a/erpnext/setup/doctype/supplier_type/supplier_type.json b/erpnext/setup/doctype/supplier_type/supplier_type.json
index 9b40e0f..d7d7084 100644
--- a/erpnext/setup/doctype/supplier_type/supplier_type.json
+++ b/erpnext/setup/doctype/supplier_type/supplier_type.json
@@ -1,5 +1,6 @@
{
"allow_copy": 0,
+ "allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 1,
"autoname": "field:supplier_type",
@@ -12,6 +13,7 @@
"editable_grid": 0,
"fields": [
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -42,6 +44,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
@@ -71,12 +74,13 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
- "fieldname": "credit_days_based_on",
- "fieldtype": "Select",
+ "fieldname": "payment_terms",
+ "fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
@@ -84,39 +88,10 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
- "label": "Credit Days Based On",
+ "label": "Default Payment Terms Template",
"length": 0,
"no_copy": 0,
- "options": "\nFixed Days\nLast Day of the Next Month",
- "permlevel": 1,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "depends_on": "eval:doc.credit_days_based_on=='Fixed Days'",
- "fieldname": "credit_days",
- "fieldtype": "Int",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Credit Days",
- "length": 0,
- "no_copy": 0,
+ "options": "Payment Terms Template",
"permlevel": 0,
"precision": "",
"print_hide": 0,
@@ -130,6 +105,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -158,6 +134,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -189,18 +166,18 @@
"unique": 0
}
],
+ "has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"icon": "fa fa-flag",
"idx": 1,
"image_view": 0,
"in_create": 0,
- "in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2017-02-20 13:25:25.641431",
+ "modified": "2017-09-04 18:54:10.093500",
"modified_by": "Administrator",
"module": "Setup",
"name": "Supplier Type",
diff --git a/erpnext/setup/doctype/supplier_type/test_supplier_type.js b/erpnext/setup/doctype/supplier_type/test_supplier_type.js
new file mode 100644
index 0000000..085dddd
--- /dev/null
+++ b/erpnext/setup/doctype/supplier_type/test_supplier_type.js
@@ -0,0 +1,23 @@
+/* eslint-disable */
+// rename this file from _test_[name] to test_[name] to activate
+// and remove above this line
+
+QUnit.test("test: Supplier Type", function (assert) {
+ let done = assert.async();
+
+ // number of asserts
+ assert.expect(1);
+
+ frappe.run_serially([
+ // insert a new Supplier Type
+ () => frappe.tests.make('Supplier Type', [
+ // values to be set
+ {key: 'value'}
+ ]),
+ () => {
+ assert.equal(cur_frm.doc.key, 'value');
+ },
+ () => done()
+ ]);
+
+});