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 89f3ad565e..7f190d2edc 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 @@ class TestLandedCostVoucher(unittest.TestCase): 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 3965417d23..585f8dd55f 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -133,7 +133,7 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({ 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 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({ 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.barcode = function(doc, cdt, cdn) { } } -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.company = function(doc, cdt, cdn) { 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 9702972604..247ce787a6 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.stock_ledger import get_previous_sle, NegativeStockError 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 @@ class StockEntry(StockController): 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 @@ class StockEntry(StockController): 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 @@ class StockEntry(StockController): } 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 @@ def get_operating_cost_per_unit(production_order=None, bom_no=None): 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