From ce8adecbadbcbc464de705d7dff9f3e1671dc242 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Fri, 15 Dec 2017 12:13:50 +0530 Subject: [PATCH] Incoming rate fixes (#11986) --- erpnext/controllers/selling_controller.py | 16 ++++-- erpnext/stock/doctype/item/item.json | 4 +- .../stock/doctype/stock_entry/stock_entry.js | 50 +++++++++++-------- .../stock/doctype/stock_entry/stock_entry.py | 40 +++++++++++---- erpnext/stock/utils.py | 21 ++++---- 5 files changed, 85 insertions(+), 46 deletions(-) diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index 75704b0720f..0cd5a070585 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -185,7 +185,10 @@ class SellingController(StockController): 'batch_no': cstr(p.batch_no).strip(), 'serial_no': cstr(p.serial_no).strip(), 'name': d.name, - 'target_warehouse': p.target_warehouse + 'target_warehouse': p.target_warehouse, + 'company': self.company, + 'voucher_type': self.doctype, + 'allow_zero_valuation': d.allow_zero_valuation_rate })) else: il.append(frappe._dict({ @@ -198,7 +201,10 @@ class SellingController(StockController): 'batch_no': cstr(d.get("batch_no")).strip(), 'serial_no': cstr(d.get("serial_no")).strip(), 'name': d.name, - 'target_warehouse': d.target_warehouse + 'target_warehouse': d.target_warehouse, + 'company': self.company, + 'voucher_type': self.doctype, + 'allow_zero_valuation': d.allow_zero_valuation_rate })) return il @@ -293,7 +299,11 @@ class SellingController(StockController): "posting_date": self.posting_date, "posting_time": self.posting_time, "qty": -1*flt(d.qty), - "serial_no": d.serial_no + "serial_no": d.serial_no, + "company": d.company, + "voucher_type": d.voucher_type, + "voucher_no": d.name, + "allow_zero_valuation": d.allow_zero_valuation }) target_warehouse_sle.update({ "incoming_rate": get_incoming_rate(args) diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json index a97a24fe9e0..428e3663c21 100644 --- a/erpnext/stock/doctype/item/item.json +++ b/erpnext/stock/doctype/item/item.json @@ -426,7 +426,7 @@ "bold": 0, "collapsible": 0, "columns": 0, - "depends_on": "eval:(doc.is_stock_item && !doc.has_serial_no && !doc.has_batch_no)", + "depends_on": "is_stock_item", "fieldname": "valuation_rate", "fieldtype": "Currency", "hidden": 0, @@ -3453,7 +3453,7 @@ "issingle": 0, "istable": 0, "max_attachments": 1, - "modified": "2017-12-08 07:20:25.932499", + "modified": "2017-12-13 07:20:25.932499", "modified_by": "Administrator", "module": "Stock", "name": "Item", diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index 40fc44d71dd..79afb214a43 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -127,7 +127,7 @@ frappe.ui.form.on('Stock Entry', { } }, - set_serial_no: function(frm, cdt, cdn) { + set_serial_no: function(frm, cdt, cdn, callback) { var d = frappe.model.get_doc(cdt, cdn); if(!d.item_code && !d.s_warehouse && !d.qty) return; var args = { @@ -142,6 +142,10 @@ frappe.ui.form.on('Stock Entry', { if (!r.exe){ frappe.model.set_value(cdt, cdn, "serial_no", r.message); } + + if (callback) { + callback(); + } } }); }, @@ -170,7 +174,7 @@ frappe.ui.form.on('Stock Entry', { frm.fields_dict["items"].grid.set_column_disp(["cost_center", "expense_account"], enabled); }, - set_basic_rate: function(frm, cdt, cdn, callback) { + set_basic_rate: function(frm, cdt, cdn) { const item = locals[cdt][cdn]; item.transfer_qty = flt(item.qty) * flt(item.conversion_factor); @@ -179,9 +183,12 @@ frappe.ui.form.on('Stock Entry', { 'posting_date' : frm.doc.posting_date, 'posting_time' : frm.doc.posting_time, 'warehouse' : cstr(item.s_warehouse) || cstr(item.t_warehouse), - 'serial_no ' : item.serial_no, + 'serial_no' : item.serial_no, 'company' : frm.doc.company, - 'qty' : item.s_warehouse ? -1*flt(item.transfer_qty) : flt(item.transfer_qty) + 'qty' : item.s_warehouse ? -1*flt(item.transfer_qty) : flt(item.transfer_qty), + 'voucher_type' : frm.doc.doctype, + 'voucher_no' : item.name, + 'allow_zero_valuation': 1, }; frappe.call({ @@ -190,17 +197,13 @@ frappe.ui.form.on('Stock Entry', { args: args }, callback: function(r) { - frappe.model.set_value(cdt, cdn, 'basic_rate', r.message); + frappe.model.set_value(cdt, cdn, 'basic_rate', (r.message || 0.0)); frm.events.calculate_basic_amount(frm, item); - - if (callback) { - callback(); - } } }) }, - get_warehouse_details: function(frm, cdt, cdn, callback) { + get_warehouse_details: function(frm, cdt, cdn) { var child = locals[cdt][cdn]; if(!child.bom_no) { frappe.call({ @@ -213,7 +216,11 @@ frappe.ui.form.on('Stock Entry', { 'serial_no': child.serial_no, 'qty': child.s_warehouse ? -1* child.transfer_qty : child.transfer_qty, 'posting_date': frm.doc.posting_date, - 'posting_time': frm.doc.posting_time + 'posting_time': frm.doc.posting_time, + 'company': frm.doc.company, + 'voucher_type': frm.doc.doctype, + 'voucher_no': child.name, + 'allow_zero_valuation': 1 } }, callback: function(r) { @@ -221,10 +228,6 @@ frappe.ui.form.on('Stock Entry', { $.extend(child, r.message); frm.events.calculate_basic_amount(frm, child); } - - if (callback) { - callback(); - } } }); } @@ -276,8 +279,8 @@ frappe.ui.form.on('Stock Entry', { frappe.ui.form.on('Stock Entry Detail', { qty: function(frm, cdt, cdn) { - frm.events.set_basic_rate(frm, cdt, cdn, () => { - frm.events.set_serial_no(frm, cdt, cdn); + frm.events.set_serial_no(frm, cdt, cdn, () => { + frm.events.set_basic_rate(frm, cdt, cdn); }); }, @@ -286,8 +289,8 @@ frappe.ui.form.on('Stock Entry Detail', { }, s_warehouse: function(frm, cdt, cdn) { - frm.events.get_warehouse_details(frm, cdt, cdn, () => { - frm.events.set_serial_no(frm, cdt, cdn); + frm.events.set_serial_no(frm, cdt, cdn, () => { + frm.events.get_warehouse_details(frm, cdt, cdn); }); }, @@ -342,11 +345,14 @@ frappe.ui.form.on('Stock Entry Detail', { 'warehouse' : cstr(d.s_warehouse) || cstr(d.t_warehouse), 'transfer_qty' : d.transfer_qty, 'serial_no' : d.serial_no, - 'bom_no' : d.bom_no, + 'bom_no' : d.bom_no, 'expense_account' : d.expense_account, 'cost_center' : d.cost_center, - 'company' : frm.doc.company, - 'qty' : d.qty + 'company' : frm.doc.company, + 'qty' : d.qty, + 'voucher_type' : frm.doc.doctype, + 'voucher_no' : d.name, + 'allow_zero_valuation': 1, }; return frappe.call({ doc: frm.doc, diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 4e870118c1f..8eb2bd20279 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -2,12 +2,12 @@ # License: GNU General Public License v3. See license.txt from __future__ import unicode_literals -import frappe +import frappe, erpnext import frappe.defaults from frappe import _ from frappe.utils import cstr, cint, flt, comma_or, getdate, nowdate, formatdate, format_time from erpnext.stock.utils import get_incoming_rate -from erpnext.stock.stock_ledger import get_previous_sle, NegativeStockError +from erpnext.stock.stock_ledger import get_previous_sle, NegativeStockError, get_valuation_rate from erpnext.stock.get_item_details import get_bin_details, get_default_cost_center, get_conversion_factor from erpnext.stock.doctype.batch.batch import get_batch_no, set_batch_nos, get_batch_qty from erpnext.manufacturing.doctype.bom.bom import validate_bom_no @@ -55,6 +55,7 @@ class StockEntry(StockController): else: set_batch_nos(self, 's_warehouse') + self.set_incoming_rate() self.set_actual_qty() self.calculate_rate_and_amount(update_finished_item_rate=False) @@ -213,6 +214,18 @@ class StockEntry(StockController): frappe.throw(_("Stock Entries already created for Production Order ") + self.production_order + ":" + ", ".join(other_ste), DuplicateEntryForProductionOrderError) + def set_incoming_rate(self): + for d in self.items: + if d.s_warehouse: + args = self.get_args_for_incoming_rate(d) + d.basic_rate = get_incoming_rate(args) + elif d.allow_zero_valuation_rate and not d.s_warehouse: + d.basic_rate = 0.0 + elif d.t_warehouse and not d.basic_rate: + d.basic_rate = get_valuation_rate(d.item_code, d.t_warehouse, + self.doctype, d.name, d.allow_zero_valuation_rate, + currency=erpnext.get_company_currency(self.company)) + def set_actual_qty(self): allow_negative_stock = cint(frappe.db.get_value("Stock Settings", None, "allow_negative_stock")) @@ -268,14 +281,7 @@ class StockEntry(StockController): for d in self.get('items'): if d.t_warehouse: fg_basic_rate = flt(d.basic_rate) - 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, - }) + args = self.get_args_for_incoming_rate(d) # get basic rate if not d.bom_no: @@ -304,6 +310,20 @@ class StockEntry(StockController): if (fg_basic_rate == 0.0 and number_of_fg_items == 1) or update_finished_item_rate: self.set_basic_rate_for_finished_goods(raw_material_cost, scrap_material_cost) + def get_args_for_incoming_rate(self, item): + return frappe._dict({ + "item_code": item.item_code, + "warehouse": item.s_warehouse or item.t_warehouse, + "posting_date": self.posting_date, + "posting_time": self.posting_time, + "qty": item.s_warehouse and -1*flt(item.transfer_qty) or flt(item.transfer_qty), + "serial_no": item.serial_no, + "voucher_type": self.doctype, + "voucher_no": item.name, + "company": self.company, + "allow_zero_valuation": item.allow_zero_valuation_rate, + }) + def set_basic_rate_for_finished_goods(self, raw_material_cost, scrap_material_cost): if self.purpose in ["Manufacture", "Repack"]: for d in self.get("items"): diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py index 6f091446d9e..ae71f355b60 100644 --- a/erpnext/stock/utils.py +++ b/erpnext/stock/utils.py @@ -2,7 +2,7 @@ # License: GNU General Public License v3. See license.txt from __future__ import unicode_literals -import frappe +import frappe, erpnext from frappe import _ import json from frappe.utils import flt, cstr, nowdate, nowtime @@ -125,8 +125,7 @@ def update_bin(args, allow_negative_stock=False, via_landed_cost_voucher=False): @frappe.whitelist() def get_incoming_rate(args): """Get Incoming Rate based on valuation method""" - from erpnext.stock.stock_ledger import get_previous_sle - + from erpnext.stock.stock_ledger import get_previous_sle, get_valuation_rate if isinstance(args, basestring): args = json.loads(args) @@ -137,15 +136,19 @@ def get_incoming_rate(args): valuation_method = get_valuation_method(args.get("item_code")) previous_sle = get_previous_sle(args) if valuation_method == 'FIFO': - if not previous_sle: - return 0.0 - previous_stock_queue = json.loads(previous_sle.get('stock_queue', '[]') or '[]') - in_rate = get_fifo_rate(previous_stock_queue, args.get("qty") or 0) if previous_stock_queue else 0 - if not in_rate and not previous_stock_queue: - in_rate = previous_sle.get('valuation_rate') or 0 + if previous_sle: + previous_stock_queue = json.loads(previous_sle.get('stock_queue', '[]') or '[]') + in_rate = get_fifo_rate(previous_stock_queue, args.get("qty") or 0) if previous_stock_queue else 0 elif valuation_method == 'Moving Average': in_rate = previous_sle.get('valuation_rate') or 0 + if not in_rate: + voucher_no = args.get('voucher_no') or args.get('name') + + in_rate = get_valuation_rate(args.get('item_code'), args.get('warehouse'), + args.get('voucher_type'), voucher_no, args.get('allow_zero_valuation'), + currency=erpnext.get_company_currency(args.get('company'))) + return in_rate def get_avg_purchase_rate(serial_nos):