From ebbdd772a912923c0781d8906dcb0e32ff55fd33 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 5 Jun 2015 18:20:21 +0530 Subject: [PATCH 01/10] [fix] Validate and update manufactured qty in Stock Entry --- .../stock/doctype/stock_entry/stock_entry.py | 41 +++++++++++-------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 0196c31bf1..e374b3b862 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -5,7 +5,7 @@ from __future__ import unicode_literals import frappe import frappe.defaults -from frappe.utils import cstr, cint, flt, comma_or, nowdate, get_datetime +from frappe.utils import cstr, cint, flt, comma_or, get_datetime from frappe import _ from erpnext.stock.utils import get_incoming_rate @@ -185,11 +185,16 @@ class StockEntry(StockController): def validate_production_order(self): if self.purpose in ("Manufacture", "Material Transfer for Manufacture"): + if not self.bom_no: + frappe.throw(_("BOM No is mandatory")) + # check if production order is entered if not self.production_order: frappe.throw(_("Production order number is mandatory for stock entry purpose manufacture")) # check for double entry if self.purpose=="Manufacture": + if not self.fg_completed_qty: + frappe.throw(_("For Quantity (Manufactured Qty) is mandatory")) self.check_if_operations_completed() self.check_duplicate_entry_for_production_order() elif self.purpose != "Material Transfer": @@ -378,10 +383,21 @@ class StockEntry(StockController): def validate_finished_goods(self): """validation: finished good quantity should be same as manufacturing quantity""" + items_with_target_warehouse = [] for d in self.get('items'): if d.bom_no and flt(d.transfer_qty) != flt(self.fg_completed_qty): frappe.throw(_("Quantity in row {0} ({1}) must be same as manufactured quantity {2}"). \ format(d.idx, d.transfer_qty, self.fg_completed_qty)) + + if self.production_order and self.purpose == "Manufacture" and d.t_warehouse: + items_with_target_warehouse.append(d.item_code) + + if self.production_order and self.purpose == "Manufacture": + production_item = frappe.db.get_value("Production Order", + self.production_order, "production_item") + if production_item not in items_with_target_warehouse: + frappe.throw(_("Finished Item {0} must be entered for Manufacture type entry") + .format(production_item)) def validate_return_reference_doc(self): """validate item with reference doc""" @@ -399,7 +415,6 @@ class StockEntry(StockController): # posting date check ref_posting_datetime = "%s %s" % (ref.doc.posting_date, ref.doc.posting_time or "00:00:00") - this_posting_datetime = "%s %s" % (self.posting_date, self.posting_time) if get_datetime(ref_posting_datetime) < get_datetime(ref_posting_datetime): from frappe.utils.dateutils import datetime_in_user_format @@ -474,9 +489,10 @@ class StockEntry(StockController): pro_doc = frappe.get_doc("Production Order", self.production_order) _validate_production_order(pro_doc) pro_doc.run_method("update_status") - pro_doc.run_method("update_production_order_qty") - if self.purpose == "Manufacture": - self.update_planned_qty(pro_doc) + if self.fg_completed_qty: + pro_doc.run_method("update_production_order_qty") + if self.purpose == "Manufacture": + self.update_planned_qty(pro_doc) def update_planned_qty(self, pro_doc): from erpnext.stock.utils import update_bin @@ -546,9 +562,6 @@ class StockEntry(StockController): return ret def get_items(self): - if not self.fg_completed_qty or not self.bom_no: - frappe.throw(_("BOM and Manufacturing Quantity are required")) - self.set('items', []) self.validate_production_order() @@ -638,17 +651,16 @@ class StockEntry(StockController): issued_item_qty = self.get_issued_qty() max_qty = flt(self.pro_doc.qty) - only_pending_fetched = [] - for item in item_dict: pending_to_issue = (max_qty * item_dict[item]["qty"]) - issued_item_qty.get(item, 0) desire_to_transfer = flt(self.fg_completed_qty) * item_dict[item]["qty"] + if desire_to_transfer <= pending_to_issue: item_dict[item]["qty"] = desire_to_transfer - else: + elif pending_to_issue > 0: item_dict[item]["qty"] = pending_to_issue - if pending_to_issue: - only_pending_fetched.append(item) + else: + item_dict[item]["qty"] = 0 # delete items with 0 qty for item in item_dict.keys(): @@ -659,9 +671,6 @@ class StockEntry(StockController): if not len(item_dict): frappe.msgprint(_("""All items have already been transferred for this Production Order.""")) - elif only_pending_fetched: - frappe.msgprint(_("Pending Items {0} updated").format(only_pending_fetched)) - return item_dict def get_issued_qty(self): From 56fcf30cb901e1c1cfe819bc627c7d57151e74c9 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 5 Jun 2015 18:21:25 +0530 Subject: [PATCH 02/10] [Patch] Update material transferred for manufacturing for existing entries --- erpnext/patches.txt | 3 ++- ...rial_transferred_for_manufacturing_again.py | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 erpnext/patches/v5_0/update_material_transferred_for_manufacturing_again.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 2104f7bcf8..0d1d0de3c2 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -164,4 +164,5 @@ erpnext.patches.v5_0.update_item_and_description_again erpnext.patches.v5_0.repost_gle_for_jv_with_multiple_party erpnext.patches.v5_0.portal_fixes erpnext.patches.v5_0.reset_values_in_tools -execute:frappe.delete_doc("Page", "users") \ No newline at end of file +execute:frappe.delete_doc("Page", "users") +erpnext.patches.v5_0.update_material_transferred_for_manufacturing_again \ No newline at end of file diff --git a/erpnext/patches/v5_0/update_material_transferred_for_manufacturing_again.py b/erpnext/patches/v5_0/update_material_transferred_for_manufacturing_again.py new file mode 100644 index 0000000000..a74b4bd776 --- /dev/null +++ b/erpnext/patches/v5_0/update_material_transferred_for_manufacturing_again.py @@ -0,0 +1,18 @@ +import frappe + +def execute(): + pro_order_qty_transferred = frappe._dict() + for se in frappe.db.sql("""select production_order, sum(fg_completed_qty) as transferred_qty + from `tabStock Entry` + where docstatus=1 and ifnull(production_order, '') != '' + and purpose = 'Material Transfer for Manufacture' + group by production_order""", as_dict=1): + pro_order_qty_transferred.setdefault(se.production_order, se.transferred_qty) + + for d in frappe.get_all("Production Order", filters={"docstatus": 1}, fields=["name", "qty"]): + if d.name in pro_order_qty_transferred: + material_transferred_for_manufacturing = pro_order_qty_transferred.get(d.name) \ + if pro_order_qty_transferred.get(d.name) <= d.qty else d.qty + + frappe.db.sql("""update `tabProduction Order` set material_transferred_for_manufacturing=%s + where name=%s""", (material_transferred_for_manufacturing, d.name)) \ No newline at end of file From 7cbd916b000f612596f02f265191595f22fd1793 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 8 Jun 2015 12:06:33 +0530 Subject: [PATCH 03/10] [fix] Fiscal year issue due to missing field in Material Request --- erpnext/controllers/accounts_controller.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 16ff4b4d29..8d51bcf48f 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -38,7 +38,8 @@ class AccountsController(TransactionBase): convert_to_recurring(self, self.get("posting_date") or self.get("transaction_date")) def before_recurring(self): - self.fiscal_year = None + if self.meta.get_field("fiscal_year"): + self.fiscal_year = None if self.meta.get_field("due_date"): self.due_date = None @@ -46,7 +47,7 @@ class AccountsController(TransactionBase): for fieldname in ["posting_date", "transaction_date"]: if not self.get(fieldname) and self.meta.get_field(fieldname): self.set(fieldname, today()) - if not self.fiscal_year: + if self.meta.get_field("fiscal_year") and not self.fiscal_year: self.fiscal_year = get_fiscal_year(self.get(fieldname))[0] break From 000835c45483dcd45a9061b45c5b10a1bd64c7e6 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 8 Jun 2015 12:10:43 +0530 Subject: [PATCH 04/10] [fix] Fiscal year issue due to missing field in Material Request --- erpnext/controllers/accounts_controller.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 8d51bcf48f..98f240958f 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -38,8 +38,8 @@ class AccountsController(TransactionBase): convert_to_recurring(self, self.get("posting_date") or self.get("transaction_date")) def before_recurring(self): - if self.meta.get_field("fiscal_year"): - self.fiscal_year = None + if self.meta.get_field("fiscal_year"): + self.fiscal_year = None if self.meta.get_field("due_date"): self.due_date = None From 9dbdef3bd559b4db6de03c58b8c0b7c2c2530152 Mon Sep 17 00:00:00 2001 From: Neil Trini Lasrado Date: Mon, 8 Jun 2015 12:28:56 +0530 Subject: [PATCH 05/10] added code to fetch item name and desc on change of item code --- .../quality_inspection/quality_inspection.js | 15 +++++++++++++++ .../quality_inspection/quality_inspection.json | 10 +++++++++- .../quality_inspection/quality_inspection.py | 4 ++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/erpnext/buying/doctype/quality_inspection/quality_inspection.js b/erpnext/buying/doctype/quality_inspection/quality_inspection.js index 3188c3c135..2e772c0b34 100644 --- a/erpnext/buying/doctype/quality_inspection/quality_inspection.js +++ b/erpnext/buying/doctype/quality_inspection/quality_inspection.js @@ -57,3 +57,18 @@ cur_frm.fields_dict['item_serial_no'].get_query = function(doc, cdt, cdn) { return { filters: filter } } + +frappe.ui.form.on("Quality Inspection", "item_code", function(frm) { + frappe.call({ + method: 'erpnext.buying.doctype.quality_inspection.quality_inspection.get_item_details', + args: { + "item_code": frm.doc.item_code + }, + callback: function(r) { + if(!r.exc && r.message) { + frm.set_value("item_name", r.message.item_name); + frm.set_value("description", r.message.description); + } + } + }); +}) diff --git a/erpnext/buying/doctype/quality_inspection/quality_inspection.json b/erpnext/buying/doctype/quality_inspection/quality_inspection.json index e1b57e4e08..875ce0dace 100644 --- a/erpnext/buying/doctype/quality_inspection/quality_inspection.json +++ b/erpnext/buying/doctype/quality_inspection/quality_inspection.json @@ -127,6 +127,14 @@ "permlevel": 0, "width": "50%" }, + { + "fieldname": "item_name", + "fieldtype": "Data", + "label": "Item Name", + "permlevel": 0, + "precision": "", + "read_only": 1 + }, { "fieldname": "description", "fieldtype": "Small Text", @@ -219,7 +227,7 @@ "icon": "icon-search", "idx": 1, "is_submittable": 1, - "modified": "2015-04-14 07:37:07.331291", + "modified": "2015-06-08 02:40:25.121948", "modified_by": "Administrator", "module": "Buying", "name": "Quality Inspection", diff --git a/erpnext/buying/doctype/quality_inspection/quality_inspection.py b/erpnext/buying/doctype/quality_inspection/quality_inspection.py index ec32b6cc29..1b55c5b95c 100644 --- a/erpnext/buying/doctype/quality_inspection/quality_inspection.py +++ b/erpnext/buying/doctype/quality_inspection/quality_inspection.py @@ -39,6 +39,10 @@ class QualityInspection(Document): where t1.parent = %s and t1.item_code = %s and t1.parent = t2.name""", (self.modified, self.purchase_receipt_no, self.item_code)) +@frappe.whitelist() +def get_item_details(item_code): + item_details = frappe.db.sql("select item_name, description from tabItem where item_code=%s", item_code, as_dict=1) + return item_details[0] if item_details else {} def item_query(doctype, txt, searchfield, start, page_len, filters): if filters.get("from"): From 42c1836db5527edbef348025284c479839d19884 Mon Sep 17 00:00:00 2001 From: Neil Trini Lasrado Date: Mon, 8 Jun 2015 13:09:11 +0530 Subject: [PATCH 06/10] function changed to add fetch --- .../quality_inspection/quality_inspection.js | 17 +++-------------- .../quality_inspection/quality_inspection.py | 6 ------ 2 files changed, 3 insertions(+), 20 deletions(-) diff --git a/erpnext/buying/doctype/quality_inspection/quality_inspection.js b/erpnext/buying/doctype/quality_inspection/quality_inspection.js index 2e772c0b34..1f7134dfcd 100644 --- a/erpnext/buying/doctype/quality_inspection/quality_inspection.js +++ b/erpnext/buying/doctype/quality_inspection/quality_inspection.js @@ -58,17 +58,6 @@ cur_frm.fields_dict['item_serial_no'].get_query = function(doc, cdt, cdn) { return { filters: filter } } -frappe.ui.form.on("Quality Inspection", "item_code", function(frm) { - frappe.call({ - method: 'erpnext.buying.doctype.quality_inspection.quality_inspection.get_item_details', - args: { - "item_code": frm.doc.item_code - }, - callback: function(r) { - if(!r.exc && r.message) { - frm.set_value("item_name", r.message.item_name); - frm.set_value("description", r.message.description); - } - } - }); -}) +cur_frm.add_fetch('item_code', 'item_name', 'item_name'); +cur_frm.add_fetch('item_code', 'description', 'description'); + diff --git a/erpnext/buying/doctype/quality_inspection/quality_inspection.py b/erpnext/buying/doctype/quality_inspection/quality_inspection.py index 1b55c5b95c..d1d9518bc2 100644 --- a/erpnext/buying/doctype/quality_inspection/quality_inspection.py +++ b/erpnext/buying/doctype/quality_inspection/quality_inspection.py @@ -31,7 +31,6 @@ class QualityInspection(Document): (self.name, self.modified, self.purchase_receipt_no, self.item_code)) - def on_cancel(self): if self.purchase_receipt_no: frappe.db.sql("""update `tabPurchase Receipt Item` t1, `tabPurchase Receipt` t2 @@ -39,11 +38,6 @@ class QualityInspection(Document): where t1.parent = %s and t1.item_code = %s and t1.parent = t2.name""", (self.modified, self.purchase_receipt_no, self.item_code)) -@frappe.whitelist() -def get_item_details(item_code): - item_details = frappe.db.sql("select item_name, description from tabItem where item_code=%s", item_code, as_dict=1) - return item_details[0] if item_details else {} - def item_query(doctype, txt, searchfield, start, page_len, filters): if filters.get("from"): from frappe.desk.reportview import get_match_cond From 5ef121bc10ab2445a3c873e1458058a192969e03 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 8 Jun 2015 12:26:52 +0530 Subject: [PATCH 07/10] get party details only if party exists --- erpnext/accounts/party.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index 00e595fcb3..08402f3a49 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -16,12 +16,16 @@ def get_party_details(party=None, account=None, party_type="Customer", company=N if not party: return {} + + if not frappe.db.exists(party_type, party): + frappe.throw(_("{0}: {1} does not exists").format(party_type, party)) return _get_party_details(party, account, party_type, company, posting_date, price_list, currency, doctype) def _get_party_details(party=None, account=None, party_type="Customer", company=None, posting_date=None, price_list=None, currency=None, doctype=None, ignore_permissions=False): + out = frappe._dict(set_account_and_due_date(party, account, party_type, company, posting_date, doctype)) party = out[party_type.lower()] From b63ad44b10b9d5be7607975d35c3b38643bfdd99 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 8 Jun 2015 14:15:02 +0530 Subject: [PATCH 08/10] Book in round-off account upto max 0.05 --- 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 d2ea4cd316..8fc785c88b 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -102,7 +102,7 @@ def round_off_debit_credit(gl_map): debit_credit_diff += entry.debit - entry.credit debit_credit_diff = flt(debit_credit_diff, precision) - if abs(debit_credit_diff) >= (2.0 / (10**precision)): + if abs(debit_credit_diff) >= (5.0 / (10**precision)): frappe.throw(_("Debit and Credit not equal for {0} #{1}. Difference is {2}.") .format(gl_map[0].voucher_type, gl_map[0].voucher_no, debit_credit_diff)) From 686ef8308a6b1faa150838a5dd761cbc22e55ab8 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 8 Jun 2015 14:22:56 +0530 Subject: [PATCH 09/10] Show item name in item grid view based 'In List View' property --- erpnext/templates/form_grid/includes/visible_cols.html | 2 +- erpnext/templates/form_grid/item_grid.html | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/erpnext/templates/form_grid/includes/visible_cols.html b/erpnext/templates/form_grid/includes/visible_cols.html index e9be40ceb2..c26f279337 100644 --- a/erpnext/templates/form_grid/includes/visible_cols.html +++ b/erpnext/templates/form_grid/includes/visible_cols.html @@ -1,6 +1,6 @@ {% $.each(visible_columns || [], function(i, df) { %} {% var val = doc.get_formatted(df.fieldname); - if((df.fieldname !== "description") && val) { %} + if((df.fieldname !== "description" && df.fieldname !== "item_name") && val) { %}
{%= __(df.label) %}: diff --git a/erpnext/templates/form_grid/item_grid.html b/erpnext/templates/form_grid/item_grid.html index 8ea3e7d72a..d2db463dcb 100644 --- a/erpnext/templates/form_grid/item_grid.html +++ b/erpnext/templates/form_grid/item_grid.html @@ -1,4 +1,4 @@ -{% var visible_columns = row.get_visible_columns(["item_code", "item_name", "qty", "rate", "amount", "stock_uom", "uom", "discount_percentage", "schedule_date", "warehouse", "against_sales_order", "sales_order"]); %} +{% var visible_columns = row.get_visible_columns(["item_code", "qty", "rate", "amount", "stock_uom", "uom", "discount_percentage", "schedule_date", "warehouse", "against_sales_order", "sales_order"]); %} {% if(!doc) { %}
@@ -8,6 +8,7 @@
{%= __("Amount") %}
{% } else { %} + {% var visible_column_fieldnames = $.map(visible_columns, function(x, i) {return x.fieldname}); %}
{% if(doc.warehouse) { @@ -45,11 +46,11 @@ {%= doc.item_code %} {% } %} - {% if(doc.item_name != doc.item_code) { %} + {% if(doc.item_name != doc.item_code && in_list(visible_column_fieldnames, "item_name")) { %}
{%= doc.item_name %}{% } %} {% if((doc.description != doc.item_code != doc.item_name) && - in_list($.map(visible_columns, function(x, i) {return x.fieldname}), "description")) { %} + in_list(visible_column_fieldnames, "description")) { %}
Description: {%= doc.get_formatted("description") %}{% } %} From dc76b3fa2052871a1276e8bb9fb31985c3045a5e Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 8 Jun 2015 15:15:13 +0600 Subject: [PATCH 10/10] bumped to version 5.0.19 --- 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 4d1f63eb39..dc8b23eea5 100644 --- a/erpnext/__version__.py +++ b/erpnext/__version__.py @@ -1,2 +1,2 @@ from __future__ import unicode_literals -__version__ = '5.0.18' +__version__ = '5.0.19' diff --git a/erpnext/hooks.py b/erpnext/hooks.py index dd46af3201..79856809dc 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -5,7 +5,7 @@ app_publisher = "Frappe Technologies Pvt. Ltd. and Contributors" app_description = "Open Source Enterprise Resource Planning for Small and Midsized Organizations" app_icon = "icon-th" app_color = "#e74c3c" -app_version = "5.0.18" +app_version = "5.0.19" error_report_email = "support@erpnext.com" diff --git a/setup.py b/setup.py index d4a9356fae..f43a11c6b2 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,6 @@ from setuptools import setup, find_packages -version = "5.0.18" +version = "5.0.19" with open("requirements.txt", "r") as f: install_requires = f.readlines()