Merge pull request #5620 from nabinhait/payment_entry

Payment Entry
This commit is contained in:
Rushabh Mehta 2016-07-04 13:01:16 +05:30 committed by GitHub
commit c1d2247f2c
56 changed files with 4513 additions and 590 deletions

View File

@ -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],

View File

@ -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",

View File

@ -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();
}
});
}

View File

@ -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
}

View File

@ -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"))

View File

@ -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
}

View File

@ -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))))

View File

@ -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) {

View File

@ -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)

View File

@ -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]
]
}
});

View File

@ -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);
}
})

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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)

View File

@ -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
}

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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");
},

View File

@ -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)

View File

@ -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
}

View File

@ -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()

View File

@ -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)
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)

View File

@ -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}

View File

@ -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",

View File

@ -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([{

View File

@ -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

View File

@ -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
}

View File

@ -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: [

View File

@ -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",

View File

@ -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
}

View File

@ -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

View File

@ -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
}

View File

@ -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)

View File

@ -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):

View File

@ -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",

View File

@ -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
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()))

View File

@ -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

View File

@ -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 = {}

View File

@ -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

View File

@ -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')
},

View File

@ -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)

View File

@ -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",

View File

@ -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)

View File

@ -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');

View File

@ -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

View File

@ -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))

View File

@ -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")

View File

@ -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()
}
});
}
});

View File

@ -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"),

View File

@ -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

View File

@ -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"}],

View File

@ -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",