diff --git a/erpnext/accounts/doctype/account/test_account.py b/erpnext/accounts/doctype/account/test_account.py index 4580d02dd4..5744721875 100644 --- a/erpnext/accounts/doctype/account/test_account.py +++ b/erpnext/accounts/doctype/account/test_account.py @@ -12,6 +12,7 @@ def _make_test_records(verbose): ["_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 @@ def _make_test_records(verbose): ["_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 d46c2d5606..aca83dad79 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json @@ -87,6 +87,55 @@ "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": "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, @@ -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 3de82418c5..4b33fd3919 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 @@ frappe.ui.form.on("Bank Reconciliation", { 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 59a3cfc1e7..1a7b7638ce 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 fb95731721..b07c82d5a2 100644 --- a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py +++ b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py @@ -12,7 +12,7 @@ form_grid_templates = { } 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 @@ class BankReconciliation(Document): 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 fc3c3b6b52..b56ad8b0e0 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", @@ -86,32 +116,6 @@ "set_only_once": 0, "unique": 0 }, - { - "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, @@ -120,6 +124,7 @@ "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 1f95fb8daf..b90cd2308c 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.py +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py @@ -38,7 +38,7 @@ class GLEntry(Document): 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 f670c905d4..cb6c36c11c 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.js +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js @@ -283,7 +283,8 @@ cur_frm.cscript.voucher_type = function(doc, cdt, cdn) { 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 40386e088a..96b95f98c0 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -525,22 +525,19 @@ class JournalEntry(AccountsController): 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 @@ def get_default_bank_cash_account(company, voucher_type, mode_of_payment=None, a 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 @@ def get_payment_entry(ref_doc, args): 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 @@ def get_exchange_rate(account, account_currency=None, company=None, 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 @@ def get_exchange_rate(account, account_currency=None, company=None, # 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 10807ff870..9115c20868 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 0000000000..e69de29bb2 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 0000000000..230d72823e --- /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 0000000000..d40ba9ddcc --- /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 0000000000..d4d93a7ae1 --- /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 0000000000..8be5f6d847 --- /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 0000000000..e69de29bb2 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 0000000000..e71d973364 --- /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 0000000000..d6686bbe42 --- /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 0000000000..e69de29bb2 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 0000000000..5d61996199 --- /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 0000000000..51f8c06165 --- /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 5075f159e3..a50ff197a8 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 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext 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 832a346ee0..5910a2402d 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.utils import flt 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 @@ class PaymentReconciliation(Document): "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 @@ class PaymentReconciliation(Document): 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 @@ class PaymentReconciliation(Document): '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 @@ class PaymentReconciliation(Document): .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 f9b603955e..401d234245 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 fc40d420d2..f7d95862ba 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -6,11 +6,11 @@ from __future__ import unicode_literals 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 @@ class PaymentRequest(Document): 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 @@ class PaymentRequest(Document): 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 @@ def make_payment_request(**args): 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 @@ def make_payment_request(**args): 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 eb8e55875c..73c412f247 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 @@ from __future__ import unicode_literals 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 @@ class TestPaymentRequest(unittest.TestCase): 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 7a83c7b0b2..8aae29ec8d 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -34,8 +34,8 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({ } 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.fields_dict.cash_bank_account.get_query = function(doc) { } } -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 c6d04c1ccc..b247e3ebfc 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 de654858a4..3bb402de98 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -65,7 +65,7 @@ class PurchaseInvoice(BuyingController): 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 @@ class PurchaseInvoice(BuyingController): 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 @@ class PurchaseInvoice(BuyingController): 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 5dacb9babe..bb489e71bc 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 @@ class TestPurchaseInvoice(unittest.TestCase): 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 34156f5b1e..462773d4b8 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, @@ -60,32 +120,6 @@ "unique": 0, "width": "80px" }, - { - "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, @@ -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": "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 d64a024222..4bc634a323 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -65,10 +65,14 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte } } + 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 @@ $.extend(cur_frm.cscript, new erpnext.accounts.SalesInvoiceController({frm: cur_ // 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 Delivery Note'] = function() { }) } -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 308db38d61..746209f20f 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 470ea913dd..82cb365145 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -77,7 +77,6 @@ class SalesInvoice(SellingController): 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 @@ class SalesInvoice(SellingController): 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 @@ def get_bank_cash_account(mode_of_payment, company): 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 cc3d95227c..ec63b1f1d2 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 @@ class TestSalesInvoice(unittest.TestCase): 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 b1fae8e47d..06c67358b2 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 b7fb053bc3..4af197e42c 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -160,7 +160,6 @@ def make_round_off_gle(gl_map, debit_credit_diff): 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 @@ def delete_gl_entries(gl_entries=None, voucher_type=None, voucher_no=None, 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 @@ def delete_gl_entries(gl_entries=None, voucher_type=None, voucher_no=None, 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 8d11374878..c65a845deb 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -181,8 +181,16 @@ def get_party_account(party_type, party, company): {"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 @@ def get_party_gle_currency(party_type, party, company): 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 @@ def validate_party_accounts(doc): 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 37d81307ad..ac3366c506 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 @@ frappe.query_reports["Bank Clearance Summary"] = { "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 cfb677fa2b..07ab969b84 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 @@ def execute(filters=None): 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 581a7c86da..76e0de2b94 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 @@ def execute(filters=None): 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 @@ def execute(filters=None): 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 @@ def get_columns(): "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 @@ def get_columns(): "width": 200 }, { - "fieldname": "reference", + "fieldname": "reference_no", "label": _("Reference"), "fieldtype": "Data", "width": 100 @@ -119,31 +119,67 @@ def get_columns(): ] 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 20bdae8121..85c7daa4e5 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 @@ from __future__ import unicode_literals 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 @@ def execute(filters=None): 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 validate_filters(filters): 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 @@ def get_columns(filters): _("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_columns(filters): 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 27389aa441..6bd5eb1301 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -172,53 +172,77 @@ def reconcile_against_document(args): 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 @@ def update_against_doc(d, jv_obj): 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 @@ def update_against_doc(d, jv_obj): # 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 @@ def get_company_default(company, fieldname): 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 @@ def get_outstanding_invoices(party_type, party, account, condition=None): 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 @@ def get_outstanding_invoices(party_type, party, account, condition=None): 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 @@ def get_outstanding_invoices(party_type, party, account, condition=None): }, 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 9e44855277..f5d4e2e4cf 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.js +++ b/erpnext/buying/doctype/purchase_order/purchase_order.js @@ -76,7 +76,7 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend( 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 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend( 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 d4b2221095..ec1bf46ec5 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -308,7 +308,7 @@ def make_purchase_invoice(source_name, target_doc=None): 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 b6b14803ef..9dd50f8c72 100644 --- a/erpnext/config/accounts.py +++ b/erpnext/config/accounts.py @@ -44,6 +44,11 @@ def get_data(): "name": "Company", "description": _("Company (not Customer or Supplier) master.") }, + { + "type": "doctype", + "name": "Payment Entry", + "description": _("Payment entries against party or for internal transfer") + }, { "type": "doctype", "name": "Journal Entry", diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index e3a66a4b11..2b67ceb243 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -42,6 +42,7 @@ class AccountsController(TransactionBase): 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 @@ class AccountsController(TransactionBase): 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 @@ class AccountsController(TransactionBase): 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") - - 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)) + 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" + 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 @@ class AccountsController(TransactionBase): 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 @@ class AccountsController(TransactionBase): 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 @@ def set_balance_in_account_currency(gl_dict, account_currency=None, conversion_r 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 d6bb12e69e..bafa008e98 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.js +++ b/erpnext/hr/doctype/expense_claim/expense_claim.js @@ -10,7 +10,7 @@ erpnext.hr.ExpenseClaimController = frappe.ui.form.Controller.extend({ 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 911e42c07e..d4de2e97bd 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -285,3 +285,4 @@ erpnext.patches.v7_0.remove_doctypes_and_reports 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 37b6c63af2..b1464d5e2a 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 @@ from __future__ import unicode_literals 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 @@ def execute(): 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 0000000000..ba8b2daf5c --- /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 ce11c8a80b..1379048955 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 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ 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 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ 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 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ 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 ce9c958e46..5f1a5c722c 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -76,11 +76,12 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend( 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 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend( }) }, - 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 8103756cfd..8fcd89de6e 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -441,7 +441,7 @@ def make_sales_invoice(source_name, target_doc=None, ignore_permissions=False): 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 2a18286343..ff12f68eb0 100644 --- a/erpnext/setup/doctype/company/company.js +++ b/erpnext/setup/doctype/company/company.js @@ -136,6 +136,8 @@ erpnext.company.setup_queries = function(frm) { ["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 9140534cee..25e0cfb2ab 100644 --- a/erpnext/setup/doctype/company/company.json +++ b/erpnext/setup/doctype/company/company.json @@ -495,6 +495,58 @@ "set_only_once": 0, "unique": 0 }, + { + "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, @@ -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",