Merge pull request #4573 from anandpdoshi/fix/stock-entry-calculate-amount-in-js

[fix] Stock Entry: calculate amount in javascript
diff --git a/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py
index 89f3ad5..7f190d2 100644
--- a/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py
+++ b/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py
@@ -15,20 +15,32 @@
 		pr = frappe.copy_doc(pr_test_records[0])
 		pr.submit()
 
-		bin_details = frappe.db.get_value("Bin", {"warehouse": "_Test Warehouse - _TC",
-			"item_code": "_Test Item"},	["actual_qty", "stock_value"], as_dict=1)
+		last_sle = frappe.db.get_value("Stock Ledger Entry", {
+				"voucher_type": pr.doctype,
+				"voucher_no": pr.name,
+				"item_code": "_Test Item",
+				"warehouse": "_Test Warehouse - _TC"
+			},
+			fieldname=["qty_after_transaction", "stock_value"],
+			as_dict=1)
 
 		self.submit_landed_cost_voucher(pr)
 
 		pr_lc_value = frappe.db.get_value("Purchase Receipt Item", {"parent": pr.name}, "landed_cost_voucher_amount")
 		self.assertEquals(pr_lc_value, 25.0)
 
-		bin_details_after_lcv = frappe.db.get_value("Bin", {"warehouse": "_Test Warehouse - _TC",
-			"item_code": "_Test Item"},	["actual_qty", "stock_value"], as_dict=1)
+		last_sle_after_landed_cost = frappe.db.get_value("Stock Ledger Entry", {
+				"voucher_type": pr.doctype,
+				"voucher_no": pr.name,
+				"item_code": "_Test Item",
+				"warehouse": "_Test Warehouse - _TC"
+			},
+			fieldname=["qty_after_transaction", "stock_value"],
+			as_dict=1)
 
-		self.assertEqual(bin_details.actual_qty, bin_details_after_lcv.actual_qty)
+		self.assertEqual(last_sle.qty_after_transaction, last_sle_after_landed_cost.qty_after_transaction)
 
-		self.assertEqual(bin_details_after_lcv.stock_value - bin_details.stock_value, 25.0)
+		self.assertEqual(last_sle_after_landed_cost.stock_value - last_sle.stock_value, 25.0)
 
 		gl_entries = get_gl_entries("Purchase Receipt", pr.name)
 
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js
index 3965417..585f8dd 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.js
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.js
@@ -133,7 +133,7 @@
 	qty: function(doc, cdt, cdn) {
 		var d = locals[cdt][cdn];
 		d.transfer_qty = flt(d.qty) * flt(d.conversion_factor);
-		refresh_field('items');
+		this.calculate_basic_amount(d);
 	},
 
 	production_order: function() {
@@ -227,7 +227,89 @@
 
 	items_on_form_rendered: function(doc, grid_row) {
 		erpnext.setup_serial_no();
-	}
+	},
+
+	basic_rate: function(doc, cdt, cdn) {
+		var item = frappe.model.get_doc(cdt, cdn);
+		this.calculate_basic_amount(item);
+	},
+
+	s_warehouse: function(doc, cdt, cdn) {
+
+	},
+
+	t_warehouse: function(doc, cdt, cdn) {
+		this.s_warehouse(doc, cdt, cdn);
+	},
+
+	get_warehouse_details: function(doc, cdt, cdn) {
+		var me = this;
+		var d = locals[cdt][cdn];
+		if(!d.bom_no) {
+			frappe.call({
+				method: "erpnext.stock.doctype.stock_entry.stock_entry.get_warehouse_details",
+				args: {
+					"args": {
+						'item_code': d.item_code,
+						'warehouse': cstr(d.s_warehouse) || cstr(d.t_warehouse),
+						'transfer_qty': d.transfer_qty,
+						'serial_no': d.serial_no,
+						'qty': d.s_warehouse ? -1* d.qty : d.qty,
+						'posting_date': this.frm.doc.posting_date,
+						'posting_time': this.frm.doc.posting_time
+					}
+				},
+				callback: function(r) {
+					if (!r.exc) {
+						$.extend(d, r.message);
+						me.calculate_basic_amount(d);
+					}
+				}
+			});
+		}
+	},
+
+	calculate_basic_amount: function(item) {
+		item.basic_amount = flt(flt(item.transfer_qty) * flt(item.basic_rate),
+			precision("basic_amount", item));
+
+		this.calculate_amount();
+	},
+
+	calculate_amount: function() {
+		this.calculate_total_additional_costs();
+
+		var total_basic_amount = frappe.utils.sum(
+			(this.frm.doc.items || []).map(function(i) { return i.t_warehouse ? flt(i.basic_amount) : 0; })
+		);
+
+		for (var i in this.frm.doc.items) {
+			var item = this.frm.doc.items[i];
+
+			if (item.t_warehouse && total_basic_amount) {
+				item.additional_cost = (flt(item.basic_amount) / total_basic_amount) * this.frm.doc.total_additional_costs;
+			} else {
+				item.additional_cost = 0;
+			}
+
+			item.amount = flt(item.basic_amount + flt(item.additional_cost),
+				precision("amount", item));
+
+			item.valuation_rate = flt(flt(item.basic_rate)
+				+ (flt(item.additional_cost) / flt(item.transfer_qty)),
+				precision("valuation_rate", item));
+		}
+
+		refresh_field('items');
+	},
+
+	calculate_total_additional_costs: function() {
+		var total_additional_costs = frappe.utils.sum(
+			(this.frm.doc.additional_costs || []).map(function(c) { return flt(c.amount); })
+		);
+
+		this.frm.set_value("total_additional_costs", flt(total_additional_costs, precision("total_additional_costs")));
+	},
 });
 
 cur_frm.script_manager.make(erpnext.stock.StockEntry);
@@ -346,23 +428,6 @@
 	}
 }
 
-cur_frm.cscript.s_warehouse = function(doc, cdt, cdn) {
-	var d = locals[cdt][cdn];
-	if(!d.bom_no) {
-		args = {
-			'item_code'		: d.item_code,
-			'warehouse'		: cstr(d.s_warehouse) || cstr(d.t_warehouse),
-			'transfer_qty'	: d.transfer_qty,
-			'serial_no'		: d.serial_no,
-			'qty'			: d.s_warehouse ? -1* d.qty : d.qty
-		}
-		return get_server_fields('get_warehouse_details', JSON.stringify(args),
-			'items', doc, cdt, cdn, 1);
-	}
-}
-
-cur_frm.cscript.t_warehouse = cur_frm.cscript.s_warehouse;
-
 cur_frm.cscript.uom = function(doc, cdt, cdn) {
 	var d = locals[cdt][cdn];
 	if(d.uom && d.item_code){
@@ -406,3 +471,9 @@
 cur_frm.cscript.posting_date = function(doc, cdt, cdn){
 	erpnext.get_fiscal_year(doc.company, doc.posting_date);
 }
+
+frappe.ui.form.on('Landed Cost Taxes and Charges', {
+	amount: function(frm) {
+		frm.cscript.calculate_amount();
+	}
+})
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 9702972..247ce78 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -11,6 +11,7 @@
 from erpnext.stock.get_item_details import get_available_qty, get_default_cost_center, get_conversion_factor
 from erpnext.manufacturing.doctype.bom.bom import validate_bom_no
 from erpnext.accounts.utils import validate_fiscal_year
+import json
 
 class IncorrectValuationRateError(frappe.ValidationError): pass
 class DuplicateEntryForProductionOrderError(frappe.ValidationError): pass
@@ -293,7 +294,9 @@
 	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.valuation_rate = flt(
+				flt(d.basic_rate)
+				+ (flt(d.additional_cost) / flt(d.transfer_qty)),
 				d.precision("valuation_rate"))
 
 	def set_total_incoming_outgoing_value(self):
@@ -473,7 +476,10 @@
 		if not ret["expense_account"]:
 			ret["expense_account"] = frappe.db.get_value("Company", self.company, "stock_adjustment_account")
 
-		stock_and_rate = args.get('warehouse') and self.get_warehouse_details(args) or {}
+		args['posting_date'] = self.posting_date
+		args['posting_time'] = self.posting_time
+
+		stock_and_rate = args.get('warehouse') and get_warehouse_details(args) or {}
 		ret.update(stock_and_rate)
 
 		return ret
@@ -495,21 +501,6 @@
 			}
 		return ret
 
-	def get_warehouse_details(self, args):
-		ret = {}
-		if args.get('warehouse') and args.get('item_code'):
-			args.update({
-				"posting_date": self.posting_date,
-				"posting_time": self.posting_time,
-			})
-			args = frappe._dict(args)
-
-			ret = {
-				"actual_qty" : get_previous_sle(args).get("qty_after_transaction") or 0,
-				"basic_rate" : get_incoming_rate(args)
-			}
-		return ret
-
 	def get_items(self):
 		self.set('items', [])
 		self.validate_production_order()
@@ -806,3 +797,23 @@
 		operating_cost_per_unit = flt(bom.operating_cost) / flt(bom.quantity)
 
 	return operating_cost_per_unit
+
+@frappe.whitelist()
+def get_warehouse_details(args):
+	if isinstance(args, basestring):
+		args = json.loads(args)
+
+	args = frappe._dict(args)
+
+	ret = {}
+	if args.warehouse and args.item_code:
+		args.update({
+			"posting_date": args.posting_date,
+			"posting_time": args.posting_time,
+		})
+		ret = {
+			"actual_qty" : get_previous_sle(args).get("qty_after_transaction") or 0,
+			"basic_rate" : get_incoming_rate(args)
+		}
+
+	return ret