diff --git a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py index 7dd021e838..f1c8820c09 100644 --- a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py +++ b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py @@ -19,7 +19,8 @@ class BankReconciliation(Document): dl = frappe.db.sql("""select t1.name, t1.cheque_no, t1.cheque_date, t2.debit, - t2.credit, t1.posting_date, t2.against_account, t1.clearance_date + t2.credit, t1.posting_date, t2.against_account, t1.clearance_date, + t2.reference_type, t2.reference_name from `tabJournal Entry` t1, `tabJournal Entry Account` t2 where diff --git a/erpnext/accounts/doctype/bank_reconciliation_detail/bank_reconciliation_detail.json b/erpnext/accounts/doctype/bank_reconciliation_detail/bank_reconciliation_detail.json index b2c6e66e04..78691a3d00 100644 --- a/erpnext/accounts/doctype/bank_reconciliation_detail/bank_reconciliation_detail.json +++ b/erpnext/accounts/doctype/bank_reconciliation_detail/bank_reconciliation_detail.json @@ -1,11 +1,19 @@ { + "allow_copy": 0, + "allow_import": 0, + "allow_rename": 0, "creation": "2013-02-22 01:27:37", + "custom": 0, "docstatus": 0, "doctype": "DocType", "fields": [ { + "allow_on_submit": 0, "fieldname": "voucher_id", "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, "in_list_view": 0, "label": "Voucher ID", "no_copy": 0, @@ -13,46 +21,84 @@ "oldfieldtype": "Link", "options": "Journal Entry", "permlevel": 0, - "search_index": 0 + "print_hide": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 }, { + "allow_on_submit": 0, "fieldname": "clearance_date", "fieldtype": "Date", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, "in_list_view": 1, "label": "Clearance Date", "no_copy": 0, "oldfieldname": "clearance_date", "oldfieldtype": "Date", "permlevel": 0, - "search_index": 0 + "print_hide": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 }, { + "allow_on_submit": 0, "fieldname": "against_account", "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, "in_list_view": 1, "label": "Against Account", "no_copy": 0, "oldfieldname": "against_account", "oldfieldtype": "Data", "permlevel": 0, + "print_hide": 0, "read_only": 1, - "search_index": 0 + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 }, { + "allow_on_submit": 0, "fieldname": "cheque_number", "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, "in_list_view": 1, "label": "Cheque Number", "no_copy": 0, "oldfieldname": "cheque_number", "oldfieldtype": "Data", "permlevel": 0, + "print_hide": 0, "read_only": 1, - "search_index": 0 + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 }, { + "allow_on_submit": 0, "fieldname": "debit", "fieldtype": "Currency", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, "in_list_view": 1, "label": "Debit", "no_copy": 0, @@ -60,12 +106,21 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "permlevel": 0, + "print_hide": 0, "read_only": 1, - "search_index": 0 + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 }, { + "allow_on_submit": 0, "fieldname": "credit", "fieldtype": "Currency", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, "in_list_view": 1, "label": "Credit", "no_copy": 0, @@ -73,40 +128,113 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "permlevel": 0, + "print_hide": 0, "read_only": 1, - "search_index": 0 + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 }, { + "allow_on_submit": 0, + "fieldname": "reference_type", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Reference Type", + "no_copy": 0, + "options": "DocType", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "read_only": 1, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "fieldname": "reference_name", + "fieldtype": "Dynamic Link", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Reference Name", + "no_copy": 0, + "options": "reference_type", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "read_only": 1, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, "fieldname": "posting_date", "fieldtype": "Date", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, "in_list_view": 0, "label": "Posting Date", "no_copy": 0, "oldfieldname": "posting_date", "oldfieldtype": "Date", "permlevel": 0, + "print_hide": 0, "read_only": 1, - "search_index": 0 + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 }, { + "allow_on_submit": 0, "fieldname": "cheque_date", "fieldtype": "Date", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, "in_list_view": 1, "label": "Cheque Date", "no_copy": 0, "oldfieldname": "cheque_date", "oldfieldtype": "Date", "permlevel": 0, + "print_hide": 0, "read_only": 1, - "search_index": 0 + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 } ], + "hide_heading": 0, + "hide_toolbar": 0, "idx": 1, + "in_create": 0, + "in_dialog": 0, + "is_submittable": 0, + "issingle": 0, "istable": 1, - "modified": "2015-04-21 01:29:29.570890", + "modified": "2015-08-10 16:59:43.974705", "modified_by": "Administrator", "module": "Accounts", "name": "Bank Reconciliation Detail", "owner": "Administrator", - "permissions": [] + "permissions": [], + "read_only": 0, + "read_only_onload": 0 } \ No newline at end of file diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js index ec17e34bb5..38f3e0c677 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.js +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js @@ -50,32 +50,43 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({ $.each([["against_voucher", "Purchase Invoice", "supplier"], ["against_invoice", "Sales Invoice", "customer"]], function(i, opts) { - me.frm.set_query(opts[0], "accounts", function(doc, cdt, cdn) { - var jvd = frappe.get_doc(cdt, cdn); - frappe.model.validate_missing(jvd, "party_type"); - frappe.model.validate_missing(jvd, "party"); - return { - filters: [ - [opts[1], opts[2], "=", jvd.party], - [opts[1], "docstatus", "=", 1], - [opts[1], "outstanding_amount", "!=", 0] - ] - }; - }); }); - this.frm.set_query("against_jv", "accounts", function(doc, cdt, cdn) { + me.frm.set_query("reference_name", "accounts", function(doc, cdt, cdn) { var jvd = frappe.get_doc(cdt, cdn); - frappe.model.validate_missing(jvd, "account"); + // expense claim + if(jvd.reference_type==="Expense Claim") { + return {}; + } + + // journal entry + if(jvd.reference_type==="Journal Entry") { + frappe.model.validate_missing(jvd, "account"); + + return { + query: "erpnext.accounts.doctype.journal_entry.journal_entry.get_against_jv", + filters: { + account: jvd.account, + party: jvd.party + } + }; + } + + // against party + + frappe.model.validate_missing(jvd, "party_type"); + frappe.model.validate_missing(jvd, "party"); return { - query: "erpnext.accounts.doctype.journal_entry.journal_entry.get_against_jv", - filters: { - account: jvd.account, - party: jvd.party - } + filters: [ + [jvd.reference_type, jvd.reference_type.indexOf("Sales")==1 ? "customer" : "supplier", "=", jvd.party], + [jvd.reference_type, "docstatus", "=", 1], + [jvd.reference_type, "outstanding_amount", "!=", 0] + ] }; }); + + }, setup_balance_formatter: function() { @@ -93,24 +104,16 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({ }) }, - against_voucher: function(doc, cdt, cdn) { + reference_name: function(doc, cdt, cdn) { var d = frappe.get_doc(cdt, cdn); - if (d.against_voucher && !flt(d.debit)) { - this.get_outstanding('Purchase Invoice', d.against_voucher, d); + if (d.reference_type==="Purchase Invoice" && !flt(d.debit)) { + this.get_outstanding('Purchase Invoice', d.reference_name, d); } - }, - - against_invoice: function(doc, cdt, cdn) { - var d = frappe.get_doc(cdt, cdn); - if (d.against_invoice && !flt(d.credit)) { - this.get_outstanding('Sales Invoice', d.against_invoice, d); + if (d.reference_type==="Sales Invoice" && !flt(d.credit)) { + this.get_outstanding('Sales Invoice', d.reference_name, d); } - }, - - against_jv: function(doc, cdt, cdn) { - var d = frappe.get_doc(cdt, cdn); - if (d.against_jv && !flt(d.credit) && !flt(d.debit)) { - this.get_outstanding('Journal Entry', d.against_jv, d); + if (d.reference_type==="Journal Entry" && !flt(d.credit) && !flt(d.debit)) { + this.get_outstanding('Journal Entry', d.reference_name, d); } }, diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 80d243580d..b48e46c5a7 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -28,13 +28,10 @@ class JournalEntry(AccountsController): self.validate_entries_for_advance() self.validate_debit_and_credit() self.validate_against_jv() - self.validate_against_sales_invoice() - self.validate_against_purchase_invoice() + self.validate_reference_doc() self.set_against_account() self.create_remarks() self.set_print_format_fields() - self.validate_against_sales_order() - self.validate_against_purchase_order() self.check_due_date() self.validate_expense_claim() self.validate_credit_debit_note() @@ -54,10 +51,8 @@ class JournalEntry(AccountsController): advance_paid = frappe._dict() for d in self.get("accounts"): if d.is_advance: - if d.against_sales_order: - advance_paid.setdefault("Sales Order", []).append(d.against_sales_order) - elif d.against_purchase_order: - advance_paid.setdefault("Purchase Order", []).append(d.against_purchase_order) + if d.reference_type in ("Sales Order", "Purchase Order"): + advance_paid.setdefault(d.reference_type, []).append(d.reference_name) for voucher_type, order_list in advance_paid.items(): for voucher_no in list(set(order_list)): @@ -65,7 +60,7 @@ class JournalEntry(AccountsController): def on_cancel(self): from erpnext.accounts.utils import remove_against_link_from_jv - remove_against_link_from_jv(self.doctype, self.name, "against_jv") + remove_against_link_from_jv(self.doctype, self.name) self.make_gl_entries(1) self.update_advance_paid() @@ -93,10 +88,8 @@ class JournalEntry(AccountsController): for d in self.get("accounts"): if d.party_type and d.party and d.get("credit" if d.party_type=="Customer" else "debit") > 0: due_date = None - if d.against_invoice: - due_date = frappe.db.get_value("Sales Invoice", d.against_invoice, "due_date") - elif d.against_voucher: - due_date = frappe.db.get_value("Purchase Invoice", d.against_voucher, "due_date") + if d.reference_type in ("Sales Invoice", "Purchase Invoice"): + due_date = frappe.db.get_value(d.reference_type, d.reference_name, "due_date") if due_date and getdate(self.cheque_date) > getdate(due_date): diff = date_diff(self.cheque_date, due_date) @@ -115,17 +108,17 @@ class JournalEntry(AccountsController): def validate_entries_for_advance(self): for d in self.get('accounts'): - if not (d.against_voucher and d.against_invoice and d.against_jv): + if d.reference_type not in ("Sales Invoice", "Purchase Invoice", "Journal Entry"): if (d.party_type == 'Customer' and flt(d.credit) > 0) or \ (d.party_type == 'Supplier' and flt(d.debit) > 0): - if not d.is_advance: + if d.is_advance=="No": msgprint(_("Row {0}: Please check 'Is Advance' against Account {1} if this is an advance entry.").format(d.idx, d.account)) - elif (d.against_sales_order or d.against_purchase_order) and d.is_advance != "Yes": + elif d.reference_type in ("Sales Order", "Purchase Order") and d.is_advance != "Yes": frappe.throw(_("Row {0}: Payment against Sales/Purchase Order should always be marked as advance").format(d.idx)) def validate_against_jv(self): for d in self.get('accounts'): - if d.against_jv: + if d.reference_type=="Journal Voucher": account_root_type = frappe.db.get_value("Account", d.account, "root_type") if account_root_type == "Asset" and flt(d.debit) > 0: frappe.throw(_("For {0}, only credit accounts can be linked against another debit entry") @@ -134,17 +127,17 @@ class JournalEntry(AccountsController): frappe.throw(_("For {0}, only debit accounts can be linked against another credit entry") .format(d.account)) - if d.against_jv == self.name: + if d.reference_name == self.name: frappe.throw(_("You can not enter current voucher in 'Against Journal Entry' column")) against_entries = frappe.db.sql("""select * from `tabJournal Entry Account` where account = %s and docstatus = 1 and parent = %s - and ifnull(against_jv, '') = '' and ifnull(against_invoice, '') = '' - and ifnull(against_voucher, '') = ''""", (d.account, d.against_jv), as_dict=True) + and ifnull(reference_type, '') = '' and ifnull(reference_name, '') = '' + """, (d.account, d.reference_name), as_dict=True) if not against_entries: frappe.throw(_("Journal Entry {0} does not have account {1} or already matched against other voucher") - .format(d.against_jv, d.account)) + .format(d.reference_name, d.account)) else: dr_or_cr = "debit" if d.credit > 0 else "credit" valid = False @@ -153,89 +146,99 @@ class JournalEntry(AccountsController): valid = True if not valid: frappe.throw(_("Against Journal Entry {0} does not have any unmatched {1} entry") - .format(d.against_jv, dr_or_cr)) + .format(d.reference_name, dr_or_cr)) - def validate_against_sales_invoice(self): - self.validate_account_in_against_voucher("against_invoice", "Sales Invoice") - - def validate_against_purchase_invoice(self): - self.validate_account_in_against_voucher("against_voucher", "Purchase Invoice") - - def validate_against_sales_order(self): - payment_against_voucher = self.validate_account_in_against_voucher("against_sales_order", "Sales Order") - self.validate_against_order_fields("Sales Order", payment_against_voucher) - - def validate_against_purchase_order(self): - payment_against_voucher = self.validate_account_in_against_voucher("against_purchase_order", "Purchase Order") - self.validate_against_order_fields("Purchase Order", payment_against_voucher) - - def validate_account_in_against_voucher(self, against_field, doctype): - payment_against_voucher = frappe._dict() - field_dict = {'Sales Invoice': ["Customer", "Debit To"], + def validate_reference_doc(self): + """Validates reference document""" + field_dict = { + 'Sales Invoice': ["Customer", "Debit To"], 'Purchase Invoice': ["Supplier", "Credit To"], 'Sales Order': ["Customer"], 'Purchase Order': ["Supplier"] - } + } + + self.reference_totals = {} + self.reference_types = {} for d in self.get("accounts"): - if d.get(against_field): - dr_or_cr = "credit" if against_field in ["against_invoice", "against_sales_order"] \ + if not d.reference_type: + d.reference_name = None + if not d.reference_type: + d.reference_name = None + if d.reference_type and d.reference_name and (d.reference_type in field_dict.keys()): + dr_or_cr = "credit" if d.reference_type in ("Sales Order", "Sales Invoice") \ else "debit" - if against_field == "against_sales_order" and flt(d.debit) > 0: - frappe.throw(_("Row {0}: Debit entry can not be linked with a {1}").format(d.idx, doctype)) - if against_field == "against_purchase_order" and flt(d.credit) > 0: - frappe.throw(_("Row {0}: Credit entry can not be linked with a {1}").format(d.idx, doctype)) + # check debit or credit type Sales / Purchase Order + if d.reference_type=="Sales Order" and flt(d.debit) > 0: + frappe.throw(_("Row {0}: Debit entry can not be linked with a {1}").format(d.idx, d.reference_type)) - against_voucher = frappe.db.get_value(doctype, d.get(against_field), - [scrub(dt) for dt in field_dict.get(doctype)]) + if d.reference_type == "Purchase Order" and flt(d.credit) > 0: + frappe.throw(_("Row {0}: Credit entry can not be linked with a {1}").format(d.idx, d.reference_type)) - if against_field in ["against_invoice", "against_voucher"]: - if (against_voucher[0] !=d.party or against_voucher[1] != d.account): + # set totals + if not d.reference_name in self.reference_totals: + self.reference_totals[d.reference_name] = 0.0 + self.reference_totals[d.reference_name] += flt(d.get(dr_or_cr)) + self.reference_types[d.reference_name] = d.reference_type + + against_voucher = frappe.db.get_value(d.reference_type, d.reference_name, + [scrub(dt) for dt in field_dict.get(d.reference_type)]) + + # check if party and account match + if d.reference_type in ("Sales Invoice", "Purchase Invoice"): + if (against_voucher[0] != d.party or against_voucher[1] != d.account): frappe.throw(_("Row {0}: Party / Account does not match with {1} / {2} in {3} {4}") - .format(d.idx, field_dict.get(doctype)[0], field_dict.get(doctype)[1], - doctype, d.get(against_field))) - else: - payment_against_voucher.setdefault(d.get(against_field), []).append(flt(d.get(dr_or_cr))) + .format(d.idx, field_dict.get(d.reference_type)[0], field_dict.get(d.reference_type)[1], + d.reference_type, d.reference_name)) - if against_field in ["against_sales_order", "against_purchase_order"]: + # check if party matches for Sales / Purchase Order + if d.reference_type in ("Sales Order", "Purchase Order"): + # set totals if against_voucher != d.party: frappe.throw(_("Row {0}: {1} {2} does not match with {3}") \ - .format(d.idx, d.party_type, d.party, doctype)) - elif d.is_advance == "Yes": - payment_against_voucher.setdefault(d.get(against_field), []).append(flt(d.get(dr_or_cr))) + .format(d.idx, d.party_type, d.party, d.reference_type)) - return payment_against_voucher + self.validate_orders() + self.validate_invoices() - def validate_against_invoice_fields(self, doctype, payment_against_voucher): - for voucher_no, payment_list in payment_against_voucher.items(): - voucher_properties = frappe.db.get_value(doctype, voucher_no, - ["docstatus", "outstanding_amount"]) + def validate_orders(self): + """Validate totals, stopped and docstatus for orders""" + for reference_name, total in self.reference_totals.iteritems(): + reference_type = self.reference_types[reference_name] - if voucher_properties[0] != 1: - frappe.throw(_("{0} {1} is not submitted").format(doctype, voucher_no)) + if reference_type in ("Sales Order", "Purchase Order"): + voucher_properties = frappe.db.get_value(reference_type, reference_name, + ["docstatus", "per_billed", "status", "advance_paid", "base_grand_total"]) - if flt(voucher_properties[1]) < flt(sum(payment_list)): - frappe.throw(_("Payment against {0} {1} cannot be greater \ - than Outstanding Amount {2}").format(doctype, voucher_no, voucher_properties[1])) + if voucher_properties[0] != 1: + frappe.throw(_("{0} {1} is not submitted").format(reference_type, reference_name)) - def validate_against_order_fields(self, doctype, payment_against_voucher): - for voucher_no, payment_list in payment_against_voucher.items(): - voucher_properties = frappe.db.get_value(doctype, voucher_no, - ["docstatus", "per_billed", "status", "advance_paid", "base_grand_total"]) + if flt(voucher_properties[1]) >= 100: + frappe.throw(_("{0} {1} is fully billed").format(reference_type, reference_name)) - if voucher_properties[0] != 1: - frappe.throw(_("{0} {1} is not submitted").format(doctype, voucher_no)) + if cstr(voucher_properties[2]) == "Stopped": + frappe.throw(_("{0} {1} is stopped").format(reference_type, reference_name)) - if flt(voucher_properties[1]) >= 100: - frappe.throw(_("{0} {1} is fully billed").format(doctype, voucher_no)) + if flt(voucher_properties[4]) < (flt(voucher_properties[3]) + total): + frappe.throw(_("Advance paid against {0} {1} cannot be greater \ + than Grand Total {2}").format(reference_type, reference_name, voucher_properties[4])) - if cstr(voucher_properties[2]) == "Stopped": - frappe.throw(_("{0} {1} is stopped").format(doctype, voucher_no)) + def validate_invoices(self): + """Validate totals and docstatus for invoices""" + for reference_name, total in self.reference_totals.iteritems(): + reference_type = self.reference_types[reference_name] - if flt(voucher_properties[4]) < flt(voucher_properties[3]) + flt(sum(payment_list)): - frappe.throw(_("Advance paid against {0} {1} cannot be greater \ - than Grand Total {2}").format(doctype, voucher_no, voucher_properties[3])) + if reference_type in ("Sales Invoice", "Purchase Invoice"): + voucher_properties = frappe.db.get_value(reference_type, reference_name, + ["docstatus", "outstanding_amount"]) + + if voucher_properties[0] != 1: + frappe.throw(_("{0} {1} is not submitted").format(reference_type, reference_name)) + + if flt(voucher_properties[1]) < total: + frappe.throw(_("Payment against {0} {1} cannot be greater \ + than Outstanding Amount {2}").format(reference_type, reference_name, voucher_properties[1])) def set_against_account(self): accounts_debited, accounts_credited = [], [] @@ -275,25 +278,25 @@ class JournalEntry(AccountsController): company_currency = get_company_currency(self.company) for d in self.get('accounts'): - if d.against_invoice and d.credit: + if d.reference_type=="Sales Invoice" and d.credit: r.append(_("{0} against Sales Invoice {1}").format(fmt_money(flt(d.credit), currency = company_currency), \ - d.against_invoice)) + d.reference_name)) - if d.against_sales_order and d.credit: + if d.reference_type=="Sales Order" and d.credit: r.append(_("{0} against Sales Order {1}").format(fmt_money(flt(d.credit), currency = company_currency), \ - d.against_sales_order)) + d.reference_name)) - if d.against_voucher and d.debit: + if d.reference_type == "Purchase Invoice" and d.debit: bill_no = frappe.db.sql("""select bill_no, bill_date - from `tabPurchase Invoice` where name=%s""", d.against_voucher) + from `tabPurchase Invoice` where name=%s""", d.reference_name) if bill_no and bill_no[0][0] and bill_no[0][0].lower().strip() \ not in ['na', 'not applicable', 'none']: r.append(_('{0} against Bill {1} dated {2}').format(fmt_money(flt(d.debit), currency=company_currency), bill_no[0][0], bill_no[0][1] and formatdate(bill_no[0][1].strftime('%Y-%m-%d')))) - if d.against_purchase_order and d.debit: + if d.reference_type == "Purchase Order" and d.debit: r.append(_("{0} against Purchase Order {1}").format(fmt_money(flt(d.credit), currency = company_currency), \ - d.against_purchase_order)) + d.reference_name)) if self.user_remark: r.append(_("Note: {0}").format(self.user_remark)) @@ -332,13 +335,8 @@ class JournalEntry(AccountsController): "against": d.against_account, "debit": flt(d.debit, self.precision("debit", "accounts")), "credit": flt(d.credit, self.precision("credit", "accounts")), - "against_voucher_type": (("Purchase Invoice" if d.against_voucher else None) - or ("Sales Invoice" if d.against_invoice else None) - or ("Journal Entry" if d.against_jv else None) - or ("Sales Order" if d.against_sales_order else None) - or ("Purchase Order" if d.against_purchase_order else None)), - "against_voucher": d.against_voucher or d.against_invoice or d.against_jv - or d.against_sales_order or d.against_purchase_order, + "against_voucher_type": d.reference_type, + "against_voucher": d.reference_name, "remarks": self.remark, "cost_center": d.cost_center }) @@ -385,11 +383,13 @@ class JournalEntry(AccountsController): if self.write_off_based_on == 'Accounts Receivable': jd1.party_type = "Customer" jd1.credit = flt(d.outstanding_amount, self.precision("credit", "accounts")) - jd1.against_invoice = cstr(d.name) + jd1.reference_type = "Sales Invoice" + jd1.reference_name = cstr(d.name) elif self.write_off_based_on == 'Accounts Payable': jd1.party_type = "Supplier" jd1.debit = flt(d.outstanding_amount, self.precision("debit", "accounts")) - jd1.against_voucher = cstr(d.name) + jd1.reference_type = "Purchase Invoice" + jd1.reference_name = cstr(d.name) jd2 = self.append('accounts', {}) if self.write_off_based_on == 'Accounts Receivable': @@ -415,19 +415,20 @@ class JournalEntry(AccountsController): def update_expense_claim(self): for d in self.accounts: - if d.against_expense_claim: + if d.reference_type=="Expense Claim": amt = frappe.db.sql("""select sum(debit) as amt from `tabJournal Entry Account` - where against_expense_claim = %s and docstatus = 1""", d.against_expense_claim ,as_dict=1)[0].amt - frappe.db.set_value("Expense Claim", d.against_expense_claim , "total_amount_reimbursed", amt) + where reference_type = "Expense Claim" and + reference_name = %s and docstatus = 1""", d.reference_name ,as_dict=1)[0].amt + frappe.db.set_value("Expense Claim", d.reference_name , "total_amount_reimbursed", amt) def validate_expense_claim(self): for d in self.accounts: - if d.against_expense_claim: + if d.reference_type=="Expense Claim": sanctioned_amount, reimbursed_amount = frappe.db.get_value("Expense Claim", - d.against_expense_claim, ("total_sanctioned_amount", "total_amount_reimbursed")) + d.reference_name, ("total_sanctioned_amount", "total_amount_reimbursed")) pending_amount = flt(sanctioned_amount) - flt(reimbursed_amount) if d.debit > pending_amount: - frappe.throw(_("Row No {0}: Amount cannot be greater than Pending Amount against Expense Claim {1}. Pending Amount is {2}".format(d.idx, d.against_expense_claim, pending_amount))) + frappe.throw(_("Row No {0}: Amount cannot be greater than Pending Amount against Expense Claim {1}. Pending Amount is {2}".format(d.idx, d.reference_name, pending_amount))) def validate_credit_debit_note(self): if self.stock_entry: @@ -467,6 +468,7 @@ def get_default_bank_cash_account(company, voucher_type, mode_of_payment=None): @frappe.whitelist() def get_payment_entry_from_sales_invoice(sales_invoice): + """Returns new Journal Entry document as dict for given Sales Invoice""" from erpnext.accounts.utils import get_balance_on si = frappe.get_doc("Sales Invoice", sales_invoice) jv = get_payment_entry(si) @@ -479,7 +481,8 @@ def get_payment_entry_from_sales_invoice(sales_invoice): jv.get("accounts")[0].balance = get_balance_on(si.debit_to) jv.get("accounts")[0].party_balance = get_balance_on(party=si.customer, party_type="Customer") jv.get("accounts")[0].credit = si.outstanding_amount - jv.get("accounts")[0].against_invoice = si.name + jv.get("accounts")[0].reference_type = si.doctype + jv.get("accounts")[0].reference_name = si.name # debit bank jv.get("accounts")[1].debit = si.outstanding_amount @@ -488,6 +491,7 @@ def get_payment_entry_from_sales_invoice(sales_invoice): @frappe.whitelist() def get_payment_entry_from_purchase_invoice(purchase_invoice): + """Returns new Journal Entry document as dict for given Purchase Invoice""" pi = frappe.get_doc("Purchase Invoice", purchase_invoice) jv = get_payment_entry(pi) jv.remark = 'Payment against Purchase Invoice {0}. {1}'.format(pi.name, pi.remarks) @@ -499,13 +503,78 @@ def get_payment_entry_from_purchase_invoice(purchase_invoice): jv.get("accounts")[0].balance = get_balance_on(pi.credit_to) jv.get("accounts")[0].party_balance = get_balance_on(party=pi.supplier, party_type="Supplier") jv.get("accounts")[0].debit = pi.outstanding_amount - jv.get("accounts")[0].against_voucher = pi.name + jv.get("accounts")[0].reference_type = pi.doctype + jv.get("accounts")[0].reference_name = pi.name # credit bank jv.get("accounts")[1].credit = pi.outstanding_amount return jv.as_dict() +@frappe.whitelist() +def get_payment_entry_from_sales_order(sales_order): + """Returns new Journal Entry document as dict for given Sales Order""" + from erpnext.accounts.utils import get_balance_on + from erpnext.accounts.party import get_party_account + so = frappe.get_doc("Sales Order", sales_order) + + if flt(so.per_billed, 2) != 0.0: + frappe.throw(_("Can only make payment against unbilled Sales Order")) + + jv = get_payment_entry(so) + jv.remark = 'Advance payment received against Sales Order {0}.'.format(so.name) + party_account = get_party_account(so.company, so.customer, "Customer") + + amount = flt(so.base_grand_total) - flt(so.advance_paid) + + # credit customer + jv.get("accounts")[0].account = party_account + jv.get("accounts")[0].party_type = "Customer" + jv.get("accounts")[0].party = so.customer + jv.get("accounts")[0].balance = get_balance_on(party_account) + jv.get("accounts")[0].party_balance = get_balance_on(party=so.customer, party_type="Customer") + jv.get("accounts")[0].credit = amount + jv.get("accounts")[0].reference_type = so.doctype + jv.get("accounts")[0].reference_name = so.name + jv.get("accounts")[0].is_advance = "Yes" + + # debit bank + jv.get("accounts")[1].debit = amount + + return jv.as_dict() + +@frappe.whitelist() +def get_payment_entry_from_purchase_order(purchase_order): + """Returns new Journal Entry document as dict for given Sales Order""" + from erpnext.accounts.utils import get_balance_on + from erpnext.accounts.party import get_party_account + po = frappe.get_doc("Purchase Order", purchase_order) + + if flt(po.per_billed, 2) != 0.0: + frappe.throw(_("Can only make payment against unbilled Sales Order")) + + jv = get_payment_entry(po) + jv.remark = 'Advance payment made against Purchase Order {0}.'.format(po.name) + party_account = get_party_account(po.company, po.supplier, "Supplier") + + amount = flt(po.base_grand_total) - flt(po.advance_paid) + + # credit customer + jv.get("accounts")[0].account = party_account + jv.get("accounts")[0].party_type = "Supplier" + jv.get("accounts")[0].party = po.supplier + jv.get("accounts")[0].balance = get_balance_on(party_account) + jv.get("accounts")[0].party_balance = get_balance_on(party=po.supplier, party_type="Supplier") + jv.get("accounts")[0].debit = amount + jv.get("accounts")[0].reference_type = po.doctype + jv.get("accounts")[0].reference_name = po.name + jv.get("accounts")[0].is_advance = "Yes" + + # debit bank + jv.get("accounts")[1].credit = amount + + return jv.as_dict() + def get_payment_entry(doc): bank_account = get_default_bank_cash_account(doc.company, "Bank Entry") @@ -536,8 +605,7 @@ def get_against_jv(doctype, txt, searchfield, start, page_len, filters): return frappe.db.sql("""select jv.name, jv.posting_date, jv.user_remark from `tabJournal Entry` jv, `tabJournal Entry Account` jv_detail where jv_detail.parent = jv.name and jv_detail.account = %s and ifnull(jv_detail.party, '') = %s - and (ifnull(jv_detail.against_invoice, '') = '' and ifnull(jv_detail.against_voucher, '') = '' - and ifnull(jv_detail.against_jv, '') = '' ) + and (ifnull(jv_detail.reference_type, '') = '' and jv.docstatus = 1 and jv.{0} like %s order by jv.name desc limit %s, %s""".format(searchfield), (filters.get("account"), cstr(filters.get("party")), "%{0}%".format(txt), start, page_len)) @@ -546,12 +614,11 @@ def get_outstanding(args): args = eval(args) if args.get("doctype") == "Journal Entry": condition = " and party=%(party)s" if args.get("party") else "" - + against_jv_amount = frappe.db.sql(""" select sum(ifnull(debit, 0)) - sum(ifnull(credit, 0)) from `tabJournal Entry Account` where parent=%(docname)s and account=%(account)s {0} - and ifnull(against_invoice, '')='' and ifnull(against_voucher, '')='' - and ifnull(against_jv, '')=''""".format(condition), args) + and ifnull(reference_type, '')=''""".format(condition), args) against_jv_amount = flt(against_jv_amount[0][0]) if against_jv_amount else 0 return { diff --git a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py index 2aa60f0056..c70f10df63 100644 --- a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py @@ -29,10 +29,6 @@ class TestJournalEntry(unittest.TestCase): def jv_against_voucher_testcase(self, base_jv, test_voucher): dr_or_cr = "credit" if test_voucher.doctype in ["Sales Order", "Journal Entry"] else "debit" - field_dict = {'Journal Entry': "against_jv", - 'Sales Order': "against_sales_order", - 'Purchase Order': "against_purchase_order" - } test_voucher.insert() test_voucher.submit() @@ -42,21 +38,20 @@ class TestJournalEntry(unittest.TestCase): where account = %s and docstatus = 1 and parent = %s""", ("_Test Receivable - _TC", test_voucher.name))) - self.assertTrue(not frappe.db.sql("""select name from `tabJournal Entry Account` - where %s=%s""" % (field_dict.get(test_voucher.doctype), '%s'), (test_voucher.name))) + self.assertFalse(frappe.db.sql("""select name from `tabJournal Entry Account` + where reference_type = %s and reference_name = %s""", (test_voucher.doctype, test_voucher.name))) base_jv.get("accounts")[0].is_advance = "Yes" if (test_voucher.doctype in ["Sales Order", "Purchase Order"]) else "No" - base_jv.get("accounts")[0].set(field_dict.get(test_voucher.doctype), test_voucher.name) + base_jv.get("accounts")[0].set("reference_type", test_voucher.doctype) + base_jv.get("accounts")[0].set("reference_name", test_voucher.name) base_jv.insert() base_jv.submit() submitted_voucher = frappe.get_doc(test_voucher.doctype, test_voucher.name) self.assertTrue(frappe.db.sql("""select name from `tabJournal Entry Account` - where %s=%s""" % (field_dict.get(test_voucher.doctype), '%s'), (submitted_voucher.name))) - - self.assertTrue(frappe.db.sql("""select name from `tabJournal Entry Account` - where %s=%s and %s=400""" % (field_dict.get(submitted_voucher.doctype), '%s', dr_or_cr), (submitted_voucher.name))) + where reference_type = %s and reference_name = %s and {0}=400""".format(dr_or_cr), + (submitted_voucher.doctype, submitted_voucher.name))) if base_jv.get("accounts")[0].is_advance == "Yes": self.advance_paid_testcase(base_jv, submitted_voucher, dr_or_cr) @@ -102,23 +97,23 @@ class TestJournalEntry(unittest.TestCase): def test_monthly_budget_crossed_ignore(self): frappe.db.set_value("Company", "_Test Company", "monthly_bgt_flag", "Ignore") - + self.set_total_expense_zero("2013-02-28") - - jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC", + + jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC", "_Test Account Bank Account - _TC", 40000, "_Test Cost Center - _TC", submit=True) - + self.assertTrue(frappe.db.get_value("GL Entry", {"voucher_type": "Journal Entry", "voucher_no": jv.name})) def test_monthly_budget_crossed_stop(self): frappe.db.set_value("Company", "_Test Company", "monthly_bgt_flag", "Stop") - + self.set_total_expense_zero("2013-02-28") - - jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC", + + jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC", "_Test Account Bank Account - _TC", 40000, "_Test Cost Center - _TC") - + self.assertRaises(BudgetError, jv.submit) frappe.db.set_value("Company", "_Test Company", "monthly_bgt_flag", "Ignore") @@ -127,37 +122,37 @@ class TestJournalEntry(unittest.TestCase): self.test_monthly_budget_crossed_ignore() frappe.db.set_value("Company", "_Test Company", "yearly_bgt_flag", "Stop") - + self.set_total_expense_zero("2013-02-28") - jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC", + jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC", "_Test Account Bank Account - _TC", 150000, "_Test Cost Center - _TC") - + self.assertRaises(BudgetError, jv.submit) frappe.db.set_value("Company", "_Test Company", "yearly_bgt_flag", "Ignore") def test_monthly_budget_on_cancellation(self): self.set_total_expense_zero("2013-02-28") - - jv1 = make_journal_entry("_Test Account Cost for Goods Sold - _TC", + + jv1 = make_journal_entry("_Test Account Cost for Goods Sold - _TC", "_Test Account Bank Account - _TC", 20000, "_Test Cost Center - _TC", submit=True) - + self.assertTrue(frappe.db.get_value("GL Entry", {"voucher_type": "Journal Entry", "voucher_no": jv1.name})) - - jv2 = make_journal_entry("_Test Account Cost for Goods Sold - _TC", + + jv2 = make_journal_entry("_Test Account Cost for Goods Sold - _TC", "_Test Account Bank Account - _TC", 20000, "_Test Cost Center - _TC", submit=True) - + self.assertTrue(frappe.db.get_value("GL Entry", {"voucher_type": "Journal Entry", "voucher_no": jv2.name})) - + frappe.db.set_value("Company", "_Test Company", "monthly_bgt_flag", "Stop") self.assertRaises(BudgetError, jv1.cancel) frappe.db.set_value("Company", "_Test Company", "monthly_bgt_flag", "Ignore") - + def get_actual_expense(self, monthly_end_date): return get_actual_expense({ "account": "_Test Account Cost for Goods Sold - _TC", @@ -166,19 +161,19 @@ class TestJournalEntry(unittest.TestCase): "company": "_Test Company", "fiscal_year": get_fiscal_year(monthly_end_date)[0] }) - + def set_total_expense_zero(self, posting_date): existing_expense = self.get_actual_expense(posting_date) - make_journal_entry("_Test Account Cost for Goods Sold - _TC", + make_journal_entry("_Test Account Cost for Goods Sold - _TC", "_Test Account Bank Account - _TC", -existing_expense, "_Test Cost Center - _TC", submit=True) - + def make_journal_entry(account1, account2, amount, cost_center=None, submit=False): jv = frappe.new_doc("Journal Entry") jv.posting_date = "2013-02-14" jv.company = "_Test Company" jv.fiscal_year = "_Test Fiscal Year 2013" jv.user_remark = "test" - + jv.set("accounts", [ { "account": account1, @@ -193,11 +188,11 @@ def make_journal_entry(account1, account2, amount, cost_center=None, submit=Fals } ]) jv.insert() - + if submit: jv.submit() - + return jv - + test_records = frappe.get_test_records('Journal Entry') diff --git a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json index b336d4910c..d09171434d 100644 --- a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json +++ b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json @@ -1,27 +1,44 @@ { + "allow_copy": 0, + "allow_import": 0, + "allow_rename": 0, "autoname": "hash", "creation": "2013-02-22 01:27:39", + "custom": 0, "docstatus": 0, "doctype": "DocType", "fields": [ { + "allow_on_submit": 0, "fieldname": "account", "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, "in_filter": 1, "in_list_view": 1, "label": "Account", + "no_copy": 0, "oldfieldname": "account", "oldfieldtype": "Link", "options": "Account", "permlevel": 0, + "print_hide": 0, "print_width": "250px", + "read_only": 0, + "report_hide": 0, "reqd": 1, "search_index": 1, + "set_only_once": 0, + "unique": 0, "width": "250px" }, { + "allow_on_submit": 0, "fieldname": "balance", "fieldtype": "Currency", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, "in_list_view": 1, "label": "Account Balance", "no_copy": 1, @@ -30,186 +47,336 @@ "options": "Company:company:default_currency", "permlevel": 0, "print_hide": 1, - "read_only": 1 + "read_only": 1, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 }, { + "allow_on_submit": 0, "default": ":Company", "description": "If Income or Expense", "fieldname": "cost_center", "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, "in_filter": 1, "in_list_view": 1, "label": "Cost Center", + "no_copy": 0, "oldfieldname": "cost_center", "oldfieldtype": "Link", "options": "Cost Center", "permlevel": 0, "print_hide": 1, "print_width": "180px", + "read_only": 0, + "report_hide": 0, + "reqd": 0, "search_index": 0, + "set_only_once": 0, + "unique": 0, "width": "180px" }, { + "allow_on_submit": 0, "fieldname": "col_break1", "fieldtype": "Column Break", - "permlevel": 0 + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "no_copy": 0, + "permlevel": 0, + "print_hide": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 }, { + "allow_on_submit": 0, "fieldname": "party_type", "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, "label": "Party Type", + "no_copy": 0, "options": "DocType", - "permlevel": 0 + "permlevel": 0, + "print_hide": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 }, { + "allow_on_submit": 0, "fieldname": "party", "fieldtype": "Dynamic Link", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, "label": "Party", + "no_copy": 0, "options": "party_type", - "permlevel": 0 + "permlevel": 0, + "print_hide": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 }, { + "allow_on_submit": 0, "fieldname": "party_balance", "fieldtype": "Currency", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, "label": "Party Balance", + "no_copy": 0, "options": "Company:company:default_currency", "permlevel": 0, "precision": "", - "read_only": 1 + "print_hide": 0, + "read_only": 1, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 }, { + "allow_on_submit": 0, "fieldname": "sec_break1", "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, "label": "Amount", - "permlevel": 0 + "no_copy": 0, + "permlevel": 0, + "print_hide": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 }, { + "allow_on_submit": 0, "fieldname": "debit", "fieldtype": "Currency", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, "in_list_view": 1, "label": "Debit", + "no_copy": 0, "oldfieldname": "debit", "oldfieldtype": "Currency", "options": "Company:company:default_currency", - "permlevel": 0 + "permlevel": 0, + "print_hide": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 }, { + "allow_on_submit": 0, "fieldname": "col_break2", "fieldtype": "Column Break", - "permlevel": 0 + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "no_copy": 0, + "permlevel": 0, + "print_hide": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 }, { + "allow_on_submit": 0, "fieldname": "credit", "fieldtype": "Currency", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, "in_list_view": 1, "label": "Credit", + "no_copy": 0, "oldfieldname": "credit", "oldfieldtype": "Currency", "options": "Company:company:default_currency", - "permlevel": 0 + "permlevel": 0, + "print_hide": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 }, { + "allow_on_submit": 0, "fieldname": "reference", "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, "label": "Reference", - "permlevel": 0 - }, - { - "fieldname": "against_invoice", - "fieldtype": "Link", - "in_filter": 1, - "label": "Against Sales Invoice", - "no_copy": 1, - "oldfieldname": "against_invoice", - "oldfieldtype": "Link", - "options": "Sales Invoice", + "no_copy": 0, "permlevel": 0, "print_hide": 0, - "search_index": 1 + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 }, { - "fieldname": "against_voucher", - "fieldtype": "Link", - "in_filter": 1, - "in_list_view": 1, - "label": "Against Purchase Invoice", - "no_copy": 1, - "oldfieldname": "against_voucher", - "oldfieldtype": "Link", - "options": "Purchase Invoice", + "allow_on_submit": 0, + "fieldname": "reference_type", + "fieldtype": "Select", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Reference Type", + "no_copy": 0, + "options": "\nSales Invoice\nPurchase Invoice\nJournal Entry\nSales Order\nPurchase Order\nExpense Claim", "permlevel": 0, + "precision": "", "print_hide": 0, - "search_index": 1 + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 }, { - "fieldname": "against_jv", - "fieldtype": "Link", - "in_filter": 1, - "label": "Against Journal Entry", - "no_copy": 1, - "oldfieldname": "against_jv", - "oldfieldtype": "Link", - "options": "Journal Entry", + "allow_on_submit": 0, + "fieldname": "reference_name", + "fieldtype": "Dynamic Link", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Reference Name", + "no_copy": 0, + "options": "reference_type", "permlevel": 0, + "precision": "", "print_hide": 0, - "search_index": 1 + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 }, { + "allow_on_submit": 0, "fieldname": "col_break3", "fieldtype": "Column Break", - "permlevel": 0 - }, - { - "fieldname": "against_sales_order", - "fieldtype": "Link", - "label": "Against Sales Order", - "options": "Sales Order", - "permlevel": 0 - }, - { - "fieldname": "against_purchase_order", - "fieldtype": "Link", - "label": "Against Purchase Order", - "options": "Purchase Order", - "permlevel": 0 - }, - { - "fieldname": "against_expense_claim", - "fieldtype": "Link", - "label": "Against Expense Claim", - "options": "Expense Claim", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "no_copy": 0, "permlevel": 0, - "precision": "" + "print_hide": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 }, { + "allow_on_submit": 0, "fieldname": "is_advance", "fieldtype": "Select", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, "label": "Is Advance", "no_copy": 1, "oldfieldname": "is_advance", "oldfieldtype": "Select", "options": "No\nYes", "permlevel": 0, - "print_hide": 1 + "print_hide": 1, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 }, { + "allow_on_submit": 0, "fieldname": "against_account", "fieldtype": "Text", "hidden": 1, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, "label": "Against Account", "no_copy": 1, "oldfieldname": "against_account", "oldfieldtype": "Text", "permlevel": 0, - "print_hide": 1 + "print_hide": 1, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 } ], + "hide_heading": 0, + "hide_toolbar": 0, "idx": 1, + "in_create": 0, + "in_dialog": 0, + "is_submittable": 0, + "issingle": 0, "istable": 1, - "modified": "2015-02-19 01:07:00.388689", + "modified": "2015-08-11 10:44:11.432623", "modified_by": "Administrator", "module": "Accounts", "name": "Journal Entry Account", "owner": "Administrator", - "permissions": [] + "permissions": [], + "read_only": 0, + "read_only_onload": 0 } \ No newline at end of file diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index bcca0f2da1..933570f9eb 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -34,8 +34,8 @@ class PaymentReconciliation(Document): 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 ifnull(t2.against_voucher, '')='' and ifnull(t2.against_invoice, '')='' - and ifnull(t2.against_jv, '')='' {cond} + and ifnull(t2.reference_type, '')='' + {cond} and (CASE WHEN t1.voucher_type in ('Debit Note', 'Credit Note') THEN 1=1 @@ -190,7 +190,7 @@ class PaymentReconciliation(Document): 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}") .format(p.idx, p.allocated_amount, p.amount)) - + invoice_outstanding = unreconciled_invoices.get(p.invoice_type, {}).get(p.invoice_number) if flt(p.allocated_amount) - invoice_outstanding > 0.009: frappe.throw(_("Row {0}: Allocated amount {1} must be less than or equals to invoice outstanding amount {2}") diff --git a/erpnext/accounts/doctype/payment_tool/payment_tool.py b/erpnext/accounts/doctype/payment_tool/payment_tool.py index 6a002af8b4..4edbebd09f 100644 --- a/erpnext/accounts/doctype/payment_tool/payment_tool.py +++ b/erpnext/accounts/doctype/payment_tool/payment_tool.py @@ -12,13 +12,6 @@ class PaymentTool(Document): def make_journal_entry(self): from erpnext.accounts.utils import get_balance_on total_payment_amount = 0.00 - invoice_voucher_type = { - 'Sales Invoice': 'against_invoice', - 'Purchase Invoice': 'against_voucher', - 'Journal Entry': 'against_jv', - 'Sales Order': 'against_sales_order', - 'Purchase Order': 'against_purchase_order', - } jv = frappe.new_doc('Journal Entry') jv.voucher_type = 'Journal Entry' @@ -41,7 +34,8 @@ class PaymentTool(Document): d1.party = self.party d1.balance = get_balance_on(self.party_account) d1.set("debit" if self.received_or_paid=="Paid" else "credit", flt(v.payment_amount)) - d1.set(invoice_voucher_type.get(v.against_voucher_type), v.against_voucher_no) + d1.set("reference_type", v.against_voucher_type) + d1.set("reference_name", v.against_voucher_no) d1.set('is_advance', 'Yes' if v.against_voucher_type in ['Sales Order', 'Purchase Order'] else 'No') total_payment_amount = flt(total_payment_amount) + flt(d1.debit) - flt(d1.credit) diff --git a/erpnext/accounts/doctype/payment_tool/test_payment_tool.py b/erpnext/accounts/doctype/payment_tool/test_payment_tool.py index a4ff33331a..321986cd46 100644 --- a/erpnext/accounts/doctype/payment_tool/test_payment_tool.py +++ b/erpnext/accounts/doctype/payment_tool/test_payment_tool.py @@ -23,10 +23,11 @@ class TestPaymentTool(unittest.TestCase): # Create SO with partial outstanding so1 = make_sales_order(customer="_Test Customer 3", qty=10, rate=100) - + self.create_against_jv(jv_test_records[0], { "party": "_Test Customer 3", - "against_sales_order": so1.name, + "reference_type": "Sales Order", + "reference_name": so1.name, "is_advance": "Yes" }) @@ -36,7 +37,8 @@ class TestPaymentTool(unittest.TestCase): self.create_against_jv(jv_test_records[0], { "party": "_Test Customer 3", - "against_sales_order": so2.name, + "reference_type": "Sales Order", + "reference_name": so2.name, "credit": 1000, "is_advance": "Yes" }) @@ -52,7 +54,8 @@ class TestPaymentTool(unittest.TestCase): self.create_against_jv(jv_test_records[0], { "party": "_Test Customer 3", - "against_invoice": si1.name + "reference_type": si1.doctype, + "reference_name": si1.name }) #Create SI with no outstanding si2 = self.create_voucher(si_test_records[0], { @@ -62,7 +65,8 @@ class TestPaymentTool(unittest.TestCase): self.create_against_jv(jv_test_records[0], { "party": "_Test Customer 3", - "against_invoice": si2.name, + "reference_type": si2.doctype, + "reference_name": si2.name, "credit": 561.80 }) @@ -125,7 +129,7 @@ class TestPaymentTool(unittest.TestCase): def make_voucher_for_party(self, args, expected_outstanding): #Make Journal Entry for Party payment_tool_doc = frappe.new_doc("Payment Tool") - + for k, v in args.items(): payment_tool_doc.set(k, v) @@ -153,29 +157,12 @@ class TestPaymentTool(unittest.TestCase): new_jv = paytool.make_journal_entry() - #Create a list of expected values as [party account, payment against, against_jv, against_invoice, - #against_voucher, against_sales_order, against_purchase_order] - expected_values = [ - [paytool.party_account, paytool.party, 100.00, expected_outstanding.get("Journal Entry")[0], None, None, None, None], - [paytool.party_account, paytool.party, 100.00, None, expected_outstanding.get("Sales Invoice")[0], None, None, None], - [paytool.party_account, paytool.party, 100.00, None, None, expected_outstanding.get("Purchase Invoice")[0], None, None], - [paytool.party_account, paytool.party, 100.00, None, None, None, expected_outstanding.get("Sales Order")[0], None], - [paytool.party_account, paytool.party, 100.00, None, None, None, None, expected_outstanding.get("Purchase Order")[0]] - ] - for jv_entry in new_jv.get("accounts"): if paytool.party_account == jv_entry.get("account") and paytool.party == jv_entry.get("party"): - row = [ - jv_entry.get("account"), - jv_entry.get("party"), - jv_entry.get("debit" if paytool.party_type=="Supplier" else "credit"), - jv_entry.get("against_jv"), - jv_entry.get("against_invoice"), - jv_entry.get("against_voucher"), - jv_entry.get("against_sales_order"), - jv_entry.get("against_purchase_order"), - ] - self.assertTrue(row in expected_values) + self.assertEquals(100.00, + jv_entry.get("debit" if paytool.party_type=="Supplier" else "credit")) + self.assertEquals(jv_entry.reference_name, + expected_outstanding[jv_entry.reference_type][0]) self.assertEquals(new_jv.get("cheque_no"), paytool.reference_no) self.assertEquals(new_jv.get("cheque_date"), paytool.reference_date) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index 8e91250ac6..6257865992 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -21,16 +21,15 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({ // Show / Hide button this.show_general_ledger(); - + if(!doc.is_return) { if(doc.docstatus==1) { if(doc.outstanding_amount > 0) { - this.frm.add_custom_button(__('Make Payment Entry'), this.make_bank_entry); + this.frm.add_custom_button(__('Payment'), this.make_bank_entry).addClass("btn-primary"); } - - cur_frm.add_custom_button(__('Make Debit Note'), this.make_debit_note); + cur_frm.add_custom_button(__('Debit Note'), this.make_debit_note); } - + if(doc.docstatus===0) { cur_frm.add_custom_button(__('From Purchase Order'), function() { frappe.model.map_current_doc({ @@ -102,7 +101,7 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({ if(row.purchase_receipt) frappe.model.clear_doc("Purchase Receipt", row.purchase_receipt) }) }, - + make_debit_note: function() { frappe.model.open_mapped_doc({ method: "erpnext.accounts.doctype.purchase_invoice.purchase_invoice.make_debit_note", diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 00ea6ae38a..132cb1027d 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -41,8 +41,8 @@ class PurchaseInvoice(BuyingController): self.po_required() self.pr_required() self.validate_supplier_invoice() - self.validate_advance_jv("advances", "purchase_order") - + self.validate_advance_jv("Purchase Order") + self.check_active_purchase_items() self.check_conversion_rate() self.validate_credit_to_acc() @@ -233,7 +233,7 @@ class PurchaseInvoice(BuyingController): self.update_against_document_in_jv() self.update_prevdoc_status() self.update_billing_status_for_zero_amount_refdoc("Purchase Order") - + self.update_project() def make_gl_entries(self): @@ -365,7 +365,7 @@ class PurchaseInvoice(BuyingController): def on_cancel(self): if not self.is_return: from erpnext.accounts.utils import remove_against_link_from_jv - remove_against_link_from_jv(self.doctype, self.name, "against_voucher") + remove_against_link_from_jv(self.doctype, self.name) self.update_prevdoc_status() self.update_billing_status_for_zero_amount_refdoc("Purchase Order") @@ -413,4 +413,4 @@ def get_expense_account(doctype, txt, searchfield, start, page_len, filters): @frappe.whitelist() def make_debit_note(source_name, target_doc=None): from erpnext.controllers.sales_and_purchase_return import make_return_doc - return make_return_doc("Purchase Invoice", source_name, target_doc) \ No newline at end of file + return make_return_doc("Purchase Invoice", source_name, target_doc) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 017ab3a10d..caf741e578 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -40,14 +40,17 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte this._super(); cur_frm.dashboard.reset(); - + this.frm.toggle_reqd("due_date", !this.frm.doc.is_return); - + this.show_general_ledger(); - + if(doc.update_stock) this.show_stock_ledger(); - + if(doc.docstatus==1 && !doc.is_return) { + cur_frm.add_custom_button(doc.update_stock ? __('Sales Return') : __('Credit Note'), + this.make_sales_return); + if(cint(doc.update_stock)!=1) { // show Make Delivery Note button only if Sales Invoice is not created from Delivery Note var from_delivery_note = false; @@ -57,16 +60,13 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte }); if(!from_delivery_note) { - cur_frm.add_custom_button(__('Make Delivery'), cur_frm.cscript['Make Delivery Note']) + cur_frm.add_custom_button(__('Delivery'), cur_frm.cscript['Make Delivery Note']).addClass("btn-primary"); } } if(doc.outstanding_amount!=0 && !cint(doc.is_return)) { - cur_frm.add_custom_button(__('Make Payment Entry'), cur_frm.cscript.make_bank_entry); + cur_frm.add_custom_button(__('Payment'), cur_frm.cscript.make_bank_entry).addClass("btn-primary"); } - - cur_frm.add_custom_button(doc.update_stock ? __('Make Sales Return') : __('Make Credit Note'), - this.make_sales_return); } // Show buttons only when pos view is active @@ -201,7 +201,7 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte items_on_form_rendered: function() { erpnext.setup_serial_no(); }, - + make_sales_return: function() { frappe.model.open_mapped_doc({ method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.make_sales_return", @@ -390,4 +390,4 @@ cur_frm.set_query("debit_to", function(doc) { ['Account', 'account_type', '=', 'Receivable'] ] } -}); \ No newline at end of file +}); diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 6d76b5322f..4285eb8b14 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -46,7 +46,7 @@ class SalesInvoice(SellingController): self.validate_debit_to_acc() self.validate_fixed_asset_account() self.clear_unallocated_advances("Sales Invoice Advance", "advances") - self.validate_advance_jv("advances", "sales_order") + self.validate_advance_jv("Sales Order") self.add_remarks() self.validate_write_off_account() @@ -105,7 +105,7 @@ class SalesInvoice(SellingController): self.check_stop_sales_order("sales_order") from erpnext.accounts.utils import remove_against_link_from_jv - remove_against_link_from_jv(self.doctype, self.name, "against_invoice") + remove_against_link_from_jv(self.doctype, self.name) if not self.is_return: self.update_status_updater_args() @@ -420,7 +420,7 @@ class SalesInvoice(SellingController): for d in self.get_item_list(): if frappe.db.get_value("Item", d.item_code, "is_stock_item") == 1 and d.warehouse and flt(d['qty']): self.update_reserved_qty(d) - + incoming_rate = 0 if cint(self.is_return) and self.return_against and self.docstatus==1: incoming_rate = self.get_incoming_rate_for_sales_return(d.item_code, @@ -447,7 +447,7 @@ class SalesInvoice(SellingController): if update_outstanding == "No": from erpnext.accounts.doctype.gl_entry.gl_entry import update_outstanding_amt - update_outstanding_amt(self.debit_to, "Customer", self.customer, + update_outstanding_amt(self.debit_to, "Customer", self.customer, self.doctype, self.return_against if cint(self.is_return) else self.name) if repost_future_gle and cint(self.update_stock) \ diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 67f4a8e716..c7a992c489 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -391,7 +391,8 @@ class TestSalesInvoice(unittest.TestCase): import test_records as jv_test_records jv = frappe.get_doc(frappe.copy_doc(jv_test_records[0])) - jv.get("accounts")[0].against_invoice = w.name + jv.get("accounts")[0].reference_type = w.doctype + jv.get("accounts")[0].reference_name = w.name jv.insert() jv.submit() @@ -656,17 +657,17 @@ class TestSalesInvoice(unittest.TestCase): si.load_from_db() self.assertTrue(frappe.db.sql("""select name from `tabJournal Entry Account` - where against_invoice=%s""", si.name)) + where reference_name=%s""", si.name)) self.assertTrue(frappe.db.sql("""select name from `tabJournal Entry Account` - where against_invoice=%s and credit=300""", si.name)) + where reference_name=%s and credit=300""", si.name)) self.assertEqual(si.outstanding_amount, 261.8) si.cancel() self.assertTrue(not frappe.db.sql("""select name from `tabJournal Entry Account` - where against_invoice=%s""", si.name)) + where reference_name=%s""", si.name)) def test_recurring_invoice(self): from erpnext.controllers.tests.test_recurring_document import test_recurring_document @@ -728,68 +729,67 @@ class TestSalesInvoice(unittest.TestCase): # hack! because stock ledger entires are already inserted and are not rolled back! self.assertRaises(SerialNoDuplicateError, si.cancel) - + def test_invoice_due_date_against_customers_credit_days(self): # set customer's credit days frappe.db.set_value("Customer", "_Test Customer", "credit_days_based_on", "Fixed Days") frappe.db.set_value("Customer", "_Test Customer", "credit_days", 10) - + si = create_sales_invoice() self.assertEqual(si.due_date, add_days(nowdate(), 10)) - + # set customer's credit days is last day of the next month frappe.db.set_value("Customer", "_Test Customer", "credit_days_based_on", "Last Day of the Next Month") - - si1 = create_sales_invoice(posting_date="2015-07-05") + + si1 = create_sales_invoice(posting_date="2015-07-05") self.assertEqual(si1.due_date, "2015-08-31") - + def test_return_sales_invoice(self): set_perpetual_inventory() - make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=50, basic_rate=100) - + actual_qty_0 = get_qty_after_transaction() - + si = create_sales_invoice(qty=5, rate=500, update_stock=1) actual_qty_1 = get_qty_after_transaction() self.assertEquals(actual_qty_0 - 5, actual_qty_1) - + # outgoing_rate - outgoing_rate = frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Sales Invoice", + outgoing_rate = frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Sales Invoice", "voucher_no": si.name}, "stock_value_difference") / 5 - + # return entry si1 = create_sales_invoice(is_return=1, return_against=si.name, qty=-2, rate=500, update_stock=1) actual_qty_2 = get_qty_after_transaction() - + self.assertEquals(actual_qty_1 + 2, actual_qty_2) - - incoming_rate, stock_value_difference = frappe.db.get_value("Stock Ledger Entry", - {"voucher_type": "Sales Invoice", "voucher_no": si1.name}, + + incoming_rate, stock_value_difference = frappe.db.get_value("Stock Ledger Entry", + {"voucher_type": "Sales Invoice", "voucher_no": si1.name}, ["incoming_rate", "stock_value_difference"]) - + self.assertEquals(flt(incoming_rate, 3), abs(flt(outgoing_rate, 3))) - - + + # Check gl entry - gle_warehouse_amount = frappe.db.get_value("GL Entry", {"voucher_type": "Sales Invoice", + gle_warehouse_amount = frappe.db.get_value("GL Entry", {"voucher_type": "Sales Invoice", "voucher_no": si1.name, "account": "_Test Warehouse - _TC"}, "debit") - + self.assertEquals(gle_warehouse_amount, stock_value_difference) - - party_credited = frappe.db.get_value("GL Entry", {"voucher_type": "Sales Invoice", + + party_credited = frappe.db.get_value("GL Entry", {"voucher_type": "Sales Invoice", "voucher_no": si1.name, "account": "Debtors - _TC", "party": "_Test Customer"}, "credit") - + self.assertEqual(party_credited, 1000) - + # Check outstanding amount self.assertFalse(si1.outstanding_amount) self.assertEqual(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount"), 1500) - + set_perpetual_inventory(0) - + def test_discount_on_net_total(self): si = frappe.copy_doc(test_records[2]) si.apply_discount_on = "Net Total" @@ -798,7 +798,7 @@ class TestSalesInvoice(unittest.TestCase): expected_values = { "keys": ["price_list_rate", "discount_percentage", "rate", "amount", - "base_price_list_rate", "base_rate", "base_amount", + "base_price_list_rate", "base_rate", "base_amount", "net_rate", "base_net_rate", "net_amount", "base_net_amount"], "_Test Item Home Desktop 100": [50, 0, 50, 500, 50, 50, 500, 25, 25, 250, 250], "_Test Item Home Desktop 200": [150, 0, 150, 750, 150, 150, 750, 75, 75, 375, 375], @@ -821,7 +821,7 @@ class TestSalesInvoice(unittest.TestCase): # check tax calculation expected_values = { - "keys": ["tax_amount", "tax_amount_after_discount_amount", + "keys": ["tax_amount", "tax_amount_after_discount_amount", "base_tax_amount_after_discount_amount"], "_Test Account Shipping Charges - _TC": [100, 100, 100], "_Test Account Customs Duty - _TC": [62.5, 62.5, 62.5], @@ -836,12 +836,12 @@ class TestSalesInvoice(unittest.TestCase): for d in si.get("taxes"): for i, k in enumerate(expected_values["keys"]): self.assertEquals(d.get(k), expected_values[d.account_head][i]) - - + + self.assertEquals(si.total_taxes_and_charges, 234.44) self.assertEquals(si.base_grand_total, 859.44) self.assertEquals(si.grand_total, 859.44) - + def create_sales_invoice(**args): si = frappe.new_doc("Sales Invoice") diff --git a/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py b/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py index 699d684c73..604bc5660a 100644 --- a/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py +++ b/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py @@ -15,21 +15,19 @@ def execute(filters=None): entries = get_entries(filters) invoice_posting_date_map = get_invoice_posting_date_map(filters) against_date = "" - outstanding_amount = 0.0 data = [] for d in entries: - if d.against_voucher: - against_date = d.against_voucher and invoice_posting_date_map[d.against_voucher] or "" + against_date = invoice_posting_date_map[d.reference_name] or "" + if d.reference_type=="Purchase Invoice": payment_amount = flt(d.debit) or -1 * flt(d.credit) else: - against_date = d.against_invoice and invoice_posting_date_map[d.against_invoice] or "" payment_amount = flt(d.credit) or -1 * flt(d.debit) - row = [d.name, d.party_type, d.party, d.posting_date, d.against_voucher or d.against_invoice, + row = [d.name, d.party_type, d.party, d.posting_date, d.reference_name, against_date, d.debit, d.credit, d.cheque_no, d.cheque_date, d.remark] - if d.against_voucher or d.against_invoice: + if d.reference_name: row += get_ageing_data(30, 60, 90, d.posting_date, against_date, payment_amount) else: row += ["", "", "", "", ""] @@ -82,7 +80,7 @@ def get_conditions(filters): def get_entries(filters): conditions = get_conditions(filters) entries = frappe.db.sql("""select jv.name, jvd.party_type, jvd.party, jv.posting_date, - jvd.against_voucher, jvd.against_invoice, jvd.debit, jvd.credit, + 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""" % diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 298ff8eea8..f41d19dddd 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -142,13 +142,6 @@ def reconcile_against_document(args): for d in args: check_if_jv_modified(d) validate_allocated_amount(d) - against_fld = { - 'Journal Entry' : 'against_jv', - 'Sales Invoice' : 'against_invoice', - 'Purchase Invoice' : 'against_voucher' - } - - d['against_fld'] = against_fld[d['against_voucher_type']] # cancel JV jv_obj = frappe.get_doc('Journal Entry', d['voucher_no']) @@ -173,8 +166,7 @@ def check_if_jv_modified(args): 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 ifnull(t2.against_voucher, '')='' - and ifnull(t2.against_invoice, '')='' and ifnull(t2.against_jv, '')='' + and ifnull(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) @@ -193,7 +185,12 @@ def update_against_doc(d, jv_obj): """ 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["against_fld"], d["against_voucher"]) + + original_reference_type = jv_detail.reference_type + original_reference_name = jv_detail.reference_name + + jv_detail.set("reference_type", d["against_voucher_type"]) + jv_detail.set("reference_name", d["against_voucher"]) if d['allocated_amt'] < d['unadjusted_amt']: jvd = frappe.db.sql("""select cost_center, balance, against_account, is_advance @@ -208,6 +205,8 @@ def update_against_doc(d, jv_obj): ch.set(d['dr_or_cr'], flt(d['unadjusted_amt']) - flt(d['allocated_amt'])) ch.set(d['dr_or_cr']== 'debit' and 'credit' or 'debit', 0) ch.against_account = cstr(jvd[0][2]) + ch.reference_type = original_reference_type + ch.reference_name = original_reference_name ch.is_advance = cstr(jvd[0][3]) ch.docstatus = 1 @@ -215,15 +214,16 @@ def update_against_doc(d, jv_obj): jv_obj.flags.ignore_validate_update_after_submit = True jv_obj.save() -def remove_against_link_from_jv(ref_type, ref_no, against_field): +def remove_against_link_from_jv(ref_type, ref_no): linked_jv = frappe.db.sql_list("""select parent from `tabJournal Entry Account` - where `%s`=%s and docstatus < 2""" % (against_field, "%s"), (ref_no)) + where reference_type=%s and reference_name=%s and docstatus < 2""", (ref_type, ref_no)) if linked_jv: - frappe.db.sql("""update `tabJournal Entry Account` set `%s`=null, + frappe.db.sql("""update `tabJournal Entry Account` + set reference_type=null, reference_name = null, modified=%s, modified_by=%s - where `%s`=%s and docstatus < 2""" % (against_field, "%s", "%s", against_field, "%s"), - (now(), frappe.session.user, ref_no)) + where reference_type=%s and reference_name=%s + and docstatus < 2""", (now(), frappe.session.user, ref_type, ref_no)) frappe.db.sql("""update `tabGL Entry` set against_voucher_type=null, against_voucher=null, diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js index 09a0e919a9..a5cd6ce6c2 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.js +++ b/erpnext/buying/doctype/purchase_order/purchase_order.js @@ -5,6 +5,14 @@ frappe.provide("erpnext.buying"); {% include 'buying/doctype/purchase_common/purchase_common.js' %}; +frappe.ui.form.on("Purchase Order", { + onload: function(frm) { + erpnext.queries.setup_queries(frm, "Warehouse", function() { + return erpnext.queries.warehouse(frm.doc); + }); + } +}); + erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend({ refresh: function(doc, cdt, cdn) { var me = this; @@ -12,31 +20,38 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend( // this.frm.dashboard.reset(); if(doc.docstatus == 1 && doc.status != 'Stopped') { + + if(flt(doc.per_billed, 2) < 100 || doc.per_received < 100) + cur_frm.add_custom_button(__('Stop'), cur_frm.cscript['Stop Purchase Order']); + + if(flt(doc.per_billed)==0) { + cur_frm.add_custom_button(__('Payment'), cur_frm.cscript.make_bank_entry); + } + if(flt(doc.per_received, 2) < 100) { - cur_frm.add_custom_button(__('Make Purchase Receipt'), this.make_purchase_receipt); - + cur_frm.add_custom_button(__('Receive'), this.make_purchase_receipt).addClass("btn-primary"); + if(doc.is_subcontracted==="Yes") { cur_frm.add_custom_button(__('Transfer Material to Supplier'), this.make_stock_entry); } } + if(flt(doc.per_billed, 2) < 100) - cur_frm.add_custom_button(__('Make Invoice'), this.make_purchase_invoice); - - if(flt(doc.per_billed, 2) < 100 || doc.per_received < 100) - cur_frm.add_custom_button(__('Stop'), cur_frm.cscript['Stop Purchase Order']); + cur_frm.add_custom_button(__('Invoice'), this.make_purchase_invoice); + } else if(doc.docstatus===0) { cur_frm.cscript.add_from_mappers(); } if(doc.docstatus == 1 && doc.status == 'Stopped') - cur_frm.add_custom_button(__('Unstop Purchase Order'), cur_frm.cscript['Unstop Purchase Order']); + cur_frm.add_custom_button(__('Unstop'), cur_frm.cscript['Unstop Purchase Order']); }, make_stock_entry: function() { var items = $.map(cur_frm.doc.items, function(d) { return d.bom ? d.item_code : false; }); var me = this; - + if(items.length===1) { me._make_stock_entry(items[0]); return; @@ -126,7 +141,21 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend( items_add: function(doc, cdt, cdn) { var row = frappe.get_doc(cdt, cdn); 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_from_purchase_order", + args: { + "purchase_order": cur_frm.doc.name + }, + callback: function(r) { + var doclist = frappe.model.sync(r.message); + frappe.set_route("Form", doclist[0].doctype, doclist[0].name); + } + }); } + }); // for backward compatibility: combine new and previous states diff --git a/erpnext/change_log/current/journal_entry_rename.md b/erpnext/change_log/current/journal_entry_rename.md new file mode 100644 index 0000000000..9bd1b66866 --- /dev/null +++ b/erpnext/change_log/current/journal_entry_rename.md @@ -0,0 +1 @@ +- For referencing a line in **Journal Entry**, now you can reference by the **Reference Type** and **Reference Name** columns, instead of "Against Sales Invoice", "Against Purchase Invoice", etc. diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 915d2947b5..ecd9e1a1ad 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -211,29 +211,32 @@ class AccountsController(TransactionBase): and ifnull(allocated_amount, 0) = 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): - so_list = list(set([d.get(against_order_field) for d in self.get("items") if d.get(against_order_field)])) - cond = "" - if so_list: - cond = "or (ifnull(t2.%s, '') in (%s))" % ("against_" + against_order_field, ', '.join(['%s']*len(so_list))) + """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)])) + + if not order_list: + return + + in_placeholder = ', '.join(['%s'] * len(order_list)) + + # conver sales_order to "Sales Order" + reference_type = against_order_field.replace("_", " ").title() res = frappe.db.sql(""" select - t1.name as jv_no, t1.remark, t2.{0} as amount, t2.name as jv_detail_no, `against_{1}` as against_order + t1.name as jv_no, t1.remark, t2.{0} as amount, t2.name as jv_detail_no, + 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.party_type = %s and t2.party = %s and t2.is_advance = 'Yes' and t1.docstatus = 1 - and (( - ifnull(t2.against_voucher, '') = '' - and ifnull(t2.against_invoice, '') = '' - and ifnull(t2.against_jv, '') = '' - and ifnull(t2.against_sales_order, '') = '' - and ifnull(t2.against_purchase_order, '') = '' - ) {2}) - order by t1.posting_date""".format(dr_or_cr, against_order_field, cond), - [account_head, party_type, party] + so_list, as_dict=1) + and ( + ifnull(t2.reference_type, '')='' + or (t2.reference_type = %s and ifnull(t2.reference_name, '') in ({1}))) + order by t1.posting_date""".format(dr_or_cr, in_placeholder), + [account_head, party_type, party, reference_type] + order_list, as_dict=1) self.set(parentfield, []) for d in res: @@ -246,25 +249,26 @@ class AccountsController(TransactionBase): "allocated_amount": flt(d.amount) if d.against_order else 0 }) - def validate_advance_jv(self, advance_table_fieldname, against_order_field): + 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, %s as against_order + 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 ifnull(against_sales_order, '') in (%s) - group by parent, against_sales_order""" % - ("against_" + against_order_field, '%s', ', '.join(['%s']*len(order_list))), - tuple([account] + order_list), as_dict=1) + 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(advance_table_fieldname)] + 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: @@ -318,10 +322,8 @@ class AccountsController(TransactionBase): def set_total_advance_paid(self): if self.doctype == "Sales Order": dr_or_cr = "credit" - against_field = "against_sales_order" else: dr_or_cr = "debit" - against_field = "against_purchase_order" advance_paid = frappe.db.sql(""" select @@ -329,8 +331,10 @@ class AccountsController(TransactionBase): from `tabJournal Entry Account` where - {against_field} = %s and docstatus = 1 and is_advance = "Yes" """.format(dr_or_cr=dr_or_cr, \ - against_field=against_field), self.name) + reference_type = %s and + reference_name = %s and + docstatus = 1 and is_advance = "Yes" """.format(dr_or_cr=dr_or_cr), + (self.doctype, self.name)) if advance_paid: advance_paid = flt(advance_paid[0][0], self.precision("advance_paid")) diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.js b/erpnext/hr/doctype/expense_claim/expense_claim.js index a56929d1c3..1d405da00a 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.js +++ b/erpnext/hr/doctype/expense_claim/expense_claim.js @@ -24,13 +24,15 @@ erpnext.hr.ExpenseClaimController = frappe.ui.form.Controller.extend({ var d1 = frappe.model.add_child(jv, 'Journal Entry Account', 'accounts'); d1.debit = expense[i].sanctioned_amount; d1.account = expense[i].default_account; - d1.against_expense_claim = cur_frm.doc.name; + d1.reference_type = cur_frm.doc.doctype; + d1.reference_name = cur_frm.doc.name; } // credit to bank var d1 = frappe.model.add_child(jv, 'Journal Entry Account', 'accounts'); d1.credit = cur_frm.doc.total_sanctioned_amount; - d1.against_expense_claim = cur_frm.doc.name; + d1.reference_type = cur_frm.doc.doctype; + d1.reference_name = cur_frm.doc.name; if(r.message) { d1.account = r.message.account; d1.balance = r.message.balance; @@ -179,5 +181,5 @@ cur_frm.fields_dict['task'].get_query = function(doc) { filters:{ 'project': doc.project } - } -} \ No newline at end of file + } +} diff --git a/erpnext/patches/v5_4/cleanup_journal_entry.py b/erpnext/patches/v5_4/cleanup_journal_entry.py new file mode 100644 index 0000000000..9968e3cc10 --- /dev/null +++ b/erpnext/patches/v5_4/cleanup_journal_entry.py @@ -0,0 +1,14 @@ +import frappe + +def execute(): + for doctype, fieldname in ( + ("Sales Invoice", "against_invoice"), + ("Purchase Invoice", "against_voucher"), + ("Sales Order", "against_sales_order"), + ("Purchase Order", "against_purchase_order"), + ("Journal Entry", "against_jv"), + ("Expense Claim", "against_expense_claim"), + ): + frappe.db.update("""update `tabJournal Entry Detail` + set reference_type=%s and reference_name={0} where ifnull({0}, '') != '' + """.format(fieldname), doctype) diff --git a/erpnext/public/js/controllers/stock_controller.js b/erpnext/public/js/controllers/stock_controller.js index f5f7aa0c3d..ff5cf534e6 100644 --- a/erpnext/public/js/controllers/stock_controller.js +++ b/erpnext/public/js/controllers/stock_controller.js @@ -12,31 +12,8 @@ erpnext.stock.StockController = frappe.ui.form.Controller.extend({ }, setup_warehouse_query: function() { - var me = this; - var warehouse_query_method = function() { + erpnext.queries.setup_queries(me.frm, "Warehouse", function() { return erpnext.queries.warehouse(me.frm.doc); - }; - - var _set_warehouse_query = function(doctype, parentfield) { - var warehouse_link_fields = frappe.meta.get_docfields(doctype, me.frm.doc.name, - {"fieldtype": "Link", "options": "Warehouse"}); - $.each(warehouse_link_fields, function(i, df) { - if(parentfield) { - me.frm.set_query(df.fieldname, parentfield, warehouse_query_method); - } else { - me.frm.set_query(df.fieldname, warehouse_query_method); - } - }); - }; - - _set_warehouse_query(me.frm.doc.doctype); - - // warehouse field in tables - var table_fields = frappe.meta.get_docfields(me.frm.doc.doctype, me.frm.doc.name, - {"fieldtype": "Table"}); - - $.each(table_fields, function(i, df) { - _set_warehouse_query(df.options, df.fieldname); }); }, diff --git a/erpnext/public/js/queries.js b/erpnext/public/js/queries.js index b487c23938..12307fb9b3 100644 --- a/erpnext/public/js/queries.js +++ b/erpnext/public/js/queries.js @@ -75,3 +75,26 @@ $.extend(erpnext.queries, { } } }); + +erpnext.queries.setup_queries = function(frm, options, query_fn) { + var me = this; + var set_query = function(doctype, parentfield) { + var link_fields = frappe.meta.get_docfields(doctype, frm.doc.name, + {"fieldtype": "Link", "options": options}); + $.each(link_fields, function(i, df) { + if(parentfield) { + frm.set_query(df.fieldname, parentfield, query_fn); + } else { + frm.set_query(df.fieldname, query_fn); + } + }); + }; + + set_query(frm.doc.doctype); + + // warehouse field in tables + $.each(frappe.meta.get_docfields(frm.doc.doctype, frm.doc.name, {"fieldtype": "Table"}), + function(i, df) { + set_query(df.options, df.fieldname); + }); +} diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index d06d550b94..4a047e41ae 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -3,6 +3,14 @@ {% include 'selling/sales_common.js' %} +frappe.ui.form.on("Sales Order", { + onload: function(frm) { + erpnext.queries.setup_queries(frm, "Warehouse", function() { + return erpnext.queries.warehouse(frm.doc); + }); + } +}); + erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend({ refresh: function(doc, dt, dn) { this._super(); @@ -16,29 +24,32 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend( // cur_frm.dashboard.add_progress(cint(doc.per_billed) + __("% Billed"), // doc.per_billed); - // delivery note - if(flt(doc.per_delivered, 2) < 100 && ["Sales", "Shopping Cart"].indexOf(doc.order_type)!==-1) - cur_frm.add_custom_button(__('Make Delivery'), this.make_delivery_note); - // indent if(!doc.order_type || ["Sales", "Shopping Cart"].indexOf(doc.order_type)!==-1) - cur_frm.add_custom_button(__('Make ') + __('Material Request'), - this.make_material_request); + cur_frm.add_custom_button(__('Material Request'), this.make_material_request); - // sales invoice - if(flt(doc.per_billed, 2) < 100) { - cur_frm.add_custom_button(__('Make Invoice'), this.make_sales_invoice); + if(flt(doc.per_billed)==0) { + cur_frm.add_custom_button(__('Payment'), cur_frm.cscript.make_bank_entry); } // stop if(flt(doc.per_delivered, 2) < 100 || doc.per_billed < 100) cur_frm.add_custom_button(__('Stop'), cur_frm.cscript['Stop Sales Order']) - // maintenance - if(flt(doc.per_delivered, 2) < 100 && ["Sales", "Shopping Cart"].indexOf(doc.order_type)===-1) { - cur_frm.add_custom_button(__('Make Maint. Visit'), this.make_maintenance_visit); - cur_frm.add_custom_button(__('Make Maint. Schedule'), this.make_maintenance_schedule); - } + // maintenance + if(flt(doc.per_delivered, 2) < 100 && ["Sales", "Shopping Cart"].indexOf(doc.order_type)===-1) { + cur_frm.add_custom_button(__('Maint. Visit'), this.make_maintenance_visit); + cur_frm.add_custom_button(__('Maint. Schedule'), this.make_maintenance_schedule); + } + + // delivery note + if(flt(doc.per_delivered, 2) < 100 && ["Sales", "Shopping Cart"].indexOf(doc.order_type)!==-1) + cur_frm.add_custom_button(__('Delivery'), this.make_delivery_note).addClass("btn-primary"); + + // sales invoice + if(flt(doc.per_billed, 2) < 100) { + cur_frm.add_custom_button(__('Invoice'), this.make_sales_invoice).addClass("btn-primary"); + } } else { // un-stop @@ -122,6 +133,20 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend( frm: cur_frm }) }, + + make_bank_entry: function() { + return frappe.call({ + method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_payment_entry_from_sales_order", + args: { + "sales_order": cur_frm.doc.name + }, + callback: function(r) { + var doclist = frappe.model.sync(r.message); + frappe.set_route("Form", doclist[0].doctype, doclist[0].name); + } + }); + } + }); // for backward compatibility: combine new and previous states diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.js b/erpnext/stock/doctype/delivery_note/delivery_note.js index 94356da8e1..e7ede652a3 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.js +++ b/erpnext/stock/doctype/delivery_note/delivery_note.js @@ -8,28 +8,28 @@ frappe.provide("erpnext.stock.delivery_note"); erpnext.stock.DeliveryNoteController = erpnext.selling.SellingController.extend({ refresh: function(doc, dt, dn) { this._super(); - + if (!doc.is_return) { if(doc.__onload && !doc.__onload.billing_complete && doc.docstatus==1) { // show Make Invoice button only if Delivery Note is not created from Sales Invoice var from_sales_invoice = false; from_sales_invoice = cur_frm.doc.items.some(function(item) { - return item.against_sales_invoice ? true : false; - }); + return item.against_sales_invoice ? true : false; + }); if(!from_sales_invoice) - cur_frm.add_custom_button(__('Make Invoice'), this.make_sales_invoice); + cur_frm.add_custom_button(__('Invoice'), this.make_sales_invoice).addClass("btn-primary"); } if(flt(doc.per_installed, 2) < 100 && doc.docstatus==1) - cur_frm.add_custom_button(__('Make Installation Note'), this.make_installation_note); + cur_frm.add_custom_button(__('Installation Note'), this.make_installation_note); if (doc.docstatus==1) { - cur_frm.add_custom_button(__('Make Sales Return'), this.make_sales_return); + cur_frm.add_custom_button(__('Sales Return'), this.make_sales_return); } if(doc.docstatus==0 && !doc.__islocal) { - cur_frm.add_custom_button(__('Make Packing Slip'), + cur_frm.add_custom_button(__('Packing Slip'), cur_frm.cscript['Make Packing Slip'], frappe.boot.doctype_icons["Packing Slip"]); } @@ -51,15 +51,15 @@ erpnext.stock.DeliveryNoteController = erpnext.selling.SellingController.extend( }); } } - + if (doc.docstatus==1) { this.show_stock_ledger(); if (cint(frappe.defaults.get_default("auto_accounting_for_stock"))) { this.show_general_ledger(); } } - - + + erpnext.stock.delivery_note.set_print_hide(doc, dt, dn); @@ -81,7 +81,7 @@ erpnext.stock.DeliveryNoteController = erpnext.selling.SellingController.extend( frm: cur_frm }); }, - + make_sales_return: function() { frappe.model.open_mapped_doc({ method: "erpnext.stock.doctype.delivery_note.delivery_note.make_sales_return", diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js index fcaf9f8074..38b054cbfd 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js @@ -50,16 +50,15 @@ erpnext.stock.PurchaseReceiptController = erpnext.buying.BuyingController.extend }) }); } - + if(this.frm.doc.docstatus == 1) { + cur_frm.add_custom_button(__('Return'), this.make_purchase_return); if(this.frm.doc.__onload && !this.frm.doc.__onload.billing_complete) { - cur_frm.add_custom_button(__('Make Purchase Invoice'), this.make_purchase_invoice); + cur_frm.add_custom_button(__('Invoice'), this.make_purchase_invoice).addClass("btn-primary"); } - - cur_frm.add_custom_button(__('Make Purchase Return'), this.make_purchase_return); } } - + this.frm.toggle_reqd("supplier_warehouse", this.frm.doc.is_subcontracted==="Yes"); }, @@ -111,7 +110,7 @@ erpnext.stock.PurchaseReceiptController = erpnext.buying.BuyingController.extend frm: cur_frm }) }, - + make_purchase_return: function() { frappe.model.open_mapped_doc({ method: "erpnext.stock.doctype.purchase_receipt.purchase_receipt.make_purchase_return",