From aa5182bb9e3cd3b59b02aa926fb5f538d6944076 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Tue, 4 Aug 2015 23:06:01 +0530 Subject: [PATCH 01/53] [fix] rename old filenames that start with FileData and fix missing Item images --- erpnext/patches.txt | 1 + .../patches/v5_4/fix_missing_item_images.py | 116 ++++++++++++++++++ 2 files changed, 117 insertions(+) create mode 100644 erpnext/patches/v5_4/fix_missing_item_images.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index b2d8068eeaf..e4eefcec21f 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -188,3 +188,4 @@ erpnext.patches.v5_4.set_root_and_report_type erpnext.patches.v5_4.notify_system_managers_regarding_wrong_tax_calculation erpnext.patches.v5_4.fix_invoice_outstanding execute:frappe.db.sql("update `tabStock Ledger Entry` set stock_queue = '[]' where voucher_type = 'Stock Reconciliation' and ifnull(qty_after_transaction, 0) = 0") +erpnext.patches.v5_4.fix_missing_item_images diff --git a/erpnext/patches/v5_4/fix_missing_item_images.py b/erpnext/patches/v5_4/fix_missing_item_images.py new file mode 100644 index 00000000000..d782cd179ec --- /dev/null +++ b/erpnext/patches/v5_4/fix_missing_item_images.py @@ -0,0 +1,116 @@ +from __future__ import unicode_literals +import frappe +import os +from frappe.utils import get_files_path +from frappe.utils.file_manager import get_content_hash + +def execute(): + files_path = get_files_path() + + # get files that don't have attached_to_name but exist + unlinked_files = get_unlinked_files(files_path) + if not unlinked_files: + return + + fixed_files = fix_files_for_item(files_path, unlinked_files) + + # fix remaining files + for key, file_data in unlinked_files.items(): + if key not in fixed_files: + rename_and_set_content_hash(files_path, unlinked_files, key) + frappe.db.commit() + +def fix_files_for_item(files_path, unlinked_files): + fixed_files = [] + + # make a list of files/something and /files/something to check in child table's image column + file_urls = [key for key in unlinked_files.keys()] + ["/" + key for key in unlinked_files.keys()] + file_item_code = get_file_item_code(file_urls) + + for (file_url, item_code), children in file_item_code.items(): + new_file_url = "/files/{0}".format(unlinked_files[file_url]["file_name"]) + + for row in children: + # print file_url, new_file_url, item_code, row.doctype, row.name + + # replace image in these rows with the new file url + frappe.db.set_value(row.doctype, row.name, "image", new_file_url, update_modified=False) + + # set it as attachment of this item code + file_data = frappe.get_doc("File Data", unlinked_files[file_url]["file"]) + file_data.attached_to_doctype = "Item" + file_data.attached_to_name = item_code + file_data.save() + + # set it as image in Item + if not frappe.db.get_value("Item", item_code, "image"): + frappe.db.set_value("Item", item_code, "image", new_file_url, update_modified=False) + + rename_and_set_content_hash(files_path, unlinked_files, file_url) + + fixed_files.append(file_url) + + # commit + frappe.db.commit() + + return fixed_files + +def rename_and_set_content_hash(files_path, unlinked_files, file_url): + # rename this file + old_filename = os.path.join(files_path, unlinked_files[file_url]["file"]) + new_filename = os.path.join(files_path, unlinked_files[file_url]["file_name"]) + + if not os.path.exists(new_filename): + os.rename(old_filename, new_filename) + + # set content hash if missing + file_data_name = unlinked_files[file_url]["file"] + if not frappe.db.get_value("File Data", file_data_name, "content_hash"): + with open(new_filename, "r") as f: + content_hash = get_content_hash(f.read()) + frappe.db.set_value("File Data", file_data_name, "content_hash", content_hash) + +def get_unlinked_files(files_path): + # find files that have the same name as a File Data doc + # and the file_name mentioned in that File Data doc doesn't exist + # and it isn't already attached to a doc + unlinked_files = {} + files = os.listdir(files_path) + for file in files: + if not frappe.db.exists("File Data", {"file_name": file}): + file_data = frappe.db.get_value("File Data", {"name": file}, + ["file_name", "attached_to_doctype", "attached_to_name"], as_dict=True) + + if (file_data + and file_data.file_name + and file_data.file_name not in files + and not file_data.attached_to_doctype + and not file_data.attached_to_name): + + file_data["file"] = file + unlinked_files["files/{0}".format(file)] = file_data + + return unlinked_files + +def get_file_item_code(file_urls): + # get a map of file_url, item_code and list of documents where file_url will need to be changed in image field + file_item_code = {} + + doctypes = frappe.db.sql_list("""select name from `tabDocType` dt + where istable=1 + and exists (select name from `tabDocField` df where df.parent=dt.name and df.fieldname='item_code') + and exists (select name from `tabDocField` df where df.parent=dt.name and df.fieldname='image')""") + + for doctype in doctypes: + result = frappe.db.sql("""select name, image, item_code, '{0}' as doctype from `tab{0}` + where image in ({1})""".format(doctype, ", ".join(["%s"]*len(file_urls))), + file_urls, as_dict=True) + + for r in result: + key = (r.image, r.item_code) + if key not in file_item_code: + file_item_code[key] = [] + + file_item_code[key].append(r) + + return file_item_code From ff04bf63460e4e185d61680bd913ba7a1c07c12c Mon Sep 17 00:00:00 2001 From: bobzz-zone Date: Wed, 5 Aug 2015 11:28:11 +0700 Subject: [PATCH 02/53] Update stock_entry.py fixed for null expense account --- erpnext/stock/doctype/stock_entry/stock_entry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 051d9fcbd88..ea5c3747eb3 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -100,7 +100,7 @@ class StockEntry(StockController): if f in ["stock_uom", "conversion_factor"] or not item.get(f): item.set(f, item_details.get(f)) - if self.difference_account: + if self.difference_account and item.expense_account=="": item.expense_account = self.difference_account if not item.transfer_qty: From 7c5124140e941a32aec5aad6660d7d09d78d4d0d Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 6 Aug 2015 12:57:18 +0530 Subject: [PATCH 03/53] [fix] Party / Account Validation message against Invoice --- erpnext/accounts/doctype/journal_entry/journal_entry.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index f0630ce91c7..80d243580d8 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -192,8 +192,9 @@ class JournalEntry(AccountsController): if against_field in ["against_invoice", "against_voucher"]: if (against_voucher[0] !=d.party or against_voucher[1] != d.account): - frappe.throw(_("Row {0}: Party / Account does not match with \ - Customer / Debit To in {1}").format(d.idx, doctype)) + 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))) From 48cccca9af26a755d4134a5a5537fbb332f712e3 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Thu, 6 Aug 2015 15:37:01 +0530 Subject: [PATCH 04/53] [minor] add close button in opportunity and project links in customer --- erpnext/crm/doctype/opportunity/opportunity.js | 18 ++++++++++++++++-- erpnext/selling/doctype/customer/customer.js | 3 +++ erpnext/selling/doctype/customer/customer.py | 3 ++- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/erpnext/crm/doctype/opportunity/opportunity.js b/erpnext/crm/doctype/opportunity/opportunity.js index 6156dcf567a..edaa15144f6 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.js +++ b/erpnext/crm/doctype/opportunity/opportunity.js @@ -80,9 +80,23 @@ cur_frm.cscript.refresh = function(doc, cdt, cdn) { if(doc.status!=="Quotation") cur_frm.add_custom_button(__('Opportunity Lost'), cur_frm.cscript['Declare Opportunity Lost'], "icon-remove", "btn-default"); - - } + + var frm = cur_frm; + if(frm.perm[0].write && doc.docstatus==0) { + if(frm.doc.status==="Open") { + frm.add_custom_button("Close", function() { + frm.set_value("status", "Closed"); + frm.save(); + }); + } else { + frm.add_custom_button("Reopen", function() { + frm.set_value("status", "Open"); + frm.save(); + }); + } + } + } cur_frm.cscript.onload_post_render = function(doc, cdt, cdn) { diff --git a/erpnext/selling/doctype/customer/customer.js b/erpnext/selling/doctype/customer/customer.js index 1f9a12bdd3c..ac0f6366cd5 100644 --- a/erpnext/selling/doctype/customer/customer.js +++ b/erpnext/selling/doctype/customer/customer.js @@ -46,6 +46,7 @@ cur_frm.cscript.setup_dashboard = function(doc) { cur_frm.dashboard.add_doctype_badge("Sales Order", "customer"); cur_frm.dashboard.add_doctype_badge("Delivery Note", "customer"); cur_frm.dashboard.add_doctype_badge("Sales Invoice", "customer"); + cur_frm.dashboard.add_doctype_badge("Project", "customer"); return frappe.call({ type: "GET", @@ -62,6 +63,8 @@ cur_frm.cscript.setup_dashboard = function(doc) { + ' / ' + __("Unpaid") + ": " + format_currency(r.message.total_unpaid, r.message["company_currency"][0]) + ''); + } else { + cur_frm.dashboard.set_headline(""); } } cur_frm.dashboard.set_badge_count(r.message); diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index f82e2fbeeb7..fe68966f5f9 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -119,7 +119,8 @@ def get_dashboard_info(customer): frappe.msgprint(_("Not permitted"), raise_exception=True) out = {} - for doctype in ["Opportunity", "Quotation", "Sales Order", "Delivery Note", "Sales Invoice"]: + for doctype in ["Opportunity", "Quotation", "Sales Order", "Delivery Note", + "Sales Invoice", "Project"]: out[doctype] = frappe.db.get_value(doctype, {"customer": customer, "docstatus": ["!=", 2] }, "count(*)") From d6bdad7adf2dd23225b5ddd028d0ab03dab1e734 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Thu, 6 Aug 2015 15:45:59 +0530 Subject: [PATCH 05/53] [fix] [hot] total in time log batch --- erpnext/projects/doctype/time_log_batch/time_log_batch.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/projects/doctype/time_log_batch/time_log_batch.py b/erpnext/projects/doctype/time_log_batch/time_log_batch.py index eaccc8c7446..55d593763d7 100644 --- a/erpnext/projects/doctype/time_log_batch/time_log_batch.py +++ b/erpnext/projects/doctype/time_log_batch/time_log_batch.py @@ -15,6 +15,8 @@ class TimeLogBatch(Document): def validate(self): self.set_status() + self.total_hours = 0.0 + self.total_billing_amount = 0.0 for d in self.get("time_logs"): tl = frappe.get_doc("Time Log", d.time_log) self.update_time_log_values(d, tl) From 69095e72855af37b891afcb80be501a03ecc9f46 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Fri, 7 Aug 2015 15:48:53 +0530 Subject: [PATCH 06/53] [fix] website search --- erpnext/templates/pages/product_search.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/templates/pages/product_search.py b/erpnext/templates/pages/product_search.py index 236fe5cbfc6..f4c4534c122 100644 --- a/erpnext/templates/pages/product_search.py +++ b/erpnext/templates/pages/product_search.py @@ -33,7 +33,7 @@ def get_product_list(search=None, start=0, limit=10): for d in data: d.route = ((d.parent_website_route + "/") if d.parent_website_route else "") \ - + d.page_name + + (d.page_name or "") return [get_item_for_list_in_html(r) for r in data] From d1416542a0932c0d63683714d9dbc60f6c0aeece Mon Sep 17 00:00:00 2001 From: Dominik Date: Sat, 8 Aug 2015 00:05:38 +0530 Subject: [PATCH 07/53] Tax ID added and Customer Details description updated Tax ID is a relatively generic descriptor for VAT (Value Added Tax) or any other countrie's tax identification system and should be required by most users of an ERP system. Since this removes the necessity to put the VAT into the Customer Description (not the ideal place to begin with) the description was updated and slimmed down. --- .../selling/doctype/customer/customer.json | 564 ++++++++++++++++-- 1 file changed, 529 insertions(+), 35 deletions(-) diff --git a/erpnext/selling/doctype/customer/customer.json b/erpnext/selling/doctype/customer/customer.json index a3bcc8acb2a..eea3f2ff148 100644 --- a/erpnext/selling/doctype/customer/customer.json +++ b/erpnext/selling/doctype/customer/customer.json @@ -1,35 +1,62 @@ { + "allow_copy": 0, "allow_import": 1, "allow_rename": 1, "autoname": "naming_series:", "creation": "2013-06-11 14:26:44", + "custom": 0, "description": "Buyer of Goods and Services.", "docstatus": 0, "doctype": "DocType", "document_type": "Master", "fields": [ { + "allow_on_submit": 0, "fieldname": "basic_info", "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, "label": "", + "no_copy": 0, "oldfieldtype": "Section Break", "options": "icon-user", "permlevel": 0, - "reqd": 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": "naming_series", "fieldtype": "Select", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, "label": "Series", "no_copy": 1, "options": "CUST-", "permlevel": 0, - "print_hide": 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": "customer_name", "fieldtype": "Data", "hidden": 0, + "ignore_user_permissions": 0, "in_filter": 1, "in_list_view": 0, "label": "Full Name", @@ -38,25 +65,43 @@ "oldfieldtype": "Data", "permlevel": 0, "print_hide": 0, + "read_only": 0, "report_hide": 0, "reqd": 1, - "search_index": 1 + "search_index": 1, + "set_only_once": 0, + "unique": 0 }, { + "allow_on_submit": 0, "fieldname": "customer_type", "fieldtype": "Select", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, "label": "Type", + "no_copy": 0, "oldfieldname": "customer_type", "oldfieldtype": "Select", "options": "\nCompany\nIndividual", "permlevel": 0, - "reqd": 1 + "print_hide": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 }, { + "allow_on_submit": 0, "fieldname": "lead_name", "fieldtype": "Link", "hidden": 0, + "ignore_user_permissions": 0, "in_filter": 1, + "in_list_view": 0, "label": "From Lead", "no_copy": 1, "oldfieldname": "lead_name", @@ -64,228 +109,580 @@ "options": "Lead", "permlevel": 0, "print_hide": 1, - "report_hide": 1 + "read_only": 0, + "report_hide": 1, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 }, { + "allow_on_submit": 0, "fieldname": "column_break0", "fieldtype": "Column Break", + "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, "width": "50%" }, { + "allow_on_submit": 0, "description": "", "fieldname": "customer_group", "fieldtype": "Link", "hidden": 0, + "ignore_user_permissions": 0, "in_filter": 1, "in_list_view": 1, "label": "Customer Group", + "no_copy": 0, "oldfieldname": "customer_group", "oldfieldtype": "Link", "options": "Customer Group", "permlevel": 0, "print_hide": 0, + "read_only": 0, + "report_hide": 0, "reqd": 1, - "search_index": 1 + "search_index": 1, + "set_only_once": 0, + "unique": 0 }, { + "allow_on_submit": 0, "description": "", "fieldname": "territory", "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, "in_list_view": 1, "label": "Territory", + "no_copy": 0, "oldfieldname": "territory", "oldfieldtype": "Link", "options": "Territory", "permlevel": 0, "print_hide": 1, - "reqd": 1 + "read_only": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 }, { + "allow_on_submit": 0, + "fieldname": "tax_id", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Tax ID", + "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_frozen", "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, "label": "Is Frozen", + "no_copy": 0, "permlevel": 0, - "precision": "" + "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, "depends_on": "eval:!doc.__islocal", "fieldname": "address_contacts", "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, "label": "", + "no_copy": 0, "options": "icon-map-marker", - "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": "address_html", "fieldtype": "HTML", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, "label": "Address HTML", + "no_copy": 0, "permlevel": 0, - "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": "column_break1", "fieldtype": "Column Break", + "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, "width": "50%" }, { + "allow_on_submit": 0, "fieldname": "contact_html", "fieldtype": "HTML", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, "label": "Contact HTML", + "no_copy": 0, "oldfieldtype": "HTML", "permlevel": 0, - "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": "default_receivable_accounts", "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, "label": "Default Receivable Accounts", - "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, "depends_on": "eval:!doc.__islocal", "description": "Mention if non-standard receivable account applicable", "fieldname": "accounts", "fieldtype": "Table", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, "label": "Accounts", + "no_copy": 0, "options": "Party Account", - "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": "more_info", "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, "label": "", + "no_copy": 0, "oldfieldtype": "Section Break", "options": "icon-file-text", - "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": "column_break2", "fieldtype": "Column Break", + "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, "width": "50%" }, { - "description": "Your Customer's TAX registration numbers (if applicable) or any general information", + "allow_on_submit": 0, + "description": "Additional information regarding the customer.", "fieldname": "customer_details", "fieldtype": "Text", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, "label": "Customer Details", + "no_copy": 0, "oldfieldname": "customer_details", "oldfieldtype": "Code", - "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": "column_break3", "fieldtype": "Column Break", + "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, "width": "50%" }, { + "allow_on_submit": 0, "fieldname": "default_currency", "fieldtype": "Link", + "hidden": 0, "ignore_user_permissions": 1, + "in_filter": 0, + "in_list_view": 0, "label": "Currency", "no_copy": 1, "options": "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": "default_price_list", "fieldtype": "Link", + "hidden": 0, "ignore_user_permissions": 1, + "in_filter": 0, + "in_list_view": 0, "label": "Price List", + "no_copy": 0, "options": "Price List", - "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": "default_taxes_and_charges", "fieldtype": "Link", + "hidden": 0, "ignore_user_permissions": 1, + "in_filter": 0, + "in_list_view": 0, "label": "Taxes and Charges", + "no_copy": 0, "options": "Sales Taxes and Charges Template", - "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": "credit_days_based_on", "fieldtype": "Select", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, "label": "Credit Days Based On", + "no_copy": 0, "options": "\nFixed Days\nLast Day of the Next Month", "permlevel": 0, - "precision": "" + "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, "depends_on": "eval:doc.credit_days_based_on=='Fixed Days'", "fieldname": "credit_days", "fieldtype": "Int", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, "label": "Credit Days", + "no_copy": 0, "oldfieldname": "credit_days", "oldfieldtype": "Int", - "permlevel": 1 + "permlevel": 1, + "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_limit", "fieldtype": "Currency", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, "label": "Credit Limit", + "no_copy": 0, "oldfieldname": "credit_limit", "oldfieldtype": "Currency", "options": "Company:company:default_currency", - "permlevel": 1 + "permlevel": 1, + "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": "website", "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, "label": "Website", - "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": "sales_team_section_break", "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, "label": "", + "no_copy": 0, "oldfieldtype": "Section Break", "options": "icon-group", - "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": "default_sales_partner", "fieldtype": "Link", + "hidden": 0, "ignore_user_permissions": 1, + "in_filter": 0, + "in_list_view": 0, "label": "Sales Partner", + "no_copy": 0, "oldfieldname": "default_sales_partner", "oldfieldtype": "Link", "options": "Sales Partner", - "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": "default_commission_rate", "fieldtype": "Float", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, "label": "Commission Rate", + "no_copy": 0, "oldfieldname": "default_commission_rate", "oldfieldtype": "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": "sales_team", "fieldtype": "Table", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, "label": "Sales Team Details", + "no_copy": 0, "oldfieldname": "sales_team", "oldfieldtype": "Table", "options": "Sales Team", - "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": "communications", "fieldtype": "Table", "hidden": 1, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, "label": "Communications", + "no_copy": 0, "options": "Communication", "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, "icon": "icon-user", "idx": 1, - "modified": "2015-07-17 09:38:50.086978", + "in_create": 0, + "in_dialog": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "modified": "2015-08-07 20:34:25.761769", "modified_by": "Administrator", "module": "Selling", "name": "Customer", @@ -294,38 +691,73 @@ { "amend": 0, "apply_user_permissions": 1, + "cancel": 0, "create": 1, "delete": 0, "email": 1, + "export": 0, + "if_owner": 0, + "import": 0, "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "Sales User", + "set_user_permissions": 0, "share": 1, "submit": 0, "write": 1 }, { + "amend": 0, + "apply_user_permissions": 0, "cancel": 0, + "create": 0, "delete": 0, + "email": 0, + "export": 0, + "if_owner": 0, + "import": 0, "permlevel": 1, + "print": 0, "read": 1, - "role": "Sales User" + "report": 0, + "role": "Sales User", + "set_user_permissions": 0, + "share": 0, + "submit": 0, + "write": 0 }, { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 0, + "delete": 0, "email": 1, + "export": 0, + "if_owner": 0, + "import": 0, "permlevel": 0, "print": 1, "read": 1, "report": 1, - "role": "Sales Manager" + "role": "Sales Manager", + "set_user_permissions": 0, + "share": 0, + "submit": 0, + "write": 0 }, { "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, "create": 1, "delete": 1, "email": 1, + "export": 0, + "if_owner": 0, + "import": 0, "permlevel": 0, "print": 1, "read": 1, @@ -337,46 +769,108 @@ "write": 1 }, { + "amend": 0, + "apply_user_permissions": 0, "cancel": 0, + "create": 0, "delete": 0, + "email": 0, + "export": 0, + "if_owner": 0, + "import": 0, "permlevel": 1, + "print": 0, "read": 1, + "report": 0, "role": "Sales Master Manager", + "set_user_permissions": 0, + "share": 0, + "submit": 0, "write": 1 }, { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 0, + "delete": 0, "email": 1, + "export": 0, + "if_owner": 0, + "import": 0, "permlevel": 0, "print": 1, "read": 1, "report": 1, - "role": "Stock User" + "role": "Stock User", + "set_user_permissions": 0, + "share": 0, + "submit": 0, + "write": 0 }, { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 0, + "delete": 0, "email": 1, + "export": 0, + "if_owner": 0, + "import": 0, "permlevel": 0, "print": 1, "read": 1, "report": 1, - "role": "Stock Manager" + "role": "Stock Manager", + "set_user_permissions": 0, + "share": 0, + "submit": 0, + "write": 0 }, { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 0, + "delete": 0, "email": 1, + "export": 0, + "if_owner": 0, + "import": 0, "permlevel": 0, "print": 1, "read": 1, "report": 1, - "role": "Accounts User" + "role": "Accounts User", + "set_user_permissions": 0, + "share": 0, + "submit": 0, + "write": 0 }, { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 0, + "delete": 0, "email": 1, + "export": 0, + "if_owner": 0, + "import": 0, "permlevel": 0, "print": 1, "read": 1, "report": 1, - "role": "Accounts Manager" + "role": "Accounts Manager", + "set_user_permissions": 0, + "share": 0, + "submit": 0, + "write": 0 } ], + "read_only": 0, + "read_only_onload": 0, "search_fields": "customer_name,customer_group,territory", "title_field": "customer_name" } \ No newline at end of file From 56f5156f3c3c39755640d76f68d6aec58aaf5c3a Mon Sep 17 00:00:00 2001 From: thecarly Date: Mon, 10 Aug 2015 12:50:44 +0530 Subject: [PATCH 08/53] [minor] fixed typo --- erpnext/setup/doctype/company/company.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py index 5660d892cc1..cf47de5f6f6 100644 --- a/erpnext/setup/doctype/company/company.py +++ b/erpnext/setup/doctype/company/company.py @@ -32,7 +32,7 @@ class Company(Document): frappe.throw(_("Abbreviation cannot have more than 5 characters")) if not self.abbr.strip(): - frappe.throw(_("Abbr can not be blank or space")) + frappe.throw(_("Abbreviation is mandatory")) self.previous_default_currency = frappe.db.get_value("Company", self.name, "default_currency") if self.default_currency and self.previous_default_currency and \ From ea925d26a867c492d6066a56d899f27b77918ef5 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 10 Aug 2015 12:56:58 +0530 Subject: [PATCH 09/53] [fix] Made expense account non-mandatory in POS Profile --- erpnext/accounts/doctype/pos_profile/pos_profile.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.py b/erpnext/accounts/doctype/pos_profile/pos_profile.py index eeb2bcfc309..98a85097921 100644 --- a/erpnext/accounts/doctype/pos_profile/pos_profile.py +++ b/erpnext/accounts/doctype/pos_profile/pos_profile.py @@ -11,7 +11,6 @@ from frappe.model.document import Document class POSProfile(Document): def validate(self): self.check_for_duplicate() - self.validate_expense_account() self.validate_all_link_fields() def check_for_duplicate(self): @@ -26,11 +25,6 @@ class POSProfile(Document): msgprint(_("Global POS Profile {0} already created for company {1}").format(res[0][0], self.company), raise_exception=1) - def validate_expense_account(self): - if cint(frappe.defaults.get_global_default("auto_accounting_for_stock")) \ - and not self.expense_account: - msgprint(_("Expense Account is mandatory"), raise_exception=1) - def validate_all_link_fields(self): accounts = {"Account": [self.cash_bank_account, self.income_account, self.expense_account], "Cost Center": [self.cost_center], From fc155c7712974434af2a60e035f7b8a97691866b Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 24 Jul 2015 17:50:40 +0530 Subject: [PATCH 10/53] Added tax table in stock entry --- erpnext/accounts/general_ledger.py | 3 +- .../stock/doctype/stock_entry/stock_entry.py | 29 ++++++++++++++++++- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index 17fe922a1c2..8081459f2e3 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -11,8 +11,7 @@ from erpnext.accounts.utils import validate_expense_against_budget class StockAccountInvalidTransaction(frappe.ValidationError): pass -def make_gl_entries(gl_map, cancel=False, adv_adj=False, merge_entries=True, - update_outstanding='Yes'): +def make_gl_entries(gl_map, cancel=False, adv_adj=False, merge_entries=True, update_outstanding='Yes'): if gl_map: if not cancel: gl_map = process_gl_map(gl_map, merge_entries) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 051d9fcbd88..61ef4cdefc9 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -367,6 +367,8 @@ class StockEntry(StockController): def update_stock_ledger(self): sl_entries = [] for d in self.get('items'): + tax_amount_per_qty = flt(flt(d.tax_amount) / flt(d.qty), d.precision("tax_amount")) + if cstr(d.s_warehouse) and self.docstatus == 1: sl_entries.append(self.get_sl_entries(d, { "warehouse": cstr(d.s_warehouse), @@ -378,7 +380,7 @@ class StockEntry(StockController): sl_entries.append(self.get_sl_entries(d, { "warehouse": cstr(d.t_warehouse), "actual_qty": flt(d.transfer_qty), - "incoming_rate": flt(d.incoming_rate) + "incoming_rate": flt(d.incoming_rate) + tax_amount_per_qty })) # On cancellation, make stock ledger entry for @@ -392,6 +394,31 @@ class StockEntry(StockController): })) self.make_sl_entries(sl_entries, self.amended_from and 'Yes' or 'No') + + def get_gl_entries(self, warehouse_account): + expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation") + + gl_entries = super(StockEntry, self).get_gl_entries(warehouse_account) + + for d in self.get("items"): + tax_amount = flt(d.tax_amount, d.precision("tax_amount")) + gl_entries.append(self.get_gl_dict({ + "account": d.expense_account, + "against": expenses_included_in_valuation, + "cost_center": d.cost_center, + "remarks": self.get("remarks") or _("Accounting Entry for Stock"), + "debit": tax_amount + })) + + gl_entries.append(self.get_gl_dict({ + "account": expenses_included_in_valuation, + "against": warehouse_account[d.warehouse], + "cost_center": d.cost_center, + "remarks": self.get("remarks") or _("Accounting Entry for Stock"), + "credit": tax_amount + })) + + return gl_entries def update_production_order(self): def _validate_production_order(pro_doc): From aa879311725303ac18b95bba78211b7326e78472 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 27 Jul 2015 11:40:54 +0530 Subject: [PATCH 11/53] Show only active BOM in stock entry --- erpnext/stock/doctype/stock_entry/stock_entry.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index 04ef935732c..22652e6820a 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -10,7 +10,10 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({ this.frm.fields_dict.bom_no.get_query = function() { return { - filters:{ 'docstatus': 1 } + filters:{ + "docstatus": 1, + "is_active": 1 + } }; }; From f6aad5ed2d0a492f968269a0948ddb782e400c8e Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 27 Jul 2015 16:00:39 +0530 Subject: [PATCH 12/53] Distribute tax amount between items in stock entry --- .../doctype/stock_entry/stock_entry.json | 29 ++++++++++- .../stock/doctype/stock_entry/stock_entry.py | 48 +++++++++++-------- .../stock_entry_detail.json | 9 +++- 3 files changed, 64 insertions(+), 22 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.json b/erpnext/stock/doctype/stock_entry/stock_entry.json index 3c39d42ff6a..f2ea6d4dc27 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.json +++ b/erpnext/stock/doctype/stock_entry/stock_entry.json @@ -369,6 +369,7 @@ "fieldname": "total_incoming_value", "fieldtype": "Currency", "label": "Total Incoming Value", + "options": "Company:company:default_currency", "permlevel": 0, "precision": "", "read_only": 1 @@ -383,6 +384,7 @@ "fieldname": "total_outgoing_value", "fieldtype": "Currency", "label": "Total Outgoing Value", + "options": "Company:company:default_currency", "permlevel": 0, "precision": "", "read_only": 1 @@ -391,6 +393,7 @@ "fieldname": "value_difference", "fieldtype": "Currency", "label": "Total Value Difference (Out - In)", + "options": "Company:company:default_currency", "permlevel": 0, "precision": "", "read_only": 1 @@ -404,6 +407,30 @@ "permlevel": 0, "precision": "" }, + { + "fieldname": "taxes_section", + "fieldtype": "Section Break", + "label": "Taxes and Charges", + "permlevel": 0, + "precision": "" + }, + { + "fieldname": "taxes", + "fieldtype": "Table", + "label": "Taxes and Charges", + "options": "Landed Cost Taxes and Charges", + "permlevel": 0, + "precision": "" + }, + { + "fieldname": "total_taxes_and_charges", + "fieldtype": "Currency", + "label": "Total Taxes and Charges", + "options": "Company:company:default_currency", + "permlevel": 0, + "precision": "", + "read_only": 1 + }, { "fieldname": "fold", "fieldtype": "Fold", @@ -678,7 +705,7 @@ "is_submittable": 1, "issingle": 0, "max_attachments": 0, - "modified": "2015-07-22 18:47:20.328749", + "modified": "2015-07-24 18:47:55.902154", "modified_by": "Administrator", "module": "Stock", "name": "Stock Entry", diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 61ef4cdefc9..06249edbc1f 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -50,6 +50,7 @@ class StockEntry(StockController): self.validate_bom() self.validate_finished_goods() self.validate_with_material_request() + self.distribute_taxes() self.validate_valuation_rate() self.set_total_incoming_outgoing_value() self.set_total_amount() @@ -221,7 +222,7 @@ class StockEntry(StockController): if d.s_warehouse and not d.t_warehouse: valuation_at_source += flt(d.amount) if d.t_warehouse and not d.s_warehouse: - valuation_at_target += flt(d.amount) + valuation_at_target += flt(d.amount) + flt(d.tax_amount) if valuation_at_target + 0.001 < valuation_at_source: frappe.throw(_("Total valuation ({0}) for manufactured or repacked item(s) can not be less than total valuation of raw materials ({1})").format(valuation_at_target, @@ -230,10 +231,10 @@ class StockEntry(StockController): def set_total_incoming_outgoing_value(self): self.total_incoming_value = self.total_outgoing_value = 0.0 for d in self.get("items"): - if d.s_warehouse: - self.total_incoming_value += flt(d.amount) if d.t_warehouse: - self.total_outgoing_value += flt(d.amount) + self.total_incoming_value += flt(d.amount) + if d.s_warehouse: + self.total_outgoing_value += flt(d.amount) + flt(d.tax_amount) self.value_difference = self.total_outgoing_value - self.total_incoming_value @@ -316,7 +317,7 @@ class StockEntry(StockController): bom = frappe.db.get_value("BOM", bom_no, ["operating_cost", "quantity"], as_dict=1) operation_cost_per_unit = flt(bom.operating_cost) / flt(bom.quantity) - return operation_cost_per_unit + (flt(self.additional_operating_cost) / flt(qty)) + return operation_cost_per_unit def validate_purchase_order(self): """Throw exception if more raw material is transferred against Purchase Order than in @@ -402,22 +403,23 @@ class StockEntry(StockController): for d in self.get("items"): tax_amount = flt(d.tax_amount, d.precision("tax_amount")) - gl_entries.append(self.get_gl_dict({ - "account": d.expense_account, - "against": expenses_included_in_valuation, - "cost_center": d.cost_center, - "remarks": self.get("remarks") or _("Accounting Entry for Stock"), - "debit": tax_amount - })) - - gl_entries.append(self.get_gl_dict({ - "account": expenses_included_in_valuation, - "against": warehouse_account[d.warehouse], - "cost_center": d.cost_center, - "remarks": self.get("remarks") or _("Accounting Entry for Stock"), - "credit": tax_amount - })) + if tax_amount: + gl_entries.append(self.get_gl_dict({ + "account": expenses_included_in_valuation, + "against": d.expense_account, + "cost_center": d.cost_center, + "remarks": self.get("remarks") or _("Accounting Entry for Stock"), + "credit": tax_amount + })) + gl_entries.append(self.get_gl_dict({ + "account": d.expense_account, + "against": expenses_included_in_valuation, + "cost_center": d.cost_center, + "remarks": self.get("remarks") or _("Accounting Entry for Stock"), + "credit": -1 * tax_amount + })) + return gl_entries def update_production_order(self): @@ -693,6 +695,12 @@ class StockEntry(StockController): if expiry_date: if getdate(self.posting_date) > getdate(expiry_date): frappe.throw(_("Batch {0} of Item {1} has expired.").format(item.batch_no, item.item_code)) + + def distribute_taxes(self): + self.total_taxes_and_charges = sum([flt(t.amount) for t in self.get("taxes")]) + for d in self.get("items"): + if d.t_warehouse and self.total_incoming_value: + d.tax_amount = (flt(d.amount) / flt(self.total_incoming_value)) * self.total_taxes_and_charges @frappe.whitelist() diff --git a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json index a373185c238..dcd46bd51fe 100644 --- a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json +++ b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json @@ -160,6 +160,13 @@ "permlevel": 0, "read_only": 1 }, + { + "fieldname": "tax_amount", + "fieldtype": "Currency", + "label": "Tax Amount", + "permlevel": 0, + "precision": "" + }, { "fieldname": "col_break3", "fieldtype": "Column Break", @@ -344,7 +351,7 @@ ], "idx": 1, "istable": 1, - "modified": "2015-07-02 05:32:56.511570", + "modified": "2015-07-24 17:03:44.214018", "modified_by": "Administrator", "module": "Stock", "name": "Stock Entry Detail", From 246e47e76e76b5b68a859c4a3bc7f8a42f8d8fc5 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 7 Aug 2015 17:12:39 +0530 Subject: [PATCH 13/53] [fix] Dashboard in customer / supplier --- erpnext/buying/doctype/supplier/supplier.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/buying/doctype/supplier/supplier.js b/erpnext/buying/doctype/supplier/supplier.js index 19050a419f3..b6b6ce58120 100644 --- a/erpnext/buying/doctype/supplier/supplier.js +++ b/erpnext/buying/doctype/supplier/supplier.js @@ -46,6 +46,8 @@ cur_frm.cscript.make_dashboard = function(doc) { + ' / ' + __("Total Unpaid") + ": " + format_currency(r.message.total_unpaid, r.message.company_currency[0]) + ''); + } else { + cur_frm.dashboard.set_headline(""); } } cur_frm.dashboard.set_badge_count(r.message); From 3c3a3ecea8eaac1678e6a0becd0d90e0e1f6a41d Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 7 Aug 2015 17:17:03 +0530 Subject: [PATCH 14/53] Additional Costs in Stock Entry --- .../sales_invoice/test_sales_invoice.py | 2 +- .../production_order/production_order.py | 9 +- .../production_order/test_production_order.py | 8 +- erpnext/patches.txt | 1 + .../v5_4/stock_entry_additional_costs.py | 42 ++ .../delivery_note/test_delivery_note.py | 12 +- erpnext/stock/doctype/item/test_item.py | 2 +- .../material_request/material_request.py | 2 +- .../material_request/test_material_request.py | 16 +- .../purchase_receipt/test_purchase_receipt.py | 4 +- .../stock/doctype/stock_entry/stock_entry.js | 62 +- .../doctype/stock_entry/stock_entry.json | 639 ++++++++++++++++-- .../stock/doctype/stock_entry/stock_entry.py | 269 ++++---- .../doctype/stock_entry/test_records.json | 10 +- .../doctype/stock_entry/test_stock_entry.py | 63 +- .../stock_entry_detail.json | 583 ++++++++++++++-- .../test_stock_reconciliation.py | 4 +- .../templates/form_grid/stock_entry_grid.html | 4 +- erpnext/utilities/repost_stock.py | 2 +- 19 files changed, 1404 insertions(+), 330 deletions(-) create mode 100644 erpnext/patches/v5_4/stock_entry_additional_costs.py diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 6c6737913ec..67f4a8e716b 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -746,7 +746,7 @@ class TestSalesInvoice(unittest.TestCase): def test_return_sales_invoice(self): set_perpetual_inventory() - make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=50, incoming_rate=100) + make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=50, basic_rate=100) actual_qty_0 = get_qty_after_transaction() diff --git a/erpnext/manufacturing/doctype/production_order/production_order.py b/erpnext/manufacturing/doctype/production_order/production_order.py index b79f8b624a1..023c43eac49 100644 --- a/erpnext/manufacturing/doctype/production_order/production_order.py +++ b/erpnext/manufacturing/doctype/production_order/production_order.py @@ -10,6 +10,9 @@ from frappe.model.document import Document from erpnext.manufacturing.doctype.bom.bom import validate_bom_no from dateutil.relativedelta import relativedelta from erpnext.stock.doctype.item.item import validate_end_of_life +from erpnext.manufacturing.doctype.workstation.workstation import WorkstationHolidayError, NotInWorkingHoursError +from erpnext.projects.doctype.time_log.time_log import OverlapError +from erpnext.stock.doctype.stock_entry.stock_entry import get_additional_costs class OverProductionError(frappe.ValidationError): pass class StockOverProductionError(frappe.ValidationError): pass @@ -17,9 +20,6 @@ class OperationTooLongError(frappe.ValidationError): pass class ProductionNotApplicableError(frappe.ValidationError): pass class ItemHasVariantError(frappe.ValidationError): pass -from erpnext.manufacturing.doctype.workstation.workstation import WorkstationHolidayError, NotInWorkingHoursError -from erpnext.projects.doctype.time_log.time_log import OverlapError - form_grid_templates = { "operations": "templates/form_grid/production_order_grid.html" } @@ -356,7 +356,6 @@ def make_stock_entry(production_order_id, purpose, qty=None): stock_entry.company = production_order.company stock_entry.from_bom = 1 stock_entry.bom_no = production_order.bom_no - stock_entry.additional_operating_cost = production_order.additional_operating_cost stock_entry.use_multi_level_bom = production_order.use_multi_level_bom stock_entry.fg_completed_qty = qty or (flt(production_order.qty) - flt(production_order.produced_qty)) @@ -365,6 +364,8 @@ def make_stock_entry(production_order_id, purpose, qty=None): else: stock_entry.from_warehouse = production_order.wip_warehouse stock_entry.to_warehouse = production_order.fg_warehouse + additional_costs = get_additional_costs(production_order, fg_qty=stock_entry.fg_completed_qty) + stock_entry.set("additional_costs", additional_costs) stock_entry.get_items() return stock_entry.as_dict() diff --git a/erpnext/manufacturing/doctype/production_order/test_production_order.py b/erpnext/manufacturing/doctype/production_order/test_production_order.py index 4d653375cbd..eb26d29896e 100644 --- a/erpnext/manufacturing/doctype/production_order/test_production_order.py +++ b/erpnext/manufacturing/doctype/production_order/test_production_order.py @@ -28,9 +28,9 @@ class TestProductionOrder(unittest.TestCase): # add raw materials to stores test_stock_entry.make_stock_entry(item_code="_Test Item", - target="Stores - _TC", qty=100, incoming_rate=100) + target="Stores - _TC", qty=100, basic_rate=100) test_stock_entry.make_stock_entry(item_code="_Test Item Home Desktop 100", - target="Stores - _TC", qty=100, incoming_rate=100) + target="Stores - _TC", qty=100, basic_rate=100) # from stores to wip s = frappe.get_doc(make_stock_entry(pro_order.name, "Material Transfer for Manufacture", 4)) @@ -58,9 +58,9 @@ class TestProductionOrder(unittest.TestCase): pro_doc = self.check_planned_qty() test_stock_entry.make_stock_entry(item_code="_Test Item", - target="_Test Warehouse - _TC", qty=100, incoming_rate=100) + target="_Test Warehouse - _TC", qty=100, basic_rate=100) test_stock_entry.make_stock_entry(item_code="_Test Item Home Desktop 100", - target="_Test Warehouse - _TC", qty=100, incoming_rate=100) + target="_Test Warehouse - _TC", qty=100, basic_rate=100) s = frappe.get_doc(make_stock_entry(pro_doc.name, "Manufacture", 7)) s.insert() diff --git a/erpnext/patches.txt b/erpnext/patches.txt index e4eefcec21f..d12154b32b0 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -189,3 +189,4 @@ erpnext.patches.v5_4.notify_system_managers_regarding_wrong_tax_calculation erpnext.patches.v5_4.fix_invoice_outstanding execute:frappe.db.sql("update `tabStock Ledger Entry` set stock_queue = '[]' where voucher_type = 'Stock Reconciliation' and ifnull(qty_after_transaction, 0) = 0") erpnext.patches.v5_4.fix_missing_item_images +erpnext.patches.v5_4.stock_entry_additional_costs diff --git a/erpnext/patches/v5_4/stock_entry_additional_costs.py b/erpnext/patches/v5_4/stock_entry_additional_costs.py new file mode 100644 index 00000000000..e064690c18b --- /dev/null +++ b/erpnext/patches/v5_4/stock_entry_additional_costs.py @@ -0,0 +1,42 @@ +# Copyright (c) 2015, 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.utils import flt + +def execute(): + frappe.reload_doctype("Stock Entry") + frappe.reload_doctype("Stock Entry Detail") + frappe.reload_doctype("Landed Cost Taxes and Charges") + + frappe.db.sql("""update `tabStock Entry Detail` sed, `tabStock Entry` se + set sed.valuation_rate=sed.incoming_rate, sed.basic_rate=sed.incoming_rate, sed.basic_amount=sed.amount + where sed.parent = se.name + and (se.purpose not in ('Manufacture', 'Repack') or ifnull(additional_operating_cost, 0)=0) + """) + + stock_entries = frappe.db.sql_list("""select name from `tabStock Entry` + where purpose in ('Manufacture', 'Repack') and ifnull(additional_operating_cost, 0)!=0""") + + for d in stock_entries: + stock_entry = frappe.get_doc("Stock Entry", d) + stock_entry.append("additional_costs", { + "description": "Additional Operating Cost", + "amount": stock_entry.additional_operating_cost + }) + + number_of_fg_items = len([t.t_warehouse for t in stock_entry.get("items") if t.t_warehouse]) + + for d in stock_entry.get("items"): + d.valuation_rate = d.incoming_rate + + if d.bom_no or (d.t_warehouse and number_of_fg_items == 1): + d.additional_cost = stock_entry.additional_operating_cost + + d.basic_rate = flt(d.valuation_rate) - flt(d.additional_cost) + d.basic_amount = flt(flt(d.basic_rate) *flt(d.transfer_qty), d.precision("basic_amount")) + + stock_entry.flags.ignore_validate = True + stock_entry.flags.ignore_validate_update_after_submit = True + stock_entry.save() \ No newline at end of file diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py index eb80014e599..e41aab7324a 100644 --- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py @@ -37,7 +37,7 @@ class TestDeliveryNote(unittest.TestCase): set_perpetual_inventory(0) self.assertEqual(cint(frappe.defaults.get_global_default("auto_accounting_for_stock")), 0) - make_stock_entry(target="_Test Warehouse - _TC", qty=5, incoming_rate=100) + make_stock_entry(target="_Test Warehouse - _TC", qty=5, basic_rate=100) stock_queue = json.loads(get_previous_sle({ "item_code": "_Test Item", @@ -59,7 +59,7 @@ class TestDeliveryNote(unittest.TestCase): self.assertEqual(cint(frappe.defaults.get_global_default("auto_accounting_for_stock")), 1) frappe.db.set_value("Item", "_Test Item", "valuation_method", "FIFO") - make_stock_entry(target="_Test Warehouse - _TC", qty=5, incoming_rate=100) + make_stock_entry(target="_Test Warehouse - _TC", qty=5, basic_rate=100) stock_in_hand_account = frappe.db.get_value("Account", {"warehouse": "_Test Warehouse - _TC"}) prev_bal = get_balance_on(stock_in_hand_account) @@ -85,7 +85,7 @@ class TestDeliveryNote(unittest.TestCase): # back dated incoming entry make_stock_entry(posting_date=add_days(nowdate(), -2), target="_Test Warehouse - _TC", - qty=5, incoming_rate=100) + qty=5, basic_rate=100) gl_entries = get_gl_entries("Delivery Note", dn.name) self.assertTrue(gl_entries) @@ -107,9 +107,9 @@ class TestDeliveryNote(unittest.TestCase): def test_delivery_note_gl_entry_packing_item(self): set_perpetual_inventory() - make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=10, incoming_rate=100) + make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=10, basic_rate=100) make_stock_entry(item_code="_Test Item Home Desktop 100", - target="_Test Warehouse - _TC", qty=10, incoming_rate=100) + target="_Test Warehouse - _TC", qty=10, basic_rate=100) stock_in_hand_account = frappe.db.get_value("Account", {"warehouse": "_Test Warehouse - _TC"}) prev_bal = get_balance_on(stock_in_hand_account) @@ -184,7 +184,7 @@ class TestDeliveryNote(unittest.TestCase): def test_sales_return_for_non_bundled_items(self): set_perpetual_inventory() - make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=50, incoming_rate=100) + make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=50, basic_rate=100) actual_qty_0 = get_qty_after_transaction() diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py index 510c0d1b483..9235becba1b 100644 --- a/erpnext/stock/doctype/item/test_item.py +++ b/erpnext/stock/doctype/item/test_item.py @@ -47,7 +47,7 @@ class TestItem(unittest.TestCase): def test_template_cannot_have_stock(self): item = self.get_item(10) - make_stock_entry(item_code=item.name, target="Stores - _TC", qty=1, incoming_rate=1) + make_stock_entry(item_code=item.name, target="Stores - _TC", qty=1, basic_rate=1) item.has_variants = 1 self.assertRaises(ItemTemplateCannotHaveStock, item.save) diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py index 04330368c6d..fd6f943a954 100644 --- a/erpnext/stock/doctype/material_request/material_request.py +++ b/erpnext/stock/doctype/material_request/material_request.py @@ -305,7 +305,7 @@ def make_stock_entry(source_name, target_doc=None): def set_missing_values(source, target): target.purpose = source.material_request_type - target.run_method("get_stock_and_rate") + target.run_method("calculate_rate_and_amount") doclist = get_mapped_doc("Material Request", source_name, { "Material Request": { diff --git a/erpnext/stock/doctype/material_request/test_material_request.py b/erpnext/stock/doctype/material_request/test_material_request.py index dcca3ce2901..99815ddc262 100644 --- a/erpnext/stock/doctype/material_request/test_material_request.py +++ b/erpnext/stock/doctype/material_request/test_material_request.py @@ -72,7 +72,7 @@ class TestMaterialRequest(unittest.TestCase): "doctype": "Stock Entry Detail", "item_code": "_Test Item Home Desktop 100", "parentfield": "items", - "incoming_rate": 100, + "basic_rate": 100, "qty": qty1, "stock_uom": "_Test UOM 1", "transfer_qty": qty1, @@ -84,7 +84,7 @@ class TestMaterialRequest(unittest.TestCase): "doctype": "Stock Entry Detail", "item_code": "_Test Item Home Desktop 200", "parentfield": "items", - "incoming_rate": 100, + "basic_rate": 100, "qty": qty2, "stock_uom": "_Test UOM 1", "transfer_qty": qty2, @@ -196,13 +196,13 @@ class TestMaterialRequest(unittest.TestCase): "qty": 27.0, "transfer_qty": 27.0, "s_warehouse": "_Test Warehouse 1 - _TC", - "incoming_rate": 1.0 + "basic_rate": 1.0 }) se_doc.get("items")[1].update({ "qty": 1.5, "transfer_qty": 1.5, "s_warehouse": "_Test Warehouse 1 - _TC", - "incoming_rate": 1.0 + "basic_rate": 1.0 }) # make available the qty in _Test Warehouse 1 before transfer @@ -279,13 +279,13 @@ class TestMaterialRequest(unittest.TestCase): "qty": 60.0, "transfer_qty": 60.0, "s_warehouse": "_Test Warehouse 1 - _TC", - "incoming_rate": 1.0 + "basic_rate": 1.0 }) se_doc.get("items")[1].update({ "qty": 3.0, "transfer_qty": 3.0, "s_warehouse": "_Test Warehouse 1 - _TC", - "incoming_rate": 1.0 + "basic_rate": 1.0 }) # make available the qty in _Test Warehouse 1 before transfer @@ -350,13 +350,13 @@ class TestMaterialRequest(unittest.TestCase): "transfer_qty": 60.0, "s_warehouse": "_Test Warehouse - _TC", "t_warehouse": "_Test Warehouse 1 - _TC", - "incoming_rate": 1.0 + "basic_rate": 1.0 }) se_doc.get("items")[1].update({ "qty": 3.0, "transfer_qty": 3.0, "s_warehouse": "_Test Warehouse 1 - _TC", - "incoming_rate": 1.0 + "basic_rate": 1.0 }) # check for stopped status of Material Request diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 343d51acc75..50179646cbd 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -80,9 +80,9 @@ class TestPurchaseReceipt(unittest.TestCase): def test_subcontracting(self): from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry - make_stock_entry(item_code="_Test Item", target="_Test Warehouse 1 - _TC", qty=100, incoming_rate=100) + make_stock_entry(item_code="_Test Item", target="_Test Warehouse 1 - _TC", qty=100, basic_rate=100) make_stock_entry(item_code="_Test Item Home Desktop 100", target="_Test Warehouse 1 - _TC", - qty=100, incoming_rate=100) + qty=100, basic_rate=100) pr = make_purchase_receipt(item_code="_Test FG Item", qty=10, rate=500, is_subcontracted="Yes") self.assertEquals(len(pr.get("supplied_items")), 2) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index 22652e6820a..e9fc134152a 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -25,7 +25,8 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({ return { "filters": { "docstatus": 1, - "is_subcontracted": "Yes" + "is_subcontracted": "Yes", + "company": me.frm.doc.company } }; }); @@ -41,6 +42,14 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({ } } } + this.frm.set_query("difference_account", function() { + return { + "filters": { + "company": me.frm.doc.company, + "is_group": 0 + } + }; + }); } }, @@ -125,11 +134,6 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({ var d = locals[cdt][cdn]; d.transfer_qty = flt(d.qty) * flt(d.conversion_factor); refresh_field('items'); - calculate_total(doc, cdt, cdn); - }, - - incoming_rate: function(doc, cdt, cdn) { - calculate_total(doc, cdt, cdn); }, production_order: function() { @@ -138,13 +142,29 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({ return frappe.call({ method: "erpnext.stock.doctype.stock_entry.stock_entry.get_production_order_details", - args: {production_order: this.frm.doc.production_order}, + args: {production_order: me.frm.doc.production_order}, callback: function(r) { if (!r.exc) { - me.frm.set_value(r.message); + $.each(["from_bom", "bom_no", "fg_completed_qty", "use_multi_level_bom"], function(i, field) { + me.frm.set_value(field, r.message[field]); + }) + if (me.frm.doc.purpose == "Material Transfer for Manufacture" && !me.frm.doc.to_warehouse) me.frm.set_value("to_warehouse", r.message["wip_warehouse"]); - me.frm.set_value("from_bom", 1); + + + if (me.frm.doc.purpose == "Manufacture") { + if(r.message["additional_costs"].length) { + $.each(r.message["additional_costs"], function(i, row) { + me.frm.add_child("additional_costs", row); + }) + refresh_field("additional_costs"); + } + + if (!me.frm.doc.from_warehouse) me.frm.set_value("from_warehouse", r.message["wip_warehouse"]); + if (!me.frm.doc.to_warehouse) me.frm.set_value("to_warehouse", r.message["fg_warehouse"]); + } + me.get_items() } } }); @@ -232,13 +252,20 @@ cur_frm.cscript.toggle_related_fields = function(doc) { if(doc.purpose == "Material Receipt") { cur_frm.set_value("from_bom", 0); } + + // Addition costs based on purpose + cur_frm.toggle_display(["additional_costs", "total_additional_costs", "additional_costs_section"], + doc.purpose!='Material Issue'); + + cur_frm.fields_dict["items"].grid.set_column_disp("additional_cost", doc.purpose!='Material Issue'); } cur_frm.fields_dict['production_order'].get_query = function(doc) { return { filters: [ ['Production Order', 'docstatus', '=', 1], - ['Production Order', 'qty', '>','`tabProduction Order`.produced_qty'] + ['Production Order', 'qty', '>','`tabProduction Order`.produced_qty'], + ['Production Order', 'company', '=', cur_frm.doc.company] ] } } @@ -378,17 +405,4 @@ cur_frm.cscript.company = function(doc, cdt, cdn) { cur_frm.cscript.posting_date = function(doc, cdt, cdn){ erpnext.get_fiscal_year(doc.company, doc.posting_date); -} - -var calculate_total = function(doc, cdt, cdn){ - var d = locals[cdt][cdn]; - amount = flt(d.incoming_rate) * flt(d.transfer_qty) - frappe.model.set_value(cdt, cdn, 'amount', amount); - var total_amount = 0.0; - var items = doc.items || []; - for(var i=0;i= qty: frappe.throw(_("Stock Entries already created for Production Order ") + self.production_order + ":" + ", ".join(other_ste), DuplicateEntryForProductionOrderError) + + def set_actual_qty(self): + allow_negative_stock = cint(frappe.db.get_value("Stock Settings", None, "allow_negative_stock")) + + for d in self.get('items'): + previous_sle = get_previous_sle({ + "item_code": d.item_code, + "warehouse": d.s_warehouse or d.t_warehouse, + "posting_date": self.posting_date, + "posting_time": self.posting_time + }) + + # get actual stock at source warehouse + d.actual_qty = previous_sle.get("qty_after_transaction") or 0 + + # validate qty during submit + if d.docstatus==1 and d.s_warehouse and not allow_negative_stock and d.actual_qty < d.transfer_qty: + frappe.throw(_("""Row {0}: Qty not avalable in warehouse {1} on {2} {3}. + Available Qty: {4}, Transfer Qty: {5}""").format(d.idx, d.s_warehouse, + self.posting_date, self.posting_time, d.actual_qty, d.transfer_qty), NegativeStockError) + + def calculate_rate_and_amount(self, force=False): + self.set_basic_rate(force) + self.distribute_additional_costs() + self.update_valuation_rate() + self.validate_valuation_rate() + self.set_total_incoming_outgoing_value() + self.set_total_amount() + + def set_basic_rate(self, force=False): + """get stock and incoming rate on posting date""" + raw_material_cost = 0.0 + + for d in self.get('items'): + args = frappe._dict({ + "item_code": d.item_code, + "warehouse": d.s_warehouse or d.t_warehouse, + "posting_date": self.posting_date, + "posting_time": self.posting_time, + "qty": d.s_warehouse and -1*flt(d.transfer_qty) or flt(d.transfer_qty), + "serial_no": d.serial_no, + }) + + # get basic rate + if not d.bom_no: + if not flt(d.basic_rate) or d.s_warehouse or force: + basic_rate = flt(get_incoming_rate(args), self.precision("basic_rate", d)) + if basic_rate > 0: + d.basic_rate = basic_rate + + d.basic_amount = flt(flt(d.transfer_qty) * flt(d.basic_rate), d.precision("basic_amount")) + if not d.t_warehouse: + raw_material_cost += flt(d.basic_amount) + + self.set_basic_rate_for_finished_goods(raw_material_cost) + + def set_basic_rate_for_finished_goods(self, raw_material_cost): + if self.purpose in ["Manufacture", "Repack"]: + number_of_fg_items = len([t.t_warehouse for t in self.get("items") if t.t_warehouse]) + for d in self.get("items"): + if d.bom_no or (d.t_warehouse and number_of_fg_items == 1): + d.basic_rate = flt(raw_material_cost / flt(d.transfer_qty), d.precision("basic_rate")) + d.basic_amount = flt(flt(d.basic_rate) * flt(d.transfer_qty), d.precision("basic_amount")) + + def distribute_additional_costs(self): + if self.purpose == "Material Issue": + self.additional_costs = [] + + self.total_additional_costs = sum([flt(t.amount) for t in self.get("additional_costs")]) + total_basic_amount = sum([flt(t.basic_amount) for t in self.get("items") if t.t_warehouse]) + + for d in self.get("items"): + if d.t_warehouse and total_basic_amount: + d.additional_cost = (flt(d.basic_amount) / total_basic_amount) * self.total_additional_costs + else: + d.additional_cost = 0 + + def update_valuation_rate(self): + for d in self.get("items"): + d.amount = flt(d.basic_amount + flt(d.additional_cost), d.precision("amount")) + d.valuation_rate = flt(flt(d.basic_rate) + flt(d.additional_cost) / flt(d.transfer_qty), + d.precision("valuation_rate")) def validate_valuation_rate(self): if self.purpose in ["Manufacture", "Repack"]: @@ -222,11 +301,11 @@ class StockEntry(StockController): if d.s_warehouse and not d.t_warehouse: valuation_at_source += flt(d.amount) if d.t_warehouse and not d.s_warehouse: - valuation_at_target += flt(d.amount) + flt(d.tax_amount) + valuation_at_target += flt(d.amount) if valuation_at_target + 0.001 < valuation_at_source: - frappe.throw(_("Total valuation ({0}) for manufactured or repacked item(s) can not be less than total valuation of raw materials ({1})").format(valuation_at_target, - valuation_at_source)) + frappe.throw(_("Total valuation ({0}) for manufactured or repacked item(s) can not be less than total valuation of raw materials ({1})") + .format(valuation_at_target, valuation_at_source)) def set_total_incoming_outgoing_value(self): self.total_incoming_value = self.total_outgoing_value = 0.0 @@ -234,91 +313,13 @@ class StockEntry(StockController): if d.t_warehouse: self.total_incoming_value += flt(d.amount) if d.s_warehouse: - self.total_outgoing_value += flt(d.amount) + flt(d.tax_amount) + self.total_outgoing_value += flt(d.amount) - self.value_difference = self.total_outgoing_value - self.total_incoming_value + self.value_difference = self.total_incoming_value - self.total_outgoing_value def set_total_amount(self): self.total_amount = sum([flt(item.amount) for item in self.get("items")]) - def get_stock_and_rate(self, force=False): - """get stock and incoming rate on posting date""" - - raw_material_cost = 0.0 - - if not self.posting_date or not self.posting_time: - frappe.throw(_("Posting date and posting time is mandatory")) - - allow_negative_stock = cint(frappe.db.get_value("Stock Settings", None, "allow_negative_stock")) - - for d in self.get('items'): - d.transfer_qty = flt(d.transfer_qty) - - args = frappe._dict({ - "item_code": d.item_code, - "warehouse": d.s_warehouse or d.t_warehouse, - "posting_date": self.posting_date, - "posting_time": self.posting_time, - "qty": d.s_warehouse and -1*d.transfer_qty or d.transfer_qty, - "serial_no": d.serial_no, - }) - - # get actual stock at source warehouse - d.actual_qty = get_previous_sle(args).get("qty_after_transaction") or 0 - - # validate qty during submit - if d.docstatus==1 and d.s_warehouse and not allow_negative_stock and d.actual_qty < d.transfer_qty: - frappe.throw(_("""Row {0}: Qty not avalable in warehouse {1} on {2} {3}. - Available Qty: {4}, Transfer Qty: {5}""").format(d.idx, d.s_warehouse, - self.posting_date, self.posting_time, d.actual_qty, d.transfer_qty), NegativeStockError) - - # get incoming rate - if not d.bom_no: - if not flt(d.incoming_rate) or d.s_warehouse or force: - incoming_rate = flt(get_incoming_rate(args), self.precision("incoming_rate", d)) - if incoming_rate > 0: - d.incoming_rate = incoming_rate - - d.amount = flt(flt(d.transfer_qty) * flt(d.incoming_rate), d.precision("amount")) - if not d.t_warehouse: - raw_material_cost += flt(d.amount) - - - self.add_operation_cost(raw_material_cost, force) - - def add_operation_cost(self, raw_material_cost, force): - """Adds operating cost if Production Order is set""" - # set incoming rate for fg item - if self.purpose in ["Manufacture", "Repack"]: - number_of_fg_items = len([t.t_warehouse for t in self.get("items") if t.t_warehouse]) - for d in self.get("items"): - if d.bom_no or (d.t_warehouse and number_of_fg_items == 1): - operation_cost_per_unit = self.get_operation_cost_per_unit(d.bom_no, d.qty) - - d.incoming_rate = operation_cost_per_unit + (raw_material_cost / flt(d.transfer_qty)) - d.amount = flt(flt(d.transfer_qty) * flt(d.incoming_rate), self.precision("transfer_qty", d)) - break - - def get_operation_cost_per_unit(self, bom_no, qty): - """Returns operating cost from Production Order for given `bom_no`""" - operation_cost_per_unit = 0 - - if self.production_order: - if not getattr(self, "pro_doc", None): - self.pro_doc = frappe.get_doc("Production Order", self.production_order) - for d in self.pro_doc.get("operations"): - if flt(d.completed_qty): - operation_cost_per_unit += flt(d.actual_operating_cost) / flt(d.completed_qty) - else: - operation_cost_per_unit += flt(d.planned_operating_cost) / flt(self.pro_doc.qty) - - # set operating cost from BOM if specified. - if not operation_cost_per_unit and bom_no: - bom = frappe.db.get_value("BOM", bom_no, ["operating_cost", "quantity"], as_dict=1) - operation_cost_per_unit = flt(bom.operating_cost) / flt(bom.quantity) - - return operation_cost_per_unit - def validate_purchase_order(self): """Throw exception if more raw material is transferred against Purchase Order than in the raw materials supplied table""" @@ -367,9 +368,7 @@ class StockEntry(StockController): def update_stock_ledger(self): sl_entries = [] - for d in self.get('items'): - tax_amount_per_qty = flt(flt(d.tax_amount) / flt(d.qty), d.precision("tax_amount")) - + for d in self.get('items'): if cstr(d.s_warehouse) and self.docstatus == 1: sl_entries.append(self.get_sl_entries(d, { "warehouse": cstr(d.s_warehouse), @@ -381,7 +380,7 @@ class StockEntry(StockController): sl_entries.append(self.get_sl_entries(d, { "warehouse": cstr(d.t_warehouse), "actual_qty": flt(d.transfer_qty), - "incoming_rate": flt(d.incoming_rate) + tax_amount_per_qty + "incoming_rate": flt(d.valuation_rate) })) # On cancellation, make stock ledger entry for @@ -402,14 +401,14 @@ class StockEntry(StockController): gl_entries = super(StockEntry, self).get_gl_entries(warehouse_account) for d in self.get("items"): - tax_amount = flt(d.tax_amount, d.precision("tax_amount")) - if tax_amount: + additional_cost = flt(d.additional_cost, d.precision("additional_cost")) + if additional_cost: gl_entries.append(self.get_gl_dict({ "account": expenses_included_in_valuation, "against": d.expense_account, "cost_center": d.cost_center, "remarks": self.get("remarks") or _("Accounting Entry for Stock"), - "credit": tax_amount + "credit": additional_cost })) gl_entries.append(self.get_gl_dict({ @@ -417,7 +416,7 @@ class StockEntry(StockController): "against": expenses_included_in_valuation, "cost_center": d.cost_center, "remarks": self.get("remarks") or _("Accounting Entry for Stock"), - "credit": -1 * tax_amount + "credit": -1 * additional_cost # put it as negative credit instead of debit purposefully })) return gl_entries @@ -471,7 +470,7 @@ class StockEntry(StockController): 'conversion_factor' : 1, 'batch_no' : '', 'actual_qty' : 0, - 'incoming_rate' : 0 + 'basic_rate' : 0 } for d in [["Account", "expense_account", "default_expense_account"], ["Cost Center", "cost_center", "cost_center"]]: @@ -519,13 +518,16 @@ class StockEntry(StockController): ret = { "actual_qty" : get_previous_sle(args).get("qty_after_transaction") or 0, - "incoming_rate" : get_incoming_rate(args) + "basic_rate" : get_incoming_rate(args) } return ret def get_items(self): self.set('items', []) self.validate_production_order() + + if not self.posting_date or not self.posting_time: + frappe.throw(_("Posting date and posting time is mandatory")) if not getattr(self, "pro_doc", None): self.pro_doc = None @@ -567,7 +569,8 @@ class StockEntry(StockController): if self.purpose in ("Manufacture", "Repack"): self.load_items_from_bom() - self.get_stock_and_rate() + self.set_actual_qty() + self.calculate_rate_and_amount() def load_items_from_bom(self): if self.production_order: @@ -695,19 +698,57 @@ class StockEntry(StockController): if expiry_date: if getdate(self.posting_date) > getdate(expiry_date): frappe.throw(_("Batch {0} of Item {1} has expired.").format(item.batch_no, item.item_code)) - - def distribute_taxes(self): - self.total_taxes_and_charges = sum([flt(t.amount) for t in self.get("taxes")]) - for d in self.get("items"): - if d.t_warehouse and self.total_incoming_value: - d.tax_amount = (flt(d.amount) / flt(self.total_incoming_value)) * self.total_taxes_and_charges - @frappe.whitelist() def get_production_order_details(production_order): - res = frappe.db.sql("""select bom_no, use_multi_level_bom, wip_warehouse, - ifnull(qty, 0) - ifnull(produced_qty, 0) as fg_completed_qty, - (ifnull(additional_operating_cost, 0) / qty)*(ifnull(qty, 0) - ifnull(produced_qty, 0)) as additional_operating_cost - from `tabProduction Order` where name = %s""", production_order, as_dict=1) - - return res and res[0] or {} + production_order = frappe.get_doc("Production Order", production_order) + pending_qty_to_produce = flt(production_order.qty) - flt(production_order.produced_qty) + + return { + "from_bom": 1, + "bom_no": production_order.bom_no, + "use_multi_level_bom": production_order.use_multi_level_bom, + "wip_warehouse": production_order.wip_warehouse, + "fg_warehouse": production_order.fg_warehouse, + "fg_completed_qty": pending_qty_to_produce, + "additional_costs": get_additional_costs(production_order, fg_qty=pending_qty_to_produce) + } + +def get_additional_costs(production_order=None, bom_no=None, fg_qty=None): + additional_costs = [] + operating_cost_per_unit = get_operating_cost_per_unit(production_order, bom_no) + if operating_cost_per_unit: + additional_costs.append({ + "description": "Operating Cost as per Production Order / BOM", + "amount": operating_cost_per_unit * flt(fg_qty) + }) + + if production_order and production_order.additional_operating_cost: + additional_operating_cost_per_unit = \ + flt(production_order.additional_operating_cost) / flt(production_order.qty) + + additional_costs.append({ + "description": "Additional Operating Cost", + "amount": additional_operating_cost_per_unit * flt(fg_qty) + }) + + return additional_costs + +def get_operating_cost_per_unit(production_order=None, bom_no=None): + operating_cost_per_unit = 0 + if production_order: + if not bom_no: + bom_no = production_order.bom_no + + for d in production_order.get("operations"): + if flt(d.completed_qty): + operating_cost_per_unit += flt(d.actual_operating_cost) / flt(d.completed_qty) + else: + operating_cost_per_unit += flt(d.planned_operating_cost) / flt(production_order.qty) + + # Get operating cost from BOM if not found in production_order. + if not operating_cost_per_unit and bom_no: + bom = frappe.db.get_value("BOM", bom_no, ["operating_cost", "quantity"], as_dict=1) + operating_cost_per_unit = flt(bom.operating_cost) / flt(bom.quantity) + + return operating_cost_per_unit \ No newline at end of file diff --git a/erpnext/stock/doctype/stock_entry/test_records.json b/erpnext/stock/doctype/stock_entry/test_records.json index f4d20449e59..f648b9e6fd6 100644 --- a/erpnext/stock/doctype/stock_entry/test_records.json +++ b/erpnext/stock/doctype/stock_entry/test_records.json @@ -8,7 +8,7 @@ "cost_center": "_Test Cost Center - _TC", "doctype": "Stock Entry Detail", "expense_account": "Stock Adjustment - _TC", - "incoming_rate": 100, + "basic_rate": 100, "item_code": "_Test Item", "parentfield": "items", "qty": 50.0, @@ -32,7 +32,7 @@ "cost_center": "_Test Cost Center - _TC", "doctype": "Stock Entry Detail", "expense_account": "Stock Adjustment - _TC", - "incoming_rate": 100, + "basic_rate": 100, "item_code": "_Test Item", "parentfield": "items", "qty": 40.0, @@ -57,7 +57,7 @@ "cost_center": "_Test Cost Center - _TC", "doctype": "Stock Entry Detail", "expense_account": "Stock Adjustment - _TC", - "incoming_rate": 100, + "basic_rate": 100, "item_code": "_Test Item", "parentfield": "items", "qty": 45.0, @@ -83,7 +83,7 @@ "cost_center": "_Test Cost Center - _TC", "doctype": "Stock Entry Detail", "expense_account": "Stock Adjustment - _TC", - "incoming_rate": 100, + "basic_rate": 100, "item_code": "_Test Item", "parentfield": "items", "qty": 50.0, @@ -97,7 +97,7 @@ "cost_center": "_Test Cost Center - _TC", "doctype": "Stock Entry Detail", "expense_account": "Stock Adjustment - _TC", - "incoming_rate": 5000, + "basic_rate": 5000, "item_code": "_Test Item Home Desktop 100", "parentfield": "items", "qty": 1, diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py index d283c3dd14b..f00a235fa76 100644 --- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py @@ -36,12 +36,12 @@ class TestStockEntry(unittest.TestCase): create_stock_reconciliation(item_code="_Test Item 2", warehouse="_Test Warehouse - _TC", qty=0, rate=100) - make_stock_entry(item_code=item_code, target=warehouse, qty=1, incoming_rate=10) + make_stock_entry(item_code=item_code, target=warehouse, qty=1, basic_rate=10) sle = get_sle(item_code = item_code, warehouse = warehouse)[0] self.assertEqual([[1, 10]], eval(sle.stock_queue)) # negative qty - make_stock_entry(item_code=item_code, source=warehouse, qty=2, incoming_rate=10) + make_stock_entry(item_code=item_code, source=warehouse, qty=2, basic_rate=10) sle = get_sle(item_code = item_code, warehouse = warehouse)[0] self.assertEqual([[-1, 10]], eval(sle.stock_queue)) @@ -53,12 +53,12 @@ class TestStockEntry(unittest.TestCase): self.assertEqual([[-2, 10]], eval(sle.stock_queue)) # move stock to positive - make_stock_entry(item_code=item_code, target=warehouse, qty=3, incoming_rate=20) + make_stock_entry(item_code=item_code, target=warehouse, qty=3, basic_rate=20) sle = get_sle(item_code = item_code, warehouse = warehouse)[0] self.assertEqual([[1, 20]], eval(sle.stock_queue)) # incoming entry with diff rate - make_stock_entry(item_code=item_code, target=warehouse, qty=1, incoming_rate=30) + make_stock_entry(item_code=item_code, target=warehouse, qty=1, basic_rate=30) sle = get_sle(item_code = item_code, warehouse = warehouse)[0] self.assertEqual([[1, 20],[1, 30]], eval(sle.stock_queue)) @@ -125,7 +125,7 @@ class TestStockEntry(unittest.TestCase): set_perpetual_inventory() mr = make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", - qty=50, incoming_rate=100) + qty=50, basic_rate=100) stock_in_hand_account = frappe.db.get_value("Account", {"account_type": "Warehouse", "warehouse": mr.get("items")[0].t_warehouse}) @@ -152,7 +152,7 @@ class TestStockEntry(unittest.TestCase): set_perpetual_inventory() make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", - qty=50, incoming_rate=100) + qty=50, basic_rate=100) mi = make_stock_entry(item_code="_Test Item", source="_Test Warehouse - _TC", qty=40) @@ -217,9 +217,9 @@ class TestStockEntry(unittest.TestCase): def test_repack_no_change_in_valuation(self): set_perpetual_inventory(0) - make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=50, incoming_rate=100) + make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=50, basic_rate=100) make_stock_entry(item_code="_Test Item Home Desktop 100", target="_Test Warehouse - _TC", - qty=50, incoming_rate=100) + qty=50, basic_rate=100) repack = frappe.copy_doc(test_records[3]) repack.posting_date = nowdate() @@ -238,15 +238,24 @@ class TestStockEntry(unittest.TestCase): set_perpetual_inventory(0) - def test_repack_with_change_in_valuation(self): + def test_repack_with_additional_costs(self): set_perpetual_inventory() - make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=50, incoming_rate=100) - + make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=50, basic_rate=100) repack = frappe.copy_doc(test_records[3]) repack.posting_date = nowdate() repack.posting_time = nowtime() - repack.additional_operating_cost = 1000.0 + + repack.set("additional_costs", [ + { + "description": "Actual Oerating Cost", + "amount": 1000 + }, + { + "description": "additional operating costs", + "amount": 200 + }, + ]) repack.insert() repack.submit() @@ -260,11 +269,13 @@ class TestStockEntry(unittest.TestCase): "voucher_no": repack.name, "item_code": "_Test Item Home Desktop 100"}, "stock_value_difference")) stock_value_diff = flt(fg_stock_value_diff - rm_stock_value_diff, 2) + + self.assertEqual(stock_value_diff, 1200) self.check_gl_entries("Stock Entry", repack.name, sorted([ - [stock_in_hand_account, stock_value_diff, 0.0], - ["Stock Adjustment - _TC", 0.0, stock_value_diff], + [stock_in_hand_account, 1200, 0.0], + ["Expenses Included In Valuation - _TC", 0.0, 1200.0] ]) ) set_perpetual_inventory(0) @@ -291,10 +302,9 @@ class TestStockEntry(unittest.TestCase): gl_entries = frappe.db.sql("""select account, debit, credit from `tabGL Entry` where voucher_type=%s and voucher_no=%s order by account asc, debit asc""", (voucher_type, voucher_no), as_list=1) - + self.assertTrue(gl_entries) gl_entries.sort(key=lambda x: x[0]) - for i, gle in enumerate(gl_entries): self.assertEquals(expected_gl_entries[i][0], gle[0]) self.assertEquals(expected_gl_entries[i][1], gle[1]) @@ -503,6 +513,8 @@ class TestStockEntry(unittest.TestCase): frappe.db.set_value("Stock Settings", None, "stock_frozen_upto_days", 0) def test_production_order(self): + from erpnext.manufacturing.doctype.production_order.production_order \ + import make_stock_entry as _make_stock_entry bom_no, bom_operation_cost = frappe.db.get_value("BOM", {"item": "_Test FG Item 2", "is_default": 1, "docstatus": 1}, ["name", "operating_cost"]) @@ -514,22 +526,15 @@ class TestStockEntry(unittest.TestCase): "bom_no": bom_no, "qty": 1.0, "stock_uom": "_Test UOM", - "wip_warehouse": "_Test Warehouse - _TC" + "wip_warehouse": "_Test Warehouse - _TC", + "additional_operating_cost": 1000 }) production_order.insert() production_order.submit() - make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=50, incoming_rate=100) + make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=50, basic_rate=100) - stock_entry = frappe.new_doc("Stock Entry") - stock_entry.update({ - "purpose": "Manufacture", - "production_order": production_order.name, - "bom_no": bom_no, - "fg_completed_qty": "1", - "additional_operating_cost": 1000 - }) - stock_entry.get_items() + stock_entry = _make_stock_entry(production_order.name, "Manufacture", 1) rm_cost = 0 for d in stock_entry.get("items"): @@ -538,7 +543,7 @@ class TestStockEntry(unittest.TestCase): fg_cost = filter(lambda x: x.item_code=="_Test FG Item 2", stock_entry.get("items"))[0].amount self.assertEqual(fg_cost, - flt(rm_cost + bom_operation_cost + stock_entry.additional_operating_cost, 2)) + flt(rm_cost + bom_operation_cost + production_order.additional_operating_cost, 2)) def test_variant_production_order(self): @@ -610,7 +615,7 @@ def make_stock_entry(**args): "s_warehouse": args.from_warehouse or args.source, "t_warehouse": args.to_warehouse or args.target, "qty": args.qty, - "incoming_rate": args.incoming_rate, + "basic_rate": args.basic_rate, "expense_account": args.expense_account or "Stock Adjustment - _TC", "conversion_factor": 1.0, "cost_center": "_Test Cost Center - _TC" diff --git a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json index dcd46bd51fe..fe8a1b15bea 100644 --- a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json +++ b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json @@ -1,25 +1,58 @@ { + "allow_copy": 0, + "allow_import": 0, + "allow_rename": 0, "autoname": "hash", "creation": "2013-03-29 18:22:12", + "custom": 0, "docstatus": 0, "doctype": "DocType", "fields": [ { + "allow_on_submit": 0, "fieldname": "barcode", "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, "label": "Barcode", + "no_copy": 0, "permlevel": 0, - "precision": "" + "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": "section_break_2", "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "no_copy": 0, "permlevel": 0, - "precision": "" + "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": "s_warehouse", "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, "in_filter": 1, "in_list_view": 1, "label": "Source Warehouse", @@ -28,16 +61,38 @@ "oldfieldtype": "Link", "options": "Warehouse", "permlevel": 0, - "read_only": 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_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": "t_warehouse", "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, "in_filter": 1, "in_list_view": 1, "label": "Target Warehouse", @@ -46,247 +101,631 @@ "oldfieldtype": "Link", "options": "Warehouse", "permlevel": 0, - "read_only": 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": "sec_break1", "fieldtype": "Section 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": "item_code", "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, "in_filter": 1, "in_list_view": 1, "label": "Item Code", + "no_copy": 0, "oldfieldname": "item_code", "oldfieldtype": "Link", "options": "Item", "permlevel": 0, + "print_hide": 0, "read_only": 0, + "report_hide": 0, "reqd": 1, - "search_index": 1 + "search_index": 1, + "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": "qty", "fieldtype": "Float", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, "in_list_view": 1, "label": "Qty", + "no_copy": 0, "oldfieldname": "qty", "oldfieldtype": "Currency", "permlevel": 0, + "print_hide": 0, "read_only": 0, - "reqd": 1 + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 }, { + "allow_on_submit": 0, "fieldname": "section_break_8", "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "no_copy": 0, "permlevel": 0, - "precision": "" + "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": "item_name", "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, "label": "Item Name", + "no_copy": 0, "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, "fieldname": "description", "fieldtype": "Text", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, "in_list_view": 0, "label": "Description", + "no_copy": 0, "oldfieldname": "description", "oldfieldtype": "Text", "permlevel": 0, + "print_hide": 0, "print_width": "300px", "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0, "width": "300px" }, { + "allow_on_submit": 0, "fieldname": "column_break_10", "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "no_copy": 0, "permlevel": 0, - "precision": "" + "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": "image", "fieldtype": "Attach", "hidden": 1, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, "label": "Image", + "no_copy": 0, "permlevel": 0, "precision": "", - "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": "image_view", "fieldtype": "Image", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, "label": "Image View", + "no_copy": 0, "options": "image", "permlevel": 0, - "precision": "" + "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": "quantity_and_rate", "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, "label": "Quantity and Rate", - "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 }, { - "fieldname": "incoming_rate", + "allow_on_submit": 0, + "fieldname": "basic_rate", "fieldtype": "Currency", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, "in_list_view": 1, - "label": "Valuation Rate", + "label": "Basic Rate (as per Stock UOM)", + "no_copy": 0, "oldfieldname": "incoming_rate", "oldfieldtype": "Currency", "options": "Company:company:default_currency", "permlevel": 0, + "print_hide": 1, "read_only": 0, - "reqd": 0 + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 }, { + "allow_on_submit": 0, + "fieldname": "basic_amount", + "fieldtype": "Currency", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Basic Amount", + "no_copy": 0, + "options": "Company:company:default_currency", + "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": "additional_cost", + "fieldtype": "Currency", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Additional Cost", + "no_copy": 0, + "options": "Company:company:default_currency", + "permlevel": 0, + "precision": "", + "print_hide": 1, + "read_only": 1, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, "fieldname": "amount", "fieldtype": "Currency", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, "label": "Amount", + "no_copy": 0, "oldfieldname": "amount", "oldfieldtype": "Currency", "options": "Company:company:default_currency", "permlevel": 0, - "read_only": 1 + "print_hide": 0, + "read_only": 1, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 }, { - "fieldname": "tax_amount", + "allow_on_submit": 0, + "fieldname": "valuation_rate", "fieldtype": "Currency", - "label": "Tax Amount", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Valuation Rate", + "no_copy": 0, + "options": "Company:company:default_currency", "permlevel": 0, - "precision": "" + "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": "col_break3", "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": "uom", "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, "in_list_view": 0, "label": "UOM", + "no_copy": 0, "oldfieldname": "uom", "oldfieldtype": "Link", "options": "UOM", "permlevel": 0, + "print_hide": 0, "read_only": 0, - "reqd": 1 + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 }, { + "allow_on_submit": 0, "fieldname": "conversion_factor", "fieldtype": "Float", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, "label": "Conversion Factor", + "no_copy": 0, "oldfieldname": "conversion_factor", "oldfieldtype": "Currency", "permlevel": 0, "print_hide": 1, "read_only": 1, - "reqd": 1 + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 }, { + "allow_on_submit": 0, "fieldname": "stock_uom", "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, "in_filter": 0, + "in_list_view": 0, "label": "Stock UOM", + "no_copy": 0, "oldfieldname": "stock_uom", "oldfieldtype": "Link", "options": "UOM", "permlevel": 0, "print_hide": 1, "read_only": 1, + "report_hide": 0, "reqd": 1, - "search_index": 0 + "search_index": 0, + "set_only_once": 0, + "unique": 0 }, { + "allow_on_submit": 0, + "fieldname": "transfer_qty", + "fieldtype": "Float", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Qty as per Stock UOM", + "no_copy": 0, + "oldfieldname": "transfer_qty", + "oldfieldtype": "Currency", + "permlevel": 0, + "print_hide": 1, + "read_only": 1, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, "fieldname": "serial_no_batch", "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, "label": "Serial No / Batch", - "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": "serial_no", "fieldtype": "Text", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, "label": "Serial No", "no_copy": 1, "oldfieldname": "serial_no", "oldfieldtype": "Text", "permlevel": 0, + "print_hide": 0, "read_only": 0, - "reqd": 0 + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 }, { + "allow_on_submit": 0, "fieldname": "col_break4", "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": "batch_no", "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, "label": "Batch No", + "no_copy": 0, "oldfieldname": "batch_no", "oldfieldtype": "Link", "options": "Batch", "permlevel": 0, "print_hide": 0, - "read_only": 0 + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 }, { + "allow_on_submit": 0, "fieldname": "accounting", "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, "label": "Accounting", - "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, "depends_on": "eval:cint(sys_defaults.auto_accounting_for_stock)", "fieldname": "expense_account", "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, "label": "Difference Account", + "no_copy": 0, "options": "Account", "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": "col_break5", "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, "default": ":Company", "depends_on": "eval:cint(sys_defaults.auto_accounting_for_stock)", "fieldname": "cost_center", "fieldtype": "Link", "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, "label": "Cost Center", + "no_copy": 0, "options": "Cost Center", "permlevel": 0, "print_hide": 1, "read_only": 0, - "reqd": 0 + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 }, { + "allow_on_submit": 0, "fieldname": "more_info", "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, "label": "More Info", - "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": 1, "fieldname": "actual_qty", "fieldtype": "Float", + "hidden": 0, "ignore_user_permissions": 0, "in_filter": 1, + "in_list_view": 0, "label": "Actual Qty (at source/target)", "no_copy": 1, "oldfieldname": "actual_qty", @@ -294,67 +733,107 @@ "permlevel": 0, "print_hide": 1, "read_only": 1, + "report_hide": 0, "reqd": 0, - "search_index": 1 + "search_index": 1, + "set_only_once": 0, + "unique": 0 }, { + "allow_on_submit": 0, "description": "BOM No. for a Finished Good Item", "fieldname": "bom_no", "fieldtype": "Link", "hidden": 1, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, "label": "BOM No", "no_copy": 0, "options": "BOM", "permlevel": 0, "print_hide": 1, - "read_only": 0 + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 }, { + "allow_on_submit": 0, "fieldname": "col_break6", "fieldtype": "Column Break", - "permlevel": 0 - }, - { - "fieldname": "transfer_qty", - "fieldtype": "Float", - "label": "Qty as per Stock UOM", - "oldfieldname": "transfer_qty", - "oldfieldtype": "Currency", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "no_copy": 0, "permlevel": 0, - "print_hide": 1, - "read_only": 1, - "reqd": 1 + "print_hide": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 }, { + "allow_on_submit": 0, "description": "Material Request used to make this Stock Entry", "fieldname": "material_request", "fieldtype": "Link", "hidden": 1, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, "label": "Material Request", "no_copy": 1, "options": "Material Request", "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, "fieldname": "material_request_item", "fieldtype": "Link", "hidden": 1, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, "label": "Material Request Item", "no_copy": 1, "options": "Material Request Item", "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 } ], + "hide_heading": 0, + "hide_toolbar": 0, "idx": 1, + "in_create": 0, + "in_dialog": 0, + "is_submittable": 0, + "issingle": 0, "istable": 1, - "modified": "2015-07-24 17:03:44.214018", + "modified": "2015-08-07 13:21:23.840052", "modified_by": "Administrator", "module": "Stock", "name": "Stock Entry Detail", "owner": "Administrator", - "permissions": [] + "permissions": [], + "read_only": 0, + "read_only_onload": 0 } \ No newline at end of file diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py index dde338611b4..fa2fa1352dd 100644 --- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py @@ -82,13 +82,13 @@ class TestStockReconciliation(unittest.TestCase): from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry make_stock_entry(posting_date="2012-12-15", posting_time="02:00", item_code="_Test Item", - target="_Test Warehouse - _TC", qty=10, incoming_rate=700) + target="_Test Warehouse - _TC", qty=10, basic_rate=700) make_stock_entry(posting_date="2012-12-25", posting_time="03:00", item_code="_Test Item", source="_Test Warehouse - _TC", qty=15) make_stock_entry(posting_date="2013-01-05", posting_time="07:00", item_code="_Test Item", - target="_Test Warehouse - _TC", qty=15, incoming_rate=1200) + target="_Test Warehouse - _TC", qty=15, basic_rate=1200) def create_stock_reconciliation(**args): args = frappe._dict(args) diff --git a/erpnext/templates/form_grid/stock_entry_grid.html b/erpnext/templates/form_grid/stock_entry_grid.html index 1782b82007a..a2bf1df738f 100644 --- a/erpnext/templates/form_grid/stock_entry_grid.html +++ b/erpnext/templates/form_grid/stock_entry_grid.html @@ -1,6 +1,6 @@ {% var visible_columns = row.get_visible_columns(["item_code", "item_name", "amount", "stock_uom", "uom", "qty", - "s_warehouse", "t_warehouse", "incoming_rate"]); + "s_warehouse", "t_warehouse", "valuation_rate"]); %} {% if(!doc) { %} @@ -43,7 +43,7 @@
{%= doc.get_formatted("amount") %}
- {%= doc.get_formatted("incoming_rate") %} + {%= doc.get_formatted("valuation_rate") %}
diff --git a/erpnext/utilities/repost_stock.py b/erpnext/utilities/repost_stock.py index 52b94aee798..3cb18eed82a 100644 --- a/erpnext/utilities/repost_stock.py +++ b/erpnext/utilities/repost_stock.py @@ -237,7 +237,7 @@ def repost_all_stock_vouchers(): doc = frappe.get_doc(voucher_type, voucher_no) if voucher_type=="Stock Entry" and doc.purpose in ["Manufacture", "Repack"]: - doc.get_stock_and_rate(force=1) + doc.calculate_rate_and_amount(force=1) elif voucher_type=="Purchase Receipt" and doc.is_subcontracted == "Yes": doc.validate() From 52f3bfca73982aabc978f523541e86b5d7886093 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 7 Aug 2015 18:58:52 +0530 Subject: [PATCH 15/53] change log and sponsors page --- .../change_log/current/stock_entry_additional_costs.md | 2 ++ sponsors.md | 8 ++++++++ 2 files changed, 10 insertions(+) create mode 100644 erpnext/change_log/current/stock_entry_additional_costs.md diff --git a/erpnext/change_log/current/stock_entry_additional_costs.md b/erpnext/change_log/current/stock_entry_additional_costs.md new file mode 100644 index 00000000000..ef03375381c --- /dev/null +++ b/erpnext/change_log/current/stock_entry_additional_costs.md @@ -0,0 +1,2 @@ +- Additional Costs in Stock Entry **[Sponsored by PT. Ridho Sribumi Sejahtera]** + - Now additional costs like shipping charges, operating costs etc can be added in Stock Entry in item valuation \ No newline at end of file diff --git a/sponsors.md b/sponsors.md index 5f6c2f2c27e..9b844871350 100644 --- a/sponsors.md +++ b/sponsors.md @@ -29,5 +29,13 @@ For Sales / Purchase Return Enhancement #3582 + + + PT. Ridho Sribumi Sejahtera + + + For Additional Costs in Stock Entry #3613 + + From 845980c01022509e01ba0d7db75ea48d91eca2d3 Mon Sep 17 00:00:00 2001 From: Neil Trini Lasrado Date: Thu, 25 Jun 2015 13:13:53 +0530 Subject: [PATCH 16/53] Added Feature - Update FG based on Material Transfer For Manufacturing --- .../manufacturing_settings.json | 11 ++++++- erpnext/public/js/financial_statements.js | 6 ++++ .../stock/doctype/stock_entry/stock_entry.py | 31 ++++++++++++++++--- 3 files changed, 43 insertions(+), 5 deletions(-) diff --git a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json index eb770a8609a..dff04a43892 100644 --- a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json +++ b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json @@ -41,6 +41,15 @@ "permlevel": 0, "precision": "" }, + { + "default": "BOM", + "fieldname": "update_fg_based_on", + "fieldtype": "Select", + "label": "Update FG Based On", + "options": "BOM\nMaterial Transfer for Manufacture", + "permlevel": 0, + "precision": "" + }, { "fieldname": "column_break_3", "fieldtype": "Column Break", @@ -79,7 +88,7 @@ "in_dialog": 0, "is_submittable": 0, "issingle": 1, - "istable": 0, + "istable": 0, "modified": "2015-07-23 08:12:33.889753", "modified_by": "Administrator", "module": "Manufacturing", diff --git a/erpnext/public/js/financial_statements.js b/erpnext/public/js/financial_statements.js index 9a1a05afaa9..2ce7a1fb3a2 100644 --- a/erpnext/public/js/financial_statements.js +++ b/erpnext/public/js/financial_statements.js @@ -25,6 +25,12 @@ erpnext.financial_statements = { "options": "Yearly\nHalf-yearly\nQuarterly\nMonthly", "default": "Yearly", "reqd": 1 + }, + { + "fieldname": "accumulated_value", + "label": __("Accumulated Value"), + "fieldtype": "Check", + "depends_on": "eval:accumulated_value= 'Yearly'" } ], "formatter": function(row, cell, value, columnDef, dataContext, default_formatter) { diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 01129bfe47c..a9b950587a0 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -551,6 +551,12 @@ class StockEntry(StockController): if self.to_warehouse and self.pro_doc: for item in item_dict.values(): item["to_warehouse"] = self.pro_doc.wip_warehouse + self.add_to_stock_entry_detail(item_dict) + + elif self.production_order and self.purpose == "Manufacture" and \ + frappe.db.get_single_value("Manufacturing Settings", "update_fg_based_on")== "Material Transfer for Manufacture": + self.get_transfered_raw_materials() + else: if not self.fg_completed_qty: frappe.throw(_("Manufacturing Quantity is mandatory")) @@ -561,9 +567,7 @@ class StockEntry(StockController): item["from_warehouse"] = self.pro_doc.wip_warehouse item["to_warehouse"] = self.to_warehouse if self.purpose=="Subcontract" else "" - - # add raw materials to Stock Entry Detail table - self.add_to_stock_entry_detail(item_dict) + self.add_to_stock_entry_detail(item_dict) # add finished goods item if self.purpose in ("Manufacture", "Repack"): @@ -604,8 +608,27 @@ class StockEntry(StockController): for item in item_dict.values(): item.from_warehouse = self.from_warehouse or item.default_warehouse - return item_dict + + def get_transfered_raw_materials(self): + items_dict = frappe.db.sql("""select item_name, item_code, sum(qty) as qty, to_warehouse, from_warehouse, + description, stock_uom, expense_account, cost_center from `tabStock Entry` se,`tabStock Entry Detail` sed + where se.name = sed.parent and se.docstatus=1 and se.purpose='Material Transfer for Manufacture' and + se.production_order= %s and se.to_warehouse= %s group by sed.item_code;""", + (self.production_order, self.from_warehouse), as_dict=1) + for item in items_dict: + self.add_to_stock_entry_detail({ + item.item_code: { + "to_warehouse": item.to_warehouse, + "from_warehouse": item.from_warehouse, + "qty": item.qty, + "item_name": item.item_name, + "description": item.description, + "stock_uom": item.stock_uom, + "expense_account": item.expense_account, + "cost_center": item.buying_cost_center, + } + }) def get_pending_raw_materials(self): """ From dde65752b695b515b80b2e6acdc27716d7fad13a Mon Sep 17 00:00:00 2001 From: Neil Trini Lasrado Date: Fri, 17 Jul 2015 16:42:10 +0530 Subject: [PATCH 17/53] Fixed code to consider partial FG entry --- erpnext/public/js/financial_statements.js | 6 --- .../stock/doctype/stock_entry/stock_entry.py | 51 ++++++++++++++----- 2 files changed, 38 insertions(+), 19 deletions(-) diff --git a/erpnext/public/js/financial_statements.js b/erpnext/public/js/financial_statements.js index 2ce7a1fb3a2..9a1a05afaa9 100644 --- a/erpnext/public/js/financial_statements.js +++ b/erpnext/public/js/financial_statements.js @@ -25,12 +25,6 @@ erpnext.financial_statements = { "options": "Yearly\nHalf-yearly\nQuarterly\nMonthly", "default": "Yearly", "reqd": 1 - }, - { - "fieldname": "accumulated_value", - "label": __("Accumulated Value"), - "fieldtype": "Check", - "depends_on": "eval:accumulated_value= 'Yearly'" } ], "formatter": function(row, cell, value, columnDef, dataContext, default_formatter) { diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index a9b950587a0..23087273de3 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -614,21 +614,46 @@ class StockEntry(StockController): items_dict = frappe.db.sql("""select item_name, item_code, sum(qty) as qty, to_warehouse, from_warehouse, description, stock_uom, expense_account, cost_center from `tabStock Entry` se,`tabStock Entry Detail` sed where se.name = sed.parent and se.docstatus=1 and se.purpose='Material Transfer for Manufacture' and - se.production_order= %s and se.to_warehouse= %s group by sed.item_code;""", + se.production_order= %s and se.to_warehouse= %s group by sed.item_code""", (self.production_order, self.from_warehouse), as_dict=1) + + transfered_materials = frappe.db.sql("""select item_code, sum(qty) as qty from `tabStock Entry` se, + `tabStock Entry Detail` sed where se.name = sed.parent and se.docstatus=1 and + se.purpose='Manufacture' and se.production_order= %s and se.from_warehouse= %s + group by sed.item_code""", (self.production_order, self.from_warehouse), as_dict=1) + + transfered_qty= {} + for d in transfered_materials: + transfered_qty.setdefault(d.item_code, d.qty) + + po_qty = frappe.db.sql("""select qty, produced_qty, material_transferred_for_manufacturing from + `tabProduction Order` where name=%s""", self.production_order, as_dict=1)[0] + manufacturing_qty = flt(po_qty.qty) + produced_qty = flt(po_qty.produced_qty) + trans_qty = flt(po_qty.material_transferred_for_manufacturing) + for item in items_dict: - self.add_to_stock_entry_detail({ - item.item_code: { - "to_warehouse": item.to_warehouse, - "from_warehouse": item.from_warehouse, - "qty": item.qty, - "item_name": item.item_name, - "description": item.description, - "stock_uom": item.stock_uom, - "expense_account": item.expense_account, - "cost_center": item.buying_cost_center, - } - }) + qty= item.qty + + if manufacturing_qty > (produced_qty + flt(self.fg_completed_qty)): + qty = (qty/trans_qty) * flt(self.fg_completed_qty) + + elif transfered_qty.get(item.item_code): + qty-= transfered_qty.get(item.item_code) + + if qty > 0: + self.add_to_stock_entry_detail({ + item.item_code: { + "to_warehouse": item.to_warehouse, + "from_warehouse": item.from_warehouse, + "qty": qty, + "item_name": item.item_name, + "description": item.description, + "stock_uom": item.stock_uom, + "expense_account": item.expense_account, + "cost_center": item.buying_cost_center, + } + }) def get_pending_raw_materials(self): """ From 8ea2f4571335af72c53434528359ba88c581b6ba Mon Sep 17 00:00:00 2001 From: Neil Trini Lasrado Date: Mon, 20 Jul 2015 16:42:07 +0530 Subject: [PATCH 18/53] Renamed update_fg_goods_based_on in Manufacturing Settings to backflush_raw_materials_based_on --- .../manufacturing_settings/manufacturing_settings.json | 8 ++++---- erpnext/stock/doctype/stock_entry/stock_entry.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json index dff04a43892..46d7bb8ca8d 100644 --- a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json +++ b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json @@ -43,10 +43,10 @@ }, { "default": "BOM", - "fieldname": "update_fg_based_on", + "fieldname": "backflush_raw_materials_based_on", "fieldtype": "Select", - "label": "Update FG Based On", - "options": "BOM\nMaterial Transfer for Manufacture", + "label": "Backflush Raw Materials Based On", + "options": "BOM\nMaterial Transferred for Manufacture", "permlevel": 0, "precision": "" }, @@ -87,7 +87,7 @@ "in_create": 0, "in_dialog": 0, "is_submittable": 0, - "issingle": 1, + "issingle": 1, "istable": 0, "modified": "2015-07-23 08:12:33.889753", "modified_by": "Administrator", diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 23087273de3..65f9ef06782 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -554,7 +554,7 @@ class StockEntry(StockController): self.add_to_stock_entry_detail(item_dict) elif self.production_order and self.purpose == "Manufacture" and \ - frappe.db.get_single_value("Manufacturing Settings", "update_fg_based_on")== "Material Transfer for Manufacture": + frappe.db.get_single_value("Manufacturing Settings", "backflush_raw_materials_based_on")== "Material Transferred for Manufacture": self.get_transfered_raw_materials() else: From b44f26d1ba19eec0c26e6a846750fc5922d349b6 Mon Sep 17 00:00:00 2001 From: Neil Trini Lasrado Date: Tue, 21 Jul 2015 16:13:02 +0530 Subject: [PATCH 19/53] Fixed Max value for update FG Item in Production Order --- .../doctype/production_order/production_order.js | 2 +- erpnext/stock/doctype/stock_entry/stock_entry.py | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_order/production_order.js b/erpnext/manufacturing/doctype/production_order/production_order.js index 657756d7578..04fcaa5c011 100644 --- a/erpnext/manufacturing/doctype/production_order/production_order.js +++ b/erpnext/manufacturing/doctype/production_order/production_order.js @@ -162,7 +162,7 @@ $.extend(cur_frm.cscript, { make_se: function(purpose) { var me = this; var max = (purpose === "Manufacture") ? - flt(this.frm.doc.qty) - flt(this.frm.doc.produced_qty) : + flt(this.frm.doc.material_transferred_for_manufacturing) - flt(this.frm.doc.produced_qty) : flt(this.frm.doc.qty) - flt(this.frm.doc.material_transferred_for_manufacturing); frappe.prompt({fieldtype:"Int", label: __("Qty for {0}", [purpose]), fieldname:"qty", diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 65f9ef06782..c49129da125 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -611,16 +611,16 @@ class StockEntry(StockController): return item_dict def get_transfered_raw_materials(self): - items_dict = frappe.db.sql("""select item_name, item_code, sum(qty) as qty, to_warehouse, from_warehouse, - description, stock_uom, expense_account, cost_center from `tabStock Entry` se,`tabStock Entry Detail` sed + items_dict = frappe.db.sql("""select item_name, item_code, sum(qty) as qty, sed.s_warehouse as s_warehouse, sed.t_warehouse + as t_warehouse, description, stock_uom, expense_account, cost_center from `tabStock Entry` se,`tabStock Entry Detail` sed where se.name = sed.parent and se.docstatus=1 and se.purpose='Material Transfer for Manufacture' and - se.production_order= %s and se.to_warehouse= %s group by sed.item_code""", - (self.production_order, self.from_warehouse), as_dict=1) + se.production_order= %s and ifnull(sed.s_warehouse, '') != '' group by sed.item_code, sed.s_warehouse, sed.t_warehouse""", + self.production_order, as_dict=1) transfered_materials = frappe.db.sql("""select item_code, sum(qty) as qty from `tabStock Entry` se, `tabStock Entry Detail` sed where se.name = sed.parent and se.docstatus=1 and - se.purpose='Manufacture' and se.production_order= %s and se.from_warehouse= %s - group by sed.item_code""", (self.production_order, self.from_warehouse), as_dict=1) + se.purpose='Manufacture' and se.production_order= %s and ifnull(sed.s_warehouse, '') != '' + group by sed.item_code, sed.s_warehouse, sed.t_warehouse""", self.production_order, as_dict=1) transfered_qty= {} for d in transfered_materials: @@ -644,8 +644,8 @@ class StockEntry(StockController): if qty > 0: self.add_to_stock_entry_detail({ item.item_code: { - "to_warehouse": item.to_warehouse, - "from_warehouse": item.from_warehouse, + "to_warehouse": item.t_warehouse, + "from_warehouse": item.s_warehouse, "qty": qty, "item_name": item.item_name, "description": item.description, From 1c6eeb228f6ba74a6a464227a27a33ed24bd961a Mon Sep 17 00:00:00 2001 From: Neil Trini Lasrado Date: Wed, 29 Jul 2015 15:08:46 +0530 Subject: [PATCH 20/53] Fixes in Stock Entry --- .../stock/doctype/stock_entry/stock_entry.py | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index c49129da125..42dd409afb2 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -611,20 +611,20 @@ class StockEntry(StockController): return item_dict def get_transfered_raw_materials(self): - items_dict = frappe.db.sql("""select item_name, item_code, sum(qty) as qty, sed.s_warehouse as s_warehouse, sed.t_warehouse - as t_warehouse, description, stock_uom, expense_account, cost_center from `tabStock Entry` se,`tabStock Entry Detail` sed + raw_materials = frappe.db.sql("""select item_name, item_code, sum(qty) as qty, sed.t_warehouse as warehouse, + description, stock_uom, expense_account, cost_center from `tabStock Entry` se,`tabStock Entry Detail` sed where se.name = sed.parent and se.docstatus=1 and se.purpose='Material Transfer for Manufacture' and - se.production_order= %s and ifnull(sed.s_warehouse, '') != '' group by sed.item_code, sed.s_warehouse, sed.t_warehouse""", + se.production_order= %s and ifnull(sed.t_warehouse, '') != '' group by sed.item_code, sed.t_warehouse""", self.production_order, as_dict=1) - transfered_materials = frappe.db.sql("""select item_code, sum(qty) as qty from `tabStock Entry` se, + transfered_materials = frappe.db.sql("""select item_code, sed.s_warehouse as warehouse, sum(qty) as qty from `tabStock Entry` se, `tabStock Entry Detail` sed where se.name = sed.parent and se.docstatus=1 and se.purpose='Manufacture' and se.production_order= %s and ifnull(sed.s_warehouse, '') != '' - group by sed.item_code, sed.s_warehouse, sed.t_warehouse""", self.production_order, as_dict=1) + group by sed.item_code, sed.s_warehouse""", self.production_order, as_dict=1) transfered_qty= {} for d in transfered_materials: - transfered_qty.setdefault(d.item_code, d.qty) + transfered_qty.setdefault(d.item_code,[]).append({d.warehouse: d.qty}) po_qty = frappe.db.sql("""select qty, produced_qty, material_transferred_for_manufacturing from `tabProduction Order` where name=%s""", self.production_order, as_dict=1)[0] @@ -632,20 +632,22 @@ class StockEntry(StockController): produced_qty = flt(po_qty.produced_qty) trans_qty = flt(po_qty.material_transferred_for_manufacturing) - for item in items_dict: + for item in raw_materials: qty= item.qty if manufacturing_qty > (produced_qty + flt(self.fg_completed_qty)): qty = (qty/trans_qty) * flt(self.fg_completed_qty) elif transfered_qty.get(item.item_code): - qty-= transfered_qty.get(item.item_code) + for d in transfered_qty.get(item.item_code): + if d.get(item.warehouse): + qty-= d.get(item.warehouse) if qty > 0: self.add_to_stock_entry_detail({ item.item_code: { - "to_warehouse": item.t_warehouse, - "from_warehouse": item.s_warehouse, + "from_warehouse": item.warehouse, + "to_warehouse": "", "qty": qty, "item_name": item.item_name, "description": item.description, @@ -719,7 +721,7 @@ class StockEntry(StockController): se_child.s_warehouse = self.from_warehouse if se_child.t_warehouse==None: se_child.t_warehouse = self.to_warehouse - + # in stock uom se_child.transfer_qty = flt(item_dict[d]["qty"]) se_child.conversion_factor = 1.00 From ac6d11eb3ce96cf72c9f1ade44fc377ed0ea8949 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 10 Aug 2015 19:13:30 +0530 Subject: [PATCH 21/53] minor improvements --- .../stock/doctype/stock_entry/stock_entry.py | 44 ++++++++++++------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 42dd409afb2..62f5b88d9a4 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -604,27 +604,39 @@ class StockEntry(StockController): from erpnext.manufacturing.doctype.bom.bom import get_bom_items_as_dict # item dict = { item_code: {qty, description, stock_uom} } - item_dict = get_bom_items_as_dict(self.bom_no, self.company, qty=qty, fetch_exploded = self.use_multi_level_bom) + item_dict = get_bom_items_as_dict(self.bom_no, self.company, qty=qty, + fetch_exploded = self.use_multi_level_bom) for item in item_dict.values(): item.from_warehouse = self.from_warehouse or item.default_warehouse return item_dict def get_transfered_raw_materials(self): - raw_materials = frappe.db.sql("""select item_name, item_code, sum(qty) as qty, sed.t_warehouse as warehouse, - description, stock_uom, expense_account, cost_center from `tabStock Entry` se,`tabStock Entry Detail` sed - where se.name = sed.parent and se.docstatus=1 and se.purpose='Material Transfer for Manufacture' and - se.production_order= %s and ifnull(sed.t_warehouse, '') != '' group by sed.item_code, sed.t_warehouse""", - self.production_order, as_dict=1) + transferred_materials = frappe.db.sql(""" + select + item_name, item_code, sum(qty) as qty, sed.t_warehouse as warehouse, + description, stock_uom, expense_account, cost_center + from `tabStock Entry` se,`tabStock Entry Detail` sed + where + se.name = sed.parent and se.docstatus=1 and se.purpose='Material Transfer for Manufacture' + and se.production_order= %s and ifnull(sed.t_warehouse, '') != '' + group by sed.item_code, sed.t_warehouse + """, self.production_order, as_dict=1) - transfered_materials = frappe.db.sql("""select item_code, sed.s_warehouse as warehouse, sum(qty) as qty from `tabStock Entry` se, - `tabStock Entry Detail` sed where se.name = sed.parent and se.docstatus=1 and - se.purpose='Manufacture' and se.production_order= %s and ifnull(sed.s_warehouse, '') != '' - group by sed.item_code, sed.s_warehouse""", self.production_order, as_dict=1) + materials_already_backflushed = frappe.db.sql(""" + select + item_code, sed.s_warehouse as warehouse, sum(qty) as qty + from + `tabStock Entry` se, `tabStock Entry Detail` sed + where + se.name = sed.parent and se.docstatus=1 and se.purpose='Manufacture' + and se.production_order= %s and ifnull(sed.s_warehouse, '') != '' + group by sed.item_code, sed.s_warehouse + """, self.production_order, as_dict=1) - transfered_qty= {} - for d in transfered_materials: - transfered_qty.setdefault(d.item_code,[]).append({d.warehouse: d.qty}) + backflushed_materials= {} + for d in materials_already_backflushed: + backflushed_materials.setdefault(d.item_code,[]).append({d.warehouse: d.qty}) po_qty = frappe.db.sql("""select qty, produced_qty, material_transferred_for_manufacturing from `tabProduction Order` where name=%s""", self.production_order, as_dict=1)[0] @@ -632,14 +644,14 @@ class StockEntry(StockController): produced_qty = flt(po_qty.produced_qty) trans_qty = flt(po_qty.material_transferred_for_manufacturing) - for item in raw_materials: + for item in transferred_materials: qty= item.qty if manufacturing_qty > (produced_qty + flt(self.fg_completed_qty)): qty = (qty/trans_qty) * flt(self.fg_completed_qty) - elif transfered_qty.get(item.item_code): - for d in transfered_qty.get(item.item_code): + elif backflushed_materials.get(item.item_code): + for d in backflushed_materials.get(item.item_code): if d.get(item.warehouse): qty-= d.get(item.warehouse) From 6dc40e9baf2ce5d52fc94728761c57ade1adfe0c Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 10 Aug 2015 19:18:39 +0530 Subject: [PATCH 22/53] Update accounts_controller.py --- erpnext/controllers/accounts_controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index bb8c65bd751..915d2947b56 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -11,7 +11,7 @@ from erpnext.utilities.transaction_base import TransactionBase from erpnext.controllers.recurring_document import convert_to_recurring, validate_recurring_document from erpnext.controllers.sales_and_purchase_return import validate_return -force_item_fields = ("item_name", "item_group", "barcode", "brand", "stock_uom") +force_item_fields = ("item_group", "barcode", "brand", "stock_uom") class CustomerFrozen(frappe.ValidationError): pass From 28eff7fb91f82171430af6e33977c2435bcfbc21 Mon Sep 17 00:00:00 2001 From: Dominik Date: Mon, 10 Aug 2015 20:22:21 +0530 Subject: [PATCH 23/53] Phone removed from mandatory in Address --- erpnext/utilities/doctype/address/address.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/utilities/doctype/address/address.json b/erpnext/utilities/doctype/address/address.json index 7cb8d82c4ed..a4d7a552567 100644 --- a/erpnext/utilities/doctype/address/address.json +++ b/erpnext/utilities/doctype/address/address.json @@ -101,7 +101,7 @@ "fieldtype": "Data", "label": "Phone", "permlevel": 0, - "reqd": 1 + "reqd": 0 }, { "fieldname": "fax", @@ -199,7 +199,7 @@ "icon": "icon-map-marker", "idx": 1, "in_dialog": 0, - "modified": "2015-06-01 06:42:18.331818", + "modified": "2015-08-10 19:42:18.331818", "modified_by": "Administrator", "module": "Utilities", "name": "Address", From 4101a4886950e10f82eb5804b57bd8e9d86c6034 Mon Sep 17 00:00:00 2001 From: Dominik Date: Mon, 10 Aug 2015 23:59:16 +0530 Subject: [PATCH 24/53] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 54a9f4c0b9b..d91136f3ff5 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ ERPNext is built on the [Frappe](https://github.com/frappe/frappe) Framework, a ### Full Install -The Easy Way install script for bench will install all dependencies (e.g. MariaDB). See https://github.com/frappe/bench +The Easy Way: our install script for bench will install all dependencies (e.g. MariaDB). See https://github.com/frappe/bench for more details. New passwords will be created for the ERPNext "Administrator" user, the MariaDB root user, and the frappe user (the script displays the passwords and saves them to ~/frappe_passwords.txt). @@ -71,6 +71,6 @@ We do not allow the use of the trademark in advertising, including AdSense/AdWor Please note that it is not the goal of this policy to limit commercial activity around ERPNext. We encourage ERPNext-based businesses, and we would love to see hundreds of them. -When in doubt about your use of the ERPNext name or logo, please contact the Frappe Technologies for clarification. +When in doubt about your use of the ERPNext name or logo, please contact Frappe Technologies for clarification. -(inspired from WordPress) +(inspired by WordPress) From ba1f4263dde6634bfa60b82b1c53f764d965f716 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Tue, 11 Aug 2015 17:04:56 +0530 Subject: [PATCH 25/53] Added Rohit Industries as sponsors for #3546 --- sponsors.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sponsors.md b/sponsors.md index 9b844871350..8e10081d47a 100644 --- a/sponsors.md +++ b/sponsors.md @@ -37,5 +37,13 @@ For Additional Costs in Stock Entry #3613 + + + Rohit Industries + + + For Mandrill Integration #3546 + + From 4a91c49e0d0e38747997ff2b68fa15f203f409b2 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 11 Aug 2015 18:15:16 +0530 Subject: [PATCH 26/53] discount percentage should not be reset to zero on applying price list --- erpnext/stock/get_item_details.py | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index c45439a90b3..a57dd59974b 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -401,7 +401,6 @@ def apply_price_list_on_item(args): item_details = frappe._dict() item_doc = frappe.get_doc("Item", args.item_code) get_price_list_rate(args, item_doc, item_details) - item_details.discount_percentage = 0.0 item_details.update(get_pricing_rule_for_item(args)) return item_details From 1828c12481a0453ff7c560055b516fa528765e55 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Mon, 10 Aug 2015 17:04:07 +0530 Subject: [PATCH 27/53] [refactor] added dynamic link in journal entry, #3847 --- .../bank_reconciliation.py | 3 +- .../bank_reconciliation_detail.json | 148 ++++++++- .../doctype/journal_entry/journal_entry.js | 71 +++-- .../doctype/journal_entry/journal_entry.py | 289 ++++++++++------- .../journal_entry/test_journal_entry.py | 69 ++-- .../journal_entry_account.json | 299 ++++++++++++++---- .../payment_reconciliation.py | 6 +- .../doctype/payment_tool/payment_tool.py | 10 +- .../doctype/payment_tool/test_payment_tool.py | 41 +-- .../purchase_invoice/purchase_invoice.js | 11 +- .../purchase_invoice/purchase_invoice.py | 10 +- .../doctype/sales_invoice/sales_invoice.js | 22 +- .../doctype/sales_invoice/sales_invoice.py | 8 +- .../sales_invoice/test_sales_invoice.py | 72 ++--- .../payment_period_based_on_invoice_date.py | 12 +- erpnext/accounts/utils.py | 30 +- .../doctype/purchase_order/purchase_order.js | 45 ++- .../current/journal_entry_rename.md | 1 + erpnext/controllers/accounts_controller.py | 56 ++-- .../hr/doctype/expense_claim/expense_claim.js | 10 +- erpnext/patches/v5_4/cleanup_journal_entry.py | 14 + .../public/js/controllers/stock_controller.js | 25 +- erpnext/public/js/queries.js | 23 ++ .../doctype/sales_order/sales_order.js | 53 +++- .../doctype/delivery_note/delivery_note.js | 22 +- .../purchase_receipt/purchase_receipt.js | 11 +- 26 files changed, 887 insertions(+), 474 deletions(-) create mode 100644 erpnext/change_log/current/journal_entry_rename.md create mode 100644 erpnext/patches/v5_4/cleanup_journal_entry.py diff --git a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py index 7dd021e8380..f1c8820c09d 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 b2c6e66e045..78691a3d009 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 ec17e34bb58..38f3e0c6778 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 80d243580d8..b48e46c5a71 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 2aa60f0056c..c70f10df631 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 b336d4910ce..d09171434d5 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 bcca0f2da15..933570f9eb8 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 6a002af8b40..4edbebd09f7 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 a4ff33331af..321986cd466 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 8e91250ac66..62578659925 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 00ea6ae38a0..132cb1027d6 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 017ab3a10d1..caf741e578d 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 6d76b5322f5..4285eb8b14b 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 67f4a8e716b..c7a992c489c 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 699d684c730..604bc5660a1 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 298ff8eea89..f41d19dddd6 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 09a0e919a9f..a5cd6ce6c2a 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 00000000000..9bd1b668666 --- /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 915d2947b56..ecd9e1a1ad8 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 a56929d1c3a..1d405da00ab 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 00000000000..9968e3cc102 --- /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 f5f7aa0c3d2..ff5cf534e6d 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 b487c239384..12307fb9b31 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 d06d550b941..4a047e41ae8 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 94356da8e1f..e7ede652a3a 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 fcaf9f8074f..38b054cbfd3 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", From 3131c732ff36df25037fd4543deebe0df73c6063 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Tue, 11 Aug 2015 15:41:52 +0530 Subject: [PATCH 28/53] [fix] capacity planning error --- .../journal_entry/test_journal_entry.py | 4 +- .../purchase_invoice/test_purchase_invoice.py | 43 +++++++++---------- .../manufacturing_settings.py | 8 ++++ .../production_order/production_order.py | 11 ++--- erpnext/projects/doctype/time_log/time_log.py | 12 ++++-- 5 files changed, 43 insertions(+), 35 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py index c70f10df631..8995e34220f 100644 --- a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py @@ -69,8 +69,8 @@ class TestJournalEntry(unittest.TestCase): if test_voucher.doctype == "Journal Entry": # if test_voucher is a Journal Entry, test cancellation of test_voucher test_voucher.cancel() - self.assertTrue(not frappe.db.sql("""select name from `tabJournal Entry Account` - where against_jv=%s""", test_voucher.name)) + self.assertFalse(frappe.db.sql("""select name from `tabJournal Entry Account` + where reference_type='Journal Entry' and reference_name=%s""", test_voucher.name)) elif test_voucher.doctype in ["Sales Order", "Purchase Order"]: # if test_voucher is a Sales Order/Purchase Order, test error on cancellation of test_voucher diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 5f3d4c8a042..d12b2d7ca91 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -218,23 +218,20 @@ class TestPurchaseInvoice(unittest.TestCase): pi.load_from_db() self.assertTrue(frappe.db.sql("""select name from `tabJournal Entry Account` - where against_voucher=%s""", pi.name)) - - self.assertTrue(frappe.db.sql("""select name from `tabJournal Entry Account` - where against_voucher=%s and debit=300""", pi.name)) + where reference_type='Purchase Invoice' and reference_name=%s and debit=300""", pi.name)) self.assertEqual(pi.outstanding_amount, 1212.30) pi.cancel() - self.assertTrue(not frappe.db.sql("""select name from `tabJournal Entry Account` - where against_voucher=%s""", pi.name)) + self.assertFalse(frappe.db.sql("""select name from `tabJournal Entry Account` + where reference_type='Purchase Invoice' and reference_name=%s""", pi.name)) def test_recurring_invoice(self): from erpnext.controllers.tests.test_recurring_document import test_recurring_document test_recurring_document(self, test_records) - - def test_total_purchase_cost_for_project(self): + + def test_total_purchase_cost_for_project(self): purchase_invoice = frappe.new_doc('Purchase Invoice') purchase_invoice.update({ "credit_to": "_Test Payable - _TC", @@ -260,29 +257,29 @@ class TestPurchaseInvoice(unittest.TestCase): ] }) purchase_invoice.save() - purchase_invoice.submit() + purchase_invoice.submit() self.assertEqual(frappe.db.get_value("Project", "_Test Project", "total_purchase_cost"), 2000) - + purchase_invoice1 = frappe.copy_doc(purchase_invoice) purchase_invoice1.save() purchase_invoice1.submit() - + self.assertEqual(frappe.db.get_value("Project", "_Test Project", "total_purchase_cost"), 4000) - - purchase_invoice1.cancel() + + purchase_invoice1.cancel() self.assertEqual(frappe.db.get_value("Project", "_Test Project", "total_purchase_cost"), 2000) - - purchase_invoice.cancel() + + purchase_invoice.cancel() self.assertEqual(frappe.db.get_value("Project", "_Test Project", "total_purchase_cost"), 0) - + def test_return_purchase_invoice(self): set_perpetual_inventory() - + pi = make_purchase_invoice() - + return_pi = make_purchase_invoice(is_return=1, return_against=pi.name, qty=-2) - - + + # check gl entries for return gl_entries = frappe.db.sql("""select account, debit, credit from `tabGL Entry` where voucher_type=%s and voucher_no=%s @@ -298,9 +295,9 @@ class TestPurchaseInvoice(unittest.TestCase): for gle in gl_entries: self.assertEquals(expected_values[gle.account][0], gle.debit) self.assertEquals(expected_values[gle.account][1], gle.credit) - + set_perpetual_inventory(0) - + def make_purchase_invoice(**args): pi = frappe.new_doc("Purchase Invoice") args = frappe._dict(args) @@ -313,7 +310,7 @@ def make_purchase_invoice(**args): pi.currency = args.currency or "INR" pi.is_return = args.is_return pi.return_against = args.return_against - + pi.append("items", { "item_code": args.item or args.item_code or "_Test Item", "warehouse": args.warehouse or "_Test Warehouse - _TC", diff --git a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.py b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.py index ab6bbab3fa4..3fabae0e075 100644 --- a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.py +++ b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.py @@ -4,6 +4,14 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document +from frappe.utils import cint +from dateutil.relativedelta import relativedelta class ManufacturingSettings(Document): pass + +def get_mins_between_operations(): + if not hasattr(frappe.local, "_mins_between_operations"): + frappe.local._mins_between_operations = cint(frappe.db.get_single_value("Manufacturing Settings", + "mins_between_operations")) or 10 + return relativedelta(minutes=frappe.local._mins_between_operations) diff --git a/erpnext/manufacturing/doctype/production_order/production_order.py b/erpnext/manufacturing/doctype/production_order/production_order.py index 023c43eac49..b6befe08673 100644 --- a/erpnext/manufacturing/doctype/production_order/production_order.py +++ b/erpnext/manufacturing/doctype/production_order/production_order.py @@ -13,6 +13,7 @@ from erpnext.stock.doctype.item.item import validate_end_of_life from erpnext.manufacturing.doctype.workstation.workstation import WorkstationHolidayError, NotInWorkingHoursError from erpnext.projects.doctype.time_log.time_log import OverlapError from erpnext.stock.doctype.stock_entry.stock_entry import get_additional_costs +from erpnext.manufacturing.doctype.manufacturing_settings.manufacturing_settings import get_mins_between_operations class OverProductionError(frappe.ValidationError): pass class StockOverProductionError(frappe.ValidationError): pass @@ -231,6 +232,7 @@ class ProductionOrder(Document): original_start_time = time_log.from_time while True: _from_time = time_log.from_time + try: time_log.save() break @@ -248,6 +250,7 @@ class ProductionOrder(Document): frappe.msgprint(_("Unable to find Time Slot in the next {0} days for Operation {1}").format(plan_days, d.operation)) break + # if time log needs to be moved, make sure that the from time is not the same if _from_time == time_log.from_time: frappe.throw("Capacity Planning Error") @@ -273,19 +276,13 @@ class ProductionOrder(Document): d.planned_start_time = self.planned_start_date else: d.planned_start_time = get_datetime(self.operations[i-1].planned_end_time)\ - + self.get_mins_between_operations() + + get_mins_between_operations() d.planned_end_time = get_datetime(d.planned_start_time) + relativedelta(minutes = d.time_in_mins) if d.planned_start_time == d.planned_end_time: frappe.throw(_("Capacity Planning Error")) - def get_mins_between_operations(self): - if not hasattr(self, "_mins_between_operations"): - self._mins_between_operations = cint(frappe.db.get_single_value("Manufacturing Settings", - "mins_between_operations")) or 10 - return relativedelta(minutes=self._mins_between_operations) - def check_operation_fits_in_working_hours(self, d): """Raises expection if operation is longer than working hours in the given workstation.""" from erpnext.manufacturing.doctype.workstation.workstation import check_if_within_operating_hours diff --git a/erpnext/projects/doctype/time_log/time_log.py b/erpnext/projects/doctype/time_log/time_log.py index 0f50e9cc5aa..6e937c09e75 100644 --- a/erpnext/projects/doctype/time_log/time_log.py +++ b/erpnext/projects/doctype/time_log/time_log.py @@ -6,6 +6,7 @@ import frappe, json from frappe import _ from frappe.utils import cstr, flt, get_datetime, get_time, getdate from dateutil.relativedelta import relativedelta +from erpnext.manufacturing.doctype.manufacturing_settings.manufacturing_settings import get_mins_between_operations class OverlapError(frappe.ValidationError): pass class OverProductionLoggedError(frappe.ValidationError): pass @@ -182,9 +183,14 @@ class TimeLog(Document): def move_to_next_non_overlapping_slot(self): """If in overlap, set start as the end point of the overlapping time log""" - overlapping = self.get_overlap_for("workstation") - if overlapping: - self.from_time = get_datetime(overlapping.to_time) + relativedelta(minutes=10) + overlapping = self.get_overlap_for("workstation") \ + or self.get_overlap_for("employee") \ + or self.get_overlap_for("user") + + if not overlapping: + frappe.throw("Logical error: Must find overlapping") + + self.from_time = get_datetime(overlapping.to_time) + get_mins_between_operations() def get_time_log_summary(self): """Returns 'Actual Operating Time'. """ From 207b3efed736196a89264dc9227d6d3f58d1072f Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Wed, 12 Aug 2015 14:33:46 +0530 Subject: [PATCH 29/53] [fix] journal entry get_query --- .../doctype/journal_entry/journal_entry.js | 14 +++++++++++--- .../doctype/journal_entry/journal_entry.py | 8 ++++---- erpnext/patches/v5_4/cleanup_journal_entry.py | 4 ++-- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js index 38f3e0c6778..eac8f44e929 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.js +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js @@ -77,13 +77,21 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({ frappe.model.validate_missing(jvd, "party_type"); frappe.model.validate_missing(jvd, "party"); - return { + + var out = { filters: [ - [jvd.reference_type, jvd.reference_type.indexOf("Sales")==1 ? "customer" : "supplier", "=", jvd.party], + [jvd.reference_type, jvd.reference_type.indexOf("Sales")===0 ? "customer" : "supplier", "=", jvd.party], [jvd.reference_type, "docstatus", "=", 1], - [jvd.reference_type, "outstanding_amount", "!=", 0] ] }; + + if(in_list(["Sales Invoice", "Purchase Invoice"], jvd.reference_type)) { + out.filters.push([jvd.reference_type, "outstanding_amount", "!=", 0]); + } else { + out.filters.push([jvd.reference_type, "per_billed", "<", 100]); + } + + return out; }); diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index b48e46c5a71..00e2e913186 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -118,7 +118,7 @@ class JournalEntry(AccountsController): def validate_against_jv(self): for d in self.get('accounts'): - if d.reference_type=="Journal Voucher": + if d.reference_type=="Journal Entry": 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") @@ -132,7 +132,7 @@ class JournalEntry(AccountsController): against_entries = frappe.db.sql("""select * from `tabJournal Entry Account` where account = %s and docstatus = 1 and parent = %s - and ifnull(reference_type, '') = '' and ifnull(reference_name, '') = '' + and ifnull(reference_type, '') in ("", "Sales Order", "Purchase Order") """, (d.account, d.reference_name), as_dict=True) if not against_entries: @@ -163,8 +163,8 @@ class JournalEntry(AccountsController): for d in self.get("accounts"): if not d.reference_type: d.reference_name = None - if not d.reference_type: - d.reference_name = None + if not d.reference_name: + d.reference_type = 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" diff --git a/erpnext/patches/v5_4/cleanup_journal_entry.py b/erpnext/patches/v5_4/cleanup_journal_entry.py index 9968e3cc102..b8edbaa9622 100644 --- a/erpnext/patches/v5_4/cleanup_journal_entry.py +++ b/erpnext/patches/v5_4/cleanup_journal_entry.py @@ -2,10 +2,10 @@ 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"), + ("Sales Invoice", "against_invoice"), + ("Purchase Invoice", "against_voucher"), ("Journal Entry", "against_jv"), ("Expense Claim", "against_expense_claim"), ): From ed40542658ecafb270eb581d931cd772bc46fa42 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Thu, 13 Aug 2015 10:34:49 +0530 Subject: [PATCH 30/53] [fix] minor auto-select party type and add has_permission in whitelisted methods --- .../doctype/journal_entry/journal_entry.js | 5 +++-- .../doctype/journal_entry/journal_entry.py | 18 ++++++++++++++++++ .../payment_reconciliation.py | 2 +- .../page/accounts_browser/accounts_browser.js | 2 +- 4 files changed, 23 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js index eac8f44e929..b05adbd2798 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.js +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js @@ -225,11 +225,12 @@ cur_frm.cscript.account = function(doc,dt,dn) { var d = locals[dt][dn]; if(d.account) { return frappe.call({ - method: "erpnext.accounts.utils.get_balance_on", + method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_account_balance_and_party_type", args: {account: d.account, date: doc.posting_date}, callback: function(r) { - d.balance = r.message; + $.extend(d, r.message); refresh_field('balance', d.name, 'accounts'); + refresh_field('party_type', d.name, 'accounts'); } }); } diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 00e2e913186..d891dd5b315 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -611,6 +611,8 @@ def get_against_jv(doctype, txt, searchfield, start, page_len, filters): @frappe.whitelist() def get_outstanding(args): + if not frappe.has_permission("Account"): + frappe.msgprint(_("No Permission"), raise_exception=1) args = eval(args) if args.get("doctype") == "Journal Entry": condition = " and party=%(party)s" if args.get("party") else "" @@ -637,6 +639,9 @@ def get_outstanding(args): @frappe.whitelist() def get_party_account_and_balance(company, party_type, party): + if not frappe.has_permission("Account"): + frappe.msgprint(_("No Permission"), raise_exception=1) + from erpnext.accounts.party import get_party_account account = get_party_account(company, party, party_type) @@ -648,3 +653,16 @@ def get_party_account_and_balance(company, party_type, party): "balance": account_balance, "party_balance": party_balance } + +@frappe.whitelist() +def get_account_balance_and_party_type(account, date): + """Returns dict of account balance and party type to be set in Journal Entry on selection of account.""" + if not frappe.has_permission("Account"): + frappe.msgprint(_("No Permission"), raise_exception=1) + + account_type = frappe.db.get_value("Account", account, "account_type") + return { + "balance": get_balance_on(account, date), + "party_type": {"Receivable":"Customer", "Payable":"Supplier"}.get(account_type, "") + } + diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index 933570f9eb8..b283c8fcb67 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -34,7 +34,7 @@ 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.reference_type, '')='' + and ifnull(t2.reference_type, '') in ('', 'Sales Order', 'Purchase Order') {cond} and (CASE WHEN t1.voucher_type in ('Debit Note', 'Credit Note') diff --git a/erpnext/accounts/page/accounts_browser/accounts_browser.js b/erpnext/accounts/page/accounts_browser/accounts_browser.js index cb36d059fde..77a87fff7c0 100644 --- a/erpnext/accounts/page/accounts_browser/accounts_browser.js +++ b/erpnext/accounts/page/accounts_browser/accounts_browser.js @@ -202,7 +202,7 @@ erpnext.AccountsChart = Class.extend({ title:__('New Account'), fields: [ {fieldtype:'Data', fieldname:'account_name', label:__('New Account Name'), reqd:true, - description: __("Name of new Account. Note: Please don't create accounts for Customers and Suppliers, they are created automatically from the Customer and Supplier master")}, + description: __("Name of new Account. Note: Please don't create accounts for Customers and Suppliers")}, {fieldtype:'Check', fieldname:'is_group', label:__('Is Group'), description: __('Further accounts can be made under Groups, but entries can be made against non-Groups')}, {fieldtype:'Select', fieldname:'account_type', label:__('Account Type'), From c51d5ba5df789eb6e7b1a90622064be060fe561d Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 11 Aug 2015 18:28:24 +0530 Subject: [PATCH 31/53] [patch] Update item description in Production Order based on item master --- erpnext/patches.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index d12154b32b0..aa89f9a3482 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -190,3 +190,4 @@ erpnext.patches.v5_4.fix_invoice_outstanding execute:frappe.db.sql("update `tabStock Ledger Entry` set stock_queue = '[]' where voucher_type = 'Stock Reconciliation' and ifnull(qty_after_transaction, 0) = 0") erpnext.patches.v5_4.fix_missing_item_images erpnext.patches.v5_4.stock_entry_additional_costs +execute:frappe.db.sql("update `tabProduction Order` pro set description = (select description from tabItem where name=pro.production_item) where ifnull(description, '') = ''") \ No newline at end of file From bcf7da6b1e50fbe074c0cc372b775ac196c81b65 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Thu, 13 Aug 2015 11:46:01 +0530 Subject: [PATCH 32/53] [patch] cleanup_journal_entry --- erpnext/patches.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index d12154b32b0..4982ba5e34a 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -190,3 +190,4 @@ erpnext.patches.v5_4.fix_invoice_outstanding execute:frappe.db.sql("update `tabStock Ledger Entry` set stock_queue = '[]' where voucher_type = 'Stock Reconciliation' and ifnull(qty_after_transaction, 0) = 0") erpnext.patches.v5_4.fix_missing_item_images erpnext.patches.v5_4.stock_entry_additional_costs +erpnext.patches.v5_4.cleanup_journal_entry From 6ddcac7ceefea4bceb014941be58a283dab63372 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 13 Aug 2015 11:47:25 +0530 Subject: [PATCH 33/53] minor fix --- erpnext/public/js/controllers/stock_controller.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/public/js/controllers/stock_controller.js b/erpnext/public/js/controllers/stock_controller.js index ff5cf534e6d..9df7d0f5d8a 100644 --- a/erpnext/public/js/controllers/stock_controller.js +++ b/erpnext/public/js/controllers/stock_controller.js @@ -12,7 +12,8 @@ erpnext.stock.StockController = frappe.ui.form.Controller.extend({ }, setup_warehouse_query: function() { - erpnext.queries.setup_queries(me.frm, "Warehouse", function() { + var me = this; + erpnext.queries.setup_queries(this.frm, "Warehouse", function() { return erpnext.queries.warehouse(me.frm.doc); }); }, From f738b951c51f2fafd960c4253d92121ce07101e0 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Thu, 13 Aug 2015 11:50:03 +0530 Subject: [PATCH 34/53] [patch] cleanup_journal_entry --- erpnext/patches/v5_4/cleanup_journal_entry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/patches/v5_4/cleanup_journal_entry.py b/erpnext/patches/v5_4/cleanup_journal_entry.py index b8edbaa9622..9d493eab05a 100644 --- a/erpnext/patches/v5_4/cleanup_journal_entry.py +++ b/erpnext/patches/v5_4/cleanup_journal_entry.py @@ -9,6 +9,6 @@ def execute(): ("Journal Entry", "against_jv"), ("Expense Claim", "against_expense_claim"), ): - frappe.db.update("""update `tabJournal Entry Detail` + frappe.db.sql("""update `tabJournal Entry Detail` set reference_type=%s and reference_name={0} where ifnull({0}, '') != '' """.format(fieldname), doctype) From b01cc1b44980130b4502fbb3d7e5d2764bd21f85 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Thu, 13 Aug 2015 11:54:04 +0530 Subject: [PATCH 35/53] [patch] cleanup_journal_entry --- erpnext/patches/v5_4/cleanup_journal_entry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/patches/v5_4/cleanup_journal_entry.py b/erpnext/patches/v5_4/cleanup_journal_entry.py index 9d493eab05a..3ae226b3835 100644 --- a/erpnext/patches/v5_4/cleanup_journal_entry.py +++ b/erpnext/patches/v5_4/cleanup_journal_entry.py @@ -9,6 +9,6 @@ def execute(): ("Journal Entry", "against_jv"), ("Expense Claim", "against_expense_claim"), ): - frappe.db.sql("""update `tabJournal Entry Detail` + frappe.db.sql("""update `tabJournal Entry Account` set reference_type=%s and reference_name={0} where ifnull({0}, '') != '' """.format(fieldname), doctype) From 7e4b93f48ea66c22af90ff8c835b29f3d375d0c5 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Thu, 13 Aug 2015 12:10:21 +0530 Subject: [PATCH 36/53] [patch] cleanup_journal_entry --- erpnext/patches/v5_4/cleanup_journal_entry.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/patches/v5_4/cleanup_journal_entry.py b/erpnext/patches/v5_4/cleanup_journal_entry.py index 3ae226b3835..5de17c7325d 100644 --- a/erpnext/patches/v5_4/cleanup_journal_entry.py +++ b/erpnext/patches/v5_4/cleanup_journal_entry.py @@ -1,6 +1,7 @@ import frappe def execute(): + frappe.reload_doctype("Journal Entry") for doctype, fieldname in ( ("Sales Order", "against_sales_order"), ("Purchase Order", "against_purchase_order"), From 2e54da2ea54b75d71c6f2bc60f4b5bfbb2e289b0 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 13 Aug 2015 12:19:20 +0530 Subject: [PATCH 37/53] [fix] fraction issue in gl entry --- erpnext/accounts/general_ledger.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index 8081459f2e3..7edd69f70a1 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -50,7 +50,7 @@ def merge_similar_entries(gl_map): merged_gl_map.append(entry) # filter zero debit and credit entries - merged_gl_map = filter(lambda x: flt(x.debit)!=0 or flt(x.credit)!=0, merged_gl_map) + merged_gl_map = filter(lambda x: flt(x.debit, 9)!=0 or flt(x.credit, 9)!=0, merged_gl_map) return merged_gl_map def check_if_in_list(gle, gl_map): From 9a73b7a3194109dacbd3906bec288f38f35d2993 Mon Sep 17 00:00:00 2001 From: Neil Trini Lasrado Date: Fri, 7 Aug 2015 16:40:12 +0530 Subject: [PATCH 38/53] Changed UOM validation for Item Master --- erpnext/stock/doctype/item/item.py | 69 +++++---- .../stock_uom_replace_utility.py | 136 +++++++++--------- 2 files changed, 109 insertions(+), 96 deletions(-) diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index 089c067bf6d..eaf904d1a87 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -48,7 +48,7 @@ class Item(WebsiteGenerator): self.website_image = self.image self.check_warehouse_is_set_for_stock_item() - self.check_stock_uom_with_bin() + self.validate_uom() self.add_default_uom_in_conversion_factor_table() self.validate_conversion_factor() self.validate_item_type() @@ -105,35 +105,6 @@ class Item(WebsiteGenerator): [self.remove(d) for d in to_remove] - - def check_stock_uom_with_bin(self): - if not self.get("__islocal"): - if self.stock_uom == frappe.db.get_value("Item", self.name, "stock_uom"): - return - - matched=True - ref_uom = frappe.db.get_value("Stock Ledger Entry", - {"item_code": self.name}, "stock_uom") - - if ref_uom: - if cstr(ref_uom) != cstr(self.stock_uom): - matched = False - else: - bin_list = frappe.db.sql("select * from tabBin where item_code=%s", - self.item_code, as_dict=1) - for bin in bin_list: - if (bin.reserved_qty > 0 or bin.ordered_qty > 0 or bin.indented_qty > 0 \ - or bin.planned_qty > 0) and cstr(bin.stock_uom) != cstr(self.stock_uom): - matched = False - break - - if matched and bin_list: - frappe.db.sql("""update tabBin set stock_uom=%s where item_code=%s""", - (self.stock_uom, self.name)) - - if not matched: - frappe.throw(_("Default Unit of Measure for Item {0} cannot be changed directly because you have already made some transaction(s) with another UOM. To change default UOM, use 'UOM Replace Utility' tool under Stock module.").format(self.name)) - def update_template_tables(self): template = frappe.get_doc("Item", self.variant_of) @@ -344,6 +315,17 @@ class Item(WebsiteGenerator): or ifnull(reserved_qty, 0) > 0 or ifnull(indented_qty, 0) > 0 or ifnull(planned_qty, 0) > 0)""", self.name) if stock_in: frappe.throw(_("Item Template cannot have stock or Open Sales/Purchase/Production Orders."), ItemTemplateCannotHaveStock) + + def validate_uom(self): + if not self.get("__islocal"): + check_stock_uom_with_bin(self.name, self.stock_uom) + if self.has_variants: + for d in frappe.db.get_all("Item", filters= {"variant_of": self.name}): + check_stock_uom_with_bin(d.name, self.stock_uom) + if self.variant_of: + template_uom = frappe.db.get_value("Item", self.variant_of, "stock_uom") + if template_uom != self.stock_uom: + frappe.throw(_("Default Unit of Measure for Variant must be same as Template")) def validate_end_of_life(item_code, end_of_life=None, verbose=1): if not end_of_life: @@ -449,3 +431,30 @@ def invalidate_cache_for_item(doc): if doc.get("old_item_group") and doc.get("old_item_group") != doc.item_group: invalidate_cache_for(doc, doc.old_item_group) + +def check_stock_uom_with_bin(item, stock_uom): + if stock_uom == frappe.db.get_value("Item", item, "stock_uom"): + return + + matched=True + ref_uom = frappe.db.get_value("Stock Ledger Entry", + {"item_code": item}, "stock_uom") + + if ref_uom: + if cstr(ref_uom) != cstr(stock_uom): + matched = False + else: + bin_list = frappe.db.sql("select * from tabBin where item_code=%s", item, as_dict=1) + for bin in bin_list: + if (bin.reserved_qty > 0 or bin.ordered_qty > 0 or bin.indented_qty > 0 \ + or bin.planned_qty > 0) and cstr(bin.stock_uom) != cstr(stock_uom): + matched = False + break + + if matched and bin_list: + frappe.db.sql("""update tabBin set stock_uom=%s where item_code=%s""", (stock_uom, item)) + + if not matched: + frappe.throw(_("Default Unit of Measure for Item {0} cannot be changed directly because \ + you have already made some transaction(s) with another UOM. To change default UOM, \ + use 'UOM Replace Utility' tool under Stock module.").format(item)) diff --git a/erpnext/stock/doctype/stock_uom_replace_utility/stock_uom_replace_utility.py b/erpnext/stock/doctype/stock_uom_replace_utility/stock_uom_replace_utility.py index c3f530a2c28..5b5419dc594 100644 --- a/erpnext/stock/doctype/stock_uom_replace_utility/stock_uom_replace_utility.py +++ b/erpnext/stock/doctype/stock_uom_replace_utility/stock_uom_replace_utility.py @@ -10,6 +10,28 @@ from frappe import _ from frappe.model.document import Document class StockUOMReplaceUtility(Document): + + # Update Stock UOM + def update_stock_uom(self): + self.validate_item() + self.validate_mandatory() + self.validate_uom_integer_type() + + update_stock_ledger_entry(self.item_code, self.new_stock_uom, self.conversion_factor) + update_bin(self.item_code, self.new_stock_uom, self.conversion_factor) + update_item_master(self.item_code, self.new_stock_uom, self.conversion_factor) + + #if item is template change UOM for all associated variants + if frappe.db.get_value("Item", self.item_code, "has_variants"): + for d in frappe.db.get_all("Item", filters= {"variant_of": self.item_code}): + update_stock_ledger_entry(d.name, self.new_stock_uom, self.conversion_factor) + update_bin(d.name, self.new_stock_uom, self.conversion_factor) + update_item_master(d.name, self.new_stock_uom, self.conversion_factor) + + def validate_item(self): + if frappe.db.get_value("Item", self.item_code, "variant_of"): + frappe.throw(_("You cannot change default UOM of Variant. To change default UOM for Variant change default UOM of the Template")) + def validate_mandatory(self): if not cstr(self.item_code): frappe.throw(_("Item is required")) @@ -27,72 +49,7 @@ class StockUOMReplaceUtility(Document): stock_uom = frappe.db.get_value("Item", self.item_code, "stock_uom") if cstr(self.new_stock_uom) == cstr(stock_uom): frappe.throw(_("Item is updated")) - - def update_item_master(self): - item_doc = frappe.get_doc("Item", self.item_code) - item_doc.stock_uom = self.new_stock_uom - item_doc.save() - - frappe.msgprint(_("Stock UOM updated for Item {0}").format(self.item_code)) - - def update_bin(self): - # update bin - if flt(self.conversion_factor) != flt(1): - frappe.db.sql("""update `tabBin` - set stock_uom = %s, - indented_qty = ifnull(indented_qty,0) * %s, - ordered_qty = ifnull(ordered_qty,0) * %s, - reserved_qty = ifnull(reserved_qty,0) * %s, - planned_qty = ifnull(planned_qty,0) * %s, - projected_qty = actual_qty + ordered_qty + indented_qty + - planned_qty - reserved_qty - where item_code = %s""", (self.new_stock_uom, self.conversion_factor, - self.conversion_factor, self.conversion_factor, - self.conversion_factor, self.item_code)) - else: - frappe.db.sql("update `tabBin` set stock_uom = %s where item_code = %s", - (self.new_stock_uom, self.item_code) ) - - # acknowledge user - frappe.msgprint(_("Stock balances updated")) - - def update_stock_ledger_entry(self): - # update stock ledger entry - from erpnext.stock.stock_ledger import update_entries_after - - if flt(self.conversion_factor) != flt(1): - frappe.db.sql("""update `tabStock Ledger Entry` - set stock_uom = %s, actual_qty = ifnull(actual_qty,0) * %s - where item_code = %s""", - (self.new_stock_uom, self.conversion_factor, self.item_code)) - else: - frappe.db.sql("""update `tabStock Ledger Entry` set stock_uom=%s - where item_code=%s""", (self.new_stock_uom, self.item_code)) - - # acknowledge user - frappe.msgprint(_("Stock Ledger entries balances updated")) - - # update item valuation - if flt(self.conversion_factor) != flt(1): - wh = frappe.db.sql("select name from `tabWarehouse`") - for w in wh: - update_entries_after({"item_code": self.item_code, "warehouse": w[0]}) - - # acknowledge user - frappe.msgprint(_("Item valuation updated")) - - # Update Stock UOM - def update_stock_uom(self): - self.validate_mandatory() - self.validate_uom_integer_type() - - self.update_stock_ledger_entry() - - self.update_bin() - - self.update_item_master() - - + def validate_uom_integer_type(self): current_is_integer = frappe.db.get_value("UOM", self.current_stock_uom, "must_be_whole_number") new_is_integer = frappe.db.get_value("UOM", self.new_stock_uom, "must_be_whole_number") @@ -103,6 +60,53 @@ class StockUOMReplaceUtility(Document): if current_is_integer and new_is_integer and cint(self.conversion_factor)!=self.conversion_factor: frappe.throw(_("Conversion factor cannot be in fractions")) +def update_item_master(item_code, new_stock_uom, conversion_factor): + frappe.db.set_value("Item", item_code, "stock_uom", new_stock_uom) + frappe.msgprint(_("Stock UOM updated for Item {0}").format(item_code)) + +def update_bin(item_code, new_stock_uom, conversion_factor): + # update bin + if flt(conversion_factor) != flt(1): + frappe.db.sql("""update `tabBin` + set stock_uom = %s, + indented_qty = ifnull(indented_qty,0) * %s, + ordered_qty = ifnull(ordered_qty,0) * %s, + reserved_qty = ifnull(reserved_qty,0) * %s, + planned_qty = ifnull(planned_qty,0) * %s, + projected_qty = actual_qty + ordered_qty + indented_qty + + planned_qty - reserved_qty + where item_code = %s""", (new_stock_uom, conversion_factor, + conversion_factor, conversion_factor, + conversion_factor, item_code)) + else: + frappe.db.sql("update `tabBin` set stock_uom = %s where item_code = %s", + (new_stock_uom, item_code) ) + +def update_stock_ledger_entry(item_code, new_stock_uom, conversion_factor): + # update stock ledger entry + from erpnext.stock.stock_ledger import update_entries_after + + if flt(conversion_factor) != flt(1): + frappe.db.sql("""update `tabStock Ledger Entry` + set stock_uom = %s, actual_qty = ifnull(actual_qty,0) * %s + where item_code = %s""", + (new_stock_uom, conversion_factor, item_code)) + else: + frappe.db.sql("""update `tabStock Ledger Entry` set stock_uom=%s + where item_code=%s""", (new_stock_uom, item_code)) + + # acknowledge user + frappe.msgprint(_("Stock Ledger entries balances updated")) + + # update item valuation + if flt(conversion_factor) != flt(1): + wh = frappe.db.sql("select name from `tabWarehouse`") + for w in wh: + update_entries_after({"item_code": item_code, "warehouse": w[0]}) + + # acknowledge user + frappe.msgprint(_("Item valuation updated")) + @frappe.whitelist() def get_stock_uom(item_code): return { 'current_stock_uom': cstr(frappe.db.get_value('Item', item_code, 'stock_uom')) } From 907ea7dd8a7fdd88f148592a07e8e43a93345651 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Thu, 13 Aug 2015 12:35:43 +0530 Subject: [PATCH 39/53] [minor] pincode label changed to postal code, #3770 --- .../utilities/doctype/address/address.json | 412 +++++++++--------- 1 file changed, 206 insertions(+), 206 deletions(-) diff --git a/erpnext/utilities/doctype/address/address.json b/erpnext/utilities/doctype/address/address.json index a4d7a552567..7f4143abbcb 100644 --- a/erpnext/utilities/doctype/address/address.json +++ b/erpnext/utilities/doctype/address/address.json @@ -1,268 +1,268 @@ { - "allow_import": 1, - "allow_rename": 1, - "creation": "2013-01-10 16:34:32", - "docstatus": 0, - "doctype": "DocType", - "document_type": "Master", + "allow_import": 1, + "allow_rename": 1, + "creation": "2013-01-10 16:34:32", + "docstatus": 0, + "doctype": "DocType", + "document_type": "Master", "fields": [ { - "fieldname": "address_details", - "fieldtype": "Section Break", - "label": "", - "options": "icon-map-marker", + "fieldname": "address_details", + "fieldtype": "Section Break", + "label": "", + "options": "icon-map-marker", "permlevel": 0 - }, + }, { - "description": "Name of person or organization that this address belongs to.", - "fieldname": "address_title", - "fieldtype": "Data", - "in_list_view": 0, - "label": "Address Title", - "permlevel": 0, + "description": "Name of person or organization that this address belongs to.", + "fieldname": "address_title", + "fieldtype": "Data", + "in_list_view": 0, + "label": "Address Title", + "permlevel": 0, "reqd": 0 - }, + }, { - "fieldname": "address_type", - "fieldtype": "Select", - "in_list_view": 1, - "label": "Address Type", - "options": "Billing\nShipping\nOffice\nPersonal\nPlant\nPostal\nShop\nSubsidiary\nWarehouse\nOther", - "permlevel": 0, + "fieldname": "address_type", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Address Type", + "options": "Billing\nShipping\nOffice\nPersonal\nPlant\nPostal\nShop\nSubsidiary\nWarehouse\nOther", + "permlevel": 0, "reqd": 1 - }, + }, { - "fieldname": "address_line1", - "fieldtype": "Data", - "label": "Address Line 1", - "permlevel": 0, + "fieldname": "address_line1", + "fieldtype": "Data", + "label": "Address Line 1", + "permlevel": 0, "reqd": 1 - }, + }, { - "fieldname": "address_line2", - "fieldtype": "Data", - "label": "Address Line 2", + "fieldname": "address_line2", + "fieldtype": "Data", + "label": "Address Line 2", "permlevel": 0 - }, + }, { - "fieldname": "city", - "fieldtype": "Data", - "in_filter": 1, - "in_list_view": 1, - "label": "City/Town", - "permlevel": 0, - "reqd": 1, + "fieldname": "city", + "fieldtype": "Data", + "in_filter": 1, + "in_list_view": 1, + "label": "City/Town", + "permlevel": 0, + "reqd": 1, "search_index": 1 - }, + }, { - "fieldname": "state", - "fieldtype": "Data", - "in_filter": 1, - "in_list_view": 0, - "label": "State", - "permlevel": 0, + "fieldname": "state", + "fieldtype": "Data", + "in_filter": 1, + "in_list_view": 0, + "label": "State", + "permlevel": 0, "search_index": 0 - }, + }, { - "fieldname": "pincode", - "fieldtype": "Data", - "in_filter": 1, - "in_list_view": 0, - "label": "Pincode", - "permlevel": 0, + "fieldname": "pincode", + "fieldtype": "Data", + "in_filter": 1, + "in_list_view": 0, + "label": "Postal Code", + "permlevel": 0, "search_index": 1 - }, + }, { - "fieldname": "country", - "fieldtype": "Link", - "in_filter": 1, - "in_list_view": 0, - "label": "Country", - "options": "Country", - "permlevel": 0, - "reqd": 1, + "fieldname": "country", + "fieldtype": "Link", + "in_filter": 1, + "in_list_view": 0, + "label": "Country", + "options": "Country", + "permlevel": 0, + "reqd": 1, "search_index": 1 - }, + }, { - "fieldname": "column_break0", - "fieldtype": "Column Break", - "permlevel": 0, - "print_hide": 0, + "fieldname": "column_break0", + "fieldtype": "Column Break", + "permlevel": 0, + "print_hide": 0, "width": "50%" - }, + }, { - "fieldname": "email_id", - "fieldtype": "Data", - "label": "Email Id", + "fieldname": "email_id", + "fieldtype": "Data", + "label": "Email Id", "permlevel": 0 - }, + }, { - "fieldname": "phone", - "fieldtype": "Data", - "label": "Phone", - "permlevel": 0, + "fieldname": "phone", + "fieldtype": "Data", + "label": "Phone", + "permlevel": 0, "reqd": 0 - }, + }, { - "fieldname": "fax", - "fieldtype": "Data", - "in_filter": 1, - "label": "Fax", + "fieldname": "fax", + "fieldtype": "Data", + "in_filter": 1, + "label": "Fax", "permlevel": 0 - }, + }, { - "default": "0", - "description": "", - "fieldname": "is_primary_address", - "fieldtype": "Check", - "label": "Preferred Billing Address", + "default": "0", + "description": "", + "fieldname": "is_primary_address", + "fieldtype": "Check", + "label": "Preferred Billing Address", "permlevel": 0 - }, + }, { - "default": "0", - "description": "", - "fieldname": "is_shipping_address", - "fieldtype": "Check", - "in_list_view": 0, - "label": "Preferred Shipping Address", + "default": "0", + "description": "", + "fieldname": "is_shipping_address", + "fieldtype": "Check", + "in_list_view": 0, + "label": "Preferred Shipping Address", "permlevel": 0 - }, + }, { - "fieldname": "linked_with", - "fieldtype": "Section Break", - "label": "Reference", - "options": "icon-pushpin", + "fieldname": "linked_with", + "fieldtype": "Section Break", + "label": "Reference", + "options": "icon-pushpin", "permlevel": 0 - }, + }, { - "fieldname": "customer", - "fieldtype": "Link", - "label": "Customer", - "options": "Customer", + "fieldname": "customer", + "fieldtype": "Link", + "label": "Customer", + "options": "Customer", "permlevel": 0 - }, + }, { - "fieldname": "customer_name", - "fieldtype": "Data", - "in_filter": 1, - "in_list_view": 0, - "label": "Customer Name", - "permlevel": 0, + "fieldname": "customer_name", + "fieldtype": "Data", + "in_filter": 1, + "in_list_view": 0, + "label": "Customer Name", + "permlevel": 0, "read_only": 1 - }, + }, { - "fieldname": "supplier", - "fieldtype": "Link", - "label": "Supplier", - "options": "Supplier", + "fieldname": "supplier", + "fieldtype": "Link", + "label": "Supplier", + "options": "Supplier", "permlevel": 0 - }, + }, { - "fieldname": "supplier_name", - "fieldtype": "Data", - "in_filter": 1, - "in_list_view": 0, - "label": "Supplier Name", - "permlevel": 0, - "read_only": 1, + "fieldname": "supplier_name", + "fieldtype": "Data", + "in_filter": 1, + "in_list_view": 0, + "label": "Supplier Name", + "permlevel": 0, + "read_only": 1, "search_index": 0 - }, + }, { - "fieldname": "sales_partner", - "fieldtype": "Link", - "label": "Sales Partner", - "options": "Sales Partner", + "fieldname": "sales_partner", + "fieldtype": "Link", + "label": "Sales Partner", + "options": "Sales Partner", "permlevel": 0 - }, + }, { - "fieldname": "column_break_22", - "fieldtype": "Column Break", + "fieldname": "column_break_22", + "fieldtype": "Column Break", "permlevel": 0 - }, + }, { - "depends_on": "eval:!doc.supplier && !doc.sales_partner", - "fieldname": "lead", - "fieldtype": "Link", - "label": "Lead", - "options": "Lead", + "depends_on": "eval:!doc.supplier && !doc.sales_partner", + "fieldname": "lead", + "fieldtype": "Link", + "label": "Lead", + "options": "Lead", "permlevel": 0 - }, + }, { - "depends_on": "eval:!doc.supplier && !doc.sales_partner", - "fieldname": "lead_name", - "fieldtype": "Data", - "label": "Lead Name", - "permlevel": 0, + "depends_on": "eval:!doc.supplier && !doc.sales_partner", + "fieldname": "lead_name", + "fieldtype": "Data", + "label": "Lead Name", + "permlevel": 0, "read_only": 1 } - ], - "icon": "icon-map-marker", - "idx": 1, - "in_dialog": 0, - "modified": "2015-08-10 19:42:18.331818", - "modified_by": "Administrator", - "module": "Utilities", - "name": "Address", - "owner": "Administrator", + ], + "icon": "icon-map-marker", + "idx": 1, + "in_dialog": 0, + "modified": "2015-08-10 19:42:18.331819", + "modified_by": "Administrator", + "module": "Utilities", + "name": "Address", + "owner": "Administrator", "permissions": [ { - "apply_user_permissions": 1, - "create": 1, - "delete": 0, - "email": 1, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Sales User", - "share": 1, - "submit": 0, + "apply_user_permissions": 1, + "create": 1, + "delete": 0, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Sales User", + "share": 1, + "submit": 0, "write": 1 - }, + }, { - "apply_user_permissions": 1, - "create": 1, - "delete": 0, - "email": 1, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Purchase User", - "share": 1, - "submit": 0, + "apply_user_permissions": 1, + "create": 1, + "delete": 0, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Purchase User", + "share": 1, + "submit": 0, "write": 1 - }, + }, { - "apply_user_permissions": 1, - "create": 1, - "delete": 0, - "email": 1, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Maintenance User", - "share": 1, - "submit": 0, + "apply_user_permissions": 1, + "create": 1, + "delete": 0, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Maintenance User", + "share": 1, + "submit": 0, "write": 1 - }, + }, { - "apply_user_permissions": 1, - "create": 1, - "delete": 0, - "email": 1, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts User", - "share": 1, - "submit": 0, + "apply_user_permissions": 1, + "create": 1, + "delete": 0, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts User", + "share": 1, + "submit": 0, "write": 1 } - ], - "search_fields": "customer, supplier, sales_partner, country, state", - "sort_field": "modified", + ], + "search_fields": "customer, supplier, sales_partner, country, state", + "sort_field": "modified", "sort_order": "DESC" -} \ No newline at end of file +} From cdbb448f6cb65723062cbf7aab53c27858be4a15 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Thu, 13 Aug 2015 12:48:04 +0530 Subject: [PATCH 40/53] [fix] salary slip to consider disable rounded total, #3792 --- erpnext/hr/doctype/salary_slip/salary_slip.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.py b/erpnext/hr/doctype/salary_slip/salary_slip.py index 24079c1c72e..12fdc205a4b 100644 --- a/erpnext/hr/doctype/salary_slip/salary_slip.py +++ b/erpnext/hr/doctype/salary_slip/salary_slip.py @@ -152,8 +152,8 @@ class SalarySlip(TransactionBase): self.gross_pay = flt(self.arrear_amount) + flt(self.leave_encashment_amount) for d in self.get("earnings"): if cint(d.e_depends_on_lwp) == 1: - d.e_modified_amount = rounded(flt(d.e_amount) * flt(self.payment_days) - / cint(self.total_days_in_month), 2) + d.e_modified_amount = rounded((flt(d.e_amount) * flt(self.payment_days) + / cint(self.total_days_in_month)), self.precision("e_modified_amount", "earnings")) elif not self.payment_days: d.e_modified_amount = 0 elif not d.e_modified_amount: @@ -164,8 +164,8 @@ class SalarySlip(TransactionBase): self.total_deduction = 0 for d in self.get('deductions'): if cint(d.d_depends_on_lwp) == 1: - d.d_modified_amount = rounded(flt(d.d_amount) * flt(self.payment_days) - / cint(self.total_days_in_month), 2) + d.d_modified_amount = rounded((flt(d.d_amount) * flt(self.payment_days) + / cint(self.total_days_in_month)), self.precision("d_modified_amount", "deductions")) elif not self.payment_days: d.d_modified_amount = 0 elif not d.d_modified_amount: @@ -174,10 +174,13 @@ class SalarySlip(TransactionBase): self.total_deduction += flt(d.d_modified_amount) def calculate_net_pay(self): + disable_rounded_total = cint(frappe.db.get_value("Global Defaults", None, "disable_rounded_total")) + self.calculate_earning_total() self.calculate_ded_total() self.net_pay = flt(self.gross_pay) - flt(self.total_deduction) - self.rounded_total = rounded(self.net_pay) + self.rounded_total = rounded(self.net_pay, + self.precision("net_pay") if disable_rounded_total else 0) def on_submit(self): if(self.email_check == 1): From 746eae4d1a8115a261769ff4d32dbb762d7fa7ab Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Thu, 13 Aug 2015 12:52:13 +0530 Subject: [PATCH 41/53] [minor] Reload Journal Entry Account --- erpnext/patches/v5_4/cleanup_journal_entry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/patches/v5_4/cleanup_journal_entry.py b/erpnext/patches/v5_4/cleanup_journal_entry.py index 5de17c7325d..442132afd1d 100644 --- a/erpnext/patches/v5_4/cleanup_journal_entry.py +++ b/erpnext/patches/v5_4/cleanup_journal_entry.py @@ -1,7 +1,7 @@ import frappe def execute(): - frappe.reload_doctype("Journal Entry") + frappe.reload_doctype("Journal Entry Account") for doctype, fieldname in ( ("Sales Order", "against_sales_order"), ("Purchase Order", "against_purchase_order"), From 2645980f62130ecea521d736d11a6d24bc5da9bf Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Thu, 13 Aug 2015 11:46:01 +0530 Subject: [PATCH 42/53] [patch] cleanup_journal_entry --- erpnext/patches.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index aa89f9a3482..8e3aab1d978 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -190,4 +190,5 @@ erpnext.patches.v5_4.fix_invoice_outstanding execute:frappe.db.sql("update `tabStock Ledger Entry` set stock_queue = '[]' where voucher_type = 'Stock Reconciliation' and ifnull(qty_after_transaction, 0) = 0") erpnext.patches.v5_4.fix_missing_item_images erpnext.patches.v5_4.stock_entry_additional_costs -execute:frappe.db.sql("update `tabProduction Order` pro set description = (select description from tabItem where name=pro.production_item) where ifnull(description, '') = ''") \ No newline at end of file +erpnext.patches.v5_4.cleanup_journal_entry +execute:frappe.db.sql("update `tabProduction Order` pro set description = (select description from tabItem where name=pro.production_item) where ifnull(description, '') = ''") From 9f02b084273b6e12d45548e90ecf8bad9008ca98 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 13 Aug 2015 11:47:25 +0530 Subject: [PATCH 43/53] minor fix --- erpnext/public/js/controllers/stock_controller.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/public/js/controllers/stock_controller.js b/erpnext/public/js/controllers/stock_controller.js index ff5cf534e6d..9df7d0f5d8a 100644 --- a/erpnext/public/js/controllers/stock_controller.js +++ b/erpnext/public/js/controllers/stock_controller.js @@ -12,7 +12,8 @@ erpnext.stock.StockController = frappe.ui.form.Controller.extend({ }, setup_warehouse_query: function() { - erpnext.queries.setup_queries(me.frm, "Warehouse", function() { + var me = this; + erpnext.queries.setup_queries(this.frm, "Warehouse", function() { return erpnext.queries.warehouse(me.frm.doc); }); }, From 682ce24f8c6857918695fd1b6984f2996549c9dd Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Thu, 13 Aug 2015 11:50:03 +0530 Subject: [PATCH 44/53] [patch] cleanup_journal_entry --- erpnext/patches/v5_4/cleanup_journal_entry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/patches/v5_4/cleanup_journal_entry.py b/erpnext/patches/v5_4/cleanup_journal_entry.py index b8edbaa9622..9d493eab05a 100644 --- a/erpnext/patches/v5_4/cleanup_journal_entry.py +++ b/erpnext/patches/v5_4/cleanup_journal_entry.py @@ -9,6 +9,6 @@ def execute(): ("Journal Entry", "against_jv"), ("Expense Claim", "against_expense_claim"), ): - frappe.db.update("""update `tabJournal Entry Detail` + frappe.db.sql("""update `tabJournal Entry Detail` set reference_type=%s and reference_name={0} where ifnull({0}, '') != '' """.format(fieldname), doctype) From 832fa2e76ba802b2a2f15a400d9d62a3b7c728b1 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Thu, 13 Aug 2015 11:54:04 +0530 Subject: [PATCH 45/53] [patch] cleanup_journal_entry --- erpnext/patches/v5_4/cleanup_journal_entry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/patches/v5_4/cleanup_journal_entry.py b/erpnext/patches/v5_4/cleanup_journal_entry.py index 9d493eab05a..3ae226b3835 100644 --- a/erpnext/patches/v5_4/cleanup_journal_entry.py +++ b/erpnext/patches/v5_4/cleanup_journal_entry.py @@ -9,6 +9,6 @@ def execute(): ("Journal Entry", "against_jv"), ("Expense Claim", "against_expense_claim"), ): - frappe.db.sql("""update `tabJournal Entry Detail` + frappe.db.sql("""update `tabJournal Entry Account` set reference_type=%s and reference_name={0} where ifnull({0}, '') != '' """.format(fieldname), doctype) From 35f94dfbc6c4849d905ffa359ee675b9a677de16 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Thu, 13 Aug 2015 12:10:21 +0530 Subject: [PATCH 46/53] [patch] cleanup_journal_entry --- erpnext/patches/v5_4/cleanup_journal_entry.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/patches/v5_4/cleanup_journal_entry.py b/erpnext/patches/v5_4/cleanup_journal_entry.py index 3ae226b3835..5de17c7325d 100644 --- a/erpnext/patches/v5_4/cleanup_journal_entry.py +++ b/erpnext/patches/v5_4/cleanup_journal_entry.py @@ -1,6 +1,7 @@ import frappe def execute(): + frappe.reload_doctype("Journal Entry") for doctype, fieldname in ( ("Sales Order", "against_sales_order"), ("Purchase Order", "against_purchase_order"), From bbed8972c3c15202f27bd97040a2d58e2caf9e16 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Thu, 13 Aug 2015 12:35:43 +0530 Subject: [PATCH 47/53] [minor] pincode label changed to postal code, #3770 --- .../utilities/doctype/address/address.json | 412 +++++++++--------- 1 file changed, 206 insertions(+), 206 deletions(-) diff --git a/erpnext/utilities/doctype/address/address.json b/erpnext/utilities/doctype/address/address.json index a4d7a552567..7f4143abbcb 100644 --- a/erpnext/utilities/doctype/address/address.json +++ b/erpnext/utilities/doctype/address/address.json @@ -1,268 +1,268 @@ { - "allow_import": 1, - "allow_rename": 1, - "creation": "2013-01-10 16:34:32", - "docstatus": 0, - "doctype": "DocType", - "document_type": "Master", + "allow_import": 1, + "allow_rename": 1, + "creation": "2013-01-10 16:34:32", + "docstatus": 0, + "doctype": "DocType", + "document_type": "Master", "fields": [ { - "fieldname": "address_details", - "fieldtype": "Section Break", - "label": "", - "options": "icon-map-marker", + "fieldname": "address_details", + "fieldtype": "Section Break", + "label": "", + "options": "icon-map-marker", "permlevel": 0 - }, + }, { - "description": "Name of person or organization that this address belongs to.", - "fieldname": "address_title", - "fieldtype": "Data", - "in_list_view": 0, - "label": "Address Title", - "permlevel": 0, + "description": "Name of person or organization that this address belongs to.", + "fieldname": "address_title", + "fieldtype": "Data", + "in_list_view": 0, + "label": "Address Title", + "permlevel": 0, "reqd": 0 - }, + }, { - "fieldname": "address_type", - "fieldtype": "Select", - "in_list_view": 1, - "label": "Address Type", - "options": "Billing\nShipping\nOffice\nPersonal\nPlant\nPostal\nShop\nSubsidiary\nWarehouse\nOther", - "permlevel": 0, + "fieldname": "address_type", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Address Type", + "options": "Billing\nShipping\nOffice\nPersonal\nPlant\nPostal\nShop\nSubsidiary\nWarehouse\nOther", + "permlevel": 0, "reqd": 1 - }, + }, { - "fieldname": "address_line1", - "fieldtype": "Data", - "label": "Address Line 1", - "permlevel": 0, + "fieldname": "address_line1", + "fieldtype": "Data", + "label": "Address Line 1", + "permlevel": 0, "reqd": 1 - }, + }, { - "fieldname": "address_line2", - "fieldtype": "Data", - "label": "Address Line 2", + "fieldname": "address_line2", + "fieldtype": "Data", + "label": "Address Line 2", "permlevel": 0 - }, + }, { - "fieldname": "city", - "fieldtype": "Data", - "in_filter": 1, - "in_list_view": 1, - "label": "City/Town", - "permlevel": 0, - "reqd": 1, + "fieldname": "city", + "fieldtype": "Data", + "in_filter": 1, + "in_list_view": 1, + "label": "City/Town", + "permlevel": 0, + "reqd": 1, "search_index": 1 - }, + }, { - "fieldname": "state", - "fieldtype": "Data", - "in_filter": 1, - "in_list_view": 0, - "label": "State", - "permlevel": 0, + "fieldname": "state", + "fieldtype": "Data", + "in_filter": 1, + "in_list_view": 0, + "label": "State", + "permlevel": 0, "search_index": 0 - }, + }, { - "fieldname": "pincode", - "fieldtype": "Data", - "in_filter": 1, - "in_list_view": 0, - "label": "Pincode", - "permlevel": 0, + "fieldname": "pincode", + "fieldtype": "Data", + "in_filter": 1, + "in_list_view": 0, + "label": "Postal Code", + "permlevel": 0, "search_index": 1 - }, + }, { - "fieldname": "country", - "fieldtype": "Link", - "in_filter": 1, - "in_list_view": 0, - "label": "Country", - "options": "Country", - "permlevel": 0, - "reqd": 1, + "fieldname": "country", + "fieldtype": "Link", + "in_filter": 1, + "in_list_view": 0, + "label": "Country", + "options": "Country", + "permlevel": 0, + "reqd": 1, "search_index": 1 - }, + }, { - "fieldname": "column_break0", - "fieldtype": "Column Break", - "permlevel": 0, - "print_hide": 0, + "fieldname": "column_break0", + "fieldtype": "Column Break", + "permlevel": 0, + "print_hide": 0, "width": "50%" - }, + }, { - "fieldname": "email_id", - "fieldtype": "Data", - "label": "Email Id", + "fieldname": "email_id", + "fieldtype": "Data", + "label": "Email Id", "permlevel": 0 - }, + }, { - "fieldname": "phone", - "fieldtype": "Data", - "label": "Phone", - "permlevel": 0, + "fieldname": "phone", + "fieldtype": "Data", + "label": "Phone", + "permlevel": 0, "reqd": 0 - }, + }, { - "fieldname": "fax", - "fieldtype": "Data", - "in_filter": 1, - "label": "Fax", + "fieldname": "fax", + "fieldtype": "Data", + "in_filter": 1, + "label": "Fax", "permlevel": 0 - }, + }, { - "default": "0", - "description": "", - "fieldname": "is_primary_address", - "fieldtype": "Check", - "label": "Preferred Billing Address", + "default": "0", + "description": "", + "fieldname": "is_primary_address", + "fieldtype": "Check", + "label": "Preferred Billing Address", "permlevel": 0 - }, + }, { - "default": "0", - "description": "", - "fieldname": "is_shipping_address", - "fieldtype": "Check", - "in_list_view": 0, - "label": "Preferred Shipping Address", + "default": "0", + "description": "", + "fieldname": "is_shipping_address", + "fieldtype": "Check", + "in_list_view": 0, + "label": "Preferred Shipping Address", "permlevel": 0 - }, + }, { - "fieldname": "linked_with", - "fieldtype": "Section Break", - "label": "Reference", - "options": "icon-pushpin", + "fieldname": "linked_with", + "fieldtype": "Section Break", + "label": "Reference", + "options": "icon-pushpin", "permlevel": 0 - }, + }, { - "fieldname": "customer", - "fieldtype": "Link", - "label": "Customer", - "options": "Customer", + "fieldname": "customer", + "fieldtype": "Link", + "label": "Customer", + "options": "Customer", "permlevel": 0 - }, + }, { - "fieldname": "customer_name", - "fieldtype": "Data", - "in_filter": 1, - "in_list_view": 0, - "label": "Customer Name", - "permlevel": 0, + "fieldname": "customer_name", + "fieldtype": "Data", + "in_filter": 1, + "in_list_view": 0, + "label": "Customer Name", + "permlevel": 0, "read_only": 1 - }, + }, { - "fieldname": "supplier", - "fieldtype": "Link", - "label": "Supplier", - "options": "Supplier", + "fieldname": "supplier", + "fieldtype": "Link", + "label": "Supplier", + "options": "Supplier", "permlevel": 0 - }, + }, { - "fieldname": "supplier_name", - "fieldtype": "Data", - "in_filter": 1, - "in_list_view": 0, - "label": "Supplier Name", - "permlevel": 0, - "read_only": 1, + "fieldname": "supplier_name", + "fieldtype": "Data", + "in_filter": 1, + "in_list_view": 0, + "label": "Supplier Name", + "permlevel": 0, + "read_only": 1, "search_index": 0 - }, + }, { - "fieldname": "sales_partner", - "fieldtype": "Link", - "label": "Sales Partner", - "options": "Sales Partner", + "fieldname": "sales_partner", + "fieldtype": "Link", + "label": "Sales Partner", + "options": "Sales Partner", "permlevel": 0 - }, + }, { - "fieldname": "column_break_22", - "fieldtype": "Column Break", + "fieldname": "column_break_22", + "fieldtype": "Column Break", "permlevel": 0 - }, + }, { - "depends_on": "eval:!doc.supplier && !doc.sales_partner", - "fieldname": "lead", - "fieldtype": "Link", - "label": "Lead", - "options": "Lead", + "depends_on": "eval:!doc.supplier && !doc.sales_partner", + "fieldname": "lead", + "fieldtype": "Link", + "label": "Lead", + "options": "Lead", "permlevel": 0 - }, + }, { - "depends_on": "eval:!doc.supplier && !doc.sales_partner", - "fieldname": "lead_name", - "fieldtype": "Data", - "label": "Lead Name", - "permlevel": 0, + "depends_on": "eval:!doc.supplier && !doc.sales_partner", + "fieldname": "lead_name", + "fieldtype": "Data", + "label": "Lead Name", + "permlevel": 0, "read_only": 1 } - ], - "icon": "icon-map-marker", - "idx": 1, - "in_dialog": 0, - "modified": "2015-08-10 19:42:18.331818", - "modified_by": "Administrator", - "module": "Utilities", - "name": "Address", - "owner": "Administrator", + ], + "icon": "icon-map-marker", + "idx": 1, + "in_dialog": 0, + "modified": "2015-08-10 19:42:18.331819", + "modified_by": "Administrator", + "module": "Utilities", + "name": "Address", + "owner": "Administrator", "permissions": [ { - "apply_user_permissions": 1, - "create": 1, - "delete": 0, - "email": 1, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Sales User", - "share": 1, - "submit": 0, + "apply_user_permissions": 1, + "create": 1, + "delete": 0, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Sales User", + "share": 1, + "submit": 0, "write": 1 - }, + }, { - "apply_user_permissions": 1, - "create": 1, - "delete": 0, - "email": 1, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Purchase User", - "share": 1, - "submit": 0, + "apply_user_permissions": 1, + "create": 1, + "delete": 0, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Purchase User", + "share": 1, + "submit": 0, "write": 1 - }, + }, { - "apply_user_permissions": 1, - "create": 1, - "delete": 0, - "email": 1, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Maintenance User", - "share": 1, - "submit": 0, + "apply_user_permissions": 1, + "create": 1, + "delete": 0, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Maintenance User", + "share": 1, + "submit": 0, "write": 1 - }, + }, { - "apply_user_permissions": 1, - "create": 1, - "delete": 0, - "email": 1, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts User", - "share": 1, - "submit": 0, + "apply_user_permissions": 1, + "create": 1, + "delete": 0, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts User", + "share": 1, + "submit": 0, "write": 1 } - ], - "search_fields": "customer, supplier, sales_partner, country, state", - "sort_field": "modified", + ], + "search_fields": "customer, supplier, sales_partner, country, state", + "sort_field": "modified", "sort_order": "DESC" -} \ No newline at end of file +} From b16474e606908f340c6f04c9aa0516714afa8bba Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Thu, 13 Aug 2015 12:52:13 +0530 Subject: [PATCH 48/53] [minor] Reload Journal Entry Account --- erpnext/patches/v5_4/cleanup_journal_entry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/patches/v5_4/cleanup_journal_entry.py b/erpnext/patches/v5_4/cleanup_journal_entry.py index 5de17c7325d..442132afd1d 100644 --- a/erpnext/patches/v5_4/cleanup_journal_entry.py +++ b/erpnext/patches/v5_4/cleanup_journal_entry.py @@ -1,7 +1,7 @@ import frappe def execute(): - frappe.reload_doctype("Journal Entry") + frappe.reload_doctype("Journal Entry Account") for doctype, fieldname in ( ("Sales Order", "against_sales_order"), ("Purchase Order", "against_purchase_order"), From 58aea1f819bd630235014862a37af785f19c979c Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 13 Aug 2015 14:15:27 +0530 Subject: [PATCH 49/53] minor fix --- erpnext/stock/doctype/stock_entry/stock_entry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 89dea2e54c1..f263f34d1d4 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -98,7 +98,7 @@ class StockEntry(StockController): if f in ["stock_uom", "conversion_factor"] or not item.get(f): item.set(f, item_details.get(f)) - if self.difference_account and item.expense_account=="": + if self.difference_account and not item.expense_account: item.expense_account = self.difference_account if not item.transfer_qty: From 0da11f1b10fe97883f7e2a8e6796974fd7e00979 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 13 Aug 2015 13:25:43 +0530 Subject: [PATCH 50/53] [fix] Reset price list rate based on Pricing Rule type --- erpnext/accounts/doctype/pricing_rule/pricing_rule.py | 1 + erpnext/controllers/accounts_controller.py | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py index 70d6fb9c141..349a89b1dc2 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -147,6 +147,7 @@ def get_pricing_rule_for_item(args): if pricing_rule: item_details.pricing_rule = pricing_rule.name + item_details.pricing_rule_for = pricing_rule.price_or_discount if pricing_rule.price_or_discount == "Price": item_details.update({ "price_list_rate": pricing_rule.price/flt(args.conversion_rate) \ diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index ecd9e1a1ad8..2c9e7d4f612 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -153,9 +153,10 @@ class AccountsController(TransactionBase): item.set(fieldname, value) if ret.get("pricing_rule"): - for field in ["base_price_list_rate", "price_list_rate", - "discount_percentage", "base_rate", "rate"]: - item.set(field, ret.get(field)) + item.set("discount_percentage", ret.get("discount_percentage")) + if ret.get("pricing_rule_for") == "Price": + item.set("pricing_list_rate", ret.get("pricing_list_rate")) + def set_taxes(self): if not self.meta.get_field("taxes"): From 4c4c534dcdb03e0f63650b288cc3cc9745b5e7e0 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Thu, 13 Aug 2015 15:00:54 +0530 Subject: [PATCH 51/53] [minor] pricing rule layout --- .../doctype/pricing_rule/pricing_rule.json | 845 +++++++++++++++--- 1 file changed, 710 insertions(+), 135 deletions(-) diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json index 537c087092f..cb9195c0c39 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json @@ -1,314 +1,889 @@ { + "allow_copy": 0, "allow_import": 1, + "allow_rename": 0, "autoname": "PRULE.#####", "creation": "2014-02-21 15:02:51", + "custom": 0, "docstatus": 0, "doctype": "DocType", - "document_type": "Master", + "document_type": "", "fields": [ { + "allow_on_submit": 0, "fieldname": "applicability_section", "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, "in_list_view": 0, - "label": "Applicability", - "permlevel": 0 + "label": "", + "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": "title", "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, "label": "Title", + "no_copy": 0, "permlevel": 0, "precision": "", - "reqd": 1 + "print_hide": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 }, { + "allow_on_submit": 0, "default": "Item Code", "fieldname": "apply_on", "fieldtype": "Select", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, "in_list_view": 1, "label": "Apply On", + "no_copy": 0, "options": "\nItem Code\nItem Group\nBrand", "permlevel": 0, - "reqd": 1 + "print_hide": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 }, { + "allow_on_submit": 0, "depends_on": "eval:doc.apply_on==\"Item Code\"", "fieldname": "item_code", "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, "in_list_view": 1, "label": "Item Code", + "no_copy": 0, "options": "Item", "permlevel": 0, - "reqd": 0 - }, - { - "depends_on": "eval:doc.apply_on==\"Item Group\"", - "fieldname": "item_group", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Item Group", - "options": "Item Group", - "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, "depends_on": "eval:doc.apply_on==\"Brand\"", "fieldname": "brand", "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, "in_list_view": 1, "label": "Brand", + "no_copy": 0, "options": "Brand", - "permlevel": 0 + "permlevel": 0, + "print_hide": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 }, { - "fieldname": "selling", - "fieldtype": "Check", - "label": "Selling", - "permlevel": 0 - }, - { - "fieldname": "buying", - "fieldtype": "Check", - "label": "Buying", - "permlevel": 0 - }, - { - "depends_on": "eval: doc.buying || doc.selling", - "fieldname": "applicable_for", - "fieldtype": "Select", - "label": "Applicable For", - "options": "\nCustomer\nCustomer Group\nTerritory\nSales Partner\nCampaign\nSupplier\nSupplier Type", - "permlevel": 0 - }, - { - "depends_on": "eval:doc.applicable_for==\"Customer\"", - "fieldname": "customer", + "allow_on_submit": 0, + "depends_on": "eval:doc.apply_on==\"Item Group\"", + "fieldname": "item_group", "fieldtype": "Link", - "label": "Customer", - "options": "Customer", - "permlevel": 0 + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 1, + "label": "Item Group", + "no_copy": 0, + "options": "Item Group", + "permlevel": 0, + "print_hide": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 }, { - "depends_on": "eval:doc.applicable_for==\"Customer Group\"", - "fieldname": "customer_group", - "fieldtype": "Link", - "label": "Customer Group", - "options": "Customer Group", - "permlevel": 0 - }, - { - "depends_on": "eval:doc.applicable_for==\"Territory\"", - "fieldname": "territory", - "fieldtype": "Link", - "label": "Territory", - "options": "Territory", - "permlevel": 0 - }, - { - "depends_on": "eval:doc.applicable_for==\"Sales Partner\"", - "fieldname": "sales_partner", - "fieldtype": "Link", - "label": "Sales Partner", - "options": "Sales Partner", - "permlevel": 0 - }, - { - "depends_on": "eval:doc.applicable_for==\"Campaign\"", - "fieldname": "campaign", - "fieldtype": "Link", - "label": "Campaign", - "options": "Campaign", - "permlevel": 0 - }, - { - "depends_on": "eval:doc.applicable_for==\"Supplier\"", - "fieldname": "supplier", - "fieldtype": "Link", - "label": "Supplier", - "options": "Supplier", - "permlevel": 0 - }, - { - "depends_on": "eval:doc.applicable_for==\"Supplier Type\"", - "fieldname": "supplier_type", - "fieldtype": "Link", - "label": "Supplier Type", - "options": "Supplier Type", - "permlevel": 0 - }, - { - "fieldname": "min_qty", - "fieldtype": "Float", - "label": "Min Qty", - "permlevel": 0 - }, - { - "fieldname": "max_qty", - "fieldtype": "Float", - "label": "Max Qty", - "permlevel": 0 - }, - { - "fieldname": "col_break1", + "allow_on_submit": 0, + "fieldname": "column_break_7", "fieldtype": "Column Break", - "permlevel": 0 - }, - { - "fieldname": "company", - "fieldtype": "Link", - "label": "Company", - "options": "Company", - "permlevel": 0 - }, - { - "default": "Today", - "fieldname": "valid_from", - "fieldtype": "Date", - "label": "Valid From", - "permlevel": 0 - }, - { - "fieldname": "valid_upto", - "fieldtype": "Date", - "label": "Valid Upto", - "permlevel": 0 + "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, "description": "Higher the number, higher the priority", "fieldname": "priority", "fieldtype": "Select", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, "label": "Priority", + "no_copy": 0, "options": "\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20", - "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": "disable", "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, "label": "Disable", - "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": "section_break_7", + "fieldtype": "Section Break", + "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": "selling", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Selling", + "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": "buying", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Buying", + "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": "column_break_11", + "fieldtype": "Column Break", + "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, + "depends_on": "eval: doc.buying || doc.selling", + "fieldname": "applicable_for", + "fieldtype": "Select", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Applicable For", + "no_copy": 0, + "options": "\nCustomer\nCustomer Group\nTerritory\nSales Partner\nCampaign\nSupplier\nSupplier Type", + "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, + "depends_on": "eval:doc.applicable_for==\"Customer\"", + "fieldname": "customer", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Customer", + "no_copy": 0, + "options": "Customer", + "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, + "depends_on": "eval:doc.applicable_for==\"Customer Group\"", + "fieldname": "customer_group", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Customer Group", + "no_copy": 0, + "options": "Customer Group", + "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, + "depends_on": "eval:doc.applicable_for==\"Territory\"", + "fieldname": "territory", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Territory", + "no_copy": 0, + "options": "Territory", + "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, + "depends_on": "eval:doc.applicable_for==\"Sales Partner\"", + "fieldname": "sales_partner", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Sales Partner", + "no_copy": 0, + "options": "Sales Partner", + "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, + "depends_on": "eval:doc.applicable_for==\"Campaign\"", + "fieldname": "campaign", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Campaign", + "no_copy": 0, + "options": "Campaign", + "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, + "depends_on": "eval:doc.applicable_for==\"Supplier\"", + "fieldname": "supplier", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Supplier", + "no_copy": 0, + "options": "Supplier", + "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, + "depends_on": "eval:doc.applicable_for==\"Supplier Type\"", + "fieldname": "supplier_type", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Supplier Type", + "no_copy": 0, + "options": "Supplier Type", + "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": "section_break_19", + "fieldtype": "Section Break", + "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": "min_qty", + "fieldtype": "Float", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Min Qty", + "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": "column_break_21", + "fieldtype": "Column Break", + "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": "max_qty", + "fieldtype": "Float", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Max Qty", + "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": "section_break_23", + "fieldtype": "Section Break", + "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, + "default": "Today", + "fieldname": "valid_from", + "fieldtype": "Date", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Valid From", + "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": "valid_upto", + "fieldtype": "Date", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Valid Upto", + "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": "col_break1", + "fieldtype": "Column Break", + "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": "company", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Company", + "no_copy": 0, + "options": "Company", + "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": "price_discount_section", "fieldtype": "Section Break", - "label": "Price / Discount", - "permlevel": 0 + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "", + "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, "default": "Discount Percentage", "fieldname": "price_or_discount", "fieldtype": "Select", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, "label": "Price or Discount", + "no_copy": 0, "options": "\nPrice\nDiscount Percentage", "permlevel": 0, - "reqd": 1 + "print_hide": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 1, + "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, "depends_on": "eval:doc.price_or_discount==\"Price\"", "fieldname": "price", "fieldtype": "Float", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, "label": "Price", - "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, "depends_on": "eval:doc.price_or_discount==\"Discount Percentage\"", "fieldname": "discount_percentage", "fieldtype": "Float", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, "label": "Discount on Price List Rate (%)", - "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, "depends_on": "eval:doc.price_or_discount==\"Discount Percentage\"", "fieldname": "for_price_list", "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, "label": "For Price List", + "no_copy": 0, "options": "Price List", - "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": "help_section", "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, "label": "", + "no_copy": 0, "options": "Simple", - "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": "pricing_rule_help", "fieldtype": "HTML", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, "label": "Pricing Rule Help", - "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 } ], + "hide_heading": 0, + "hide_toolbar": 0, "icon": "icon-gift", "idx": 1, + "in_create": 0, + "in_dialog": 0, + "is_submittable": 0, + "issingle": 0, "istable": 0, - "modified": "2015-05-27 02:47:16.777466", + "modified": "2015-08-13 14:58:29.194326", "modified_by": "Administrator", "module": "Accounts", "name": "Pricing Rule", "owner": "Administrator", "permissions": [ { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, "create": 1, "delete": 1, + "email": 0, "export": 1, + "if_owner": 0, "import": 1, "permlevel": 0, + "print": 0, "read": 1, "report": 1, "role": "Accounts Manager", + "set_user_permissions": 0, "share": 1, + "submit": 0, "write": 1 }, { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, "create": 1, "delete": 1, + "email": 0, "export": 0, + "if_owner": 0, "import": 0, "permlevel": 0, "print": 0, "read": 1, "report": 1, "role": "Sales Manager", + "set_user_permissions": 0, "share": 1, + "submit": 0, "write": 1 }, { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, "create": 1, "delete": 1, + "email": 0, + "export": 0, + "if_owner": 0, + "import": 0, "permlevel": 0, + "print": 0, "read": 1, "report": 1, "role": "Purchase Manager", + "set_user_permissions": 0, "share": 1, + "submit": 0, "write": 1 }, { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, "create": 1, "delete": 1, + "email": 0, + "export": 0, + "if_owner": 0, + "import": 0, "permlevel": 0, + "print": 0, "read": 1, "report": 1, "role": "Website Manager", + "set_user_permissions": 0, "share": 1, + "submit": 0, "write": 1 }, { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, "create": 1, "delete": 1, + "email": 0, "export": 1, + "if_owner": 0, "import": 1, "permlevel": 0, + "print": 0, "read": 1, "report": 1, "role": "System Manager", + "set_user_permissions": 0, "share": 1, + "submit": 0, "write": 1 } ], + "read_only": 0, + "read_only_onload": 0, "sort_field": "modified", "sort_order": "DESC", "title_field": "title" From 2b6915519f21de0f491bbf5e5d3e40f635686784 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Thu, 13 Aug 2015 15:35:55 +0530 Subject: [PATCH 52/53] [change-log] --- erpnext/change_log/current/journal_entry_rename.md | 1 - erpnext/change_log/current/stock_entry_additional_costs.md | 2 -- erpnext/change_log/v5/v5_6_0.md | 6 ++++++ 3 files changed, 6 insertions(+), 3 deletions(-) delete mode 100644 erpnext/change_log/current/journal_entry_rename.md delete mode 100644 erpnext/change_log/current/stock_entry_additional_costs.md create mode 100644 erpnext/change_log/v5/v5_6_0.md diff --git a/erpnext/change_log/current/journal_entry_rename.md b/erpnext/change_log/current/journal_entry_rename.md deleted file mode 100644 index 9bd1b668666..00000000000 --- a/erpnext/change_log/current/journal_entry_rename.md +++ /dev/null @@ -1 +0,0 @@ -- 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/change_log/current/stock_entry_additional_costs.md b/erpnext/change_log/current/stock_entry_additional_costs.md deleted file mode 100644 index ef03375381c..00000000000 --- a/erpnext/change_log/current/stock_entry_additional_costs.md +++ /dev/null @@ -1,2 +0,0 @@ -- Additional Costs in Stock Entry **[Sponsored by PT. Ridho Sribumi Sejahtera]** - - Now additional costs like shipping charges, operating costs etc can be added in Stock Entry in item valuation \ No newline at end of file diff --git a/erpnext/change_log/v5/v5_6_0.md b/erpnext/change_log/v5/v5_6_0.md new file mode 100644 index 00000000000..0ef5a3a2d09 --- /dev/null +++ b/erpnext/change_log/v5/v5_6_0.md @@ -0,0 +1,6 @@ +- 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. +- Additional Costs in Stock Entry **[Sponsored by PT. Ridho Sribumi Sejahtera]** + Now additional costs like shipping charges, operating costs etc can be added in Stock Entry in item valuation +- **Update Finished Goods** in Production Order can now use the items from **Transfer Materials for Manufacture** step instead of items from the Bill of Materials. This can be configured in Manufacturing Settings +- Added field **Tax ID** in Customer +- Bug fixes in Item, Time Log Batch, Pricing Rule, Salary Slip, Address and Stock Entry From bcfd4277f4fca8ac8c46329f91af2e6a19e9c2d1 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Thu, 13 Aug 2015 16:14:03 +0600 Subject: [PATCH 53/53] bumped to version 5.6.0 --- erpnext/__version__.py | 2 +- erpnext/hooks.py | 2 +- setup.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/__version__.py b/erpnext/__version__.py index b34f021c31c..e630695031f 100644 --- a/erpnext/__version__.py +++ b/erpnext/__version__.py @@ -1,2 +1,2 @@ from __future__ import unicode_literals -__version__ = '5.5.1' +__version__ = '5.6.0' diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 79f70f742bd..ba159edea26 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -27,7 +27,7 @@ blogs. """ app_icon = "icon-th" app_color = "#e74c3c" -app_version = "5.5.1" +app_version = "5.6.0" github_link = "https://github.com/frappe/erpnext" error_report_email = "support@erpnext.com" diff --git a/setup.py b/setup.py index 264ec61d6a3..f8f28c488d8 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,6 @@ from setuptools import setup, find_packages -version = "5.5.1" +version = "5.6.0" with open("requirements.txt", "r") as f: install_requires = f.readlines()