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]
] ]
} }
} }
@@ -379,16 +406,3 @@ 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,16 +45,14 @@ 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()
@@ -215,56 +212,19 @@ class StockEntry(StockController):
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 validate_valuation_rate(self): def set_actual_qty(self):
if self.purpose in ["Manufacture", "Repack"]:
valuation_at_source, valuation_at_target = 0, 0
for d in self.get("items"):
if d.s_warehouse and not d.t_warehouse:
valuation_at_source += flt(d.amount)
if d.t_warehouse and not d.s_warehouse:
valuation_at_target += flt(d.amount) + flt(d.tax_amount)
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,
valuation_at_source))
def set_total_incoming_outgoing_value(self):
self.total_incoming_value = self.total_outgoing_value = 0.0
for d in self.get("items"):
if d.t_warehouse:
self.total_incoming_value += flt(d.amount)
if d.s_warehouse:
self.total_outgoing_value += flt(d.amount) + flt(d.tax_amount)
self.value_difference = self.total_outgoing_value - self.total_incoming_value
def set_total_amount(self):
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")) allow_negative_stock = cint(frappe.db.get_value("Stock Settings", None, "allow_negative_stock"))
for d in self.get('items'): for d in self.get('items'):
d.transfer_qty = flt(d.transfer_qty) previous_sle = get_previous_sle({
args = frappe._dict({
"item_code": d.item_code, "item_code": d.item_code,
"warehouse": d.s_warehouse or d.t_warehouse, "warehouse": d.s_warehouse or d.t_warehouse,
"posting_date": self.posting_date, "posting_date": self.posting_date,
"posting_time": self.posting_time, "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 # get actual stock at source warehouse
d.actual_qty = get_previous_sle(args).get("qty_after_transaction") or 0 d.actual_qty = previous_sle.get("qty_after_transaction") or 0
# validate qty during submit # validate qty during submit
if d.docstatus==1 and d.s_warehouse and not allow_negative_stock and d.actual_qty < d.transfer_qty: if d.docstatus==1 and d.s_warehouse and not allow_negative_stock and d.actual_qty < d.transfer_qty:
@@ -272,52 +232,93 @@ class StockEntry(StockController):
Available Qty: {4}, Transfer Qty: {5}""").format(d.idx, d.s_warehouse, 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) self.posting_date, self.posting_time, d.actual_qty, d.transfer_qty), NegativeStockError)
# get incoming rate 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 d.bom_no:
if not flt(d.incoming_rate) or d.s_warehouse or force: if not flt(d.basic_rate) or d.s_warehouse or force:
incoming_rate = flt(get_incoming_rate(args), self.precision("incoming_rate", d)) basic_rate = flt(get_incoming_rate(args), self.precision("basic_rate", d))
if incoming_rate > 0: if basic_rate > 0:
d.incoming_rate = incoming_rate d.basic_rate = basic_rate
d.amount = flt(flt(d.transfer_qty) * flt(d.incoming_rate), d.precision("amount")) d.basic_amount = flt(flt(d.transfer_qty) * flt(d.basic_rate), d.precision("basic_amount"))
if not d.t_warehouse: if not d.t_warehouse:
raw_material_cost += flt(d.amount) raw_material_cost += flt(d.basic_amount)
self.set_basic_rate_for_finished_goods(raw_material_cost)
self.add_operation_cost(raw_material_cost, force) def set_basic_rate_for_finished_goods(self, raw_material_cost):
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"]: if self.purpose in ["Manufacture", "Repack"]:
number_of_fg_items = len([t.t_warehouse for t in self.get("items") if t.t_warehouse]) number_of_fg_items = len([t.t_warehouse for t in self.get("items") if t.t_warehouse])
for d in self.get("items"): for d in self.get("items"):
if d.bom_no or (d.t_warehouse and number_of_fg_items == 1): 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.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"))
d.incoming_rate = operation_cost_per_unit + (raw_material_cost / flt(d.transfer_qty)) def distribute_additional_costs(self):
d.amount = flt(flt(d.transfer_qty) * flt(d.incoming_rate), self.precision("transfer_qty", d)) if self.purpose == "Material Issue":
break self.additional_costs = []
def get_operation_cost_per_unit(self, bom_no, qty): self.total_additional_costs = sum([flt(t.amount) for t in self.get("additional_costs")])
"""Returns operating cost from Production Order for given `bom_no`""" total_basic_amount = sum([flt(t.basic_amount) for t in self.get("items") if t.t_warehouse])
operation_cost_per_unit = 0
if self.production_order: for d in self.get("items"):
if not getattr(self, "pro_doc", None): if d.t_warehouse and total_basic_amount:
self.pro_doc = frappe.get_doc("Production Order", self.production_order) d.additional_cost = (flt(d.basic_amount) / total_basic_amount) * self.total_additional_costs
for d in self.pro_doc.get("operations"): else:
if flt(d.completed_qty): d.additional_cost = 0
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. def update_valuation_rate(self):
if not operation_cost_per_unit and bom_no: for d in self.get("items"):
bom = frappe.db.get_value("BOM", bom_no, ["operating_cost", "quantity"], as_dict=1) d.amount = flt(d.basic_amount + flt(d.additional_cost), d.precision("amount"))
operation_cost_per_unit = flt(bom.operating_cost) / flt(bom.quantity) d.valuation_rate = flt(flt(d.basic_rate) + flt(d.additional_cost) / flt(d.transfer_qty),
d.precision("valuation_rate"))
return operation_cost_per_unit def validate_valuation_rate(self):
if self.purpose in ["Manufacture", "Repack"]:
valuation_at_source, valuation_at_target = 0, 0
for d in self.get("items"):
if d.s_warehouse and not d.t_warehouse:
valuation_at_source += flt(d.amount)
if d.t_warehouse and not d.s_warehouse:
valuation_at_target += flt(d.amount)
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, valuation_at_source))
def set_total_incoming_outgoing_value(self):
self.total_incoming_value = self.total_outgoing_value = 0.0
for d in self.get("items"):
if d.t_warehouse:
self.total_incoming_value += flt(d.amount)
if d.s_warehouse:
self.total_outgoing_value += flt(d.amount)
self.value_difference = self.total_incoming_value - self.total_outgoing_value
def set_total_amount(self):
self.total_amount = sum([flt(item.amount) for item in self.get("items")])
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
@@ -368,8 +369,6 @@ 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,7 +518,7 @@ 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
@@ -527,6 +526,9 @@ class StockEntry(StockController):
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:
@@ -696,18 +699,56 @@ class StockEntry(StockController):
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 res and res[0] or {} return {
"from_bom": 1,
"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()
@@ -261,10 +270,12 @@ class TestStockEntry(unittest.TestCase):
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)
@@ -294,7 +305,6 @@ class TestStockEntry(unittest.TestCase):
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()