Additional Costs in Stock Entry

This commit is contained in:
Nabin Hait 2015-08-07 17:17:03 +05:30
parent 246e47e76e
commit 3c3a3ecea8
19 changed files with 1404 additions and 330 deletions

View File

@ -746,7 +746,7 @@ class TestSalesInvoice(unittest.TestCase):
def test_return_sales_invoice(self): def test_return_sales_invoice(self):
set_perpetual_inventory() 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() actual_qty_0 = get_qty_after_transaction()

View File

@ -10,6 +10,9 @@ from frappe.model.document import Document
from erpnext.manufacturing.doctype.bom.bom import validate_bom_no from erpnext.manufacturing.doctype.bom.bom import validate_bom_no
from dateutil.relativedelta import relativedelta from dateutil.relativedelta import relativedelta
from erpnext.stock.doctype.item.item import validate_end_of_life 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 OverProductionError(frappe.ValidationError): pass
class StockOverProductionError(frappe.ValidationError): pass class StockOverProductionError(frappe.ValidationError): pass
@ -17,9 +20,6 @@ class OperationTooLongError(frappe.ValidationError): pass
class ProductionNotApplicableError(frappe.ValidationError): pass class ProductionNotApplicableError(frappe.ValidationError): pass
class ItemHasVariantError(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 = { form_grid_templates = {
"operations": "templates/form_grid/production_order_grid.html" "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.company = production_order.company
stock_entry.from_bom = 1 stock_entry.from_bom = 1
stock_entry.bom_no = production_order.bom_no 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.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)) 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: else:
stock_entry.from_warehouse = production_order.wip_warehouse stock_entry.from_warehouse = production_order.wip_warehouse
stock_entry.to_warehouse = production_order.fg_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() stock_entry.get_items()
return stock_entry.as_dict() return stock_entry.as_dict()

View File

@ -28,9 +28,9 @@ class TestProductionOrder(unittest.TestCase):
# add raw materials to stores # add raw materials to stores
test_stock_entry.make_stock_entry(item_code="_Test Item", 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", 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 # from stores to wip
s = frappe.get_doc(make_stock_entry(pro_order.name, "Material Transfer for Manufacture", 4)) 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() pro_doc = self.check_planned_qty()
test_stock_entry.make_stock_entry(item_code="_Test Item", 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", 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 = frappe.get_doc(make_stock_entry(pro_doc.name, "Manufacture", 7))
s.insert() s.insert()

View File

@ -189,3 +189,4 @@ erpnext.patches.v5_4.notify_system_managers_regarding_wrong_tax_calculation
erpnext.patches.v5_4.fix_invoice_outstanding 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") 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.fix_missing_item_images
erpnext.patches.v5_4.stock_entry_additional_costs

View File

@ -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()

View File

@ -37,7 +37,7 @@ class TestDeliveryNote(unittest.TestCase):
set_perpetual_inventory(0) set_perpetual_inventory(0)
self.assertEqual(cint(frappe.defaults.get_global_default("auto_accounting_for_stock")), 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({ stock_queue = json.loads(get_previous_sle({
"item_code": "_Test Item", "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) self.assertEqual(cint(frappe.defaults.get_global_default("auto_accounting_for_stock")), 1)
frappe.db.set_value("Item", "_Test Item", "valuation_method", "FIFO") 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"}) stock_in_hand_account = frappe.db.get_value("Account", {"warehouse": "_Test Warehouse - _TC"})
prev_bal = get_balance_on(stock_in_hand_account) prev_bal = get_balance_on(stock_in_hand_account)
@ -85,7 +85,7 @@ class TestDeliveryNote(unittest.TestCase):
# back dated incoming entry # back dated incoming entry
make_stock_entry(posting_date=add_days(nowdate(), -2), target="_Test Warehouse - _TC", 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) gl_entries = get_gl_entries("Delivery Note", dn.name)
self.assertTrue(gl_entries) self.assertTrue(gl_entries)
@ -107,9 +107,9 @@ class TestDeliveryNote(unittest.TestCase):
def test_delivery_note_gl_entry_packing_item(self): def test_delivery_note_gl_entry_packing_item(self):
set_perpetual_inventory() 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", 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"}) stock_in_hand_account = frappe.db.get_value("Account", {"warehouse": "_Test Warehouse - _TC"})
prev_bal = get_balance_on(stock_in_hand_account) 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): def test_sales_return_for_non_bundled_items(self):
set_perpetual_inventory() 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() actual_qty_0 = get_qty_after_transaction()

View File

@ -47,7 +47,7 @@ class TestItem(unittest.TestCase):
def test_template_cannot_have_stock(self): def test_template_cannot_have_stock(self):
item = self.get_item(10) 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 item.has_variants = 1
self.assertRaises(ItemTemplateCannotHaveStock, item.save) self.assertRaises(ItemTemplateCannotHaveStock, item.save)

View File

@ -305,7 +305,7 @@ def make_stock_entry(source_name, target_doc=None):
def set_missing_values(source, target): def set_missing_values(source, target):
target.purpose = source.material_request_type 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, { doclist = get_mapped_doc("Material Request", source_name, {
"Material Request": { "Material Request": {

View File

@ -72,7 +72,7 @@ class TestMaterialRequest(unittest.TestCase):
"doctype": "Stock Entry Detail", "doctype": "Stock Entry Detail",
"item_code": "_Test Item Home Desktop 100", "item_code": "_Test Item Home Desktop 100",
"parentfield": "items", "parentfield": "items",
"incoming_rate": 100, "basic_rate": 100,
"qty": qty1, "qty": qty1,
"stock_uom": "_Test UOM 1", "stock_uom": "_Test UOM 1",
"transfer_qty": qty1, "transfer_qty": qty1,
@ -84,7 +84,7 @@ class TestMaterialRequest(unittest.TestCase):
"doctype": "Stock Entry Detail", "doctype": "Stock Entry Detail",
"item_code": "_Test Item Home Desktop 200", "item_code": "_Test Item Home Desktop 200",
"parentfield": "items", "parentfield": "items",
"incoming_rate": 100, "basic_rate": 100,
"qty": qty2, "qty": qty2,
"stock_uom": "_Test UOM 1", "stock_uom": "_Test UOM 1",
"transfer_qty": qty2, "transfer_qty": qty2,
@ -196,13 +196,13 @@ class TestMaterialRequest(unittest.TestCase):
"qty": 27.0, "qty": 27.0,
"transfer_qty": 27.0, "transfer_qty": 27.0,
"s_warehouse": "_Test Warehouse 1 - _TC", "s_warehouse": "_Test Warehouse 1 - _TC",
"incoming_rate": 1.0 "basic_rate": 1.0
}) })
se_doc.get("items")[1].update({ se_doc.get("items")[1].update({
"qty": 1.5, "qty": 1.5,
"transfer_qty": 1.5, "transfer_qty": 1.5,
"s_warehouse": "_Test Warehouse 1 - _TC", "s_warehouse": "_Test Warehouse 1 - _TC",
"incoming_rate": 1.0 "basic_rate": 1.0
}) })
# make available the qty in _Test Warehouse 1 before transfer # make available the qty in _Test Warehouse 1 before transfer
@ -279,13 +279,13 @@ class TestMaterialRequest(unittest.TestCase):
"qty": 60.0, "qty": 60.0,
"transfer_qty": 60.0, "transfer_qty": 60.0,
"s_warehouse": "_Test Warehouse 1 - _TC", "s_warehouse": "_Test Warehouse 1 - _TC",
"incoming_rate": 1.0 "basic_rate": 1.0
}) })
se_doc.get("items")[1].update({ se_doc.get("items")[1].update({
"qty": 3.0, "qty": 3.0,
"transfer_qty": 3.0, "transfer_qty": 3.0,
"s_warehouse": "_Test Warehouse 1 - _TC", "s_warehouse": "_Test Warehouse 1 - _TC",
"incoming_rate": 1.0 "basic_rate": 1.0
}) })
# make available the qty in _Test Warehouse 1 before transfer # make available the qty in _Test Warehouse 1 before transfer
@ -350,13 +350,13 @@ class TestMaterialRequest(unittest.TestCase):
"transfer_qty": 60.0, "transfer_qty": 60.0,
"s_warehouse": "_Test Warehouse - _TC", "s_warehouse": "_Test Warehouse - _TC",
"t_warehouse": "_Test Warehouse 1 - _TC", "t_warehouse": "_Test Warehouse 1 - _TC",
"incoming_rate": 1.0 "basic_rate": 1.0
}) })
se_doc.get("items")[1].update({ se_doc.get("items")[1].update({
"qty": 3.0, "qty": 3.0,
"transfer_qty": 3.0, "transfer_qty": 3.0,
"s_warehouse": "_Test Warehouse 1 - _TC", "s_warehouse": "_Test Warehouse 1 - _TC",
"incoming_rate": 1.0 "basic_rate": 1.0
}) })
# check for stopped status of Material Request # check for stopped status of Material Request

View File

@ -80,9 +80,9 @@ class TestPurchaseReceipt(unittest.TestCase):
def test_subcontracting(self): def test_subcontracting(self):
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry 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", 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") pr = make_purchase_receipt(item_code="_Test FG Item", qty=10, rate=500, is_subcontracted="Yes")
self.assertEquals(len(pr.get("supplied_items")), 2) self.assertEquals(len(pr.get("supplied_items")), 2)

View File

@ -25,7 +25,8 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({
return { return {
"filters": { "filters": {
"docstatus": 1, "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]; var d = locals[cdt][cdn];
d.transfer_qty = flt(d.qty) * flt(d.conversion_factor); d.transfer_qty = flt(d.qty) * flt(d.conversion_factor);
refresh_field('items'); refresh_field('items');
calculate_total(doc, cdt, cdn);
},
incoming_rate: function(doc, cdt, cdn) {
calculate_total(doc, cdt, cdn);
}, },
production_order: function() { production_order: function() {
@ -138,13 +142,29 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({
return frappe.call({ return frappe.call({
method: "erpnext.stock.doctype.stock_entry.stock_entry.get_production_order_details", 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) { callback: function(r) {
if (!r.exc) { 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) 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("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") { if(doc.purpose == "Material Receipt") {
cur_frm.set_value("from_bom", 0); 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) { cur_frm.fields_dict['production_order'].get_query = function(doc) {
return { return {
filters: [ filters: [
['Production Order', 'docstatus', '=', 1], ['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){ cur_frm.cscript.posting_date = function(doc, cdt, cdn){
erpnext.get_fiscal_year(doc.company, doc.posting_date); 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<items.length;i++) {
total_amount += flt(items[i].amount);
}
doc.total_amount = total_amount;
refresh_field("total_amount");
}

File diff suppressed because it is too large Load Diff

View File

@ -29,8 +29,7 @@ class StockEntry(StockController):
def onload(self): def onload(self):
if self.docstatus==1: if self.docstatus==1:
for item in self.get("items"): for item in self.get("items"):
item.update(get_available_qty(item.item_code, item.update(get_available_qty(item.item_code, item.s_warehouse))
item.s_warehouse))
def validate(self): def validate(self):
self.pro_doc = None self.pro_doc = None
@ -46,15 +45,13 @@ class StockEntry(StockController):
self.validate_uom_is_integer("stock_uom", "transfer_qty") self.validate_uom_is_integer("stock_uom", "transfer_qty")
self.validate_warehouse() self.validate_warehouse()
self.validate_production_order() self.validate_production_order()
self.get_stock_and_rate()
self.validate_bom() self.validate_bom()
self.validate_finished_goods() self.validate_finished_goods()
self.validate_with_material_request() self.validate_with_material_request()
self.distribute_taxes()
self.validate_valuation_rate()
self.set_total_incoming_outgoing_value()
self.set_total_amount()
self.validate_batch() self.validate_batch()
self.set_actual_qty()
self.calculate_rate_and_amount()
def on_submit(self): def on_submit(self):
self.update_stock_ledger() self.update_stock_ledger()
@ -214,6 +211,88 @@ class StockEntry(StockController):
if fg_qty_already_entered >= qty: if fg_qty_already_entered >= qty:
frappe.throw(_("Stock Entries already created for Production Order ") frappe.throw(_("Stock Entries already created for Production Order ")
+ self.production_order + ":" + ", ".join(other_ste), DuplicateEntryForProductionOrderError) + 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): def validate_valuation_rate(self):
if self.purpose in ["Manufacture", "Repack"]: if self.purpose in ["Manufacture", "Repack"]:
@ -222,11 +301,11 @@ class StockEntry(StockController):
if d.s_warehouse and not d.t_warehouse: if d.s_warehouse and not d.t_warehouse:
valuation_at_source += flt(d.amount) valuation_at_source += flt(d.amount)
if d.t_warehouse and not d.s_warehouse: 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: 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, frappe.throw(_("Total valuation ({0}) for manufactured or repacked item(s) can not be less than total valuation of raw materials ({1})")
valuation_at_source)) .format(valuation_at_target, valuation_at_source))
def set_total_incoming_outgoing_value(self): def set_total_incoming_outgoing_value(self):
self.total_incoming_value = self.total_outgoing_value = 0.0 self.total_incoming_value = self.total_outgoing_value = 0.0
@ -234,91 +313,13 @@ class StockEntry(StockController):
if d.t_warehouse: if d.t_warehouse:
self.total_incoming_value += flt(d.amount) self.total_incoming_value += flt(d.amount)
if d.s_warehouse: 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): def set_total_amount(self):
self.total_amount = sum([flt(item.amount) for item in self.get("items")]) 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): def validate_purchase_order(self):
"""Throw exception if more raw material is transferred against Purchase Order than in """Throw exception if more raw material is transferred against Purchase Order than in
the raw materials supplied table""" the raw materials supplied table"""
@ -367,9 +368,7 @@ class StockEntry(StockController):
def update_stock_ledger(self): def update_stock_ledger(self):
sl_entries = [] sl_entries = []
for d in self.get('items'): 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: if cstr(d.s_warehouse) and self.docstatus == 1:
sl_entries.append(self.get_sl_entries(d, { sl_entries.append(self.get_sl_entries(d, {
"warehouse": cstr(d.s_warehouse), "warehouse": cstr(d.s_warehouse),
@ -381,7 +380,7 @@ class StockEntry(StockController):
sl_entries.append(self.get_sl_entries(d, { sl_entries.append(self.get_sl_entries(d, {
"warehouse": cstr(d.t_warehouse), "warehouse": cstr(d.t_warehouse),
"actual_qty": flt(d.transfer_qty), "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 # On cancellation, make stock ledger entry for
@ -402,14 +401,14 @@ class StockEntry(StockController):
gl_entries = super(StockEntry, self).get_gl_entries(warehouse_account) gl_entries = super(StockEntry, self).get_gl_entries(warehouse_account)
for d in self.get("items"): for d in self.get("items"):
tax_amount = flt(d.tax_amount, d.precision("tax_amount")) additional_cost = flt(d.additional_cost, d.precision("additional_cost"))
if tax_amount: if additional_cost:
gl_entries.append(self.get_gl_dict({ gl_entries.append(self.get_gl_dict({
"account": expenses_included_in_valuation, "account": expenses_included_in_valuation,
"against": d.expense_account, "against": d.expense_account,
"cost_center": d.cost_center, "cost_center": d.cost_center,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"), "remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"credit": tax_amount "credit": additional_cost
})) }))
gl_entries.append(self.get_gl_dict({ gl_entries.append(self.get_gl_dict({
@ -417,7 +416,7 @@ class StockEntry(StockController):
"against": expenses_included_in_valuation, "against": expenses_included_in_valuation,
"cost_center": d.cost_center, "cost_center": d.cost_center,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"), "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 return gl_entries
@ -471,7 +470,7 @@ class StockEntry(StockController):
'conversion_factor' : 1, 'conversion_factor' : 1,
'batch_no' : '', 'batch_no' : '',
'actual_qty' : 0, 'actual_qty' : 0,
'incoming_rate' : 0 'basic_rate' : 0
} }
for d in [["Account", "expense_account", "default_expense_account"], for d in [["Account", "expense_account", "default_expense_account"],
["Cost Center", "cost_center", "cost_center"]]: ["Cost Center", "cost_center", "cost_center"]]:
@ -519,13 +518,16 @@ class StockEntry(StockController):
ret = { ret = {
"actual_qty" : get_previous_sle(args).get("qty_after_transaction") or 0, "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 return ret
def get_items(self): def get_items(self):
self.set('items', []) self.set('items', [])
self.validate_production_order() 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): if not getattr(self, "pro_doc", None):
self.pro_doc = None self.pro_doc = None
@ -567,7 +569,8 @@ class StockEntry(StockController):
if self.purpose in ("Manufacture", "Repack"): if self.purpose in ("Manufacture", "Repack"):
self.load_items_from_bom() 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): def load_items_from_bom(self):
if self.production_order: if self.production_order:
@ -695,19 +698,57 @@ class StockEntry(StockController):
if expiry_date: if expiry_date:
if getdate(self.posting_date) > getdate(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)) 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() @frappe.whitelist()
def get_production_order_details(production_order): def get_production_order_details(production_order):
res = frappe.db.sql("""select bom_no, use_multi_level_bom, wip_warehouse, production_order = frappe.get_doc("Production Order", production_order)
ifnull(qty, 0) - ifnull(produced_qty, 0) as fg_completed_qty, pending_qty_to_produce = flt(production_order.qty) - flt(production_order.produced_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 {
"from_bom": 1,
return res and res[0] or {} "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

View File

@ -8,7 +8,7 @@
"cost_center": "_Test Cost Center - _TC", "cost_center": "_Test Cost Center - _TC",
"doctype": "Stock Entry Detail", "doctype": "Stock Entry Detail",
"expense_account": "Stock Adjustment - _TC", "expense_account": "Stock Adjustment - _TC",
"incoming_rate": 100, "basic_rate": 100,
"item_code": "_Test Item", "item_code": "_Test Item",
"parentfield": "items", "parentfield": "items",
"qty": 50.0, "qty": 50.0,
@ -32,7 +32,7 @@
"cost_center": "_Test Cost Center - _TC", "cost_center": "_Test Cost Center - _TC",
"doctype": "Stock Entry Detail", "doctype": "Stock Entry Detail",
"expense_account": "Stock Adjustment - _TC", "expense_account": "Stock Adjustment - _TC",
"incoming_rate": 100, "basic_rate": 100,
"item_code": "_Test Item", "item_code": "_Test Item",
"parentfield": "items", "parentfield": "items",
"qty": 40.0, "qty": 40.0,
@ -57,7 +57,7 @@
"cost_center": "_Test Cost Center - _TC", "cost_center": "_Test Cost Center - _TC",
"doctype": "Stock Entry Detail", "doctype": "Stock Entry Detail",
"expense_account": "Stock Adjustment - _TC", "expense_account": "Stock Adjustment - _TC",
"incoming_rate": 100, "basic_rate": 100,
"item_code": "_Test Item", "item_code": "_Test Item",
"parentfield": "items", "parentfield": "items",
"qty": 45.0, "qty": 45.0,
@ -83,7 +83,7 @@
"cost_center": "_Test Cost Center - _TC", "cost_center": "_Test Cost Center - _TC",
"doctype": "Stock Entry Detail", "doctype": "Stock Entry Detail",
"expense_account": "Stock Adjustment - _TC", "expense_account": "Stock Adjustment - _TC",
"incoming_rate": 100, "basic_rate": 100,
"item_code": "_Test Item", "item_code": "_Test Item",
"parentfield": "items", "parentfield": "items",
"qty": 50.0, "qty": 50.0,
@ -97,7 +97,7 @@
"cost_center": "_Test Cost Center - _TC", "cost_center": "_Test Cost Center - _TC",
"doctype": "Stock Entry Detail", "doctype": "Stock Entry Detail",
"expense_account": "Stock Adjustment - _TC", "expense_account": "Stock Adjustment - _TC",
"incoming_rate": 5000, "basic_rate": 5000,
"item_code": "_Test Item Home Desktop 100", "item_code": "_Test Item Home Desktop 100",
"parentfield": "items", "parentfield": "items",
"qty": 1, "qty": 1,

View File

@ -36,12 +36,12 @@ class TestStockEntry(unittest.TestCase):
create_stock_reconciliation(item_code="_Test Item 2", warehouse="_Test Warehouse - _TC", create_stock_reconciliation(item_code="_Test Item 2", warehouse="_Test Warehouse - _TC",
qty=0, rate=100) 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] sle = get_sle(item_code = item_code, warehouse = warehouse)[0]
self.assertEqual([[1, 10]], eval(sle.stock_queue)) self.assertEqual([[1, 10]], eval(sle.stock_queue))
# negative qty # 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] sle = get_sle(item_code = item_code, warehouse = warehouse)[0]
self.assertEqual([[-1, 10]], eval(sle.stock_queue)) self.assertEqual([[-1, 10]], eval(sle.stock_queue))
@ -53,12 +53,12 @@ class TestStockEntry(unittest.TestCase):
self.assertEqual([[-2, 10]], eval(sle.stock_queue)) self.assertEqual([[-2, 10]], eval(sle.stock_queue))
# move stock to positive # 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] sle = get_sle(item_code = item_code, warehouse = warehouse)[0]
self.assertEqual([[1, 20]], eval(sle.stock_queue)) self.assertEqual([[1, 20]], eval(sle.stock_queue))
# incoming entry with diff rate # 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] sle = get_sle(item_code = item_code, warehouse = warehouse)[0]
self.assertEqual([[1, 20],[1, 30]], eval(sle.stock_queue)) self.assertEqual([[1, 20],[1, 30]], eval(sle.stock_queue))
@ -125,7 +125,7 @@ class TestStockEntry(unittest.TestCase):
set_perpetual_inventory() set_perpetual_inventory()
mr = make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", 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", stock_in_hand_account = frappe.db.get_value("Account", {"account_type": "Warehouse",
"warehouse": mr.get("items")[0].t_warehouse}) "warehouse": mr.get("items")[0].t_warehouse})
@ -152,7 +152,7 @@ class TestStockEntry(unittest.TestCase):
set_perpetual_inventory() set_perpetual_inventory()
make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", 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) 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): def test_repack_no_change_in_valuation(self):
set_perpetual_inventory(0) 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", 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 = frappe.copy_doc(test_records[3])
repack.posting_date = nowdate() repack.posting_date = nowdate()
@ -238,15 +238,24 @@ class TestStockEntry(unittest.TestCase):
set_perpetual_inventory(0) set_perpetual_inventory(0)
def test_repack_with_change_in_valuation(self): def test_repack_with_additional_costs(self):
set_perpetual_inventory() 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 = frappe.copy_doc(test_records[3])
repack.posting_date = nowdate() repack.posting_date = nowdate()
repack.posting_time = nowtime() 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.insert()
repack.submit() repack.submit()
@ -260,11 +269,13 @@ class TestStockEntry(unittest.TestCase):
"voucher_no": repack.name, "item_code": "_Test Item Home Desktop 100"}, "stock_value_difference")) "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) 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, self.check_gl_entries("Stock Entry", repack.name,
sorted([ sorted([
[stock_in_hand_account, stock_value_diff, 0.0], [stock_in_hand_account, 1200, 0.0],
["Stock Adjustment - _TC", 0.0, stock_value_diff], ["Expenses Included In Valuation - _TC", 0.0, 1200.0]
]) ])
) )
set_perpetual_inventory(0) set_perpetual_inventory(0)
@ -291,10 +302,9 @@ class TestStockEntry(unittest.TestCase):
gl_entries = frappe.db.sql("""select account, debit, credit gl_entries = frappe.db.sql("""select account, debit, credit
from `tabGL Entry` where voucher_type=%s and voucher_no=%s from `tabGL Entry` where voucher_type=%s and voucher_no=%s
order by account asc, debit asc""", (voucher_type, voucher_no), as_list=1) order by account asc, debit asc""", (voucher_type, voucher_no), as_list=1)
self.assertTrue(gl_entries) self.assertTrue(gl_entries)
gl_entries.sort(key=lambda x: x[0]) gl_entries.sort(key=lambda x: x[0])
for i, gle in enumerate(gl_entries): for i, gle in enumerate(gl_entries):
self.assertEquals(expected_gl_entries[i][0], gle[0]) self.assertEquals(expected_gl_entries[i][0], gle[0])
self.assertEquals(expected_gl_entries[i][1], gle[1]) 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) frappe.db.set_value("Stock Settings", None, "stock_frozen_upto_days", 0)
def test_production_order(self): 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", bom_no, bom_operation_cost = frappe.db.get_value("BOM", {"item": "_Test FG Item 2",
"is_default": 1, "docstatus": 1}, ["name", "operating_cost"]) "is_default": 1, "docstatus": 1}, ["name", "operating_cost"])
@ -514,22 +526,15 @@ class TestStockEntry(unittest.TestCase):
"bom_no": bom_no, "bom_no": bom_no,
"qty": 1.0, "qty": 1.0,
"stock_uom": "_Test UOM", "stock_uom": "_Test UOM",
"wip_warehouse": "_Test Warehouse - _TC" "wip_warehouse": "_Test Warehouse - _TC",
"additional_operating_cost": 1000
}) })
production_order.insert() production_order.insert()
production_order.submit() 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 = _make_stock_entry(production_order.name, "Manufacture", 1)
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()
rm_cost = 0 rm_cost = 0
for d in stock_entry.get("items"): 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 fg_cost = filter(lambda x: x.item_code=="_Test FG Item 2", stock_entry.get("items"))[0].amount
self.assertEqual(fg_cost, 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): def test_variant_production_order(self):
@ -610,7 +615,7 @@ def make_stock_entry(**args):
"s_warehouse": args.from_warehouse or args.source, "s_warehouse": args.from_warehouse or args.source,
"t_warehouse": args.to_warehouse or args.target, "t_warehouse": args.to_warehouse or args.target,
"qty": args.qty, "qty": args.qty,
"incoming_rate": args.incoming_rate, "basic_rate": args.basic_rate,
"expense_account": args.expense_account or "Stock Adjustment - _TC", "expense_account": args.expense_account or "Stock Adjustment - _TC",
"conversion_factor": 1.0, "conversion_factor": 1.0,
"cost_center": "_Test Cost Center - _TC" "cost_center": "_Test Cost Center - _TC"

View File

@ -1,25 +1,58 @@
{ {
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "hash", "autoname": "hash",
"creation": "2013-03-29 18:22:12", "creation": "2013-03-29 18:22:12",
"custom": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"fields": [ "fields": [
{ {
"allow_on_submit": 0,
"fieldname": "barcode", "fieldname": "barcode",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Barcode", "label": "Barcode",
"no_copy": 0,
"permlevel": 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", "fieldname": "section_break_2",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"no_copy": 0,
"permlevel": 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", "fieldname": "s_warehouse",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 1, "in_filter": 1,
"in_list_view": 1, "in_list_view": 1,
"label": "Source Warehouse", "label": "Source Warehouse",
@ -28,16 +61,38 @@
"oldfieldtype": "Link", "oldfieldtype": "Link",
"options": "Warehouse", "options": "Warehouse",
"permlevel": 0, "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", "fieldname": "col_break1",
"fieldtype": "Column Break", "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", "fieldname": "t_warehouse",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 1, "in_filter": 1,
"in_list_view": 1, "in_list_view": 1,
"label": "Target Warehouse", "label": "Target Warehouse",
@ -46,247 +101,631 @@
"oldfieldtype": "Link", "oldfieldtype": "Link",
"options": "Warehouse", "options": "Warehouse",
"permlevel": 0, "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", "fieldname": "sec_break1",
"fieldtype": "Section Break", "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", "fieldname": "item_code",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 1, "in_filter": 1,
"in_list_view": 1, "in_list_view": 1,
"label": "Item Code", "label": "Item Code",
"no_copy": 0,
"oldfieldname": "item_code", "oldfieldname": "item_code",
"oldfieldtype": "Link", "oldfieldtype": "Link",
"options": "Item", "options": "Item",
"permlevel": 0, "permlevel": 0,
"print_hide": 0,
"read_only": 0, "read_only": 0,
"report_hide": 0,
"reqd": 1, "reqd": 1,
"search_index": 1 "search_index": 1,
"set_only_once": 0,
"unique": 0
}, },
{ {
"allow_on_submit": 0,
"fieldname": "col_break2", "fieldname": "col_break2",
"fieldtype": "Column Break", "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", "fieldname": "qty",
"fieldtype": "Float", "fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 1, "in_list_view": 1,
"label": "Qty", "label": "Qty",
"no_copy": 0,
"oldfieldname": "qty", "oldfieldname": "qty",
"oldfieldtype": "Currency", "oldfieldtype": "Currency",
"permlevel": 0, "permlevel": 0,
"print_hide": 0,
"read_only": 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", "fieldname": "section_break_8",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"no_copy": 0,
"permlevel": 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", "fieldname": "item_name",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Item Name", "label": "Item Name",
"no_copy": 0,
"permlevel": 0, "permlevel": 0,
"print_hide": 1, "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", "fieldname": "description",
"fieldtype": "Text", "fieldtype": "Text",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Description", "label": "Description",
"no_copy": 0,
"oldfieldname": "description", "oldfieldname": "description",
"oldfieldtype": "Text", "oldfieldtype": "Text",
"permlevel": 0, "permlevel": 0,
"print_hide": 0,
"print_width": "300px", "print_width": "300px",
"read_only": 0, "read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0,
"width": "300px" "width": "300px"
}, },
{ {
"allow_on_submit": 0,
"fieldname": "column_break_10", "fieldname": "column_break_10",
"fieldtype": "Column Break", "fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"no_copy": 0,
"permlevel": 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", "fieldname": "image",
"fieldtype": "Attach", "fieldtype": "Attach",
"hidden": 1, "hidden": 1,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Image", "label": "Image",
"no_copy": 0,
"permlevel": 0, "permlevel": 0,
"precision": "", "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", "fieldname": "image_view",
"fieldtype": "Image", "fieldtype": "Image",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Image View", "label": "Image View",
"no_copy": 0,
"options": "image", "options": "image",
"permlevel": 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": "quantity_and_rate", "fieldname": "quantity_and_rate",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Quantity and Rate", "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", "fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 1, "in_list_view": 1,
"label": "Valuation Rate", "label": "Basic Rate (as per Stock UOM)",
"no_copy": 0,
"oldfieldname": "incoming_rate", "oldfieldname": "incoming_rate",
"oldfieldtype": "Currency", "oldfieldtype": "Currency",
"options": "Company:company:default_currency", "options": "Company:company:default_currency",
"permlevel": 0, "permlevel": 0,
"print_hide": 1,
"read_only": 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": "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", "fieldname": "amount",
"fieldtype": "Currency", "fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Amount", "label": "Amount",
"no_copy": 0,
"oldfieldname": "amount", "oldfieldname": "amount",
"oldfieldtype": "Currency", "oldfieldtype": "Currency",
"options": "Company:company:default_currency", "options": "Company:company:default_currency",
"permlevel": 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
}, },
{ {
"fieldname": "tax_amount", "allow_on_submit": 0,
"fieldname": "valuation_rate",
"fieldtype": "Currency", "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, "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", "fieldname": "col_break3",
"fieldtype": "Column Break", "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", "fieldname": "uom",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "UOM", "label": "UOM",
"no_copy": 0,
"oldfieldname": "uom", "oldfieldname": "uom",
"oldfieldtype": "Link", "oldfieldtype": "Link",
"options": "UOM", "options": "UOM",
"permlevel": 0, "permlevel": 0,
"print_hide": 0,
"read_only": 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", "fieldname": "conversion_factor",
"fieldtype": "Float", "fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Conversion Factor", "label": "Conversion Factor",
"no_copy": 0,
"oldfieldname": "conversion_factor", "oldfieldname": "conversion_factor",
"oldfieldtype": "Currency", "oldfieldtype": "Currency",
"permlevel": 0, "permlevel": 0,
"print_hide": 1, "print_hide": 1,
"read_only": 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", "fieldname": "stock_uom",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0,
"label": "Stock UOM", "label": "Stock UOM",
"no_copy": 0,
"oldfieldname": "stock_uom", "oldfieldname": "stock_uom",
"oldfieldtype": "Link", "oldfieldtype": "Link",
"options": "UOM", "options": "UOM",
"permlevel": 0, "permlevel": 0,
"print_hide": 1, "print_hide": 1,
"read_only": 1, "read_only": 1,
"report_hide": 0,
"reqd": 1, "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", "fieldname": "serial_no_batch",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Serial No / Batch", "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", "fieldname": "serial_no",
"fieldtype": "Text", "fieldtype": "Text",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Serial No", "label": "Serial No",
"no_copy": 1, "no_copy": 1,
"oldfieldname": "serial_no", "oldfieldname": "serial_no",
"oldfieldtype": "Text", "oldfieldtype": "Text",
"permlevel": 0, "permlevel": 0,
"print_hide": 0,
"read_only": 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", "fieldname": "col_break4",
"fieldtype": "Column Break", "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", "fieldname": "batch_no",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Batch No", "label": "Batch No",
"no_copy": 0,
"oldfieldname": "batch_no", "oldfieldname": "batch_no",
"oldfieldtype": "Link", "oldfieldtype": "Link",
"options": "Batch", "options": "Batch",
"permlevel": 0, "permlevel": 0,
"print_hide": 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", "fieldname": "accounting",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Accounting", "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)", "depends_on": "eval:cint(sys_defaults.auto_accounting_for_stock)",
"fieldname": "expense_account", "fieldname": "expense_account",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Difference Account", "label": "Difference Account",
"no_copy": 0,
"options": "Account", "options": "Account",
"permlevel": 0, "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", "fieldname": "col_break5",
"fieldtype": "Column Break", "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", "default": ":Company",
"depends_on": "eval:cint(sys_defaults.auto_accounting_for_stock)", "depends_on": "eval:cint(sys_defaults.auto_accounting_for_stock)",
"fieldname": "cost_center", "fieldname": "cost_center",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Cost Center", "label": "Cost Center",
"no_copy": 0,
"options": "Cost Center", "options": "Cost Center",
"permlevel": 0, "permlevel": 0,
"print_hide": 1, "print_hide": 1,
"read_only": 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": "more_info", "fieldname": "more_info",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "More Info", "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, "allow_on_submit": 1,
"fieldname": "actual_qty", "fieldname": "actual_qty",
"fieldtype": "Float", "fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 1, "in_filter": 1,
"in_list_view": 0,
"label": "Actual Qty (at source/target)", "label": "Actual Qty (at source/target)",
"no_copy": 1, "no_copy": 1,
"oldfieldname": "actual_qty", "oldfieldname": "actual_qty",
@ -294,67 +733,107 @@
"permlevel": 0, "permlevel": 0,
"print_hide": 1, "print_hide": 1,
"read_only": 1, "read_only": 1,
"report_hide": 0,
"reqd": 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", "description": "BOM No. for a Finished Good Item",
"fieldname": "bom_no", "fieldname": "bom_no",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 1, "hidden": 1,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "BOM No", "label": "BOM No",
"no_copy": 0, "no_copy": 0,
"options": "BOM", "options": "BOM",
"permlevel": 0, "permlevel": 0,
"print_hide": 1, "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", "fieldname": "col_break6",
"fieldtype": "Column Break", "fieldtype": "Column Break",
"permlevel": 0 "hidden": 0,
}, "ignore_user_permissions": 0,
{ "in_filter": 0,
"fieldname": "transfer_qty", "in_list_view": 0,
"fieldtype": "Float", "no_copy": 0,
"label": "Qty as per Stock UOM",
"oldfieldname": "transfer_qty",
"oldfieldtype": "Currency",
"permlevel": 0, "permlevel": 0,
"print_hide": 1, "print_hide": 0,
"read_only": 1, "read_only": 0,
"reqd": 1 "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", "description": "Material Request used to make this Stock Entry",
"fieldname": "material_request", "fieldname": "material_request",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 1, "hidden": 1,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Material Request", "label": "Material Request",
"no_copy": 1, "no_copy": 1,
"options": "Material Request", "options": "Material Request",
"permlevel": 0, "permlevel": 0,
"print_hide": 1, "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", "fieldname": "material_request_item",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 1, "hidden": 1,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Material Request Item", "label": "Material Request Item",
"no_copy": 1, "no_copy": 1,
"options": "Material Request Item", "options": "Material Request Item",
"permlevel": 0, "permlevel": 0,
"print_hide": 1, "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, "idx": 1,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1, "istable": 1,
"modified": "2015-07-24 17:03:44.214018", "modified": "2015-08-07 13:21:23.840052",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Stock Entry Detail", "name": "Stock Entry Detail",
"owner": "Administrator", "owner": "Administrator",
"permissions": [] "permissions": [],
"read_only": 0,
"read_only_onload": 0
} }

View File

@ -82,13 +82,13 @@ class TestStockReconciliation(unittest.TestCase):
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry 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", 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", make_stock_entry(posting_date="2012-12-25", posting_time="03:00", item_code="_Test Item",
source="_Test Warehouse - _TC", qty=15) source="_Test Warehouse - _TC", qty=15)
make_stock_entry(posting_date="2013-01-05", posting_time="07:00", item_code="_Test Item", 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): def create_stock_reconciliation(**args):
args = frappe._dict(args) args = frappe._dict(args)

View File

@ -1,6 +1,6 @@
{% var visible_columns = row.get_visible_columns(["item_code", {% var visible_columns = row.get_visible_columns(["item_code",
"item_name", "amount", "stock_uom", "uom", "qty", "item_name", "amount", "stock_uom", "uom", "qty",
"s_warehouse", "t_warehouse", "incoming_rate"]); "s_warehouse", "t_warehouse", "valuation_rate"]);
%} %}
{% if(!doc) { %} {% if(!doc) { %}
@ -43,7 +43,7 @@
<div class="col-sm-2 col-xs-2 text-right"> <div class="col-sm-2 col-xs-2 text-right">
{%= doc.get_formatted("amount") %} {%= doc.get_formatted("amount") %}
<div class="small text-muted"> <div class="small text-muted">
{%= doc.get_formatted("incoming_rate") %} {%= doc.get_formatted("valuation_rate") %}
</div> </div>
</div> </div>
</div> </div>

View File

@ -237,7 +237,7 @@ def repost_all_stock_vouchers():
doc = frappe.get_doc(voucher_type, voucher_no) doc = frappe.get_doc(voucher_type, voucher_no)
if voucher_type=="Stock Entry" and doc.purpose in ["Manufacture", "Repack"]: 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": elif voucher_type=="Purchase Receipt" and doc.is_subcontracted == "Yes":
doc.validate() doc.validate()