Merge pull request #5620 from nabinhait/payment_entry
Payment Entry
diff --git a/erpnext/accounts/doctype/account/test_account.py b/erpnext/accounts/doctype/account/test_account.py
index 4580d02..5744721 100644
--- a/erpnext/accounts/doctype/account/test_account.py
+++ b/erpnext/accounts/doctype/account/test_account.py
@@ -12,6 +12,7 @@
["_Test Bank", "Bank Accounts", 0, "Bank", None],
["_Test Bank USD", "Bank Accounts", 0, "Bank", "USD"],
["_Test Bank EUR", "Bank Accounts", 0, "Bank", "EUR"],
+ ["_Test Cash", "Cash In Hand", 0, "Cash", None],
["_Test Account Stock Expenses", "Direct Expenses", 1, None, None],
["_Test Account Shipping Charges", "_Test Account Stock Expenses", 0, "Chargeable", None],
@@ -32,6 +33,7 @@
["_Test Account CST", "Direct Expenses", 0, "Tax", None],
["_Test Account Discount", "Direct Expenses", 0, None, None],
["_Test Write Off", "Indirect Expenses", 0, None, None],
+ ["_Test Exchange Gain/Loss", "Indirect Expenses", 0, None, None],
# related to Account Inventory Integration
["_Test Account Stock In Hand", "Current Assets", 0, None, None],
diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
index d46c2d5..aca83da 100644
--- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
+++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
@@ -91,6 +91,55 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "fieldname": "column_break_4",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "allow_payment_entry_via_journal_entry",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Allow Payment Entry via Journal Entry",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
"description": "Role that is allowed to submit transactions that exceed credit limits set.",
"fieldname": "credit_controller",
"fieldtype": "Link",
@@ -150,7 +199,7 @@
"issingle": 1,
"istable": 0,
"max_attachments": 0,
- "modified": "2016-06-27 15:18:27.566087",
+ "modified": "2016-06-27 15:18:28.566087",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounts Settings",
diff --git a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.js b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.js
index 3de8241..4b33fd3 100644
--- a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.js
+++ b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.js
@@ -3,11 +3,22 @@
frappe.ui.form.on("Bank Reconciliation", {
setup: function(frm) {
- frm.get_docfield("journal_entries").allow_bulk_edit = 1;
+ frm.get_docfield("payment_entries").allow_bulk_edit = 1;
frm.add_fetch("bank_account", "account_currency", "account_currency");
+
+ frm.get_field('payment_entries').grid.editable_fields = [
+ {fieldname: 'against_account', columns: 3},
+ {fieldname: 'amount', columns: 2},
+ {fieldname: 'cheque_number', columns: 3},
+ {fieldname: 'clearance_date', columns: 3}
+ ];
},
onload: function(frm) {
+ var default_bank_account = locals[":Company"][frappe.defaults.get_user_default("Company")]["default_bank_account"];
+
+ frm.set_value("bank_account", default_bank_account);
+
frm.set_query("bank_account", function() {
return {
"filters": {
@@ -27,16 +38,21 @@
update_clearance_date: function(frm) {
return frappe.call({
- method: "update_details",
- doc: frm.doc
- });
- },
- get_relevant_entries: function(frm) {
- return frappe.call({
- method: "get_details",
+ method: "update_clearance_date",
doc: frm.doc,
callback: function(r, rt) {
- frm.refresh()
+ frm.refresh_field("payment_entries");
+ frm.refresh_fields();
+ }
+ });
+ },
+ get_payment_entries: function(frm) {
+ return frappe.call({
+ method: "get_payment_entries",
+ doc: frm.doc,
+ callback: function(r, rt) {
+ frm.refresh_field("payment_entries");
+ frm.refresh_fields();
}
});
}
diff --git a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.json b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.json
index 59a3cfc..1a7b763 100644
--- a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.json
+++ b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.json
@@ -2,6 +2,7 @@
"allow_copy": 1,
"allow_import": 0,
"allow_rename": 0,
+ "beta": 0,
"creation": "2013-01-10 16:34:05",
"custom": 0,
"docstatus": 0,
@@ -16,6 +17,7 @@
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Bank Account",
@@ -40,6 +42,7 @@
"fieldtype": "Link",
"hidden": 1,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Account Currency",
@@ -64,6 +67,7 @@
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "From Date",
@@ -87,6 +91,7 @@
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "To Date",
@@ -110,6 +115,7 @@
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Include Reconciled Entries",
@@ -129,13 +135,14 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "fieldname": "get_relevant_entries",
+ "fieldname": "get_payment_entries",
"fieldtype": "Button",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
- "label": "Get Relevant Entries",
+ "label": "Get Payment Entries",
"length": 0,
"no_copy": 0,
"options": "",
@@ -153,13 +160,14 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "fieldname": "journal_entries",
+ "fieldname": "payment_entries",
"fieldtype": "Table",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
- "label": "Journal Entries",
+ "label": "Payment Entries",
"length": 0,
"no_copy": 0,
"options": "Bank Reconciliation Detail",
@@ -181,6 +189,7 @@
"fieldtype": "Button",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Update Clearance Date",
@@ -205,6 +214,7 @@
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Total Amount",
@@ -226,6 +236,7 @@
"hide_toolbar": 1,
"icon": "icon-check",
"idx": 1,
+ "image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
@@ -233,7 +244,7 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
- "modified": "2015-11-30 12:44:45.105451",
+ "modified": "2016-06-28 13:11:09.396353",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Bank Reconciliation",
@@ -260,7 +271,10 @@
"write": 1
}
],
+ "quick_entry": 1,
"read_only": 1,
"read_only_onload": 0,
+ "sort_order": "ASC",
+ "track_seen": 0,
"version": 0
}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py
index fb95731..b07c82d 100644
--- a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py
+++ b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py
@@ -12,7 +12,7 @@
}
class BankReconciliation(Document):
- def get_details(self):
+ def get_payment_entries(self):
if not (self.bank_account and self.from_date and self.to_date):
msgprint("Bank Account, From Date and To Date are Mandatory")
return
@@ -22,48 +22,68 @@
condition = "and (clearance_date is null or clearance_date='0000-00-00')"
- dl = frappe.db.sql("""select t1.name, t1.cheque_no, t1.cheque_date, t2.debit_in_account_currency,
- t2.credit_in_account_currency, t1.posting_date, t2.against_account, t1.clearance_date
+ journal_entries = frappe.db.sql("""
+ select
+ "Journal Entry" as payment_document, t1.name as payment_entry,
+ t1.cheque_no as cheque_number, t1.cheque_date,
+ abs(t2.debit_in_account_currency - t2.credit_in_account_currency) as amount,
+ t1.posting_date, t2.against_account, t1.clearance_date
from
`tabJournal Entry` t1, `tabJournal Entry Account` t2
where
- t2.parent = t1.name and t2.account = %s
- and t1.posting_date >= %s and t1.posting_date <= %s and t1.docstatus=1
- and ifnull(t1.is_opening, 'No') = 'No' %s
- order by t1.posting_date DESC, t1.name DESC""" %
- ('%s', '%s', '%s', condition), (self.bank_account, self.from_date, self.to_date), as_dict=1)
-
- self.set('journal_entries', [])
+ t2.parent = t1.name and t2.account = %s and t1.docstatus=1
+ and t1.posting_date >= %s and t1.posting_date <= %s
+ and ifnull(t1.is_opening, 'No') = 'No' {0}
+ order by t1.posting_date DESC, t1.name DESC
+ """.format(condition), (self.bank_account, self.from_date, self.to_date), as_dict=1)
+
+ payment_entries = frappe.db.sql("""
+ select
+ "Payment Entry" as payment_document, name as payment_entry,
+ reference_no as cheque_number, reference_date as cheque_date,
+ if(paid_from=%s, paid_amount, received_amount) as amount,
+ posting_date, party as against_account, clearance_date
+ from `tabPayment Entry`
+ where
+ (paid_from=%s or paid_to=%s) and docstatus=1
+ and posting_date >= %s and posting_date <= %s {0}
+ order by
+ posting_date DESC, name DESC
+ """.format(condition),
+ (self.bank_account, self.bank_account, self.bank_account, self.from_date, self.to_date), as_dict=1)
+
+ entries = sorted(list(payment_entries)+list(journal_entries),
+ key=lambda k: k['posting_date'] or getdate(nowdate()))
+
+ self.set('payment_entries', [])
self.total_amount = 0.0
- for d in dl:
- nl = self.append('journal_entries', {})
- nl.posting_date = d.posting_date
- nl.voucher_id = d.name
- nl.cheque_number = d.cheque_no
- nl.cheque_date = d.cheque_date
- nl.debit = d.debit_in_account_currency
- nl.credit = d.credit_in_account_currency
- nl.against_account = d.against_account
- nl.clearance_date = d.clearance_date
- self.total_amount += flt(d.debit_in_account_currency) - flt(d.credit_in_account_currency)
+ for d in entries:
+ row = self.append('payment_entries', {})
+ row.update(d)
+ self.total_amount += flt(d.amount)
- def update_details(self):
- vouchers = []
- for d in self.get('journal_entries'):
+ def update_clearance_date(self):
+ clearance_date_updated = False
+ for d in self.get('payment_entries'):
if d.clearance_date:
if d.cheque_date and getdate(d.clearance_date) < getdate(d.cheque_date):
- frappe.throw(_("Clearance date cannot be before check date in row {0}").format(d.idx))
+ frappe.throw(_("Row #{0}: Clearance date {1} cannot be before Cheque Date {2}")
+ .format(d.idx, d.clearance_date, d.cheque_date))
if d.clearance_date or self.include_reconciled_entries:
if not d.clearance_date:
d.clearance_date = None
- frappe.db.set_value("Journal Entry", d.voucher_id, "clearance_date", d.clearance_date)
- frappe.db.sql("""update `tabJournal Entry` set clearance_date = %s, modified = %s
- where name=%s""", (d.clearance_date, nowdate(), d.voucher_id))
- vouchers.append(d.voucher_id)
- if vouchers:
- msgprint("Clearance Date updated in: {0}".format(", ".join(vouchers)))
+ frappe.db.set_value(d.payment_document, d.payment_entry, "clearance_date", d.clearance_date)
+ frappe.db.sql("""update `tab{0}` set clearance_date = %s, modified = %s
+ where name=%s""".format(d.payment_document),
+ (d.clearance_date, nowdate(), d.payment_entry))
+
+ clearance_date_updated = True
+
+ if clearance_date_updated:
+ self.get_payment_entries()
+ msgprint(_("Clearance Date updated"))
else:
msgprint(_("Clearance Date not mentioned"))
diff --git a/erpnext/accounts/doctype/bank_reconciliation_detail/bank_reconciliation_detail.json b/erpnext/accounts/doctype/bank_reconciliation_detail/bank_reconciliation_detail.json
index fc3c3b6..b56ad8b 100644
--- a/erpnext/accounts/doctype/bank_reconciliation_detail/bank_reconciliation_detail.json
+++ b/erpnext/accounts/doctype/bank_reconciliation_detail/bank_reconciliation_detail.json
@@ -2,6 +2,7 @@
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 0,
+ "beta": 0,
"creation": "2013-02-22 01:27:37",
"custom": 0,
"docstatus": 0,
@@ -11,18 +12,45 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "fieldname": "voucher_id",
+ "fieldname": "payment_document",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Payment Document",
+ "length": 0,
+ "no_copy": 0,
+ "options": "DocType",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "payment_entry",
+ "fieldtype": "Dynamic Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
- "label": "Voucher ID",
+ "label": "Payment Entry",
"length": 0,
"no_copy": 0,
"oldfieldname": "voucher_id",
"oldfieldtype": "Link",
- "options": "Journal Entry",
+ "options": "payment_document",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
@@ -42,6 +70,7 @@
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Against Account",
@@ -64,13 +93,14 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "fieldname": "debit",
+ "fieldname": "amount",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
- "in_list_view": 0,
- "label": "Debit",
+ "in_list_view": 1,
+ "label": "Amount",
"length": 0,
"no_copy": 0,
"oldfieldname": "debit",
@@ -90,36 +120,11 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "fieldname": "credit",
- "fieldtype": "Currency",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "label": "Credit",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "credit",
- "oldfieldtype": "Currency",
- "options": "account_currency",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
"fieldname": "column_break_5",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"length": 0,
@@ -144,8 +149,9 @@
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
- "in_list_view": 1,
+ "in_list_view": 0,
"label": "Posting Date",
"length": 0,
"no_copy": 0,
@@ -169,6 +175,7 @@
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Cheque Number",
@@ -194,6 +201,7 @@
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Cheque Date",
@@ -219,6 +227,7 @@
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Clearance Date",
@@ -240,6 +249,7 @@
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 1,
+ "image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
@@ -247,12 +257,15 @@
"istable": 1,
"max_attachments": 0,
"menu_index": 0,
- "modified": "2016-02-17 06:50:40.074578",
+ "modified": "2016-06-28 13:23:15.412477",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Bank Reconciliation Detail",
"owner": "Administrator",
"permissions": [],
+ "quick_entry": 1,
"read_only": 0,
- "read_only_onload": 0
+ "read_only_onload": 0,
+ "sort_order": "ASC",
+ "track_seen": 0
}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py
index 1f95fb8..b90cd23 100644
--- a/erpnext/accounts/doctype/gl_entry/gl_entry.py
+++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py
@@ -38,7 +38,7 @@
self.against_voucher)
def check_mandatory(self):
- mandatory = ['account','remarks','voucher_type','voucher_no','company']
+ mandatory = ['account','voucher_type','voucher_no','company']
for k in mandatory:
if not self.get(k):
frappe.throw(_("{0} is required").format(_(self.meta.get_label(k))))
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js
index f670c90..cb6c36c 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.js
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js
@@ -283,7 +283,8 @@
type: "GET",
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_default_bank_cash_account",
args: {
- "voucher_type": doc.voucher_type,
+ "account_type": (doc.voucher_type=="Bank Entry" ?
+ "Bank" : (doc.voucher_type=="Cash" ? "Cash" : null)),
"company": doc.company
},
callback: function(r) {
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py
index 40386e0..96b95f9 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.py
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py
@@ -525,22 +525,19 @@
d.party_balance = party_balance[(d.party_type, d.party)]
@frappe.whitelist()
-def get_default_bank_cash_account(company, voucher_type, mode_of_payment=None, account=None):
+def get_default_bank_cash_account(company, account_type=None, mode_of_payment=None, account=None):
from erpnext.accounts.doctype.sales_invoice.sales_invoice import get_bank_cash_account
if mode_of_payment:
- account = get_bank_cash_account(mode_of_payment, company)
- if account.get("account"):
- account.update({"balance": get_balance_on(account.get("account"))})
- return account
+ account = get_bank_cash_account(mode_of_payment, company).get("account")
if not account:
- if voucher_type=="Bank Entry":
+ if account_type=="Bank":
account = frappe.db.get_value("Company", company, "default_bank_account")
if not account:
account = frappe.db.get_value("Account",
{"company": company, "account_type": "Bank", "is_group": 0})
- elif voucher_type=="Cash Entry":
+ elif account_type=="Cash":
account = frappe.db.get_value("Company", company, "default_cash_account")
if not account:
account = frappe.db.get_value("Account",
@@ -549,12 +546,13 @@
if account:
account_details = frappe.db.get_value("Account", account,
["account_currency", "account_type"], as_dict=1)
- return {
+
+ return frappe._dict({
"account": account,
"balance": get_balance_on(account),
"account_currency": account_details.account_currency,
"account_type": account_details.account_type
- }
+ })
@frappe.whitelist()
def get_payment_entry_against_order(dt, dn, amount=None, debit_in_account_currency=None, journal_entry=False, bank_account=None):
@@ -662,7 +660,7 @@
bank_row = je.append("accounts")
#make it bank_details
- bank_account = get_default_bank_cash_account(ref_doc.company, "Bank Entry", account=args.get("bank_account"))
+ bank_account = get_default_bank_cash_account(ref_doc.company, "Bank", account=args.get("bank_account"))
if bank_account:
bank_row.update(bank_account)
bank_row.exchange_rate = get_exchange_rate(bank_account["account"],
@@ -814,7 +812,7 @@
company_currency = get_company_currency(company)
if account_currency != company_currency:
- if reference_type and reference_name and frappe.get_meta(reference_type).get_field("conversion_rate"):
+ if reference_type in ("Sales Invoice", "Purchase Invoice") and reference_name:
exchange_rate = frappe.db.get_value(reference_type, reference_name, "conversion_rate")
elif account_details and account_details.account_type == "Bank" and \
@@ -831,6 +829,7 @@
# don't return None or 0 as it is multipled with a value and that value could be lost
return exchange_rate or 1
+@frappe.whitelist()
def get_average_exchange_rate(account):
exchange_rate = 0
bank_balance_in_account_currency = get_balance_on(account)
diff --git a/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.js b/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.js
index 10807ff..9115c20 100644
--- a/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.js
+++ b/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.js
@@ -2,11 +2,12 @@
// License: GNU General Public License v3. See license.txt
cur_frm.set_query("default_account", "accounts", function(doc, cdt, cdn) {
+ var d = locals[cdt][cdn];
return{
filters: [
['Account', 'account_type', 'in', 'Bank, Cash'],
['Account', 'is_group', '=', 0],
- ['Account', 'company', '=', doc.company]
+ ['Account', 'company', '=', d.company]
]
}
});
diff --git a/erpnext/accounts/doctype/payment_entry/__init__.py b/erpnext/accounts/doctype/payment_entry/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_entry/__init__.py
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js
new file mode 100644
index 0000000..230d728
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js
@@ -0,0 +1,724 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Payment Entry', {
+ onload: function(frm) {
+ if(frm.doc.__islocal) {
+ if (!frm.doc.paid_from) frm.set_value("paid_from_account_currency", null);
+ if (!frm.doc.paid_to) frm.set_value("paid_to_account_currency", null);
+ }
+ },
+
+ setup: function(frm) {
+ frm.get_field('references').grid.editable_fields = [
+ {fieldname: 'reference_doctype', columns: 2},
+ {fieldname: 'reference_name', columns: 3},
+ {fieldname: 'outstanding_amount', columns: 3},
+ {fieldname: 'allocated_amount', columns: 3}
+ ];
+
+ var party_account_type = frm.doc.party_type=="Customer" ? "Receivable" : "Payable";
+
+ frm.set_query("paid_from", function() {
+ var account_types = in_list(["Pay", "Internal Transfer"], frm.doc.payment_type) ?
+ ["Bank", "Cash"] : party_account_type;
+
+ return {
+ filters: {
+ "account_type": ["in", account_types],
+ "is_group": 0,
+ "company": frm.doc.company
+ }
+ }
+ });
+
+ frm.set_query("paid_to", function() {
+ var account_types = in_list(["Receive", "Internal Transfer"], frm.doc.payment_type) ?
+ ["Bank", "Cash"] : party_account_type;
+
+ return {
+ filters: {
+ "account_type": ["in", account_types],
+ "is_group": 0,
+ "company": frm.doc.company
+ }
+ }
+ });
+
+ frm.set_query("account", "deductions", function() {
+ return {
+ filters: {
+ "is_group": 0,
+ "company": frm.doc.company
+ }
+ }
+ });
+
+ frm.set_query("cost_center", "deductions", function() {
+ return {
+ filters: {
+ "is_group": 0,
+ "company": frm.doc.company
+ }
+ }
+ });
+
+ frm.set_query("reference_doctype", "references", function() {
+ if (frm.doc.party_type=="Customer") {
+ var doctypes = ["Sales Order", "Sales Invoice", "Journal Entry"];
+ } else if (frm.doc.party_type=="Supplier") {
+ var doctypes = ["Purchase Order", "Purchase Invoice", "Journal Entry"];
+ } else {
+ var doctypes = ["Journal Entry"];
+ }
+
+ return {
+ filters: { "name": ["in", doctypes] }
+ };
+ });
+ },
+
+ refresh: function(frm) {
+ frm.events.hide_unhide_fields(frm);
+ frm.events.set_dynamic_labels(frm);
+ frm.events.show_general_ledger(frm);
+ },
+
+ hide_unhide_fields: function(frm) {
+ var company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
+
+ frm.toggle_display("source_exchange_rate",
+ (frm.doc.paid_amount && frm.doc.paid_from_account_currency != company_currency));
+
+ frm.toggle_display("target_exchange_rate", (frm.doc.received_amount &&
+ frm.doc.paid_to_account_currency != company_currency &&
+ frm.doc.paid_from_account_currency != frm.doc.paid_to_account_currency));
+
+ frm.toggle_display("base_paid_amount", frm.doc.paid_from_account_currency != company_currency);
+
+ frm.toggle_display("base_received_amount", (frm.doc.paid_to_account_currency != company_currency &&
+ frm.doc.paid_from_account_currency != frm.doc.paid_to_account_currency));
+
+ frm.toggle_display("received_amount",
+ frm.doc.paid_from_account_currency != frm.doc.paid_to_account_currency)
+
+ frm.toggle_display(["base_total_allocated_amount"],
+ (frm.doc.paid_amount && frm.doc.received_amount && frm.doc.base_total_allocated_amount &&
+ ((frm.doc.payment_type=="Receive" && frm.doc.paid_from_account_currency != company_currency) ||
+ (frm.doc.payment_type=="Pay" && frm.doc.paid_to_account_currency != company_currency))));
+
+ var party_amount = frm.doc.payment_type=="Receive" ?
+ frm.doc.paid_amount : frm.doc.received_amount;
+
+ frm.toggle_display("write_off_difference_amount", (frm.doc.difference_amount && frm.doc.party &&
+ (frm.doc.paid_from_account_currency == frm.doc.paid_to_account_currency) &&
+ (frm.doc.total_allocated_amount > party_amount)));
+
+ frm.toggle_display("set_exchange_gain_loss",
+ (frm.doc.paid_amount && frm.doc.received_amount && frm.doc.difference_amount &&
+ (frm.doc.paid_from_account_currency != company_currency ||
+ frm.doc.paid_to_account_currency != company_currency)));
+
+ frm.refresh_fields();
+ },
+
+ set_dynamic_labels: function(frm) {
+ var company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
+
+ var field_label_map = {};
+ var grid_field_label_map = {};
+
+ var setup_field_label_map = function(fields_list, currency, parentfield) {
+ var doctype = parentfield ? frm.fields_dict[parentfield].grid.doctype : frm.doc.doctype;
+ $.each(fields_list, function(i, fname) {
+ var docfield = frappe.meta.docfield_map[doctype][fname];
+ if(docfield) {
+ var label = __(docfield.label || "").replace(/\([^\)]*\)/g, "");
+ if(parentfield) {
+ grid_field_label_map[doctype + "-" + fname] =
+ label.trim() + " (" + __(currency) + ")";
+ } else {
+ field_label_map[fname] = label.trim() + " (" + currency + ")";
+ }
+ }
+ });
+ }
+
+ setup_field_label_map(["base_paid_amount", "base_received_amount", "base_total_allocated_amount",
+ "difference_amount"], company_currency);
+
+ setup_field_label_map(["paid_amount"], frm.doc.paid_from_account_currency);
+ setup_field_label_map(["received_amount"], frm.doc.paid_to_account_currency);
+
+ var party_account_currency = frm.doc.payment_type=="Receive" ?
+ frm.doc.paid_from_account_currency : frm.doc.paid_to_account_currency;
+
+ setup_field_label_map(["total_allocated_amount", "unallocated_amount"], party_account_currency);
+
+ $.each(field_label_map, function(fname, label) {
+ me.frm.fields_dict[fname].set_label(label);
+ });
+
+ setup_field_label_map(["total_amount", "outstanding_amount", "allocated_amount"],
+ party_account_currency, "references");
+
+ setup_field_label_map(["amount"], company_currency, "deductions");
+
+ $.each(grid_field_label_map, function(fname, label) {
+ fname = fname.split("-");
+ var df = frappe.meta.get_docfield(fname[0], fname[1], me.frm.doc.name);
+ if(df) df.label = label;
+ });
+
+ cur_frm.set_df_property("source_exchange_rate", "description",
+ ("1 " + frm.doc.paid_from_account_currency + " = [?] " + company_currency));
+
+ cur_frm.set_df_property("target_exchange_rate", "description",
+ ("1 " + frm.doc.paid_to_account_currency + " = [?] " + company_currency));
+
+ frm.refresh_fields();
+ },
+
+ show_general_ledger: function(frm) {
+ if(frm.doc.docstatus==1) {
+ frm.add_custom_button(__('Ledger'), function() {
+ frappe.route_options = {
+ "voucher_no": frm.doc.name,
+ "from_date": frm.doc.posting_date,
+ "to_date": frm.doc.posting_date,
+ "company": frm.doc.company,
+ group_by_voucher: 0
+ };
+ frappe.set_route("query-report", "General Ledger");
+ }, "icon-table");
+ }
+ },
+
+ payment_type: function(frm) {
+ if(frm.doc.payment_type == "Internal Transfer") {
+ $.each(["party", "party_balance", "paid_from", "paid_to",
+ "references", "total_allocated_amount"], function(i, field) {
+ frm.set_value(field, null);
+ })
+ } else {
+ if(!frm.doc.party)
+ frm.set_value("party_type", frm.doc.payment_type=="Receive" ? "Customer" : "Supplier");
+ else
+ frm.events.party(frm);
+
+ if(frm.doc.mode_of_payment)
+ frm.events.mode_of_payment(frm);
+ }
+ },
+
+ party_type: function(frm) {
+ if(frm.doc.party) {
+ $.each(["party", "party_balance", "paid_from", "paid_to",
+ "paid_from_account_currency", "paid_from_account_balance",
+ "paid_to_account_currency", "paid_to_account_balance",
+ "references", "total_allocated_amount"], function(i, field) {
+ frm.set_value(field, null);
+ })
+ }
+ },
+
+ party: function(frm) {
+ if(frm.doc.payment_type && frm.doc.party_type && frm.doc.party) {
+ frm.set_party_account_based_on_party = true;
+
+ return frappe.call({
+ method: "erpnext.accounts.doctype.payment_entry.payment_entry.get_party_details",
+ args: {
+ company: frm.doc.company,
+ party_type: frm.doc.party_type,
+ party: frm.doc.party,
+ date: frm.doc.posting_date
+ },
+ callback: function(r, rt) {
+ if(r.message) {
+ if(frm.doc.payment_type == "Receive") {
+ frm.set_value("paid_from", r.message.party_account);
+ frm.set_value("paid_from_account_currency", r.message.party_account_currency);
+ frm.set_value("paid_from_account_balance", r.message.account_balance);
+ } else if (frm.doc.payment_type == "Pay"){
+ frm.set_value("paid_to", r.message.party_account);
+ frm.set_value("paid_to_account_currency", r.message.party_account_currency);
+ frm.set_value("paid_to_account_balance", r.message.account_balance);
+ }
+ frm.set_value("party_balance", r.message.party_balance);
+ frm.events.get_outstanding_documents(frm);
+ frm.events.hide_unhide_fields(frm);
+ frm.events.set_dynamic_labels(frm);
+ frm.set_party_account_based_on_party = false;
+ }
+ }
+ });
+ }
+ },
+
+ mode_of_payment: function(frm) {
+ return frappe.call({
+ method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.get_bank_cash_account",
+ args: {
+ "mode_of_payment": frm.doc.mode_of_payment,
+ "company": frm.doc.company
+ },
+ callback: function(r, rt) {
+ if(r.message) {
+ var payment_account_field = frm.doc.payment_type == "Receive" ? "paid_to" : "paid_from";
+ frm.set_value(payment_account_field, r.message['account']);
+ }
+ }
+ });
+ },
+
+ paid_from: function(frm) {
+ if(frm.set_party_account_based_on_party) return;
+
+ frm.events.set_account_currency_and_balance(frm, frm.doc.paid_from,
+ "paid_from_account_currency", "paid_from_account_balance", function(frm) {
+ if(frm.doc.payment_type == "Receive") frm.events.get_outstanding_documents(frm);
+ }
+ );
+ },
+
+ paid_to: function(frm) {
+ if(frm.set_party_account_based_on_party) return;
+
+ frm.events.set_account_currency_and_balance(frm, frm.doc.paid_to,
+ "paid_to_account_currency", "paid_to_account_balance", function(frm) {
+ if(frm.doc.payment_type == "Pay") frm.events.get_outstanding_documents(frm);
+ }
+ );
+ },
+
+ set_account_currency_and_balance: function(frm, account, currency_field,
+ balance_field, callback_function) {
+ frappe.call({
+ method: "erpnext.accounts.doctype.payment_entry.payment_entry.get_account_details",
+ args: {
+ "account": account,
+ "date": frm.doc.posting_date
+ },
+ callback: function(r, rt) {
+ if(r.message) {
+ frm.set_value(currency_field, r.message['account_currency']);
+ frm.set_value(balance_field, r.message['account_balance']);
+
+ if(frm.doc.payment_type=="Receive" && currency_field=="paid_to_account_currency") {
+ frm.toggle_reqd(["reference_no", "reference_date"],
+ (r.message['account_type'] == "Bank" ? 1 : 0));
+ if(!frm.doc.received_amount && frm.doc.paid_amount)
+ frm.events.paid_amount(frm);
+ } else if(frm.doc.payment_type=="Pay" && currency_field=="paid_from_account_currency") {
+ frm.toggle_reqd(["reference_no", "reference_date"],
+ (r.message['account_type'] == "Bank" ? 1 : 0));
+
+ if(!frm.doc.paid_amount && frm.doc.received_amount)
+ frm.events.received_amount(frm);
+ }
+
+ if(callback_function) callback_function(frm);
+
+ frm.events.hide_unhide_fields(frm);
+ frm.events.set_dynamic_labels(frm);
+ }
+ }
+ });
+ },
+
+ paid_from_account_currency: function(frm) {
+ if(!frm.doc.paid_from_account_currency) return;
+ var company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
+
+ if (frm.doc.paid_from_account_currency == company_currency) {
+ frm.set_value("source_exchange_rate", 1);
+ } else if (frm.doc.paid_from){
+ if (in_list(["Internal Transfer", "Pay"], frm.doc.payment_type)) {
+ frappe.call({
+ method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_average_exchange_rate",
+ args: {
+ account: frm.doc.paid_from
+ },
+ callback: function(r, rt) {
+ frm.set_value("source_exchange_rate", r.message);
+ }
+ })
+ } else {
+ frm.events.set_current_exchange_rate(frm, "source_exchange_rate",
+ frm.doc.paid_from_account_currency, company_currency);
+ }
+ }
+ },
+
+ paid_to_account_currency: function(frm) {
+ if(!frm.doc.paid_to_account_currency) return;
+ var company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
+
+ frm.events.set_current_exchange_rate(frm, "target_exchange_rate",
+ frm.doc.paid_to_account_currency, company_currency);
+ },
+
+ set_current_exchange_rate: function(frm, exchange_rate_field, from_currency, to_currency) {
+ frappe.call({
+ method: "erpnext.setup.utils.get_exchange_rate",
+ args: {
+ from_currency: from_currency,
+ to_currency: to_currency
+ },
+ callback: function(r, rt) {
+ frm.set_value(exchange_rate_field, r.message);
+ }
+ })
+ },
+
+ source_exchange_rate: function(frm) {
+ if (frm.doc.paid_amount) {
+ frm.set_value("base_paid_amount", flt(frm.doc.paid_amount) * flt(frm.doc.source_exchange_rate));
+ }
+ },
+
+ target_exchange_rate: function(frm) {
+ if (frm.doc.received_amount) {
+ frm.set_value("base_received_amount",
+ flt(frm.doc.received_amount) * flt(frm.doc.target_exchange_rate));
+ }
+ },
+
+ paid_amount: function(frm) {
+ frm.set_value("base_paid_amount", flt(frm.doc.paid_amount) * flt(frm.doc.source_exchange_rate));
+
+ if(!frm.set_paid_amount_based_on_received_amount &&
+ (frm.doc.paid_from_account_currency == frm.doc.paid_to_account_currency)) {
+ frm.set_value("received_amount", frm.doc.paid_amount);
+ frm.set_value("target_exchange_rate", frm.doc.source_exchange_rate);
+ frm.set_value("base_received_amount", frm.doc.base_paid_amount);
+ }
+ if(frm.doc.payment_type == "Receive")
+ frm.events.allocate_party_amount_against_ref_docs(frm, frm.doc.paid_amount);
+ else
+ frm.events.set_difference_amount(frm);
+
+ frm.set_paid_amount_based_on_received_amount = false;
+ },
+
+ received_amount: function(frm) {
+ frm.set_paid_amount_based_on_received_amount = true;
+
+ if(!frm.doc.paid_amount && frm.doc.paid_from_account_currency == frm.doc.paid_to_account_currency) {
+ frm.set_value("paid_amount", frm.doc.received_amount);
+ frm.set_value("source_exchange_rate", frm.doc.target_exchange_rate);
+ frm.set_value("base_paid_amount", frm.doc.base_received_amount);
+ }
+
+ frm.set_value("base_received_amount",
+ flt(frm.doc.received_amount) * flt(frm.doc.target_exchange_rate));
+
+ if(frm.doc.payment_type == "Pay")
+ frm.events.allocate_party_amount_against_ref_docs(frm, frm.doc.received_amount);
+ else
+ frm.events.set_difference_amount(frm);
+ },
+
+ get_outstanding_documents: function(frm) {
+ frm.clear_table("references");
+
+ if(!frm.doc.party) return;
+
+ frm.events.check_mandatory_to_fetch(frm);
+ var company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
+
+ return frappe.call({
+ method: 'erpnext.accounts.doctype.payment_entry.payment_entry.get_outstanding_reference_documents',
+ args: {
+ args: {
+ "company": frm.doc.company,
+ "party_type": frm.doc.party_type,
+ "payment_type": frm.doc.payment_type,
+ "party": frm.doc.party,
+ "party_account": frm.doc.payment_type=="Receive" ? frm.doc.paid_from : frm.doc.paid_to
+ }
+ },
+ callback: function(r, rt) {
+ if(r.message) {
+ var total_positive_outstanding = 0;
+ var total_negative_outstanding = 0;
+
+ $.each(r.message, function(i, d) {
+ var c = frm.add_child("references");
+ c.reference_doctype = d.voucher_type;
+ c.reference_name = d.voucher_no;
+ c.total_amount = d.invoice_amount;
+ c.outstanding_amount = d.outstanding_amount;
+ if(!in_list(["Sales Order", "Purchase Order"], d.voucher_type)) {
+ if(flt(d.outstanding_amount) > 0)
+ total_positive_outstanding += flt(d.outstanding_amount);
+ else
+ total_negative_outstanding += Math.abs(flt(d.outstanding_amount));
+ }
+
+ var party_account_currency = frm.doc.payment_type=="Receive" ?
+ frm.doc.paid_from_account_currency : frm.doc.paid_to_account_currency;
+
+ if(party_account_currency != company_currency) {
+ c.exchange_rate = d.exchange_rate;
+ } else {
+ c.exchange_rate = 1;
+ }
+ if (in_list(['Sales Invoice', 'Purchase Invoice'], d.reference_doctype)){
+ c.due_date = d.due_date;
+ }
+ });
+
+ if((frm.doc.payment_type=="Receive" && frm.doc.party_type=="Customer") ||
+ (frm.doc.payment_type=="Pay" && frm.doc.party_type=="Supplier")) {
+ if(total_positive_outstanding > total_negative_outstanding)
+ frm.set_value("paid_amount",
+ total_positive_outstanding - total_negative_outstanding);
+ } else if (total_negative_outstanding &&
+ (total_positive_outstanding < total_negative_outstanding)) {
+ frm.set_value("received_amount",
+ total_negative_outstanding - total_positive_outstanding);
+ }
+ }
+
+ frm.events.allocate_party_amount_against_ref_docs(frm,
+ (frm.doc.payment_type=="Receive" ? frm.doc.paid_amount : frm.doc.received_amount));
+ }
+ });
+ },
+
+ allocate_party_amount_against_ref_docs: function(frm, paid_amount) {
+ var total_positive_outstanding_including_order = 0;
+ var total_negative_outstanding = 0;
+
+ $.each(frm.doc.references || [], function(i, row) {
+ if(flt(row.outstanding_amount) > 0)
+ total_positive_outstanding_including_order += flt(row.outstanding_amount);
+ else
+ total_negative_outstanding += Math.abs(flt(row.outstanding_amount));
+ })
+
+ var allocated_negative_outstanding = 0;
+ if((frm.doc.payment_type=="Receive" && frm.doc.party_type=="Customer") ||
+ (frm.doc.payment_type=="Pay" && frm.doc.party_type=="Supplier")) {
+ if(total_positive_outstanding_including_order > paid_amount) {
+ var remaining_outstanding = total_positive_outstanding_including_order - paid_amount;
+ allocated_negative_outstanding = total_negative_outstanding < remaining_outstanding ?
+ total_negative_outstanding : remaining_outstanding;
+ }
+
+ var allocated_positive_outstanding = paid_amount + allocated_negative_outstanding;
+ } else {
+ if(paid_amount > total_negative_outstanding) {
+ if(total_negative_outstanding == 0) {
+ frappe.msgprint(__("Cannot {0} {1} {2} without any negative outstanding invoice",
+ [frm.doc.payment_type,
+ (frm.doc.party_type=="Customer" ? "to" : "from"), frm.doc.party_type]));
+ return false
+ } else {
+ frappe.msgprint(__("Paid Amount cannot be greater than total negative outstanding amount {0}", [total_negative_outstanding]));
+ return false;
+ }
+ } else {
+ allocated_positive_outstanding = total_negative_outstanding - paid_amount;
+ allocated_negative_outstanding = paid_amount +
+ (total_positive_outstanding_including_order < allocated_positive_outstanding ?
+ total_positive_outstanding_including_order : allocated_positive_outstanding)
+ }
+ }
+
+ $.each(frm.doc.references || [], function(i, row) {
+ row.allocated_amount = 0
+
+ if(row.outstanding_amount > 0 && allocated_positive_outstanding > 0) {
+ if(row.outstanding_amount >= allocated_positive_outstanding)
+ row.allocated_amount = allocated_positive_outstanding;
+ else row.allocated_amount = row.outstanding_amount;
+
+ allocated_positive_outstanding -= flt(row.allocated_amount);
+ } else if (row.outstanding_amount < 0 && allocated_negative_outstanding) {
+ if(Math.abs(row.outstanding_amount) >= allocated_negative_outstanding)
+ row.allocated_amount = -1*allocated_negative_outstanding;
+ else row.allocated_amount = row.outstanding_amount;
+
+ allocated_negative_outstanding -= Math.abs(flt(row.allocated_amount));
+ }
+ })
+ frm.refresh_fields()
+ frm.events.set_total_allocated_amount(frm);
+ },
+
+ set_total_allocated_amount: function(frm) {
+ var total_allocated_amount = base_total_allocated_amount = 0.0;
+ $.each(frm.doc.references || [], function(i, row) {
+ if (row.allocated_amount) {
+ total_allocated_amount += flt(row.allocated_amount);
+ base_total_allocated_amount += flt(flt(row.allocated_amount)*flt(row.exchange_rate),
+ precision("base_paid_amount"));
+ }
+ });
+ frm.set_value("total_allocated_amount", Math.abs(total_allocated_amount));
+ frm.set_value("base_total_allocated_amount", Math.abs(base_total_allocated_amount));
+
+ frm.events.set_difference_amount(frm);
+ },
+
+ set_difference_amount: function(frm) {
+ var unallocated_amount = 0;
+ if(frm.doc.party) {
+ var party_amount = frm.doc.payment_type=="Receive" ?
+ frm.doc.paid_amount : frm.doc.received_amount;
+
+ if(frm.doc.total_allocated_amount < party_amount)
+ unallocated_amount = party_amount - frm.doc.total_allocated_amount;
+ }
+
+ frm.set_value("unallocated_amount", unallocated_amount);
+
+ var difference_amount = 0;
+ var base_unallocated_amount = flt(frm.doc.unallocated_amount) *
+ (frm.doc.payment_type=="Receive" ? frm.doc.source_exchange_rate : frm.doc.target_exchange_rate);
+
+ var base_party_amount = flt(frm.doc.base_total_allocated_amount) + base_unallocated_amount;
+
+ if(frm.doc.payment_type == "Receive") {
+ difference_amount = base_party_amount - flt(frm.doc.base_received_amount);
+ } else if (frm.doc.payment_type == "Pay") {
+ difference_amount = flt(frm.doc.base_paid_amount) - base_party_amount;
+ } else {
+ difference_amount = flt(frm.doc.base_paid_amount) - flt(frm.doc.base_received_amount);
+ }
+
+ $.each(frm.doc.deductions || [], function(i, d) {
+ if(d.amount) difference_amount -= flt(d.amount);
+ })
+
+ frm.set_value("difference_amount", difference_amount);
+
+ frm.events.hide_unhide_fields(frm);
+ },
+
+ check_mandatory_to_fetch: function(frm) {
+ $.each(["Company", "Party Type", "Party", "payment_type"], function(i, field) {
+ if(!frm.doc[frappe.model.scrub(field)]) {
+ frappe.msgprint(__("Please select {0} first", [field]));
+ return false;
+ }
+
+ });
+ },
+
+ validate_reference_document: function(frm, row) {
+ var _validate = function(i, row) {
+ if (!row.reference_doctype) {
+ return;
+ }
+
+ if(frm.doc.party_type=="Customer"
+ && !in_list(["Sales Order", "Sales Invoice", "Journal Entry"], row.reference_doctype)) {
+ frappe.model.set_value(row.doctype, row.name, "reference_doctype", null);
+ frappe.msgprint(__("Row #{0}: Reference Document Type must be one of Sales Order, Sales Invoice or Journal Entry", [row.idx]));
+ return false;
+ }
+
+ if(frm.doc.party_type=="Supplier" && !in_list(["Purchase Order",
+ "Purchase Invoice", "Journal Entry"], row.reference_doctype)) {
+ frappe.model.set_value(row.doctype, row.name, "against_voucher_type", null);
+ frappe.msgprint(__("Row #{0}: Reference Document Type must be one of Purchase Order, Purchase Invoice or Journal Entry", [row.idx]));
+ return false;
+ }
+ }
+
+ if (row) {
+ _validate(0, row);
+ } else {
+ $.each(frm.doc.vouchers || [], _validate);
+ }
+ },
+
+ write_off_difference_amount: function(frm) {
+ frm.events.set_deductions_entry(frm, "write_off_account");
+ },
+
+ set_exchange_gain_loss: function(frm) {
+ frm.events.set_deductions_entry(frm, "exchange_gain_loss_account");
+ },
+
+ set_deductions_entry: function(frm, account) {
+ if(frm.doc.difference_amount) {
+ frappe.call({
+ method: "erpnext.accounts.doctype.payment_entry.payment_entry.get_company_defaults",
+ args: {
+ company: frm.doc.company
+ },
+ callback: function(r, rt) {
+ if(r.message) {
+ var write_off_row = $.map(frm.doc["deductions"] || [], function(t) {
+ return t.account==r.message[account] ? t : null; });
+
+ if (!write_off_row.length) {
+ var row = frm.add_child("deductions");
+ row.account = r.message[account];
+ row.cost_center = r.message["cost_center"];
+ } else {
+ var row = write_off_row[0];
+ }
+
+ row.amount = flt(row.amount) + flt(frm.doc.difference_amount);
+ refresh_field("deductions");
+
+ frm.events.set_difference_amount(frm);
+ }
+ }
+ })
+ }
+ }
+});
+
+
+frappe.ui.form.on('Payment Entry Reference', {
+ reference_doctype: function(frm, cdt, cdn) {
+ var row = locals[cdt][cdn];
+ frm.events.validate_reference_document(frm, row);
+ },
+
+ reference_name: function(frm, cdt, cdn) {
+ var row = locals[cdt][cdn];
+ return frappe.call({
+ method: "erpnext.accounts.doctype.payment_entry.payment_entry.get_reference_details",
+ args: {
+ reference_doctype: row.reference_doctype,
+ reference_name: row.reference_name,
+ party_account_currency: frm.doc.payment_type=="Receive" ?
+ frm.doc.paid_from_account_currency : frm.doc.paid_to_account_currency
+ },
+ callback: function(r, rt) {
+ if(r.message) {
+ $.each(r.message, function(field, value) {
+ frappe.model.set_value(cdt, cdn, field, value);
+ })
+ frm.refresh_fields();
+ }
+ }
+ })
+ },
+
+ allocated_amount: function(frm) {
+ frm.events.set_total_allocated_amount(frm);
+ },
+
+ references_remove: function(frm) {
+ frm.events.set_total_allocated_amount(frm);
+ }
+})
+
+frappe.ui.form.on('Payment Entry Deduction', {
+ amount: function(frm) {
+ frm.events.set_difference_amount(frm);
+ },
+
+ deductions_remove: function(frm) {
+ frm.events.set_difference_amount(frm);
+ }
+})
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.json b/erpnext/accounts/doctype/payment_entry/payment_entry.json
new file mode 100644
index 0000000..d40ba9d
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.json
@@ -0,0 +1,1430 @@
+{
+ "allow_copy": 0,
+ "allow_import": 1,
+ "allow_rename": 0,
+ "autoname": "naming_series:",
+ "beta": 0,
+ "creation": "2016-06-01 14:38:51.012597",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "",
+ "fields": [
+ {
+ "allow_on_submit": 0,
+ "bold": 1,
+ "collapsible": 0,
+ "fieldname": "naming_series",
+ "fieldtype": "Select",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Series",
+ "length": 0,
+ "no_copy": 0,
+ "options": "PE-",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 1,
+ "collapsible": 0,
+ "fieldname": "payment_type",
+ "fieldtype": "Select",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Payment Type",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Receive\nPay\nInternal Transfer",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "column_break_5",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 1,
+ "collapsible": 0,
+ "default": "Today",
+ "fieldname": "posting_date",
+ "fieldtype": "Date",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Posting Date",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "company",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Company",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Company",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "mode_of_payment",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Mode of Payment",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Mode of Payment",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "depends_on": "eval:in_list([\"Receive\", \"Pay\"], doc.payment_type)",
+ "fieldname": "party_section",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "depends_on": "eval:in_list([\"Receive\", \"Pay\"], doc.payment_type)",
+ "fieldname": "party_type",
+ "fieldtype": "Select",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Party Type",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Customer\nSupplier",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "column_break_10",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 1,
+ "collapsible": 0,
+ "depends_on": "eval:in_list([\"Receive\", \"Pay\"], doc.payment_type) && doc.party_type",
+ "fieldname": "party",
+ "fieldtype": "Dynamic Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Party",
+ "length": 0,
+ "no_copy": 0,
+ "options": "party_type",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "depends_on": "party",
+ "fieldname": "party_balance",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Party Balance",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "payment_accounts_section",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 1,
+ "collapsible": 0,
+ "depends_on": "eval:(in_list([\"Internal Transfer\", \"Pay\"], doc.payment_type) || doc.party)",
+ "fieldname": "paid_from",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Account Paid From",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Account",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "depends_on": "paid_from",
+ "fieldname": "paid_from_account_currency",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Account Currency",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Currency",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "depends_on": "paid_from",
+ "fieldname": "paid_from_account_balance",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Account Balance",
+ "length": 0,
+ "no_copy": 0,
+ "options": "paid_from_account_currency",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "column_break_18",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "depends_on": "eval:(in_list([\"Internal Transfer\", \"Receive\"], doc.payment_type) || doc.party)",
+ "fieldname": "paid_to",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Account Paid To",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Account",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "depends_on": "paid_to",
+ "fieldname": "paid_to_account_currency",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Account Currency",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Currency",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "depends_on": "paid_to",
+ "fieldname": "paid_to_account_balance",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Account Balance",
+ "length": 0,
+ "no_copy": 0,
+ "options": "paid_to_account_currency",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "collapsible_depends_on": "",
+ "depends_on": "eval:(doc.paid_to && doc.paid_from)",
+ "fieldname": "payment_amounts_section",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 1,
+ "collapsible": 0,
+ "depends_on": "",
+ "fieldname": "paid_amount",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Paid Amount",
+ "length": 0,
+ "no_copy": 0,
+ "options": "paid_from_account_currency",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "depends_on": "",
+ "fieldname": "source_exchange_rate",
+ "fieldtype": "Float",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Exchange Rate",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "depends_on": "",
+ "fieldname": "base_paid_amount",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Paid Amount (Company Currency)",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Company:company:default_currency",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "column_break_21",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 1,
+ "collapsible": 0,
+ "depends_on": "",
+ "fieldname": "received_amount",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Received Amount",
+ "length": 0,
+ "no_copy": 0,
+ "options": "paid_to_account_currency",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "depends_on": "",
+ "fieldname": "target_exchange_rate",
+ "fieldtype": "Float",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Exchange Rate",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "depends_on": "",
+ "fieldname": "base_received_amount",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Received Amount (Company Currency)",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Company:company:default_currency",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 1,
+ "collapsible_depends_on": "references",
+ "depends_on": "eval:(doc.party && doc.paid_from && doc.paid_to && doc.paid_amount && doc.received_amount)",
+ "fieldname": "section_break_14",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Reference Documents",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "depends_on": "",
+ "fieldname": "references",
+ "fieldtype": "Table",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Payment References",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Payment Entry Reference",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "depends_on": "",
+ "fieldname": "section_break_34",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 1,
+ "collapsible": 0,
+ "depends_on": "eval:(doc.paid_amount && doc.received_amount && doc.references)",
+ "fieldname": "total_allocated_amount",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Total Allocated Amount",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "depends_on": "",
+ "fieldname": "base_total_allocated_amount",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Total Allocated Amount (Company Currency)",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Company:company:default_currency",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "depends_on": "difference_amount",
+ "fieldname": "write_off_difference_amount",
+ "fieldtype": "Button",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Write Off Difference Amount",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "set_exchange_gain_loss",
+ "fieldtype": "Button",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Set Exchange Gain / Loss",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "column_break_36",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "depends_on": "eval:(doc.paid_amount && doc.received_amount && doc.references)",
+ "fieldname": "unallocated_amount",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Unallocated Amount",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 1,
+ "collapsible": 0,
+ "depends_on": "eval:(doc.paid_amount && doc.received_amount)",
+ "fieldname": "difference_amount",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Difference Amount (Company Currency)",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Company:company:default_currency",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 1,
+ "collapsible_depends_on": "deductions",
+ "depends_on": "eval:(doc.paid_amount && doc.received_amount)",
+ "fieldname": "deductions_or_loss_section",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Deductions or Loss",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "deductions",
+ "fieldtype": "Table",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Payment Deductions or Loss",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Payment Entry Deduction",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "transaction_references",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Transaction References",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 1,
+ "collapsible": 0,
+ "depends_on": "eval:(doc.paid_from && doc.paid_to)",
+ "fieldname": "reference_no",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Cheque/Reference No",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "column_break_23",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 1,
+ "collapsible": 0,
+ "depends_on": "eval:(doc.paid_from && doc.paid_to)",
+ "fieldname": "reference_date",
+ "fieldtype": "Date",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Cheque/Reference Date",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "depends_on": "eval:doc.docstatus==1",
+ "fieldname": "clearance_date",
+ "fieldtype": "Date",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Clearance Date",
+ "length": 0,
+ "no_copy": 1,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 1,
+ "collapsible_depends_on": "",
+ "depends_on": "eval:(doc.paid_from && doc.paid_to && doc.paid_amount && doc.received_amount)",
+ "fieldname": "section_break_12",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "More Information",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "depends_on": "",
+ "fieldname": "project",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Project",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Project",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "remarks",
+ "fieldtype": "Small Text",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Remarks",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "column_break_16",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "letter_head",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Letter Head",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Letter Head",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "print_heading",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Print Heading",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Print Heading",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "amended_from",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Amended From",
+ "length": 0,
+ "no_copy": 1,
+ "options": "Payment Entry",
+ "permlevel": 0,
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "title",
+ "fieldtype": "Data",
+ "hidden": 1,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Title",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ }
+ ],
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
+ "in_create": 0,
+ "in_dialog": 0,
+ "is_submittable": 1,
+ "issingle": 0,
+ "istable": 0,
+ "max_attachments": 0,
+ "modified": "2016-07-01 17:14:17.562924",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Payment Entry",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "amend": 1,
+ "apply_user_permissions": 0,
+ "cancel": 1,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "if_owner": 0,
+ "import": 1,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Accounts User",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 1,
+ "write": 1
+ },
+ {
+ "amend": 1,
+ "apply_user_permissions": 0,
+ "cancel": 1,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "if_owner": 0,
+ "import": 1,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Accounts Manager",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 1,
+ "write": 1
+ }
+ ],
+ "quick_entry": 0,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "title_field": "title",
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py
new file mode 100644
index 0000000..d4d93a7
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -0,0 +1,666 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe, json
+from frappe import _, scrub, ValidationError
+from frappe.utils import flt, comma_or, nowdate
+from erpnext.accounts.utils import get_outstanding_invoices, get_account_currency, get_balance_on
+from erpnext.accounts.party import get_party_account
+from erpnext.accounts.doctype.journal_entry.journal_entry \
+ import get_average_exchange_rate, get_default_bank_cash_account
+from erpnext.setup.utils import get_exchange_rate
+from erpnext.accounts.general_ledger import make_gl_entries
+
+from erpnext.controllers.accounts_controller import AccountsController
+
+class InvalidPaymentEntry(ValidationError): pass
+
+class PaymentEntry(AccountsController):
+ def setup_party_account_field(self):
+ self.party_account_field = None
+ self.party_account = None
+ self.party_account_currency = None
+
+ if self.payment_type == "Receive":
+ self.party_account_field = "paid_from"
+ self.party_account = self.paid_from
+ self.party_account_currency = self.paid_from_account_currency
+
+ elif self.payment_type == "Pay":
+ self.party_account_field = "paid_to"
+ self.party_account = self.paid_to
+ self.party_account_currency = self.paid_to_account_currency
+
+ def validate(self):
+ self.setup_party_account_field()
+ self.set_missing_values()
+ self.validate_party_details()
+ self.validate_bank_accounts()
+ self.set_exchange_rate()
+ self.validate_mandatory()
+ self.validate_reference_documents()
+ self.set_amounts()
+ self.clear_unallocated_reference_document_rows()
+ self.validate_payment_against_negative_invoice()
+ self.validate_transaction_reference()
+ self.set_title()
+ self.set_remarks()
+
+ def on_submit(self):
+ if self.difference_amount:
+ frappe.throw(_("Difference Amount must be zero"))
+ self.make_gl_entries()
+ self.update_advance_paid()
+
+ def on_cancel(self):
+ self.setup_party_account_field()
+ self.make_gl_entries(cancel=1)
+ self.update_advance_paid()
+
+ def set_missing_values(self):
+ if self.payment_type == "Internal Transfer":
+ for field in ("party", "party_balance", "total_allocated_amount",
+ "base_total_allocated_amount", "unallocated_amount"):
+ self.set(field, None)
+ self.references = []
+ else:
+ if not self.party_type:
+ frappe.throw(_("Party Type is mandatory"))
+
+ if not self.party:
+ frappe.throw(_("Party is mandatory"))
+
+ if self.party:
+ if not self.party_balance:
+ self.party_balance = get_balance_on(party_type=self.party_type,
+ party=self.party, date=self.posting_date)
+
+ if not self.party_account:
+ party_account = get_party_account(self.party_type, self.party, self.company)
+ self.set(self.party_account_field, party_account)
+ self.party_account = party_account
+
+ if self.paid_from and not (self.paid_from_account_currency or self.paid_from_account_balance):
+ acc = get_account_details(self.paid_from, self.posting_date)
+ self.paid_from_account_currency = acc.account_currency
+ self.paid_from_account_balance = acc.account_balance
+
+ if self.paid_to and not (self.paid_to_account_currency or self.paid_to_account_balance):
+ acc = get_account_details(self.paid_to, self.posting_date)
+ self.paid_to_account_currency = acc.account_currency
+ self.paid_to_account_balance = acc.account_balance
+
+ self.party_account_currency = self.paid_from_account_currency \
+ if self.payment_type=="Receive" else self.paid_to_account_currency
+
+ self.set_missing_ref_details()
+
+
+ def set_missing_ref_details(self):
+ for d in self.get("references"):
+ if d.allocated_amount:
+ ref_details = get_reference_details(d.reference_doctype,
+ d.reference_name, self.party_account_currency)
+
+ for field, value in ref_details.items():
+ if not d.get(field):
+ d.set(field, value)
+
+ def validate_party_details(self):
+ if self.party:
+ if not frappe.db.exists(self.party_type, self.party):
+ frappe.throw(_("Invalid {0}: {1}").format(self.party_type, self.party))
+
+ if self.party_account:
+ party_account_type = "Receivable" if self.party_type=="Customer" else "Payable"
+ self.validate_account_type(self.party_account, [party_account_type])
+
+ def validate_bank_accounts(self):
+ if self.payment_type in ("Pay", "Internal Transfer"):
+ self.validate_account_type(self.paid_from, ["Bank", "Cash"])
+
+ if self.payment_type in ("Receive", "Internal Transfer"):
+ self.validate_account_type(self.paid_to, ["Bank", "Cash"])
+
+ def validate_account_type(self, account, account_types):
+ account_type = frappe.db.get_value("Account", account, "account_type")
+ if account_type not in account_types:
+ frappe.throw(_("Account Type for {0} must be {1}").format(comma_or(account_types)))
+
+ def set_exchange_rate(self):
+ if self.paid_from and not self.source_exchange_rate:
+ if self.paid_from_account_currency == self.company_currency:
+ self.source_exchange_rate = 1
+ elif self.payment_type in ("Pay", "Internal Transfer"):
+ self.source_exchange_rate = get_average_exchange_rate(self.paid_from)
+ else:
+ self.source_exchange_rate = get_exchange_rate(self.paid_from_account_currency,
+ self.company_currency)
+
+ if self.paid_to and not self.target_exchange_rate:
+ self.target_exchange_rate = get_exchange_rate(self.paid_to_account_currency,
+ self.company_currency)
+
+ def validate_mandatory(self):
+ for field in ("paid_amount", "received_amount", "source_exchange_rate", "target_exchange_rate"):
+ if not self.get(field):
+ frappe.throw(_("{0} is mandatory").format(self.meta.get_label(field)))
+
+ def validate_reference_documents(self):
+ if self.party_type == "Customer":
+ valid_reference_doctypes = ("Sales Order", "Sales Invoice", "Journal Entry")
+ else:
+ valid_reference_doctypes = ("Purchase Order", "Purchase Invoice", "Journal Entry")
+
+ for d in self.get("references"):
+ if not d.allocated_amount:
+ continue
+ if d.reference_doctype not in valid_reference_doctypes:
+ frappe.throw(_("Reference Doctype must be one of {0}")
+ .format(comma_or(valid_reference_doctypes)))
+
+ elif d.reference_name:
+ if not frappe.db.exists(d.reference_doctype, d.reference_name):
+ frappe.throw(_("{0} {1} does not exist").format(d.reference_doctype, d.reference_name))
+ else:
+ ref_doc = frappe.get_doc(d.reference_doctype, d.reference_name)
+
+ if d.reference_doctype != "Journal Entry":
+ if self.party != ref_doc.get(scrub(self.party_type)):
+ frappe.throw(_("{0} {1} does not associated with {2} {3}")
+ .format(d.reference_doctype, d.reference_name, self.party_type, self.party))
+ else:
+ self.validate_journal_entry()
+
+ if d.reference_doctype in ("Sales Invoice", "Purchase Invoice"):
+ ref_party_account = ref_doc.debit_to \
+ if self.party_type=="Customer" else ref_doc.credit_to
+ if ref_party_account != self.party_account:
+ frappe.throw(_("{0} {1} does not associated with Party Account {2}")
+ .format(d.reference_doctype, d.reference_name, self.party_account))
+
+ if ref_doc.docstatus != 1:
+ frappe.throw(_("{0} {1} must be submitted")
+ .format(d.reference_doctype, d.reference_name))
+
+ def validate_journal_entry(self):
+ for d in self.get("references"):
+ if d.allocated_amount and d.reference_doctype == "Journal Entry":
+ je_accounts = frappe.db.sql("""select debit, credit from `tabJournal Entry Account`
+ where account = %s and party=%s and docstatus = 1 and parent = %s
+ and (reference_type is null or reference_type in ("", "Sales Order", "Purchase Order"))
+ """, (self.party_account, self.party, d.reference_name), as_dict=True)
+
+ if not je_accounts:
+ frappe.throw(_("Row #{0}: Journal Entry {0} does not have account {1} or already matched against another voucher")
+ .format(d.idx, d.reference_name, self.party_account))
+ else:
+ dr_or_cr = "debit" if self.payment_type == "Receive" else "credit"
+ valid = False
+ for jvd in je_accounts:
+ if flt(jvd[dr_or_cr]) > 0:
+ valid = True
+ if not valid:
+ frappe.throw(_("Against Journal Entry {0} does not have any unmatched {1} entry")
+ .format(d.reference_name, dr_or_cr))
+
+ def set_amounts(self):
+ self.set_amounts_in_company_currency()
+ self.set_total_allocated_amount()
+ self.set_unallocated_amount()
+ self.set_difference_amount()
+
+ def set_amounts_in_company_currency(self):
+ self.base_paid_amount, self.base_received_amount, self.difference_amount = 0, 0, 0
+ if self.paid_amount:
+ self.base_paid_amount = flt(flt(self.paid_amount) * flt(self.source_exchange_rate),
+ self.precision("base_paid_amount"))
+
+ if self.received_amount:
+ self.base_received_amount = flt(flt(self.received_amount) * flt(self.target_exchange_rate),
+ self.precision("base_received_amount"))
+
+ def set_total_allocated_amount(self):
+ if self.payment_type == "Internal Transfer":
+ return
+
+ total_allocated_amount, base_total_allocated_amount = 0, 0
+ for d in self.get("references"):
+ if d.allocated_amount:
+ total_allocated_amount += flt(d.allocated_amount)
+ base_total_allocated_amount += flt(flt(d.allocated_amount) * flt(d.exchange_rate),
+ self.precision("base_paid_amount"))
+
+ self.total_allocated_amount = abs(total_allocated_amount)
+ self.base_total_allocated_amount = abs(base_total_allocated_amount)
+
+ def set_unallocated_amount(self):
+ self.unallocated_amount = 0;
+ if self.party:
+ party_amount = self.paid_amount if self.payment_type=="Receive" else self.received_amount
+
+ if self.total_allocated_amount < party_amount:
+ self.unallocated_amount = party_amount - self.total_allocated_amount
+
+ def set_difference_amount(self):
+ base_unallocated_amount = self.unallocated_amount * \
+ (self.source_exchange_rate if self.payment_type=="Receive" else self.target_exchange_rate)
+
+ base_party_amount = flt(self.base_total_allocated_amount) + flt(base_unallocated_amount)
+
+ if self.payment_type == "Receive":
+ self.difference_amount = base_party_amount - self.base_received_amount
+ elif self.payment_type == "Pay":
+ self.difference_amount = self.base_paid_amount - base_party_amount
+ else:
+ self.difference_amount = self.base_paid_amount - flt(self.base_received_amount)
+
+ for d in self.get("deductions"):
+ if d.amount:
+ self.difference_amount -= flt(d.amount)
+
+ def clear_unallocated_reference_document_rows(self):
+ self.set("references", self.get("references", {"allocated_amount": ["not in", [0, None, ""]]}))
+
+ frappe.db.sql("""delete from `tabPayment Entry Reference`
+ where parent = %s and allocated_amount = 0""", self.name)
+
+ def validate_payment_against_negative_invoice(self):
+ if ((self.payment_type=="Pay" and self.party_type=="Customer")
+ or (self.payment_type=="Receive" and self.party_type=="Supplier")):
+ total_negative_outstanding = sum([abs(flt(d.outstanding_amount))
+ for d in self.get("references") if flt(d.outstanding_amount) < 0])
+
+ party_amount = self.paid_amount if self.payment_type=="Receive" else self.received_amount
+
+ if not total_negative_outstanding:
+ frappe.throw(_("Cannot {0} {1} {2} without any negative outstanding invoice")
+ .format(self.payment_type, ("to" if self.party_type=="Customer" else "from"),
+ self.party_type), InvalidPaymentEntry)
+
+ elif party_amount > total_negative_outstanding:
+ frappe.throw(_("Paid Amount cannot be greater than total negative outstanding amount {0}")
+ .format(total_negative_outstanding), InvalidPaymentEntry)
+
+ def set_title(self):
+ if self.payment_type in ("Receive", "Pay"):
+ self.title = self.party
+ else:
+ self.title = self.paid_from + " - " + self.paid_to
+
+ def validate_transaction_reference(self):
+ bank_account = self.paid_to if self.payment_type == "Receive" else self.paid_from
+ bank_account_type = frappe.db.get_value("Account", bank_account, "account_type")
+
+ if bank_account_type == "Bank":
+ if not self.reference_no or not self.reference_date:
+ frappe.throw(_("Reference No and Reference Date is mandatory for Bank transaction"))
+
+ def set_remarks(self):
+ if self.remarks: return
+
+ if self.payment_type=="Internal Transfer":
+ remarks = [_("Amount {0} {1} transferred from {2} to {3}")
+ .format(self.paid_from_account_currency, self.paid_amount, self.paid_from, self.paid_to)]
+ else:
+
+ remarks = [_("Amount {0} {1} {2} {3}").format(
+ self.party_account_currency,
+ self.paid_amount if self.payment_type=="Receive" else self.received_amount,
+ _("received from") if self.payment_type=="Receive" else _("to"), self.party
+ )]
+
+ if self.reference_no:
+ remarks.append(_("Transaction reference no {0} dated {1}")
+ .format(self.reference_no, self.reference_date))
+
+ if self.payment_type in ["Receive", "Pay"]:
+ for d in self.get("references"):
+ if d.allocated_amount:
+ remarks.append(_("Amount {0} {1} against {2} {3}").format(self.party_account_currency,
+ d.allocated_amount, d.reference_doctype, d.reference_name))
+
+ for d in self.get("deductions"):
+ if d.amount:
+ remarks.append(_("Amount {0} {1} deducted against {2}")
+ .format(self.company_currency, d.amount, d.account))
+
+ self.set("remarks", "\n".join(remarks))
+
+ def make_gl_entries(self, cancel=0, adv_adj=0):
+ gl_entries = []
+ self.add_party_gl_entries(gl_entries)
+ self.add_bank_gl_entries(gl_entries)
+ self.add_deductions_gl_entries(gl_entries)
+
+ make_gl_entries(gl_entries, cancel=cancel, adv_adj=adv_adj)
+
+ def add_party_gl_entries(self, gl_entries):
+ if self.party_account:
+ if self.payment_type=="Receive":
+ against_account = self.paid_to
+ else:
+ against_account = self.paid_from
+
+
+ party_gl_dict = self.get_gl_dict({
+ "account": self.party_account,
+ "party_type": self.party_type,
+ "party": self.party,
+ "against": against_account,
+ "account_currency": self.party_account_currency
+ })
+
+ dr_or_cr = "credit" if self.party_type == "Customer" else "debit"
+
+ for d in self.get("references"):
+ gle = party_gl_dict.copy()
+ gle.update({
+ "against_voucher_type": d.reference_doctype,
+ "against_voucher": d.reference_name
+ })
+
+ allocated_amount_in_company_currency = flt(flt(d.allocated_amount) * flt(d.exchange_rate),
+ self.precision("paid_amount"))
+
+ gle.update({
+ dr_or_cr + "_in_account_currency": d.allocated_amount,
+ dr_or_cr: allocated_amount_in_company_currency
+ })
+
+ gl_entries.append(gle)
+
+ if self.unallocated_amount:
+ base_unallocated_amount = base_unallocated_amount = self.unallocated_amount * \
+ (self.source_exchange_rate if self.payment_type=="Receive" else self.target_exchange_rate)
+
+ gle = party_gl_dict.copy()
+
+ gle.update({
+ dr_or_cr + "_in_account_currency": d.unallocated_amount,
+ dr_or_cr: base_unallocated_amount
+ })
+
+ gl_entries.append(gle)
+
+ def add_bank_gl_entries(self, gl_entries):
+ if self.payment_type in ("Pay", "Internal Transfer"):
+ gl_entries.append(
+ self.get_gl_dict({
+ "account": self.paid_from,
+ "account_currency": self.paid_from_account_currency,
+ "against": self.party if self.payment_type=="Pay" else self.paid_to,
+ "credit_in_account_currency": self.paid_amount,
+ "credit": self.base_paid_amount
+ })
+ )
+ if self.payment_type in ("Receive", "Internal Transfer"):
+ gl_entries.append(
+ self.get_gl_dict({
+ "account": self.paid_to,
+ "account_currency": self.paid_to_account_currency,
+ "against": self.party if self.payment_type=="Receive" else self.paid_from,
+ "debit_in_account_currency": self.received_amount,
+ "debit": self.base_received_amount
+ })
+ )
+
+ def add_deductions_gl_entries(self, gl_entries):
+ for d in self.get("deductions"):
+ if d.amount:
+ account_currency = get_account_currency(d.account)
+ if account_currency != self.company_currency:
+ frappe.throw(_("Currency for {0} must be {1}").format(d.account, self.company_currency))
+
+ gl_entries.append(
+ self.get_gl_dict({
+ "account": d.account,
+ "account_currency": account_currency,
+ "against": self.party or self.paid_from,
+ "debit_in_account_currency": d.amount,
+ "debit": d.amount,
+ "cost_center": d.cost_center
+ })
+ )
+
+ def update_advance_paid(self):
+ if self.payment_type in ("Receive", "Pay") and self.party:
+ for d in self.get("references"):
+ if d.allocated_amount and d.reference_doctype in ("Sales Order", "Purchase Order"):
+ frappe.get_doc(d.reference_doctype, d.reference_name).set_total_advance_paid()
+
+@frappe.whitelist()
+def get_outstanding_reference_documents(args):
+ args = json.loads(args)
+
+ party_account_currency = get_account_currency(args.get("party_account"))
+ company_currency = frappe.db.get_value("Company", args.get("company"), "default_currency")
+
+ # Get negative outstanding sales /purchase invoices
+ total_field = "base_grand_total" if party_account_currency == company_currency else "grand_total"
+
+ negative_outstanding_invoices = get_negative_outstanding_invoices(args.get("party_type"),
+ args.get("party"), args.get("party_account"), total_field)
+
+ # Get positive outstanding sales /purchase invoices
+ outstanding_invoices = get_outstanding_invoices(args.get("party_type"), args.get("party"),
+ args.get("party_account"))
+
+ for d in outstanding_invoices:
+ d["exchange_rate"] = 1
+ if party_account_currency != company_currency \
+ and d.voucher_type in ("Sales Invoice", "Purchase Invoice"):
+ d["exchange_rate"] = frappe.db.get_value(d.voucher_type, d.voucher_no, "conversion_rate")
+
+ # Get all SO / PO which are not fully billed or aginst which full advance not paid
+ orders_to_be_billed = get_orders_to_be_billed(args.get("party_type"), args.get("party"),
+ party_account_currency, company_currency)
+
+ return negative_outstanding_invoices + outstanding_invoices + orders_to_be_billed
+
+def get_orders_to_be_billed(party_type, party, party_account_currency, company_currency):
+ voucher_type = 'Sales Order' if party_type == "Customer" else 'Purchase Order'
+
+ ref_field = "base_grand_total" if party_account_currency == company_currency else "grand_total"
+
+ orders = frappe.db.sql("""
+ select
+ name as voucher_no,
+ {ref_field} as invoice_amount,
+ ({ref_field} - advance_paid) as outstanding_amount,
+ transaction_date as posting_date
+ from
+ `tab{voucher_type}`
+ where
+ {party_type} = %s
+ and docstatus = 1
+ and ifnull(status, "") != "Closed"
+ and {ref_field} > advance_paid
+ and abs(100 - per_billed) > 0.01
+ order by
+ transaction_date, name
+ """.format(**{
+ "ref_field": ref_field,
+ "voucher_type": voucher_type,
+ "party_type": scrub(party_type)
+ }), party, as_dict = True)
+
+ order_list = []
+ for d in orders:
+ d["voucher_type"] = voucher_type
+ d["exchange_rate"] = get_exchange_rate(party_account_currency, company_currency)
+ order_list.append(d)
+
+ return order_list
+
+def get_negative_outstanding_invoices(party_type, party, party_account, total_field):
+ voucher_type = "Sales Invoice" if party_type == "Customer" else "Purchase Invoice"
+ return frappe.db.sql("""
+ select
+ "{voucher_type}" as voucher_type, name as voucher_no,
+ {total_field} as invoice_amount, outstanding_amount, posting_date,
+ due_date, conversion_rate as exchange_rate
+ from
+ `tab{voucher_type}`
+ where
+ {party_type} = %s and {party_account} = %s and docstatus = 1 and outstanding_amount < 0
+ order by
+ posting_date, name
+ """.format(**{
+ "total_field": total_field,
+ "voucher_type": voucher_type,
+ "party_type": scrub(party_type),
+ "party_account": "debit_to" if party_type=="Customer" else "credit_to"
+ }), (party, party_account), as_dict = True)
+
+@frappe.whitelist()
+def get_party_details(company, party_type, party, date):
+ if not frappe.db.exists(party_type, party):
+ frappe.throw(_("Invalid {0}: {1}").format(party_type, party))
+
+ party_account = get_party_account(party_type, party, company)
+
+ account_currency = get_account_currency(party_account)
+ account_balance = get_balance_on(party_account, date)
+ party_balance = get_balance_on(party_type=party_type, party=party)
+
+ return {
+ "party_account": party_account,
+ "party_account_currency": account_currency,
+ "party_balance": party_balance,
+ "account_balance": account_balance
+ }
+
+@frappe.whitelist()
+def get_account_details(account, date):
+ return frappe._dict({
+ "account_currency": get_account_currency(account),
+ "account_balance": get_balance_on(account, date),
+ "account_type": frappe.db.get_value("Account", account, "account_type")
+ })
+
+@frappe.whitelist()
+def get_company_defaults(company):
+ fields = ["write_off_account", "exchange_gain_loss_account", "cost_center"]
+ ret = frappe.db.get_value("Company", company, fields, as_dict=1)
+
+ for fieldname in fields:
+ if not ret[fieldname]:
+ frappe.throw(_("Please set default {0} in Company {1}")
+ .format(frappe.get_meta("Company").get_label(fieldname), company))
+
+ return ret
+
+@frappe.whitelist()
+def get_reference_details(reference_doctype, reference_name, party_account_currency):
+ total_amount = outstanding_amount = exchange_rate = None
+
+ if reference_doctype != "Journal Entry":
+ ref_doc = frappe.get_doc(reference_doctype, reference_name)
+
+ if party_account_currency == ref_doc.company_currency:
+ total_amount = ref_doc.base_grand_total
+ exchange_rate = 1
+ else:
+ total_amount = ref_doc.grand_total
+ exchange_rate = ref_doc.get("conversion_rate") or \
+ get_exchange_rate(party_account_currency, ref_doc.company_currency)
+
+ outstanding_amount = ref_doc.get("outstanding_amount") \
+ if reference_doctype in ("Sales Invoice", "Purchase Invoice") \
+ else flt(total_amount) - flt(ref_doc.advance_paid)
+ else:
+ exchange_rate = get_exchange_rate(party_account_currency, ref_doc.company_currency)
+
+ return frappe._dict({
+ "due_date": ref_doc.get("due_date"),
+ "total_amount": total_amount,
+ "outstanding_amount": outstanding_amount,
+ "exchange_rate": exchange_rate
+ })
+
+@frappe.whitelist()
+def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=None):
+ doc = frappe.get_doc(dt, dn)
+
+ if dt in ("Sales Order", "Purchase Order") and flt(doc.per_billed, 2) > 0:
+ frappe.throw(_("Can only make payment against unbilled {0}").format(dt))
+
+ party_type = "Customer" if dt in ("Sales Invoice", "Sales Order") else "Supplier"
+
+ # party account
+ if dt == "Sales Invoice":
+ party_account = doc.debit_to
+ elif dt == "Purchase Invoice":
+ party_account = doc.credit_to
+ else:
+ party_account = get_party_account(party_type, doc.get(party_type.lower()), doc.company)
+
+ party_account_currency = doc.get("party_account_currency") or get_account_currency(party_account)
+
+ # payment type
+ if (dt == "Sales Order" or (dt=="Sales Invoice" and doc.outstanding_amount > 0)) \
+ or (dt=="Purchase Invoice" and doc.outstanding_amount < 0):
+ payment_type = "Receive"
+ else:
+ payment_type = "Pay"
+
+ # amounts
+ grand_total = outstanding_amount = 0
+ if party_amount:
+ grand_total = outstanding_amount = party_amount
+ elif dt in ("Sales Invoice", "Purchase Invoice"):
+ grand_total = doc.grand_total
+ outstanding_amount = doc.outstanding_amount
+ else:
+ total_field = "base_grand_total" if party_account_currency == doc.company_currency else "grand_total"
+ grand_total = flt(doc.get(total_field))
+ outstanding_amount = grand_total - flt(doc.advance_paid)
+
+ # bank or cash
+ bank = get_default_bank_cash_account(doc.company, "Bank", mode_of_payment=doc.get("mode_of_payment"),
+ account=bank_account)
+
+ paid_amount = received_amount = 0
+ if party_account_currency == bank.account_currency:
+ paid_amount = received_amount = abs(outstanding_amount)
+ elif payment_type == "Receive":
+ paid_amount = abs(outstanding_amount)
+ if bank_amount:
+ received_amount = bank_amount
+ else:
+ received_amount = abs(outstanding_amount)
+ if bank_amount:
+ paid_amount = bank_amount
+
+ pe = frappe.new_doc("Payment Entry")
+ pe.payment_type = payment_type
+ pe.company = doc.company
+ pe.posting_date = nowdate()
+ pe.mode_of_payment = doc.get("mode_of_payment")
+ pe.party_type = party_type
+ pe.party = doc.get(scrub(party_type))
+ pe.paid_from = party_account if payment_type=="Receive" else bank.account
+ pe.paid_to = party_account if payment_type=="Pay" else bank.account
+ pe.paid_from_account_currency = party_account_currency \
+ if payment_type=="Receive" else bank.account_currency
+ pe.paid_to_account_currency = party_account_currency if payment_type=="Pay" else bank.account_currency
+ pe.paid_amount = paid_amount
+ pe.received_amount = received_amount
+
+ 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()
+ pe.set_exchange_rate()
+ pe.set_amounts()
+ return pe
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py
new file mode 100644
index 0000000..8be5f6d
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py
@@ -0,0 +1,218 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import frappe
+import unittest
+from frappe.utils import flt, nowdate
+from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
+from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry, InvalidPaymentEntry
+from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
+from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
+
+test_dependencies = ["Item"]
+
+class TestPaymentEntry(unittest.TestCase):
+ def test_payment_entry_against_order(self):
+ so = make_sales_order()
+ pe = get_payment_entry("Sales Order", so.name, bank_account="_Test Cash - _TC")
+ pe.paid_from = "Debtors - _TC"
+ pe.insert()
+ pe.submit()
+
+ expected_gle = dict((d[0], d) for d in [
+ ["Debtors - _TC", 0, 1000, so.name],
+ ["_Test Cash - _TC", 1000.0, 0, None]
+ ])
+
+ self.validate_gl_entries(pe.name, expected_gle)
+
+ so_advance_paid = frappe.db.get_value("Sales Order", so.name, "advance_paid")
+ self.assertEqual(so_advance_paid, 1000)
+
+ pe.cancel()
+
+ self.assertFalse(self.get_gle(pe.name))
+
+ so_advance_paid = frappe.db.get_value("Sales Order", so.name, "advance_paid")
+ self.assertEqual(so_advance_paid, 0)
+
+ def test_payment_entry_against_si_usd_to_usd(self):
+ si = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC",
+ currency="USD", conversion_rate=50)
+ pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank USD - _TC")
+ pe.reference_no = "1"
+ pe.reference_date = "2016-01-01"
+ pe.target_exchange_rate = 50
+ pe.insert()
+ pe.submit()
+
+ expected_gle = dict((d[0], d) for d in [
+ ["_Test Receivable USD - _TC", 0, 5000, si.name],
+ ["_Test Bank USD - _TC", 5000.0, 0, None]
+ ])
+
+ self.validate_gl_entries(pe.name, expected_gle)
+
+ outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount"))
+ self.assertEqual(outstanding_amount, 0)
+
+ pe.cancel()
+ self.assertFalse(self.get_gle(pe.name))
+
+ outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount"))
+ self.assertEqual(outstanding_amount, 100)
+
+ def test_payment_entry_against_pi(self):
+ pi = make_purchase_invoice(supplier="_Test Supplier USD", debit_to="_Test Payable USD - _TC",
+ currency="USD", conversion_rate=50)
+ pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank USD - _TC")
+ pe.reference_no = "1"
+ pe.reference_date = "2016-01-01"
+ pe.source_exchange_rate = 50
+ pe.insert()
+ pe.submit()
+
+ expected_gle = dict((d[0], d) for d in [
+ ["_Test Payable USD - _TC", 12500, 0, pi.name],
+ ["_Test Bank USD - _TC", 0, 12500, None]
+ ])
+
+ self.validate_gl_entries(pe.name, expected_gle)
+
+ outstanding_amount = flt(frappe.db.get_value("Sales Invoice", pi.name, "outstanding_amount"))
+ self.assertEqual(outstanding_amount, 0)
+
+ def test_payment_entry_against_si_usd_to_inr(self):
+ si = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC",
+ currency="USD", conversion_rate=50)
+ pe = get_payment_entry("Sales Invoice", si.name, party_amount=20,
+ bank_account="_Test Bank - _TC", bank_amount=900)
+ pe.reference_no = "1"
+ pe.reference_date = "2016-01-01"
+
+ self.assertEqual(pe.difference_amount, 100)
+
+ pe.append("deductions", {
+ "account": "_Test Exchange Gain/Loss - _TC",
+ "cost_center": "_Test Cost Center - _TC",
+ "amount": 100
+ })
+ pe.insert()
+ pe.submit()
+
+ expected_gle = dict((d[0], d) for d in [
+ ["_Test Receivable USD - _TC", 0, 1000, si.name],
+ ["_Test Bank - _TC", 900, 0, None],
+ ["_Test Exchange Gain/Loss - _TC", 100.0, 0, None],
+ ])
+
+ self.validate_gl_entries(pe.name, expected_gle)
+
+ outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount"))
+ self.assertEqual(outstanding_amount, 80)
+
+ def test_internal_transfer_usd_to_inr(self):
+ pe = frappe.new_doc("Payment Entry")
+ pe.payment_type = "Internal Transfer"
+ pe.company = "_Test Company"
+ pe.paid_from = "_Test Bank USD - _TC"
+ pe.paid_to = "_Test Bank - _TC"
+ pe.paid_amount = 100
+ pe.source_exchange_rate = 50
+ pe.received_amount = 4500
+ pe.reference_no = "2"
+ pe.reference_date = nowdate()
+
+ pe.setup_party_account_field()
+ pe.set_missing_values()
+ pe.set_exchange_rate()
+ pe.set_amounts()
+
+ self.assertEquals(pe.difference_amount, 500)
+
+ pe.append("deductions", {
+ "account": "_Test Exchange Gain/Loss - _TC",
+ "cost_center": "_Test Cost Center - _TC",
+ "amount": 500
+ })
+
+ pe.insert()
+ pe.submit()
+
+ expected_gle = dict((d[0], d) for d in [
+ ["_Test Bank USD - _TC", 0, 5000, None],
+ ["_Test Bank - _TC", 4500, 0, None],
+ ["_Test Exchange Gain/Loss - _TC", 500.0, 0, None],
+ ])
+
+ self.validate_gl_entries(pe.name, expected_gle)
+
+ def test_payment_against_negative_sales_invoice(self):
+ pe1 = frappe.new_doc("Payment Entry")
+ pe1.payment_type = "Pay"
+ pe1.company = "_Test Company"
+ pe1.party_type = "Customer"
+ pe1.party = "_Test Customer"
+ pe1.paid_from = "_Test Cash - _TC"
+ pe1.paid_amount = 100
+ pe1.received_amount = 100
+
+ self.assertRaises(InvalidPaymentEntry, pe1.validate)
+
+ si1 = create_sales_invoice()
+
+ # create full payment entry against si1
+ pe2 = get_payment_entry("Sales Invoice", si1.name, bank_account="_Test Cash - _TC")
+ pe2.insert()
+ pe2.submit()
+
+ # create return entry against si1
+ create_sales_invoice(is_return=1, return_against=si1.name, qty=-1)
+ si1_outstanding = frappe.db.get_value("Sales Invoice", si1.name, "outstanding_amount")
+ self.assertEqual(si1_outstanding, -100)
+
+ # pay more than outstanding against si1
+ pe3 = get_payment_entry("Sales Invoice", si1.name, bank_account="_Test Cash - _TC")
+ pe3.paid_amount = pe3.received_amount = 300
+ self.assertRaises(InvalidPaymentEntry, pe3.validate)
+
+ # pay negative outstanding against si1
+ pe3.paid_to = "Debtors - _TC"
+ pe3.paid_amount = pe3.received_amount = 100
+
+ pe3.insert()
+ pe3.submit()
+
+ expected_gle = dict((d[0], d) for d in [
+ ["Debtors - _TC", 100, 0, si1.name],
+ ["_Test Cash - _TC", 0, 100, None]
+ ])
+
+ self.validate_gl_entries(pe3.name, expected_gle)
+
+ outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si1.name, "outstanding_amount"))
+ self.assertEqual(outstanding_amount, 0)
+
+ pe3.cancel()
+ self.assertFalse(self.get_gle(pe3.name))
+
+ outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si1.name, "outstanding_amount"))
+ self.assertEqual(outstanding_amount, -100)
+
+ def validate_gl_entries(self, voucher_no, expected_gle):
+ gl_entries = self.get_gle(voucher_no)
+
+ self.assertTrue(gl_entries)
+
+ for i, gle in enumerate(gl_entries):
+ self.assertEquals(expected_gle[gle.account][0], gle.account)
+ self.assertEquals(expected_gle[gle.account][1], gle.debit)
+ self.assertEquals(expected_gle[gle.account][2], gle.credit)
+ self.assertEquals(expected_gle[gle.account][3], gle.against_voucher)
+
+ def get_gle(self, voucher_no):
+ return frappe.db.sql("""select account, debit, credit, against_voucher
+ from `tabGL Entry` where voucher_type='Payment Entry' and voucher_no=%s
+ order by account asc""", voucher_no, as_dict=1)
diff --git a/erpnext/accounts/doctype/payment_entry_deduction/__init__.py b/erpnext/accounts/doctype/payment_entry_deduction/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_entry_deduction/__init__.py
diff --git a/erpnext/accounts/doctype/payment_entry_deduction/payment_entry_deduction.json b/erpnext/accounts/doctype/payment_entry_deduction/payment_entry_deduction.json
new file mode 100644
index 0000000..e71d973
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_entry_deduction/payment_entry_deduction.json
@@ -0,0 +1,113 @@
+{
+ "allow_copy": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "beta": 0,
+ "creation": "2016-06-15 15:56:30.815503",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "",
+ "fields": [
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "account",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "label": "Account",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Account",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "cost_center",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "label": "Cost Center",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Cost Center",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "amount",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "label": "Amount",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ }
+ ],
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
+ "in_create": 0,
+ "in_dialog": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 1,
+ "max_attachments": 0,
+ "modified": "2016-06-27 11:18:35.021945",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Payment Entry Deduction",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/payment_entry_deduction/payment_entry_deduction.py b/erpnext/accounts/doctype/payment_entry_deduction/payment_entry_deduction.py
new file mode 100644
index 0000000..d6686bb
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_entry_deduction/payment_entry_deduction.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.model.document import Document
+
+class PaymentEntryDeduction(Document):
+ pass
diff --git a/erpnext/accounts/doctype/payment_entry_reference/__init__.py b/erpnext/accounts/doctype/payment_entry_reference/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_entry_reference/__init__.py
diff --git a/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.json b/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.json
new file mode 100644
index 0000000..5d61996
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.json
@@ -0,0 +1,237 @@
+{
+ "allow_copy": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "beta": 0,
+ "creation": "2016-06-01 16:55:32.196722",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "",
+ "fields": [
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "reference_doctype",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Type",
+ "length": 0,
+ "no_copy": 0,
+ "options": "DocType",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "reference_name",
+ "fieldtype": "Dynamic Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "label": "Name",
+ "length": 0,
+ "no_copy": 0,
+ "options": "reference_doctype",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "due_date",
+ "fieldtype": "Date",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Due Date",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "column_break_4",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "total_amount",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "label": "Total Amount",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "outstanding_amount",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "label": "Outstanding Amount",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "allocated_amount",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Allocated Amount",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "exchange_rate",
+ "fieldtype": "Float",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Exchange Rate",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ }
+ ],
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
+ "in_create": 0,
+ "in_dialog": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 1,
+ "max_attachments": 0,
+ "modified": "2016-06-27 11:18:20.442383",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Payment Entry Reference",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.py b/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.py
new file mode 100644
index 0000000..51f8c06
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.model.document import Document
+
+class PaymentEntryReference(Document):
+ pass
diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js
index 5075f15..a50ff19 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js
+++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js
@@ -4,7 +4,15 @@
frappe.provide("erpnext.accounts");
erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.extend({
-
+ setup: function() {
+ this.frm.get_field('payments').grid.editable_fields = [
+ {fieldname: 'reference_name', columns: 3},
+ {fieldname: 'amount', columns: 2},
+ {fieldname: 'invoice_number', columns: 3},
+ {fieldname: 'allocated_amount', columns: 3}
+ ];
+ },
+
onload: function() {
var me = this
this.frm.set_query('party_type', function() {
@@ -105,14 +113,16 @@
if (row.invoice_number && !inList(invoices, row.invoice_number))
invoices.push(row.invoice_type + " | " + row.invoice_number);
});
+
+ if (invoices) {
+ frappe.meta.get_docfield("Payment Reconciliation Payment", "invoice_number",
+ me.frm.doc.name).options = "\n" + invoices.join("\n");
- frappe.meta.get_docfield("Payment Reconciliation Payment", "invoice_number",
- me.frm.doc.name).options = invoices.join("\n");
-
- $.each(me.frm.doc.payments || [], function(i, p) {
- if(!inList(invoices, cstr(p.invoice_number))) p.invoice_number = null;
- });
-
+ $.each(me.frm.doc.payments || [], function(i, p) {
+ if(!inList(invoices, cstr(p.invoice_number))) p.invoice_number = null;
+ });
+ }
+
refresh_field("payments");
},
diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
index 832a346..5910a24 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
+++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
@@ -7,31 +7,49 @@
from frappe import msgprint, _
from frappe.model.document import Document
from erpnext.accounts.utils import get_outstanding_invoices
+from erpnext.controllers.accounts_controller import get_advance_payment_entries
class PaymentReconciliation(Document):
def get_unreconciled_entries(self):
- self.get_jv_entries()
+ self.get_nonreconciled_payment_entries()
self.get_invoice_entries()
+
+ def get_nonreconciled_payment_entries(self):
+ self.check_mandatory_to_fetch()
+
+ payment_entries = self.get_payment_entries()
+ journal_entries = self.get_jv_entries()
+
+ self.add_payment_entries(payment_entries + journal_entries)
+
+ def get_payment_entries(self):
+ order_doctype = "Sales Order" if self.party_type=="Customer" else "Purchase Order"
+ payment_entries = get_advance_payment_entries(self.party_type, self.party,
+ self.receivable_payable_account, order_doctype, against_all_orders=True)
+
+ return payment_entries
def get_jv_entries(self):
- self.check_mandatory_to_fetch()
dr_or_cr = "credit_in_account_currency" if self.party_type == "Customer" \
else "debit_in_account_currency"
bank_account_condition = "t2.against_account like %(bank_cash_account)s" \
if self.bank_cash_account else "1=1"
- jv_entries = frappe.db.sql("""
+ journal_entries = frappe.db.sql("""
select
- t1.name as voucher_no, t1.posting_date, t1.remark,
- t2.name as voucher_detail_no, {dr_or_cr} as payment_amount, t2.is_advance
+ "Journal Entry" as reference_type, t1.name as reference_name,
+ t1.posting_date, t1.remark as remarks, t2.name as reference_row,
+ {dr_or_cr} as amount, t2.is_advance
from
`tabJournal Entry` t1, `tabJournal Entry Account` t2
where
t1.name = t2.parent and t1.docstatus = 1 and t2.docstatus = 1
and t2.party_type = %(party_type)s and t2.party = %(party)s
and t2.account = %(account)s and {dr_or_cr} > 0
- and (t2.reference_type is null or t2.reference_type in ('', 'Sales Order', 'Purchase Order'))
+ and (t2.reference_type is null or t2.reference_type = '' or
+ (t2.reference_type in ('Sales Order', 'Purchase Order')
+ and t2.reference_name is not null and t2.reference_name != ''))
and (CASE
WHEN t1.voucher_type in ('Debit Note', 'Credit Note')
THEN 1=1
@@ -47,18 +65,13 @@
"bank_cash_account": "%%%s%%" % self.bank_cash_account
}, as_dict=1)
- self.add_payment_entries(jv_entries)
+ return list(journal_entries)
- def add_payment_entries(self, jv_entries):
+ def add_payment_entries(self, entries):
self.set('payments', [])
- for e in jv_entries:
- ent = self.append('payments', {})
- ent.journal_entry = e.get('voucher_no')
- ent.posting_date = e.get('posting_date')
- ent.amount = flt(e.get('payment_amount'))
- ent.remark = e.get('remark')
- ent.voucher_detail_number = e.get('voucher_detail_no')
- ent.is_advance = e.get('is_advance')
+ for e in entries:
+ row = self.append('payments', {})
+ row.update(e)
def get_invoice_entries(self):
#Fetch JVs, Sales and Purchase Invoices for 'invoices' to reconcile against
@@ -90,14 +103,16 @@
self.get_invoice_entries()
self.validate_invoice()
- dr_or_cr = "credit_in_account_currency" if self.party_type == "Customer" \
- else "debit_in_account_currency"
+ dr_or_cr = "credit_in_account_currency" \
+ if self.party_type == "Customer" else "debit_in_account_currency"
+
lst = []
for e in self.get('payments'):
if e.invoice_number and e.allocated_amount:
- lst.append({
- 'voucher_no' : e.journal_entry,
- 'voucher_detail_no' : e.voucher_detail_number,
+ lst.append(frappe._dict({
+ 'voucher_type': e.reference_type,
+ 'voucher_no' : e.reference_name,
+ 'voucher_detail_no' : e.reference_row,
'against_voucher_type' : e.invoice_type,
'against_voucher' : e.invoice_number,
'account' : self.receivable_payable_account,
@@ -105,13 +120,14 @@
'party': self.party,
'is_advance' : e.is_advance,
'dr_or_cr' : dr_or_cr,
- 'unadjusted_amt' : flt(e.amount),
- 'allocated_amt' : flt(e.allocated_amount)
- })
-
+ 'unadjusted_amount' : flt(e.amount),
+ 'allocated_amount' : flt(e.allocated_amount)
+ }))
+
if lst:
from erpnext.accounts.utils import reconcile_against_document
reconcile_against_document(lst)
+
msgprint(_("Successfully Reconciled"))
self.get_unreconciled_entries()
@@ -142,7 +158,7 @@
.format(p.invoice_type, p.invoice_number))
if flt(p.allocated_amount) > flt(p.amount):
- frappe.throw(_("Row {0}: Allocated amount {1} must be less than or equals to JV amount {2}")
+ frappe.throw(_("Row {0}: Allocated amount {1} must be less than or equals to Payment Entry amount {2}")
.format(p.idx, p.allocated_amount, p.amount))
invoice_outstanding = unreconciled_invoices.get(p.invoice_type, {}).get(p.invoice_number)
diff --git a/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json b/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json
index f9b6039..401d234 100644
--- a/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json
+++ b/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json
@@ -2,6 +2,7 @@
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 0,
+ "beta": 0,
"creation": "2014-07-09 16:13:35.452759",
"custom": 0,
"docstatus": 0,
@@ -12,18 +13,46 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "fieldname": "journal_entry",
+ "fieldname": "reference_type",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
- "in_list_view": 1,
- "label": "Journal Entry",
+ "in_list_view": 0,
+ "label": "Reference Type",
"length": 0,
"no_copy": 0,
- "options": "Journal Entry",
+ "options": "DocType",
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "reference_name",
+ "fieldtype": "Dynamic Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "label": "Reference_name",
+ "length": 0,
+ "no_copy": 0,
+ "options": "reference_type",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
@@ -39,6 +68,7 @@
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Posting Date",
@@ -46,6 +76,7 @@
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
@@ -61,6 +92,7 @@
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Amount",
@@ -68,6 +100,7 @@
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
@@ -83,6 +116,7 @@
"fieldtype": "Data",
"hidden": 1,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Is Advance",
@@ -90,6 +124,7 @@
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
@@ -101,17 +136,19 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "fieldname": "voucher_detail_number",
+ "fieldname": "reference_row",
"fieldtype": "Data",
"hidden": 1,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
- "label": "Voucher Detail Number",
+ "label": "Reference Row",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
@@ -127,6 +164,7 @@
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "",
@@ -134,6 +172,7 @@
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -149,6 +188,7 @@
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Invoice Number",
@@ -157,6 +197,7 @@
"options": "",
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
@@ -172,6 +213,7 @@
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Allocated amount",
@@ -180,6 +222,7 @@
"permlevel": 0,
"precision": "",
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
@@ -195,6 +238,7 @@
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "",
@@ -202,6 +246,7 @@
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -217,6 +262,7 @@
"fieldtype": "Small Text",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Remark",
@@ -224,6 +270,7 @@
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
@@ -234,21 +281,27 @@
],
"hide_heading": 0,
"hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
- "modified": "2015-11-16 06:29:51.563989",
+ "menu_index": 0,
+ "modified": "2016-06-27 18:27:15.663498",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Reconciliation Payment",
"name_case": "",
"owner": "Administrator",
"permissions": [],
+ "quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"sort_field": "modified",
- "sort_order": "DESC"
+ "sort_order": "DESC",
+ "track_seen": 0,
+ "version": 0
}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py
index fc40d42..f7d9586 100644
--- a/erpnext/accounts/doctype/payment_request/payment_request.py
+++ b/erpnext/accounts/doctype/payment_request/payment_request.py
@@ -6,11 +6,11 @@
import frappe
from frappe import _
from frappe.model.document import Document
-from frappe.utils import flt, nowdate, get_url, cstr
+from frappe.utils import flt, get_url, nowdate
from erpnext.accounts.party import get_party_account
from erpnext.accounts.utils import get_account_currency
-from erpnext.accounts.doctype.journal_entry.journal_entry import (get_payment_entry_against_invoice,
-get_payment_entry_against_order)
+from erpnext.setup.utils import get_exchange_rate
+from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry, get_company_defaults
class PaymentRequest(Document):
def validate(self):
@@ -41,7 +41,6 @@
def on_submit(self):
send_mail = True
self.make_communication_entry()
-
ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name)
if hasattr(ref_doc, "order_type") and getattr(ref_doc, "order_type") == "Shopping Cart":
@@ -77,55 +76,56 @@
if frappe.session.user == "Guest":
frappe.set_user("Administrator")
- jv = self.create_journal_entry()
+ payment_entry = self.create_payment_entry()
self.make_invoice()
- return jv
+ return payment_entry
- def create_journal_entry(self):
+ def create_payment_entry(self):
"""create entry"""
frappe.flags.ignore_account_permission = True
ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name)
-
- party_account = get_party_account("Customer", ref_doc.get("customer"), ref_doc.company)
- party_account_currency = get_account_currency(party_account)
-
- debit_in_account_currency = 0.0
-
- if party_account_currency == ref_doc.company_currency:
- amount = flt(flt(self.grand_total) * \
- flt(ref_doc.conversion_rate, ref_doc.precision("conversion_rate")), \
- ref_doc.precision("base_grand_total"))
-
- if self.currency != ref_doc.company_currency:
- debit_in_account_currency = self.grand_total
-
- else:
- amount = debit_in_account_currency = self.grand_total
-
- if self.reference_doctype == "Sales Order":
- jv = get_payment_entry_against_order(self.reference_doctype, self.reference_name,
- amount=amount, debit_in_account_currency=debit_in_account_currency , journal_entry=True,
- bank_account=self.payment_account)
-
+
if self.reference_doctype == "Sales Invoice":
- jv = get_payment_entry_against_invoice(self.reference_doctype, self.reference_name,
- amount=amount, debit_in_account_currency=debit_in_account_currency, journal_entry=True,
- bank_account=self.payment_account)
+ party_account = ref_doc.debit_to
+ elif self.reference_doctype == "Purchase Invoice":
+ party_account = ref_doc.credit_to
+ else:
+ party_account = get_party_account("Customer", ref_doc.get("customer"), ref_doc.company)
- jv.update({
- "voucher_type": "Journal Entry",
- "posting_date": nowdate()
- })
+ party_account_currency = ref_doc.get("party_account_currency") or get_account_currency(party_account)
- jv.insert(ignore_permissions=True)
- jv.submit()
+ bank_amount = self.grand_total
+ if party_account_currency == ref_doc.company_currency and party_account_currency != self.currency:
+ party_amount = self.base_grand_total
+ else:
+ party_amount = self.grand_total
+
+ payment_entry = get_payment_entry(self.reference_doctype, self.reference_name,
+ party_amount=party_amount, bank_account=self.payment_account, bank_amount=bank_amount)
+
+ payment_entry.update({
+ "reference_no": self.name,
+ "reference_date": nowdate(),
+ "remarks": "Payment Entry against {0} {1} via Payment Request {2}".format(self.reference_doctype,
+ self.reference_name, self.name)
+ })
+
+ company_details = get_company_defaults(ref_doc.company)
+ if payment_entry.difference_amount:
+ payment_entry.append("deductions", {
+ "account": company_details.exchange_gain_loss_account,
+ "cost_center": company_details.cost_center,
+ "amount": payment_entry.difference_amount
+ })
+ payment_entry.insert(ignore_permissions=True)
+ payment_entry.submit()
#set status as paid for Payment Request
frappe.db.set_value(self.doctype, self.name, "status", "Paid")
- return jv
+ return payment_entry
def send_email(self):
"""send email with payment link"""
@@ -177,7 +177,7 @@
grand_total = get_amount(ref_doc, args.dt)
existing_payment_request = frappe.db.get_value("Payment Request",
- {"reference_doctype": args.dt, "reference_name": args.dn})
+ {"reference_doctype": args.dt, "reference_name": args.dn, "docstatus": ["!=", 2]})
if existing_payment_request:
pr = frappe.get_doc("Payment Request", existing_payment_request)
@@ -199,7 +199,6 @@
if args.return_doc:
return pr
-
if args.submit_doc:
pr.insert(ignore_permissions=True)
pr.submit()
diff --git a/erpnext/accounts/doctype/payment_request/test_payment_request.py b/erpnext/accounts/doctype/payment_request/test_payment_request.py
index eb8e558..73c412f 100644
--- a/erpnext/accounts/doctype/payment_request/test_payment_request.py
+++ b/erpnext/accounts/doctype/payment_request/test_payment_request.py
@@ -6,7 +6,7 @@
import frappe
import unittest
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
-from erpnext.accounts.doctype.payment_request.payment_request import make_payment_request, get_gateway_details
+from erpnext.accounts.doctype.payment_request.payment_request import make_payment_request
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.setup.utils import get_exchange_rate
# test_records = frappe.get_test_records('Payment Request')
@@ -62,27 +62,44 @@
self.assertEquals(pr.currency, "USD")
def test_payment_entry(self):
+ frappe.db.set_value("Company", "_Test Company",
+ "exchange_gain_loss_account", "_Test Exchange Gain/Loss - _TC")
+ frappe.db.set_value("Company", "_Test Company",
+ "write_off_account", "_Test Write Off - _TC")
+ frappe.db.set_value("Company", "_Test Company",
+ "cost_center", "_Test Cost Center - _TC")
+
so_inr = make_sales_order(currency="INR")
pr = make_payment_request(dt="Sales Order", dn=so_inr.name, recipient_id="saurabh@erpnext.com",
mute_email=1, submit_doc=1)
- jv = pr.set_as_paid()
+ pe = pr.set_as_paid()
so_inr = frappe.get_doc("Sales Order", so_inr.name)
- self.assertEquals(so_inr.advance_paid, jv.total_debit)
-
- conversion_rate = get_exchange_rate("USD", "INR")
+ self.assertEquals(so_inr.advance_paid, 1000)
si_usd = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC",
- currency="USD", conversion_rate=conversion_rate)
+ currency="USD", conversion_rate=50)
pr = make_payment_request(dt="Sales Invoice", dn=si_usd.name, recipient_id="saurabh@erpnext.com",
- mute_email=1, return_doc=1, payment_gateway="_Test Gateway - USD")
+ mute_email=1, payment_gateway="_Test Gateway - USD", submit_doc=1)
+
+ pe = pr.set_as_paid()
+
+ expected_gle = dict((d[0], d) for d in [
+ ["_Test Receivable USD - _TC", 0, 5000, si_usd.name],
+ [pr.payment_account, 6000.0, 0, None],
+ ["_Test Exchange Gain/Loss - _TC", 0, 1000, None]
+ ])
+
+ gl_entries = frappe.db.sql("""select account, debit, credit, against_voucher
+ from `tabGL Entry` where voucher_type='Payment Entry' and voucher_no=%s
+ order by account asc""", pe.name, as_dict=1)
- jv = pr.set_as_paid()
+ self.assertTrue(gl_entries)
- payment_gateway_details = get_gateway_details({"payment_gateway": "_Test Gateway - USD"})
-
- self.assertEquals(jv.accounts[0].account, "_Test Receivable USD - _TC")
- self.assertEquals(jv.accounts[1].account, payment_gateway_details.payment_account)
-
\ No newline at end of file
+ for i, gle in enumerate(gl_entries):
+ self.assertEquals(expected_gle[gle.account][0], gle.account)
+ self.assertEquals(expected_gle[gle.account][1], gle.debit)
+ self.assertEquals(expected_gle[gle.account][2], gle.credit)
+ self.assertEquals(expected_gle[gle.account][3], gle.against_voucher)
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
index 7a83c7b..8aae29e 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
@@ -34,8 +34,8 @@
}
if(!doc.is_return && doc.docstatus==1) {
- if(doc.outstanding_amount > 0) {
- this.frm.add_custom_button(__('Payment'), this.make_bank_entry, __("Make"));
+ if(doc.outstanding_amount != 0) {
+ this.frm.add_custom_button(__('Payment'), this.make_payment_entry, __("Make"));
cur_frm.page.set_inner_btn_group_as_primary(__("Make"));
}
@@ -218,21 +218,6 @@
}
}
-cur_frm.cscript.make_bank_entry = function() {
- return frappe.call({
- method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_payment_entry_against_invoice",
- args: {
- "dt": "Purchase Invoice",
- "dn": cur_frm.doc.name
- },
- callback: function(r) {
- var doclist = frappe.model.sync(r.message);
- frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
- }
- });
-}
-
-
cur_frm.fields_dict['supplier_address'].get_query = function(doc, cdt, cdn) {
return{
filters:{'supplier': doc.supplier}
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
index c6d04c1..b247e3e 100755
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
@@ -2055,7 +2055,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "fieldname": "get_advances_paid",
+ "fieldname": "get_advances",
"fieldtype": "Button",
"hidden": 0,
"ignore_user_permissions": 0,
@@ -2066,7 +2066,7 @@
"length": 0,
"no_copy": 0,
"oldfieldtype": "Button",
- "options": "get_advances",
+ "options": "",
"permlevel": 0,
"print_hide": 1,
"print_hide_if_no_value": 0,
@@ -3031,7 +3031,7 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
- "modified": "2016-06-30 13:40:39.440646",
+ "modified": "2016-06-30 13:40:39.440648",
"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 de65485..3bb402d 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -65,7 +65,7 @@
self.po_required()
self.pr_required()
self.validate_supplier_invoice()
- self.validate_advance_jv("Purchase Order")
+
# validate cash purchase
if (self.is_paid == 1):
@@ -109,11 +109,6 @@
super(PurchaseInvoice, self).set_missing_values(for_validate)
- def get_advances(self):
- if not self.is_return:
- super(PurchaseInvoice, self).get_advances(self.credit_to, "Supplier", self.supplier,
- "Purchase Invoice Advance", "advances", "debit_in_account_currency", "purchase_order")
-
def check_conversion_rate(self):
default_currency = get_company_currency(self.company)
if not default_currency:
@@ -234,37 +229,6 @@
if not submitted:
frappe.throw(_("Purchase Receipt {0} is not submitted").format(d.purchase_receipt))
-
- def update_against_document_in_jv(self):
- """
- Links invoice and advance voucher:
- 1. cancel advance voucher
- 2. split into multiple rows if partially adjusted, assign against voucher
- 3. submit advance voucher
- """
-
- lst = []
- for d in self.get('advances'):
- if flt(d.allocated_amount) > 0:
- args = {
- 'voucher_no' : d.journal_entry,
- 'voucher_detail_no' : d.jv_detail_no,
- 'against_voucher_type' : 'Purchase Invoice',
- 'against_voucher' : self.name,
- 'account' : self.credit_to,
- 'party_type': 'Supplier',
- 'party': self.supplier,
- 'is_advance' : 'Yes',
- 'dr_or_cr' : 'debit_in_account_currency',
- 'unadjusted_amt' : flt(d.advance_amount),
- 'allocated_amt' : flt(d.allocated_amount)
- }
- lst.append(args)
-
- if lst:
- from erpnext.accounts.utils import reconcile_against_document
- reconcile_against_document(lst)
-
def update_status_updater_args(self):
if cint(self.update_stock):
self.status_updater.extend([{
diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
index 5dacb9b..bb489e7 100644
--- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
@@ -196,8 +196,9 @@
pi = frappe.copy_doc(test_records[0])
pi.append("advances", {
- "journal_entry": jv.name,
- "jv_detail_no": jv.get("accounts")[0].name,
+ "reference_type": "Journal Entry",
+ "reference_name": jv.name,
+ "reference_row": jv.get("accounts")[0].name,
"advance_amount": 400,
"allocated_amount": 300,
"remarks": jv.remark
diff --git a/erpnext/accounts/doctype/purchase_invoice_advance/purchase_invoice_advance.json b/erpnext/accounts/doctype/purchase_invoice_advance/purchase_invoice_advance.json
index 34156f5..462773d 100644
--- a/erpnext/accounts/doctype/purchase_invoice_advance/purchase_invoice_advance.json
+++ b/erpnext/accounts/doctype/purchase_invoice_advance/purchase_invoice_advance.json
@@ -2,29 +2,33 @@
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 0,
+ "beta": 0,
"creation": "2013-03-08 15:36:46",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
+ "document_type": "Document",
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "fieldname": "journal_entry",
+ "fieldname": "reference_type",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
- "in_list_view": 1,
- "label": "Journal Entry",
+ "in_list_view": 0,
+ "label": "Reference Type",
"length": 0,
"no_copy": 1,
"oldfieldname": "journal_voucher",
"oldfieldtype": "Link",
- "options": "Journal Entry",
+ "options": "DocType",
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"print_width": "180px",
"read_only": 1,
"report_hide": 0,
@@ -38,19 +42,75 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "fieldname": "jv_detail_no",
+ "fieldname": "reference_name",
+ "fieldtype": "Dynamic Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "label": "Reference Name",
+ "length": 0,
+ "no_copy": 1,
+ "options": "reference_type",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "remarks",
+ "fieldtype": "Read Only",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "label": "Remarks",
+ "length": 0,
+ "no_copy": 1,
+ "oldfieldname": "remarks",
+ "oldfieldtype": "Small Text",
+ "permlevel": 0,
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "print_width": "150px",
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0,
+ "width": "150px"
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "reference_row",
"fieldtype": "Data",
"hidden": 1,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
- "label": "Journal Entry Detail No",
+ "label": "Reference Row",
"length": 0,
"no_copy": 1,
"oldfieldname": "jv_detail_no",
"oldfieldtype": "Date",
"permlevel": 0,
"print_hide": 1,
+ "print_hide_if_no_value": 0,
"print_width": "80px",
"read_only": 1,
"report_hide": 0,
@@ -64,42 +124,18 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "fieldname": "remarks",
- "fieldtype": "Small Text",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "in_filter": 0,
- "in_list_view": 1,
- "label": "Remarks",
- "length": 0,
- "no_copy": 1,
- "oldfieldname": "remarks",
- "oldfieldtype": "Small Text",
- "permlevel": 0,
- "print_hide": 0,
- "print_width": "150px",
- "read_only": 1,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0,
- "width": "150px"
- },
- {
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
"fieldname": "col_break1",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -115,6 +151,7 @@
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Advance Amount",
@@ -125,6 +162,7 @@
"options": "party_account_currency",
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"print_width": "100px",
"read_only": 1,
"report_hide": 0,
@@ -142,6 +180,7 @@
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Allocated Amount",
@@ -152,6 +191,7 @@
"options": "party_account_currency",
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"print_width": "100px",
"read_only": 0,
"report_hide": 0,
@@ -165,18 +205,23 @@
"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": "2015-11-16 06:29:53.288895",
+ "menu_index": 0,
+ "modified": "2016-06-27 12:37:42.845967",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice Advance",
"owner": "Administrator",
"permissions": [],
+ "quick_entry": 1,
"read_only": 0,
- "read_only_onload": 0
+ "read_only_onload": 0,
+ "track_seen": 0,
+ "version": 0
}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
index d64a024..4bc634a 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
@@ -65,10 +65,14 @@
}
}
+ if(doc.outstanding_amount!=0 && !cint(doc.is_return)) {
+ cur_frm.add_custom_button(__('Payment'), this.make_payment_entry, __("Make"));
+ }
+
if(doc.outstanding_amount>0 && !cint(doc.is_return)) {
cur_frm.add_custom_button(__('Payment Request'), this.make_payment_request, __("Make"));
- cur_frm.add_custom_button(__('Payment'), cur_frm.cscript.make_bank_entry, __("Make"));
}
+
}
@@ -244,7 +248,7 @@
// Hide Fields
// ------------
cur_frm.cscript.hide_fields = function(doc) {
- parent_fields = ['project', 'due_date', 'is_opening', 'source', 'total_advance', 'get_advances_received',
+ parent_fields = ['project', 'due_date', 'is_opening', 'source', 'total_advance', 'get_advances',
'advances', 'sales_partner', 'commission_rate', 'total_commission', 'advances', 'from_date', 'to_date'];
if(cint(doc.is_pos) == 1) {
@@ -280,20 +284,6 @@
})
}
-cur_frm.cscript.make_bank_entry = function() {
- return frappe.call({
- method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_payment_entry_against_invoice",
- args: {
- "dt": "Sales Invoice",
- "dn": cur_frm.doc.name
- },
- callback: function(r) {
- var doclist = frappe.model.sync(r.message);
- frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
- }
- });
-}
-
cur_frm.fields_dict.cash_bank_account.get_query = function(doc) {
return {
filters: [
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
index 308db38..746209f 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
@@ -2019,7 +2019,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "fieldname": "get_advances_received",
+ "fieldname": "get_advances",
"fieldtype": "Button",
"hidden": 0,
"ignore_user_permissions": 0,
@@ -2030,7 +2030,7 @@
"length": 0,
"no_copy": 0,
"oldfieldtype": "Button",
- "options": "get_advances",
+ "options": "",
"permlevel": 0,
"print_hide": 1,
"print_hide_if_no_value": 0,
@@ -3676,7 +3676,7 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
- "modified": "2016-07-02 20:10:14.146763",
+ "modified": "2016-07-02 20:10:14.146963",
"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 470ea91..82cb365 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -77,7 +77,6 @@
self.check_close_sales_order("sales_order")
self.validate_debit_to_acc()
self.clear_unallocated_advances("Sales Invoice Advance", "advances")
- self.validate_advance_jv("Sales Order")
self.add_remarks()
self.validate_write_off_account()
self.validate_fixed_asset()
@@ -294,44 +293,9 @@
return pos
- def get_advances(self):
- if not self.is_return:
- super(SalesInvoice, self).get_advances(self.debit_to, "Customer", self.customer,
- "Sales Invoice Advance", "advances", "credit_in_account_currency", "sales_order")
-
def get_company_abbr(self):
return frappe.db.sql("select abbr from tabCompany where name=%s", self.company)[0][0]
- def update_against_document_in_jv(self):
- """
- Links invoice and advance voucher:
- 1. cancel advance voucher
- 2. split into multiple rows if partially adjusted, assign against voucher
- 3. submit advance voucher
- """
-
- lst = []
- for d in self.get('advances'):
- if flt(d.allocated_amount) > 0:
- args = {
- 'voucher_no' : d.journal_entry,
- 'voucher_detail_no' : d.jv_detail_no,
- 'against_voucher_type' : 'Sales Invoice',
- 'against_voucher' : self.name,
- 'account' : self.debit_to,
- 'party_type': 'Customer',
- 'party': self.customer,
- 'is_advance' : 'Yes',
- 'dr_or_cr' : 'credit_in_account_currency',
- 'unadjusted_amt' : flt(d.advance_amount),
- 'allocated_amt' : flt(d.allocated_amount)
- }
- lst.append(args)
-
- if lst:
- from erpnext.accounts.utils import reconcile_against_document
- reconcile_against_document(lst)
-
def validate_debit_to_acc(self):
account = frappe.db.get_value("Account", self.debit_to,
["account_type", "report_type", "account_currency"], as_dict=True)
@@ -740,7 +704,8 @@
account = frappe.db.get_value("Mode of Payment Account",
{"parent": mode_of_payment, "company": company}, "default_account")
if not account:
- frappe.throw(_("Please set default Cash or Bank account in Mode of Payment {0}").format(mode_of_payment))
+ frappe.throw(_("Please set default Cash or Bank account in Mode of Payment {0}")
+ .format(mode_of_payment))
return {
"account": account
}
diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
index cc3d952..ec63b1f 100644
--- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
@@ -676,8 +676,9 @@
si = frappe.copy_doc(test_records[0])
si.append("advances", {
"doctype": "Sales Invoice Advance",
- "journal_entry": jv.name,
- "jv_detail_no": jv.get("accounts")[0].name,
+ "reference_type": "Journal Entry",
+ "reference_name": jv.name,
+ "reference_row": jv.get("accounts")[0].name,
"advance_amount": 400,
"allocated_amount": 300,
"remarks": jv.remark
diff --git a/erpnext/accounts/doctype/sales_invoice_advance/sales_invoice_advance.json b/erpnext/accounts/doctype/sales_invoice_advance/sales_invoice_advance.json
index b1fae8e..06c6735 100644
--- a/erpnext/accounts/doctype/sales_invoice_advance/sales_invoice_advance.json
+++ b/erpnext/accounts/doctype/sales_invoice_advance/sales_invoice_advance.json
@@ -2,29 +2,33 @@
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 0,
+ "beta": 0,
"creation": "2013-02-22 01:27:41",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
+ "document_type": "Document",
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "fieldname": "journal_entry",
+ "fieldname": "reference_type",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
- "in_list_view": 1,
- "label": "Journal Entry",
+ "in_list_view": 0,
+ "label": "Reference Type",
"length": 0,
"no_copy": 1,
"oldfieldname": "journal_voucher",
"oldfieldtype": "Link",
- "options": "Journal Entry",
+ "options": "DocType",
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"print_width": "250px",
"read_only": 1,
"report_hide": 0,
@@ -38,10 +42,37 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "fieldname": "remarks",
- "fieldtype": "Small Text",
+ "fieldname": "reference_name",
+ "fieldtype": "Dynamic Link",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "label": "Reference Name",
+ "length": 0,
+ "no_copy": 1,
+ "options": "reference_type",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "remarks",
+ "fieldtype": "Read Only",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Remarks",
@@ -51,8 +82,9 @@
"oldfieldtype": "Small Text",
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"print_width": "150px",
- "read_only": 1,
+ "read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
@@ -64,19 +96,21 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "fieldname": "jv_detail_no",
+ "fieldname": "reference_row",
"fieldtype": "Data",
"hidden": 1,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
- "label": "Journal Entry Detail No",
+ "label": "Reference Row",
"length": 0,
"no_copy": 1,
"oldfieldname": "jv_detail_no",
"oldfieldtype": "Data",
"permlevel": 0,
"print_hide": 1,
+ "print_hide_if_no_value": 0,
"print_width": "120px",
"read_only": 1,
"report_hide": 0,
@@ -94,12 +128,14 @@
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -115,6 +151,7 @@
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Advance amount",
@@ -125,6 +162,7 @@
"options": "party_account_currency",
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"print_width": "120px",
"read_only": 1,
"report_hide": 0,
@@ -142,6 +180,7 @@
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Allocated amount",
@@ -152,6 +191,7 @@
"options": "party_account_currency",
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"print_width": "120px",
"read_only": 0,
"report_hide": 0,
@@ -165,18 +205,24 @@
"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": "2015-11-16 06:29:56.263776",
+ "menu_index": 0,
+ "modified": "2016-06-27 12:38:58.845134",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice Advance",
"owner": "Administrator",
"permissions": [],
+ "quick_entry": 1,
"read_only": 0,
- "read_only_onload": 0
+ "read_only_onload": 0,
+ "sort_order": "DESC",
+ "track_seen": 0,
+ "version": 0
}
\ No newline at end of file
diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py
index b7fb053..4af197e 100644
--- a/erpnext/accounts/general_ledger.py
+++ b/erpnext/accounts/general_ledger.py
@@ -160,7 +160,6 @@
gl_map.append(round_off_gle)
-
def delete_gl_entries(gl_entries=None, voucher_type=None, voucher_no=None,
adv_adj=False, update_outstanding="Yes"):
@@ -168,8 +167,12 @@
check_freezing_date, update_outstanding_amt, validate_frozen_account
if not gl_entries:
- gl_entries = frappe.db.sql("""select * from `tabGL Entry`
+ gl_entries = frappe.db.sql("""
+ select account, posting_date, party_type, party, cost_center, fiscal_year,
+ voucher_type, voucher_no, against_voucher_type, against_voucher, cost_center
+ from `tabGL Entry`
where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no), as_dict=True)
+
if gl_entries:
check_freezing_date(gl_entries[0]["posting_date"], adv_adj)
@@ -180,7 +183,7 @@
validate_frozen_account(entry["account"], adv_adj)
validate_balance_type(entry["account"], adv_adj)
validate_expense_against_budget(entry)
-
+
if entry.get("against_voucher") and update_outstanding == 'Yes':
update_outstanding_amt(entry["account"], entry.get("party_type"), entry.get("party"), entry.get("against_voucher_type"),
entry.get("against_voucher"), on_cancel=True)
diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py
index 8d11374..c65a845 100644
--- a/erpnext/accounts/party.py
+++ b/erpnext/accounts/party.py
@@ -181,8 +181,16 @@
{"parenttype": party_group_doctype, "parent": group, "company": company}, "account")
if not account:
- default_account_name = "default_receivable_account" if party_type=="Customer" else "default_payable_account"
+ default_account_name = "default_receivable_account" \
+ if party_type=="Customer" else "default_payable_account"
account = frappe.db.get_value("Company", company, default_account_name)
+
+ existing_gle_currency = get_party_gle_currency(party_type, party, company)
+ if existing_gle_currency:
+ if account:
+ account_currency = frappe.db.get_value("Account", account, "account_currency")
+ if (account and account_currency != existing_gle_currency) or not account:
+ account = get_party_gle_account(party_type, party, company)
return account
@@ -203,6 +211,17 @@
return frappe.local_cache("party_gle_currency", (party_type, party, company), generator,
regenerate_if_none=True)
+
+def get_party_gle_account(party_type, party, company):
+ def generator():
+ existing_gle_account = frappe.db.sql("""select account from `tabGL Entry`
+ where docstatus=1 and company=%(company)s and party_type=%(party_type)s and party=%(party)s
+ limit 1""", { "company": company, "party_type": party_type, "party": party })
+
+ return existing_gle_account[0][0] if existing_gle_account else None
+
+ return frappe.local_cache("party_gle_account", (party_type, party, company), generator,
+ regenerate_if_none=True)
def validate_party_gle_currency(party_type, party, company, party_account_currency=None):
"""Validate party account currency with existing GL Entry's currency"""
@@ -235,7 +254,7 @@
if doc.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's payble account currency"))
+ frappe.throw(_("Billing currency must be equal to either default comapany's currency or party account currency"))
@frappe.whitelist()
def get_due_date(posting_date, party_type, party, company):
diff --git a/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.js b/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.js
index 37d8130..ac3366c 100644
--- a/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.js
+++ b/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.js
@@ -22,6 +22,7 @@
"fieldtype": "Link",
"options": "Account",
"reqd": 1,
+ "default": locals[":Company"][frappe.defaults.get_user_default("Company")]["default_bank_account"],
"get_query": function() {
return {
"query": "erpnext.controllers.queries.get_account_list",
diff --git a/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py b/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py
index cfb677f..07ab969 100644
--- a/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py
+++ b/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py
@@ -3,7 +3,8 @@
from __future__ import unicode_literals
import frappe
-from frappe import _, msgprint
+from frappe import _
+from frappe.utils import nowdate, getdate
def execute(filters=None):
if not filters: filters = {}
@@ -14,28 +15,36 @@
return columns, data
def get_columns():
- return [_("Journal Entry") + ":Link/Journal Entry:140", _("Account") + ":Link/Account:140",
- _("Posting Date") + ":Date:100", _("Clearance Date") + ":Date:110", _("Against Account") + ":Link/Account:200",
- _("Debit") + ":Currency:120", _("Credit") + ":Currency:120"
+ return [
+ _("Payment Document") + ":Link/DocType:130",
+ _("Payment Entry") + ":Dynamic Link/"+_("Payment Document")+":110",
+ _("Posting Date") + ":Date:100",
+ _("Cheque/Reference No") + "::120",
+ _("Clearance Date") + ":Date:100",
+ _("Against Account") + ":Link/Account:170",
+ _("Amount") + ":Currency:120"
]
def get_conditions(filters):
conditions = ""
- if not filters.get("account"):
- msgprint(_("Please select Bank Account"), raise_exception=1)
- else:
- conditions += " and jvd.account = %(account)s"
- if filters.get("from_date"): conditions += " and jv.posting_date>=%(from_date)s"
- if filters.get("to_date"): conditions += " and jv.posting_date<=%(to_date)s"
+ if filters.get("from_date"): conditions += " and posting_date>=%(from_date)s"
+ if filters.get("to_date"): conditions += " and posting_date<=%(to_date)s"
return conditions
def get_entries(filters):
conditions = get_conditions(filters)
- entries = frappe.db.sql("""select jv.name, jvd.account, jv.posting_date,
- jv.clearance_date, jvd.against_account, jvd.debit, jvd.credit
+ journal_entries = frappe.db.sql("""select "Journal Entry", jv.name, jv.posting_date,
+ jv.cheque_no, jv.clearance_date, jvd.against_account, (jvd.debit - jvd.credit)
from `tabJournal Entry Account` jvd, `tabJournal Entry` jv
- where jvd.parent = jv.name and jv.docstatus=1 %s
- order by jv.name DESC""" % conditions, filters, as_list=1)
- return entries
\ No newline at end of file
+ where jvd.parent = jv.name and jv.docstatus=1 and jvd.account = %(account)s {0}
+ order by posting_date DESC, jv.name DESC""".format(conditions), filters, as_list=1)
+
+ payment_entries = frappe.db.sql("""select "Payment Entry", name, posting_date,
+ reference_no, clearance_date, party, if(paid_from=%(account)s, paid_amount, received_amount)
+ from `tabPayment Entry`
+ where docstatus=1 and (paid_from = %(account)s or paid_to = %(account)s) {0}
+ order by posting_date DESC, name DESC""".format(conditions), filters, as_list=1, debug=1)
+
+ return sorted(journal_entries + payment_entries, key=lambda k: k[2] or getdate(nowdate()))
\ No newline at end of file
diff --git a/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py b/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py
index 581a7c8..76e0de2 100644
--- a/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py
+++ b/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py
@@ -3,7 +3,7 @@
from __future__ import unicode_literals
import frappe
-from frappe.utils import flt
+from frappe.utils import flt, getdate, nowdate
from frappe import _
def execute(filters=None):
@@ -24,16 +24,8 @@
for d in data:
total_debit += flt(d.debit)
total_credit += flt(d.credit)
-
- amounts_not_reflected_in_system = frappe.db.sql("""
- select sum(jvd.debit_in_account_currency - jvd.credit_in_account_currency)
- from `tabJournal Entry Account` jvd, `tabJournal Entry` jv
- where jvd.parent = jv.name and jv.docstatus=1 and jvd.account=%s
- and jv.posting_date > %s and jv.clearance_date <= %s and ifnull(jv.is_opening, 'No') = 'No'
- """, (filters["account"], filters["report_date"], filters["report_date"]))
-
- amounts_not_reflected_in_system = flt(amounts_not_reflected_in_system[0][0]) \
- if amounts_not_reflected_in_system else 0.0
+
+ amounts_not_reflected_in_system = get_amounts_not_reflected_in_system(filters)
bank_bal = flt(balance_as_per_system) - flt(total_debit) + flt(total_credit) \
+ amounts_not_reflected_in_system
@@ -42,7 +34,7 @@
get_balance_row(_("Bank Statement balance as per General Ledger"), balance_as_per_system, account_currency),
{},
{
- "journal_entry": _("Outstanding Cheques and Deposits to clear"),
+ "payment_entry": _("Outstanding Cheques and Deposits to clear"),
"debit": total_debit,
"credit": total_credit,
"account_currency": account_currency
@@ -61,13 +53,21 @@
"fieldname": "posting_date",
"label": _("Posting Date"),
"fieldtype": "Date",
- "width": 100
+ "width": 90
},
{
- "fieldname": "journal_entry",
- "label": _("Journal Entry"),
+ "fieldname": "payment_document",
+ "label": _("Payment Document"),
"fieldtype": "Link",
- "options": "Journal Entry",
+ "options": "DocType",
+ "width": 120,
+ "hidden": 1
+ },
+ {
+ "fieldname": "payment_entry",
+ "label": _("Payment Entry"),
+ "fieldtype": "Dynamic Link",
+ "options": "payment_document",
"width": 220
},
{
@@ -92,7 +92,7 @@
"width": 200
},
{
- "fieldname": "reference",
+ "fieldname": "reference_no",
"label": _("Reference"),
"fieldtype": "Data",
"width": 100
@@ -119,31 +119,67 @@
]
def get_entries(filters):
- entries = frappe.db.sql("""select
- jv.posting_date, jv.name as journal_entry, jvd.debit_in_account_currency as debit,
+ journal_entries = frappe.db.sql("""
+ select "Journal Entry" as payment_document, jv.posting_date,
+ jv.name as payment_entry, jvd.debit_in_account_currency as debit,
jvd.credit_in_account_currency as credit, jvd.against_account,
- jv.cheque_no as reference, jv.cheque_date as ref_date, jv.clearance_date, jvd.account_currency
+ jv.cheque_no as reference_no, jv.cheque_date as ref_date, jv.clearance_date, jvd.account_currency
from
`tabJournal Entry Account` jvd, `tabJournal Entry` jv
where jvd.parent = jv.name and jv.docstatus=1
and jvd.account = %(account)s and jv.posting_date <= %(report_date)s
and ifnull(jv.clearance_date, '4000-01-01') > %(report_date)s
- and ifnull(jv.is_opening, 'No') = 'No'
- order by jv.posting_date DESC,jv.name DESC""", filters, as_dict=1)
+ and ifnull(jv.is_opening, 'No') = 'No'""", filters, as_dict=1)
+
+ payment_entries = frappe.db.sql("""
+ select
+ "Payment Entry" as payment_document, name as payment_entry,
+ reference_no, reference_date as ref_date,
+ if(paid_to=%(account)s, received_amount, 0) as debit,
+ if(paid_from=%(account)s, paid_amount, 0) as credit,
+ posting_date, party as against_account, clearance_date,
+ if(paid_to=%(account)s, paid_to_account_currency, paid_from_account_currency) as account_currency
+ from `tabPayment Entry`
+ where
+ (paid_from=%(account)s or paid_to=%(account)s) and docstatus=1
+ and posting_date <= %(report_date)s
+ and ifnull(clearance_date, '4000-01-01') > %(report_date)s
+ """, filters, as_dict=1)
- return entries
+ return sorted(list(payment_entries)+list(journal_entries),
+ key=lambda k: k['posting_date'] or getdate(nowdate()))
+
+def get_amounts_not_reflected_in_system(filters):
+ je_amount = frappe.db.sql("""
+ select sum(jvd.debit_in_account_currency - jvd.credit_in_account_currency)
+ from `tabJournal Entry Account` jvd, `tabJournal Entry` jv
+ where jvd.parent = jv.name and jv.docstatus=1 and jvd.account=%(account)s
+ and jv.posting_date > %(report_date)s and jv.clearance_date <= %(report_date)s
+ and ifnull(jv.is_opening, 'No') = 'No' """, filters)
+
+ je_amount = flt(je_amount[0][0]) if je_amount else 0.0
+
+ pe_amount = frappe.db.sql("""
+ select sum(if(paid_from=%(account)s, paid_amount, received_amount))
+ from `tabPayment Entry`
+ where (paid_from=%(account)s or paid_to=%(account)s) and docstatus=1
+ and posting_date > %(report_date)s and clearance_date <= %(report_date)s""", filters)
+
+ pe_amount = flt(pe_amount[0][0]) if pe_amount else 0.0
+
+ return je_amount + pe_amount
def get_balance_row(label, amount, account_currency):
if amount > 0:
return {
- "journal_entry": label,
+ "payment_entry": label,
"debit": amount,
"credit": 0,
"account_currency": account_currency
}
else:
return {
- "journal_entry": label,
+ "payment_entry": label,
"debit": 0,
"credit": abs(amount),
"account_currency": account_currency
diff --git a/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py b/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py
index 20bdae8..85c7daa 100644
--- a/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py
+++ b/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py
@@ -5,7 +5,7 @@
import frappe
from frappe import _
from erpnext.accounts.report.accounts_receivable.accounts_receivable import get_ageing_data
-from frappe.utils import flt, getdate
+from frappe.utils import getdate, flt
def execute(filters=None):
if not filters: filters = {}
@@ -14,21 +14,21 @@
columns = get_columns(filters)
entries = get_entries(filters)
invoice_details = get_invoice_posting_date_map(filters)
- against_date = ""
data = []
for d in entries:
- invoice = invoice_details.get(d.reference_name) or frappe._dict()
+ invoice = invoice_details.get(d.against_voucher) or frappe._dict()
+
if d.reference_type=="Purchase Invoice":
payment_amount = flt(d.debit) or -1 * flt(d.credit)
else:
payment_amount = flt(d.credit) or -1 * flt(d.debit)
- row = [d.name, d.party_type, d.party, d.posting_date, d.reference_name, invoice.posting_date,
- invoice.due_date, d.debit, d.credit, d.cheque_no, d.cheque_date, d.remark]
+ row = [d.voucher_type, d.voucher_no, d.party_type, d.party, d.posting_date, d.against_voucher,
+ invoice.posting_date, invoice.due_date, d.debit, d.credit, d.remarks]
- if d.reference_name:
- row += get_ageing_data(30, 60, 90, d.posting_date, against_date, payment_amount)
+ if d.against_voucher:
+ row += get_ageing_data(30, 60, 90, d.posting_date, invoice.posting_date, payment_amount)
else:
row += ["", "", "", "", ""]
if invoice.due_date:
@@ -46,7 +46,8 @@
def get_columns(filters):
return [
- _("Journal Entry") + ":Link/Journal Entry:140",
+ _("Payment Document") + ":Link/DocType: 100",
+ _("Payment Entry") + ":Dynamic Link/"+_("Payment Document")+":140",
_("Party Type") + "::100",
_("Party") + ":Dynamic Link/Party Type:140",
_("Posting Date") + ":Date:100",
@@ -55,8 +56,6 @@
_("Payment Due Date") + ":Date:130",
_("Debit") + ":Currency:120",
_("Credit") + ":Currency:120",
- _("Reference No") + "::100",
- _("Reference Date") + ":Date:100",
_("Remarks") + "::150",
_("Age") +":Int:40",
"0-30:Currency:100",
@@ -69,45 +68,36 @@
def get_conditions(filters):
conditions = []
- if not filters.get("party_type"):
- if filters.get("payment_type") == "Outgoing":
- filters["party_type"] = "Supplier"
+ if not filters.party_type:
+ if filters.payment_type == "Outgoing":
+ filters.party_type = "Supplier"
else:
- filters["party_type"] = "Customer"
+ filters.party_type = "Customer"
- if filters.get("party_type"):
- conditions.append("jvd.party_type=%(party_type)s")
+ if filters.party_type:
+ conditions.append("party_type=%(party_type)s")
- if filters.get("party"):
- conditions.append("jvd.party=%(party)s")
+ if filters.party:
+ conditions.append("party=%(party)s")
- if filters.get("party_type"):
- conditions.append("jvd.reference_type=%(reference_type)s")
- if filters.get("party_type") == "Customer":
- filters["reference_type"] = "Sales Invoice"
- else:
- filters["reference_type"] = "Purchase Invoice"
-
- if filters.get("company"):
- conditions.append("jv.company=%(company)s")
+ if filters.party_type:
+ conditions.append("against_voucher_type=%(reference_type)s")
+ filters["reference_type"] = "Sales Invoice" if filters.party_type=="Customer" else "Purchase Invoice"
if filters.get("from_date"):
- conditions.append("jv.posting_date >= %(from_date)s")
+ conditions.append("posting_date >= %(from_date)s")
+
if filters.get("to_date"):
- conditions.append("jv.posting_date <= %(to_date)s")
+ conditions.append("posting_date <= %(to_date)s")
- return "and {}".format(" and ".join(conditions)) if conditions else ""
+ return "and " + " and ".join(conditions) if conditions else ""
def get_entries(filters):
- conditions = get_conditions(filters)
- entries = frappe.db.sql("""select jv.name, jvd.party_type, jvd.party, jv.posting_date,
- jvd.reference_type, jvd.reference_name, jvd.debit, jvd.credit,
- jv.cheque_no, jv.cheque_date, jv.remark
- from `tabJournal Entry Account` jvd, `tabJournal Entry` jv
- where jvd.parent = jv.name and jv.docstatus=1 %s order by jv.name DESC""" %
- conditions, filters, as_dict=1)
-
- return entries
+ return frappe.db.sql("""select
+ voucher_type, voucher_no, party_type, party, posting_date, debit, credit, remarks, against_voucher
+ from `tabGL Entry`
+ where company=%(company)s and voucher_type in ('Journal Entry', 'Payment Entry') {0}
+ """.format(get_conditions(filters)), filters, as_dict=1)
def get_invoice_posting_date_map(filters):
invoice_details = {}
diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py
index 27389aa..6bd5eb1 100644
--- a/erpnext/accounts/utils.py
+++ b/erpnext/accounts/utils.py
@@ -172,53 +172,77 @@
Cancel JV, Update aginst document, split if required and resubmit jv
"""
for d in args:
- check_if_jv_modified(d)
+
+ check_if_advance_entry_modified(d)
validate_allocated_amount(d)
+
+ # cancel advance entry
+ doc = frappe.get_doc(d.voucher_type, d.voucher_no)
- # cancel JV
- jv_obj = frappe.get_doc('Journal Entry', d['voucher_no'])
+ doc.make_gl_entries(cancel=1, adv_adj=1)
- jv_obj.make_gl_entries(cancel=1, adv_adj=1)
+ # update ref in advance entry
+ if d.voucher_type == "Journal Entry":
+ update_reference_in_journal_entry(d, doc)
+ else:
+ update_reference_in_payment_entry(d, doc)
- # update ref in JV Detail
- update_against_doc(d, jv_obj)
+ # re-submit advance entry
+ doc = frappe.get_doc(d.voucher_type, d.voucher_no)
+ doc.make_gl_entries(cancel = 0, adv_adj =1)
- # re-submit JV
- jv_obj = frappe.get_doc('Journal Entry', d['voucher_no'])
- jv_obj.make_gl_entries(cancel = 0, adv_adj =1)
-
-
-def check_if_jv_modified(args):
+def check_if_advance_entry_modified(args):
"""
check if there is already a voucher reference
check if amount is same
check if jv is submitted
"""
- ret = frappe.db.sql("""
- select t2.{dr_or_cr} from `tabJournal Entry` t1, `tabJournal Entry Account` t2
- where t1.name = t2.parent and t2.account = %(account)s
- and t2.party_type = %(party_type)s and t2.party = %(party)s
- and (t2.reference_type is null or t2.reference_type in ("", "Sales Order", "Purchase Order"))
- and t1.name = %(voucher_no)s and t2.name = %(voucher_detail_no)s
- and t1.docstatus=1 """.format(dr_or_cr = args.get("dr_or_cr")), args)
+ ret = None
+ if args.voucher_type == "Journal Entry":
+ ret = frappe.db.sql("""
+ select t2.{dr_or_cr} from `tabJournal Entry` t1, `tabJournal Entry Account` t2
+ where t1.name = t2.parent and t2.account = %(account)s
+ and t2.party_type = %(party_type)s and t2.party = %(party)s
+ and (t2.reference_type is null or t2.reference_type in ("", "Sales Order", "Purchase Order"))
+ and t1.name = %(voucher_no)s and t2.name = %(voucher_detail_no)s
+ and t1.docstatus=1 """.format(dr_or_cr = args.get("dr_or_cr")), args)
+ else:
+ party_account_field = "paid_from" if args.party_type == "Customer" else "paid_to"
+ if args.voucher_detail_no:
+ ret = frappe.db.sql("""select t1.name
+ from `tabPayment Entry` t1, `tabPayment Entry Reference` t2
+ where
+ t1.name = t2.parent and t1.docstatus = 1
+ and t1.name = %(voucher_no)s and t2.name = %(voucher_detail_no)s
+ and t1.party_type = %(party_type)s and t1.party = %(party)s and t1.{0} = %(account)s
+ and t2.reference_doctype in ("", "Sales Order", "Purchase Order")
+ and t2.allocated_amount = %(unadjusted_amount)s
+ """.format(party_account_field), args)
+ else:
+ ret = frappe.db.sql("""select name from `tabPayment Entry`
+ where
+ name = %(voucher_no)s and docstatus = 1
+ and party_type = %(party_type)s and party = %(party)s and {0} = %(account)s
+ and unallocated_amount = %(unadjusted_amount)s
+ """.format(party_account_field), args)
if not ret:
throw(_("""Payment Entry has been modified after you pulled it. Please pull it again."""))
def validate_allocated_amount(args):
- if args.get("allocated_amt") < 0:
+ if args.get("allocated_amount") < 0:
throw(_("Allocated amount can not be negative"))
- elif args.get("allocated_amt") > args.get("unadjusted_amt"):
- throw(_("Allocated amount can not greater than unadusted amount"))
+ elif args.get("allocated_amount") > args.get("unadjusted_amount"):
+ throw(_("Allocated amount can not greater than unadjusted amount"))
-def update_against_doc(d, jv_obj):
+def update_reference_in_journal_entry(d, jv_obj):
"""
Updates against document, if partial amount splits into rows
"""
jv_detail = jv_obj.get("accounts", {"name": d["voucher_detail_no"]})[0]
- jv_detail.set(d["dr_or_cr"], d["allocated_amt"])
+ jv_detail.set(d["dr_or_cr"], d["allocated_amount"])
jv_detail.set('debit' if d['dr_or_cr']=='debit_in_account_currency' else 'credit',
- d["allocated_amt"]*flt(jv_detail.exchange_rate))
+ d["allocated_amount"]*flt(jv_detail.exchange_rate))
original_reference_type = jv_detail.reference_type
original_reference_name = jv_detail.reference_name
@@ -226,14 +250,14 @@
jv_detail.set("reference_type", d["against_voucher_type"])
jv_detail.set("reference_name", d["against_voucher"])
- if d['allocated_amt'] < d['unadjusted_amt']:
+ if d['allocated_amount'] < d['unadjusted_amount']:
jvd = frappe.db.sql("""
select cost_center, balance, against_account, is_advance,
account_type, exchange_rate, account_currency
from `tabJournal Entry Account` where name = %s
""", d['voucher_detail_no'], as_dict=True)
- amount_in_account_currency = flt(d['unadjusted_amt']) - flt(d['allocated_amt'])
+ amount_in_account_currency = flt(d['unadjusted_amount']) - flt(d['allocated_amount'])
amount_in_company_currency = amount_in_account_currency * flt(jvd[0]['exchange_rate'])
# new entry with balance amount
@@ -263,6 +287,39 @@
# will work as update after submit
jv_obj.flags.ignore_validate_update_after_submit = True
jv_obj.save(ignore_permissions=True)
+
+def update_reference_in_payment_entry(d, payment_entry):
+ reference_details = {
+ "reference_doctype": d.against_voucher_type,
+ "reference_name": d.against_voucher,
+ "total_amount": d.grand_total,
+ "outstanding_amount": d.outstanding_amount,
+ "allocated_amount": d.allocated_amount,
+ "exchange_rate": d.exchange_rate
+ }
+
+ if d.voucher_detail_no:
+ existing_row = payment_entry.get("references", {"name": d["voucher_detail_no"]})[0]
+ original_row = existing_row.as_dict().copy()
+ existing_row.update(reference_details)
+
+ if d.allocated_amount < original_row.allocated_amount:
+ new_row = payment_entry.append("references")
+ new_row.docstatus = 1
+ for field in reference_details.keys():
+ new_row.set(field, original_row[field])
+
+ new_row.allocated_amount = original_row.allocated_amount - d.allocated_amount
+ else:
+ new_row = payment_entry.append("references")
+ new_row.docstatus = 1
+ new_row.update(reference_details)
+
+ payment_entry.flags.ignore_validate_update_after_submit = True
+ payment_entry.setup_party_account_field()
+ payment_entry.set_missing_values()
+ payment_entry.set_amounts()
+ payment_entry.save(ignore_permissions=True)
def remove_against_link_from_jv(ref_type, ref_no):
linked_jv = frappe.db.sql_list("""select parent from `tabJournal Entry Account`
@@ -290,7 +347,7 @@
value = frappe.db.get_value("Company", company, fieldname)
if not value:
- throw(_("Please set default value {0} in Company {1}").format(frappe.get_meta("Company").get_label(fieldname), company))
+ throw(_("Please set default {0} in Company {1}").format(frappe.get_meta("Company").get_label(fieldname), company))
return value
@@ -378,7 +435,7 @@
payment_dr_or_cr = "payment_gl_entry.debit_in_account_currency - payment_gl_entry.credit_in_account_currency"
invoice_list = frappe.db.sql("""select
- voucher_no, voucher_type, posting_date,
+ voucher_no, voucher_type, posting_date,
ifnull(sum({dr_or_cr}), 0) as invoice_amount,
(
select
@@ -401,11 +458,11 @@
and {dr_or_cr} > 0
{condition}
and ((voucher_type = 'Journal Entry'
- and (against_voucher = ''
- or against_voucher is null))
- or (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
- having (invoice_amount - payment_amount) > 0.005""".format(
+ having (invoice_amount - payment_amount) > 0.005
+ order by posting_date, name""".format(
dr_or_cr = dr_or_cr,
payment_dr_or_cr = payment_dr_or_cr,
condition = condition or ""
@@ -416,16 +473,18 @@
}, as_dict=True)
for d in invoice_list:
- outstanding_invoices.append({
+ outstanding_invoices.append(frappe._dict({
'voucher_no': d.voucher_no,
'voucher_type': d.voucher_type,
'posting_date': d.posting_date,
'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, "due_date")
- })
-
+ 'due_date': frappe.db.get_value(d.voucher_type, d.voucher_no, "due_date"),
+ }))
+
+ outstanding_invoices = sorted(outstanding_invoices, key=lambda k: k['due_date'] or getdate(nowdate()))
+
return outstanding_invoices
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js
index 9e44855..f5d4e2e 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.js
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.js
@@ -76,7 +76,7 @@
this.make_purchase_invoice, __("Make"));
if(flt(doc.per_billed)==0 && doc.status != "Delivered") {
- cur_frm.add_custom_button(__('Payment'), cur_frm.cscript.make_bank_entry, __("Make"));
+ cur_frm.add_custom_button(__('Payment'), cur_frm.cscript.make_payment_entry, __("Make"));
}
cur_frm.page.set_inner_btn_group_as_primary(__("Make"));
@@ -184,20 +184,6 @@
this.frm.script_manager.copy_from_first_row("items", row, ["schedule_date"]);
},
- make_bank_entry: function() {
- return frappe.call({
- method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_payment_entry_against_order",
- args: {
- "dt": "Purchase Order",
- "dn": cur_frm.doc.name
- },
- callback: function(r) {
- var doclist = frappe.model.sync(r.message);
- frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
- }
- });
- },
-
unclose_purchase_order: function(){
cur_frm.cscript.update_status('Re-open', 'Submitted')
},
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py
index d4b2221..ec1bf46 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.py
@@ -308,7 +308,7 @@
def postprocess(source, target):
set_missing_values(source, target)
#Get the advance paid Journal Entries in Purchase Invoice Advance
- target.get_advances()
+ target.set_advances()
def update_item(obj, target, source_parent):
target.amount = flt(obj.amount) - flt(obj.billed_amt)
diff --git a/erpnext/config/accounts.py b/erpnext/config/accounts.py
index b6b1480..9dd50f8 100644
--- a/erpnext/config/accounts.py
+++ b/erpnext/config/accounts.py
@@ -46,6 +46,11 @@
},
{
"type": "doctype",
+ "name": "Payment Entry",
+ "description": _("Payment entries against party or for internal transfer")
+ },
+ {
+ "type": "doctype",
"name": "Journal Entry",
"description": _("Accounting journal entries.")
},
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index e3a66a4..2b67ceb 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -42,6 +42,7 @@
if self.doctype in ("Sales Invoice", "Purchase Invoice") and not self.is_return:
self.validate_due_date()
+ self.validate_advance_entries()
if self.meta.get_field("taxes_and_charges"):
self.validate_enabled_taxes_and_charges()
@@ -255,7 +256,7 @@
if not account_currency:
account_currency = get_account_currency(gl_dict.account)
- if self.doctype not in ["Journal Entry", "Period Closing Voucher"]:
+ if self.doctype not in ["Journal Entry", "Period Closing Voucher", "Payment Entry"]:
self.validate_account_currency(gl_dict.account, account_currency)
set_balance_in_account_currency(gl_dict, account_currency, self.get("conversion_rate"), self.company_currency)
@@ -276,71 +277,114 @@
frappe.db.sql("""delete from `tab%s` where parentfield=%s and parent = %s
and allocated_amount = 0""" % (childtype, '%s', '%s'), (parentfield, self.name))
- def get_advances(self, account_head, party_type, party, child_doctype, parentfield, dr_or_cr, against_order_field):
+ def set_advances(self):
"""Returns list of advances against Account, Party, Reference"""
- order_list = list(set([d.get(against_order_field) for d in self.get("items") if d.get(against_order_field)]))
+
+ res = self.get_advance_entries()
- # conver sales_order to "Sales Order"
- reference_type = against_order_field.replace("_", " ").title()
-
- condition = ""
- if order_list:
- in_placeholder = ', '.join(['%s'] * len(order_list))
- condition = "or (t2.reference_type = '{0}' and ifnull(t2.reference_name, '') in ({1}))"\
- .format(reference_type, in_placeholder)
-
- res = frappe.db.sql("""
- select
- t1.name as jv_no, t1.remark, t2.{0} as amount, t2.name as jv_detail_no,
- t2.reference_name as against_order
- from
- `tabJournal Entry` t1, `tabJournal Entry Account` t2
- where
- t1.name = t2.parent and t2.account = %s
- and t2.party_type = %s and t2.party = %s
- and t2.is_advance = 'Yes' and t1.docstatus = 1
- and (ifnull(t2.reference_type, '')='' {1})
- order by t1.posting_date""".format(dr_or_cr, condition),
- [account_head, party_type, party] + order_list, as_dict=1)
-
- self.set(parentfield, [])
+ self.set("advances", [])
for d in res:
- self.append(parentfield, {
- "doctype": child_doctype,
- "journal_entry": d.jv_no,
- "jv_detail_no": d.jv_detail_no,
- "remarks": d.remark,
+ self.append("advances", {
+ "doctype": self.doctype + " Advance",
+ "reference_type": d.reference_type,
+ "reference_name": d.reference_name,
+ "reference_row": d.reference_row,
+ "remarks": d.remarks,
"advance_amount": flt(d.amount),
"allocated_amount": flt(d.amount) if d.against_order else 0
})
+
+ def get_advance_entries(self, include_unallocated=True):
+ if self.doctype == "Sales Invoice":
+ party_account = self.debit_to
+ party_type = "Customer"
+ party = self.customer
+ amount_field = "credit_in_account_currency"
+ order_field = "sales_order"
+ order_doctype = "Sales Order"
+ else:
+ party_account = self.credit_to
+ party_type = "Supplier"
+ party = self.supplier
+ amount_field = "debit_in_account_currency"
+ order_field = "purchase_order"
+ order_doctype = "Purchase Order"
+
+ order_list = list(set([d.get(order_field)
+ for d in self.get("items") if d.get(order_field)]))
+
+ journal_entries = get_advance_journal_entries(party_type, party, party_account,
+ amount_field, order_doctype, order_list, include_unallocated)
+
+ payment_entries = get_advance_payment_entries(party_type, party, party_account,
+ order_doctype, order_list, include_unallocated)
+
+ res = journal_entries + payment_entries
+
+ return res
- def validate_advance_jv(self, reference_type):
- against_order_field = frappe.scrub(reference_type)
- order_list = list(set([d.get(against_order_field) for d in self.get("items") if d.get(against_order_field)]))
- if order_list:
- account = self.get("debit_to" if self.doctype=="Sales Invoice" else "credit_to")
+ def validate_advance_entries(self):
+ order_field = "sales_order" if self.doctype == "Sales Invoice" else "purchase_order"
+ order_list = list(set([d.get(order_field)
+ for d in self.get("items") if d.get(order_field)]))
+
+ if not order_list: return
+
+ advance_entries = self.get_advance_entries(include_unallocated=False)
+
+ if advance_entries:
+ advance_entries_against_si = [d.reference_name for d in self.get("advances")]
+ for d in advance_entries:
+ if not advance_entries_against_si or d.reference_name not in advance_entries_against_si:
+ frappe.msgprint(_("Payment Entry {0} is linked against Order {1}, check if it should be pulled as advance in this invoice.")
+ .format(d.reference_name, d.against_order))
+
+ def update_against_document_in_jv(self):
+ """
+ Links invoice and advance voucher:
+ 1. cancel advance voucher
+ 2. split into multiple rows if partially adjusted, assign against voucher
+ 3. submit advance voucher
+ """
+
+ if self.doctype == "Sales Invoice":
+ party_type = "Customer"
+ party = self.customer
+ party_account = self.debit_to
+ dr_or_cr = "credit_in_account_currency"
+ else:
+ party_type = "Supplier"
+ party = self.supplier
+ party_account = self.credit_to
+ dr_or_cr = "debit_in_account_currency"
- jv_against_order = frappe.db.sql("""select parent, reference_name as against_order
- from `tabJournal Entry Account`
- where docstatus=1 and account=%s and ifnull(is_advance, 'No') = 'Yes'
- and reference_type=%s
- and ifnull(reference_name, '') in ({0})
- group by parent, reference_name""".format(', '.join(['%s']*len(order_list))),
- tuple([account, reference_type] + order_list), as_dict=1)
-
- if jv_against_order:
- order_jv_map = {}
- for d in jv_against_order:
- order_jv_map.setdefault(d.against_order, []).append(d.parent)
-
- advance_jv_against_si = [d.journal_entry for d in self.get("advances")]
-
- for order, jv_list in order_jv_map.items():
- for jv in jv_list:
- if not advance_jv_against_si or jv not in advance_jv_against_si:
- frappe.msgprint(_("Journal Entry {0} is linked against Order {1}, check if it should be pulled as advance in this invoice.")
- .format(jv, order))
-
+ lst = []
+ for d in self.get('advances'):
+ if flt(d.allocated_amount) > 0:
+ args = frappe._dict({
+ 'voucher_type': d.reference_type,
+ 'voucher_no' : d.reference_name,
+ 'voucher_detail_no' : d.reference_row,
+ 'against_voucher_type' : self.doctype,
+ 'against_voucher' : self.name,
+ 'account' : party_account,
+ 'party_type': party_type,
+ 'party': party,
+ 'is_advance' : 'Yes',
+ 'dr_or_cr' : dr_or_cr,
+ 'unadjusted_amount' : flt(d.advance_amount),
+ 'allocated_amount' : flt(d.allocated_amount),
+ 'exchange_rate': (self.conversion_rate
+ if self.party_account_currency != self.company_currency else 1),
+ 'grand_total': (self.base_grand_total
+ if self.party_account_currency==self.company_currency else self.grand_total),
+ 'outstanding_amount': self.outstanding_amount
+ })
+ lst.append(args)
+
+ if lst:
+ from erpnext.accounts.utils import reconcile_against_document
+ reconcile_against_document(lst)
def validate_multiple_billing(self, ref_dt, item_ref_dn, based_on, parentfield):
from erpnext.controllers.status_updater import get_tolerance_for
@@ -396,10 +440,10 @@
select
account_currency, sum({dr_or_cr}) as amount
from
- `tabJournal Entry Account`
+ `tabGL Entry`
where
- reference_type = %s and reference_name = %s and party=%s
- and docstatus = 1 and is_advance = "Yes"
+ against_voucher_type = %s and against_voucher = %s and party=%s
+ and docstatus = 1
""".format(dr_or_cr=dr_or_cr), (self.doctype, self.name, party), as_dict=1)
if advance:
@@ -420,11 +464,11 @@
formatted_order_total = fmt_money(order_total, precision=self.precision("base_grand_total"),
currency=advance.account_currency)
- if order_total >= advance_paid:
- frappe.db.set_value(self.doctype, self.name, "advance_paid", advance_paid)
- else:
+ if self.currency == self.company_currency and advance_paid > order_total:
frappe.throw(_("Total advance ({0}) against Order {1} cannot be greater than the Grand Total ({2})")
.format(formatted_advance_paid, self.name, formatted_order_total))
+
+ frappe.db.set_value(self.doctype, self.name, "advance_paid", advance_paid)
@property
def company_abbr(self):
@@ -600,3 +644,77 @@
if flt(gl_dict.credit) and not flt(gl_dict.credit_in_account_currency):
gl_dict.credit_in_account_currency = gl_dict.credit if account_currency==company_currency \
else flt(gl_dict.credit / conversion_rate, 2)
+
+
+def get_advance_journal_entries(party_type, party, party_account, amount_field,
+ order_doctype, order_list, include_unallocated=True):
+
+ dr_or_cr = "credit_in_account_currency" if party_type=="Customer" else "debit_in_account_currency"
+
+ conditions = []
+ if include_unallocated:
+ conditions.append("ifnull(t2.reference_name, '')=''")
+
+ if order_list:
+ order_condition = ', '.join(['%s'] * len(order_list))
+ conditions.append(" (t2.reference_type = '{0}' and ifnull(t2.reference_name, '') in ({1}))"\
+ .format(order_doctype, order_condition))
+
+ reference_condition = " and (" + " or ".join(conditions) + ")" if conditions else ""
+
+ journal_entries = frappe.db.sql("""
+ select
+ "Journal Entry" as reference_type, t1.name as reference_name,
+ t1.remark as remarks, t2.{0} as amount, t2.name as reference_row,
+ t2.reference_name as against_order
+ from
+ `tabJournal Entry` t1, `tabJournal Entry Account` t2
+ where
+ t1.name = t2.parent and t2.account = %s
+ and t2.party_type = %s and t2.party = %s
+ and t2.is_advance = 'Yes' and t1.docstatus = 1
+ and {1} > 0
+ and (ifnull(t2.reference_name, '')='' {2})
+ order by t1.posting_date""".format(amount_field, dr_or_cr, reference_condition),
+ [party_account, party_type, party] + order_list, as_dict=1)
+
+ return list(journal_entries)
+
+def get_advance_payment_entries(party_type, party, party_account,
+ order_doctype, order_list=None, include_unallocated=True, against_all_orders=False):
+ party_account_field = "paid_from" if party_type == "Customer" else "paid_to"
+ payment_type = "Receive" if party_type == "Customer" else "Pay"
+ payment_entries_against_order, unallocated_payment_entries = [], []
+
+ if order_list or against_all_orders:
+ if order_list:
+ reference_condition = " and t2.reference_name in ({0})"\
+ .format(', '.join(['%s'] * len(order_list)))
+ else:
+ reference_condition = ""
+ order_list = []
+
+ payment_entries_against_order = frappe.db.sql("""
+ select
+ "Payment Entry" as reference_type, t1.name as reference_name,
+ t1.remarks, t2.allocated_amount as amount, t2.name as reference_row,
+ t2.reference_name as against_order, t1.posting_date
+ from `tabPayment Entry` t1, `tabPayment Entry Reference` t2
+ where
+ t1.name = t2.parent and t1.{0} = %s and t1.payment_type = %s
+ and t1.party_type = %s and t1.party = %s and t1.docstatus = 1
+ and t2.reference_doctype = %s {1}
+ """.format(party_account_field, reference_condition),
+ [party_account, payment_type, party_type, party, order_doctype] + order_list, as_dict=1)
+
+ if include_unallocated:
+ unallocated_payment_entries = frappe.db.sql("""
+ select "Payment Entry" as reference_type, name as reference_name,
+ remarks, unallocated_amount as amount
+ from `tabPayment Entry`
+ where
+ {0} = %s and party_type = %s and party = %s and payment_type = %s
+ and docstatus = 1 and unallocated_amount > 0
+ """.format(party_account_field), (party_account, party_type, party, payment_type), as_dict=1)
+
+ return list(payment_entries_against_order) + list(unallocated_payment_entries)
\ No newline at end of file
diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.js b/erpnext/hr/doctype/expense_claim/expense_claim.js
index d6bb12e..bafa008 100644
--- a/erpnext/hr/doctype/expense_claim/expense_claim.js
+++ b/erpnext/hr/doctype/expense_claim/expense_claim.js
@@ -10,7 +10,7 @@
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_default_bank_cash_account",
args: {
"company": cur_frm.doc.company,
- "voucher_type": "Bank Entry"
+ "account_type": "Bank"
},
callback: function(r) {
var jv = frappe.model.make_new_doc_and_get_name('Journal Entry');
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 911e42c..d4de2e9 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -285,3 +285,4 @@
erpnext.patches.v7_0.set_is_group_for_warehouse
erpnext.patches.v7_0.update_maintenance_module_in_doctype
erpnext.patches.v7_0.update_prevdoc_values_for_supplier_quotation_item
+erpnext.patches.v7_0.rename_advance_table_fields
\ No newline at end of file
diff --git a/erpnext/patches/v6_4/fix_journal_entries_due_to_reconciliation.py b/erpnext/patches/v6_4/fix_journal_entries_due_to_reconciliation.py
index 37b6c63..b1464d5 100644
--- a/erpnext/patches/v6_4/fix_journal_entries_due_to_reconciliation.py
+++ b/erpnext/patches/v6_4/fix_journal_entries_due_to_reconciliation.py
@@ -5,6 +5,9 @@
import frappe
def execute():
+ frappe.reload_doctype("Sales Invoice Advance")
+ frappe.reload_doctype("Purchase Invoice Advance")
+
je_rows = frappe.db.sql("""
select name, parent, reference_type, reference_name, debit, credit
from `tabJournal Entry Account`
@@ -23,7 +26,7 @@
is_advance_entry=None
if d.reference_type in ("Sales Invoice", "Purchase Invoice") and d.reference_name:
is_advance_entry = frappe.db.sql("""select name from `tab{0}`
- where journal_entry=%s and jv_detail_no=%s
+ where reference_name=%s and reference_row=%s
and ifnull(allocated_amount, 0) > 0 and docstatus=1"""
.format(d.reference_type + " Advance"), (d.parent, d.name))
diff --git a/erpnext/patches/v7_0/rename_advance_table_fields.py b/erpnext/patches/v7_0/rename_advance_table_fields.py
new file mode 100644
index 0000000..ba8b2da
--- /dev/null
+++ b/erpnext/patches/v7_0/rename_advance_table_fields.py
@@ -0,0 +1,15 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.model.utils.rename_field import rename_field
+
+def execute():
+ for dt in ("Sales Invoice Advance", "Purchase Invoice Advance"):
+ frappe.reload_doctype(dt)
+
+ frappe.db.sql("update `tab{0}` set reference_type = 'Journal Entry'".format(dt))
+
+ rename_field(dt, "journal_entry", "reference_name")
+ rename_field(dt, "jv_detail_no", "reference_row")
\ No newline at end of file
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index ce11c8a..1379048 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -4,6 +4,16 @@
erpnext.TransactionController = erpnext.taxes_and_totals.extend({
setup: function() {
this._super();
+
+ if(in_list(["Sales Invoice", "Purchase Invoice"], this.frm.doc.doctype)) {
+ this.frm.get_field('advances').grid.editable_fields = [
+ {fieldname: 'reference_name', columns: 2},
+ {fieldname: 'remarks', columns: 3},
+ {fieldname: 'advance_amount', columns: 3},
+ {fieldname: 'allocated_amount', columns: 3}
+ ];
+ }
+
frappe.ui.form.on(this.frm.doctype + " Item", "rate", function(frm, cdt, cdn) {
var item = frappe.get_doc(cdt, cdn);
frappe.model.round_floats_in(item, ["rate", "price_list_rate"]);
@@ -159,7 +169,6 @@
this.show_item_wise_taxes();
this.set_dynamic_labels();
this.setup_sms();
- this.make_show_payments_btn();
},
apply_default_taxes: function() {
@@ -195,22 +204,6 @@
var sms_man = new SMSManager(this.frm.doc);
},
- make_show_payments_btn: function() {
- var me = this;
- if (in_list(["Purchase Invoice", "Sales Invoice"], this.frm.doctype)) {
- if(this.frm.doc.outstanding_amount !== this.frm.doc.base_grand_total) {
- this.frm.add_custom_button(__("Payments"), function() {
- frappe.route_options = {
- "Journal Entry Account.reference_type": me.frm.doc.doctype,
- "Journal Entry Account.reference_name": me.frm.doc.name
- };
-
- frappe.set_route("List", "Journal Entry");
- }, __("View"));
- }
- }
- },
-
barcode: function(doc, cdt, cdn) {
var d = locals[cdt][cdn];
if(d.barcode=="" || d.barcode==null) {
@@ -978,5 +971,32 @@
if(!this.item_selector) {
this.item_selector = new erpnext.ItemSelector({frm: this.frm});
}
+ },
+
+ get_advances: function() {
+ if(!this.frm.is_return) {
+ return this.frm.call({
+ method: "set_advances",
+ doc: this.frm.doc,
+ callback: function(r, rt) {
+ refresh_field("advances");
+ }
+ })
+ }
+ },
+
+ make_payment_entry: function() {
+ return frappe.call({
+ method: "erpnext.accounts.doctype.payment_entry.payment_entry.get_payment_entry",
+ args: {
+ "dt": cur_frm.doc.doctype,
+ "dn": cur_frm.doc.name
+ },
+ callback: function(r) {
+ var doclist = frappe.model.sync(r.message);
+ frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
+ // cur_frm.refresh_fields()
+ }
+ });
}
});
\ No newline at end of file
diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js
index ce9c958..5f1a5c7 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.js
+++ b/erpnext/selling/doctype/sales_order/sales_order.js
@@ -76,11 +76,12 @@
if(flt(doc.per_billed)==0) {
cur_frm.add_custom_button(__('Payment Request'), this.make_payment_request, __("Make"));
- cur_frm.add_custom_button(__('Payment'), cur_frm.cscript.make_bank_entry, __("Make"));
+ cur_frm.add_custom_button(__('Payment'), cur_frm.cscript.make_payment_entry, __("Make"));
}
// maintenance
- if(flt(doc.per_delivered, 2) < 100 && ["Sales", "Shopping Cart"].indexOf(doc.order_type)===-1) {
+ if(flt(doc.per_delivered, 2) < 100 &&
+ ["Sales", "Shopping Cart"].indexOf(doc.order_type)===-1) {
cur_frm.add_custom_button(__('Maintenance Visit'), this.make_maintenance_visit, __("Make"));
cur_frm.add_custom_button(__('Maintenance Schedule'), this.make_maintenance_schedule, __("Make"));
}
@@ -157,19 +158,6 @@
})
},
- make_bank_entry: function() {
- return frappe.call({
- method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_payment_entry_against_order",
- args: {
- "dt": "Sales Order",
- "dn": cur_frm.doc.name
- },
- callback: function(r) {
- var doclist = frappe.model.sync(r.message);
- frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
- }
- });
- },
make_purchase_order: function(){
var dialog = new frappe.ui.Dialog({
title: __("For Supplier"),
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index 8103756..8fcd89d 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -441,7 +441,7 @@
def postprocess(source, target):
set_missing_values(source, target)
#Get the advance paid Journal Entries in Sales Invoice Advance
- target.get_advances()
+ target.set_advances()
def set_missing_values(source, target):
target.is_pos = 0
diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js
index 2a18286..ff12f68 100644
--- a/erpnext/setup/doctype/company/company.js
+++ b/erpnext/setup/doctype/company/company.js
@@ -136,6 +136,8 @@
["default_expense_account", {"root_type": "Expense"}],
["default_income_account", {"root_type": "Income"}],
["round_off_account", {"root_type": "Expense"}],
+ ["write_off_account", {"root_type": "Expense"}],
+ ["exchange_gain_loss_account", {"root_type": "Expense"}],
["accumulated_depreciation_account", {"root_type": "Asset"}],
["depreciation_expense_account", {"root_type": "Expense"}],
["disposal_account", {"report_type": "Profit and Loss"}],
diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json
index 9140534..25e0cfb 100644
--- a/erpnext/setup/doctype/company/company.json
+++ b/erpnext/setup/doctype/company/company.json
@@ -499,6 +499,58 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "fieldname": "write_off_account",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Write Off Account",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Account",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "exchange_gain_loss_account",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Exchange Gain / Loss Account",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Account",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
"fieldname": "column_break0",
"fieldtype": "Column Break",
"hidden": 0,
@@ -1325,6 +1377,7 @@
"hide_toolbar": 0,
"icon": "icon-building",
"idx": 1,
+ "image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
@@ -1332,7 +1385,7 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
- "modified": "2016-05-16 15:24:47.178826",
+ "modified": "2016-06-26 09:08:50.476200",
"modified_by": "Administrator",
"module": "Setup",
"name": "Company",