Merge branch 'master' of github.com:webnotes/erpnext

This commit is contained in:
Nabin Hait 2013-08-12 15:15:34 +05:30
commit 9fa830805e
15 changed files with 222 additions and 72 deletions

View File

@ -61,30 +61,25 @@ cur_frm.cscript['Unstop Production Order'] = function() {
} }
cur_frm.cscript['Transfer Raw Materials'] = function() { cur_frm.cscript['Transfer Raw Materials'] = function() {
var doc = cur_frm.doc; cur_frm.cscript.make_se('Material Transfer');
cur_frm.cscript.make_se(doc, 'Material Transfer');
} }
cur_frm.cscript['Update Finished Goods'] = function() { cur_frm.cscript['Update Finished Goods'] = function() {
var doc = cur_frm.doc; cur_frm.cscript.make_se('Manufacture/Repack');
cur_frm.cscript.make_se(doc, 'Manufacture/Repack');
} }
cur_frm.cscript.make_se = function(doc, purpose) { cur_frm.cscript.make_se = function(purpose) {
var se = wn.model.get_new_doc("Stock Entry"); wn.call({
se.purpose = purpose; method:"manufacturing.doctype.production_order.production_order.make_stock_entry",
se.production_order = doc.name; args: {
if(purpose==="Material Transfer") { "production_order_id": cur_frm.doc.name,
se.to_warehouse = doc.wip_warehouse; "purpose": purpose
} else { },
se.from_warehouse = doc.wip_warehouse; callback: function(r) {
se.to_warehouse = doc.fg_warehouse; var doclist = wn.model.sync(r.message);
} wn.set_route("Form", doclist[0].doctype, doclist[0].name);
se.company = doc.company; }
se.fg_completed_qty = doc.qty - doc.produced_qty; })
se.bom_no = doc.bom_no;
se.use_multi_level_bom = doc.use_multi_level_bom;
loaddoc('Stock Entry', se.name);
} }
cur_frm.fields_dict['production_item'].get_query = function(doc) { cur_frm.fields_dict['production_item'].get_query = function(doc) {

View File

@ -137,4 +137,23 @@ def get_item_details(item):
if bom: if bom:
res.bom_no = bom[0][0] res.bom_no = bom[0][0]
return res return res
@webnotes.whitelist()
def make_stock_entry(production_order_id, purpose):
production_order = webnotes.bean("Production Order", production_order_id)
stock_entry = webnotes.new_bean("Stock Entry")
stock_entry.doc.purpose = purpose
stock_entry.doc.production_order = production_order_id
stock_entry.doc.company = production_order.doc.company
stock_entry.doc.bom_no = production_order.doc.bom_no
stock_entry.doc.fg_completed_qty = flt(production_order.doc.qty) - flt(production_order.doc.produced_qty)
if purpose=="Material Transfer":
stock_entry.doc.to_warehouse = production_order.doc.wip_warehouse
else:
stock_entry.doc.from_warehouse = production_order.doc.wip_warehouse
stock_entry.doc.to_warehouse = production_order.doc.fg_warehouse
return [d.fields for d in stock_entry.doclist]

View File

@ -64,11 +64,26 @@ wn.module_page["Manufacturing"] = [
right: true, right: true,
icon: "icon-list", icon: "icon-list",
items: [ items: [
{
"label":wn._("Open Production Orders"),
route: "query-report/Open Production Orders",
doctype:"Production Order"
},
{
"label":wn._("Production Orders in Progress"),
route: "query-report/Production Orders in Progress",
doctype:"Production Order"
},
{ {
"label":wn._("Issued Items Against Production Order"), "label":wn._("Issued Items Against Production Order"),
route: "query-report/Issued Items Against Production Order", route: "query-report/Issued Items Against Production Order",
doctype:"Production Order" doctype:"Production Order"
}, },
{
"label":wn._("Completed Production Orders"),
route: "query-report/Completed Production Orders",
doctype:"Production Order"
},
] ]
} }
] ]

View File

@ -0,0 +1,22 @@
[
{
"creation": "2013-08-12 12:44:27",
"docstatus": 0,
"modified": "2013-08-12 12:44:27",
"modified_by": "Administrator",
"owner": "Administrator"
},
{
"doctype": "Report",
"is_standard": "Yes",
"name": "__common__",
"query": "SELECT\n `tabProduction Order`.name as \"Production Order:Link/Production Order:200\",\n `tabProduction Order`.creation as \"Date:Date:120\",\n `tabProduction Order`.production_item as \"Item:Link/Item:150\",\n `tabProduction Order`.qty as \"To Produce:Int:100\",\n `tabProduction Order`.produced_qty as \"Produced:Int:100\"\nFROM\n `tabProduction Order`\nWHERE\n `tabProduction Order`.docstatus=1\n AND ifnull(`tabProduction Order`.produced_qty,0) = `tabProduction Order`.qty",
"ref_doctype": "Production Order",
"report_name": "Completed Production Orders",
"report_type": "Query Report"
},
{
"doctype": "Report",
"name": "Completed Production Orders"
}
]

View File

@ -0,0 +1,22 @@
[
{
"creation": "2013-08-12 12:32:30",
"docstatus": 0,
"modified": "2013-08-12 12:42:29",
"modified_by": "Administrator",
"owner": "Administrator"
},
{
"doctype": "Report",
"is_standard": "Yes",
"name": "__common__",
"query": "SELECT\n `tabProduction Order`.name as \"Production Order:Link/Production Order:200\",\n `tabProduction Order`.creation as \"Date:Date:120\",\n `tabProduction Order`.production_item as \"Item:Link/Item:150\",\n `tabProduction Order`.qty as \"To Produce:Int:100\",\n `tabProduction Order`.produced_qty as \"Produced:Int:100\"\nFROM\n `tabProduction Order`\nWHERE\n `tabProduction Order`.docstatus=1\n AND ifnull(`tabProduction Order`.produced_qty,0) < `tabProduction Order`.qty\n AND NOT EXISTS (SELECT name from `tabStock Entry` where production_order =`tabProduction Order`.name) ",
"ref_doctype": "Production Order",
"report_name": "Open Production Orders",
"report_type": "Query Report"
},
{
"doctype": "Report",
"name": "Open Production Orders"
}
]

View File

@ -0,0 +1,22 @@
[
{
"creation": "2013-08-12 12:43:47",
"docstatus": 0,
"modified": "2013-08-12 12:43:47",
"modified_by": "Administrator",
"owner": "Administrator"
},
{
"doctype": "Report",
"is_standard": "Yes",
"name": "__common__",
"query": "SELECT\n `tabProduction Order`.name as \"Production Order:Link/Production Order:200\",\n `tabProduction Order`.creation as \"Date:Date:120\",\n `tabProduction Order`.production_item as \"Item:Link/Item:150\",\n `tabProduction Order`.qty as \"To Produce:Int:100\",\n `tabProduction Order`.produced_qty as \"Produced:Int:100\"\nFROM\n `tabProduction Order`\nWHERE\n `tabProduction Order`.docstatus=1\n AND ifnull(`tabProduction Order`.produced_qty,0) < `tabProduction Order`.qty\n AND EXISTS (SELECT name from `tabStock Entry` where production_order =`tabProduction Order`.name) ",
"ref_doctype": "Production Order",
"report_name": "Production Orders in Progress",
"report_type": "Query Report"
},
{
"doctype": "Report",
"name": "Production Orders in Progress"
}
]

View File

@ -15,11 +15,12 @@ def execute():
("Item Price", "price_list_name", "price_list"), ("Item Price", "price_list_name", "price_list"),
("BOM", "price_list", "buying_price_list"), ("BOM", "price_list", "buying_price_list"),
]: ]:
if t[2] in webnotes.conn.get_table_columns(t[0]): table_columns = webnotes.conn.get_table_columns(t[0])
if t[2] in table_columns and t[1] in table_columns:
# already reloaded, so copy into new column and drop old column # already reloaded, so copy into new column and drop old column
webnotes.conn.sql("""update `tab%s` set `%s`=`%s`""" % (t[0], t[2], t[1])) webnotes.conn.sql("""update `tab%s` set `%s`=`%s`""" % (t[0], t[2], t[1]))
webnotes.conn.sql("""alter table `tab%s` drop column `%s`""" % (t[0], t[1])) webnotes.conn.sql("""alter table `tab%s` drop column `%s`""" % (t[0], t[1]))
else: elif t[1] in table_columns:
webnotes.conn.sql_ddl("alter table `tab%s` change `%s` `%s` varchar(180)" % t) webnotes.conn.sql_ddl("alter table `tab%s` change `%s` `%s` varchar(180)" % t)
webnotes.reload_doc(webnotes.conn.get_value("DocType", t[0], "module"), "DocType", t[0]) webnotes.reload_doc(webnotes.conn.get_value("DocType", t[0], "module"), "DocType", t[0])

View File

@ -4,34 +4,7 @@
wn.require("public/app/js/controllers/stock_controller.js"); wn.require("public/app/js/controllers/stock_controller.js");
wn.provide("erpnext.stock"); wn.provide("erpnext.stock");
erpnext.stock.StockEntry = erpnext.stock.StockController.extend({ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({
onload: function() {
this.set_default_account();
},
set_default_account: function() {
var me = this;
if (cint(wn.defaults.get_default("auto_inventory_accounting")) && !this.frm.doc.expense_adjustment_account) {
if (this.frm.doc.purpose == "Sales Return")
account_for = "stock_in_hand_account";
else if (this.frm.doc.purpose == "Purchase Return")
account_for = "stock_received_but_not_billed";
else account_for = "stock_adjustment_account";
return this.frm.call({
method: "accounts.utils.get_company_default",
args: {
"fieldname": account_for,
"company": this.frm.doc.company
},
callback: function(r) {
if (!r.exc) me.frm.set_value("expense_adjustment_account", r.message);
}
});
}
},
setup: function() { setup: function() {
var me = this; var me = this;
@ -80,11 +53,7 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({
}, },
onload_post_render: function() { onload_post_render: function() {
if(this.frm.doc.__islocal && (this.frm.doc.production_order || this.frm.doc.bom_no) this.set_default_account();
&& !getchildren('Stock Entry Detail', this.frm.doc.name, 'mtn_details').length) {
// if production order / bom is mentioned, get items
this.get_items();
}
}, },
refresh: function() { refresh: function() {
@ -115,6 +84,33 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({
after_cancel: function() { after_cancel: function() {
this.clean_up(); this.clean_up();
}, },
set_default_account: function() {
var me = this;
if (cint(wn.defaults.get_default("auto_inventory_accounting")) && !this.frm.doc.expense_adjustment_account) {
var account_for = "stock_adjustment_account";
if (this.frm.doc.purpose == "Sales Return")
account_for = "stock_in_hand_account";
else if (this.frm.doc.purpose == "Purchase Return")
account_for = "stock_received_but_not_billed";
return this.frm.call({
method: "accounts.utils.get_company_default",
args: {
"fieldname": account_for,
"company": this.frm.doc.company
},
callback: function(r) {
if (!r.exc) me.frm.set_value("expense_adjustment_account", r.message);
me.get_items();
}
});
} else {
me.get_items();
}
},
clean_up: function() { clean_up: function() {
// Clear Production Order record from locals, because it is updated via Stock Entry // Clear Production Order record from locals, because it is updated via Stock Entry
@ -126,13 +122,17 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({
}, },
get_items: function() { get_items: function() {
return this.frm.call({ if(this.frm.doc.__islocal && (this.frm.doc.production_order || this.frm.doc.bom_no)
doc: this.frm.doc, && !getchildren('Stock Entry Detail', this.frm.doc.name, 'mtn_details').length) {
method: "get_items", // if production order / bom is mentioned, get items
callback: function(r) { return this.frm.call({
if(!r.exc) refresh_field("mtn_details"); doc: this.frm.doc,
} method: "get_items",
}); callback: function(r) {
if(!r.exc) refresh_field("mtn_details");
}
});
}
}, },
qty: function(doc, cdt, cdn) { qty: function(doc, cdt, cdn) {
@ -212,7 +212,6 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({
}); });
loaddoc("Journal Voucher", jv_name); loaddoc("Journal Voucher", jv_name);
} }
} }
}); });
} }

View File

@ -19,6 +19,7 @@ sql = webnotes.conn.sql
class NotUpdateStockError(webnotes.ValidationError): pass class NotUpdateStockError(webnotes.ValidationError): pass
class StockOverReturnError(webnotes.ValidationError): pass class StockOverReturnError(webnotes.ValidationError): pass
class IncorrectValuationRateError(webnotes.ValidationError): pass
from controllers.stock_controller import StockController from controllers.stock_controller import StockController
@ -245,7 +246,7 @@ class DocType(StockController):
def validate_incoming_rate(self): def validate_incoming_rate(self):
for d in getlist(self.doclist, 'mtn_details'): for d in getlist(self.doclist, 'mtn_details'):
if d.t_warehouse: if d.t_warehouse:
self.validate_value("incoming_rate", ">", 0, d) self.validate_value("incoming_rate", ">", 0, d, raise_exception=IncorrectValuationRateError)
def validate_bom(self): def validate_bom(self):
for d in getlist(self.doclist, 'mtn_details'): for d in getlist(self.doclist, 'mtn_details'):

View File

@ -8,6 +8,7 @@ from stock.utils import get_valuation_method
import json import json
# future reposting # future reposting
class NegativeStockError(webnotes.ValidationError): pass
_exceptions = [] _exceptions = []
def update_entries_after(args, verbose=1): def update_entries_after(args, verbose=1):
@ -253,9 +254,9 @@ def _raise_exceptions(args, verbose=1):
_exceptions[0]["voucher_type"], _exceptions[0]["voucher_no"], _exceptions[0]["voucher_type"], _exceptions[0]["voucher_no"],
abs(deficiency)) abs(deficiency))
if verbose: if verbose:
msgprint(msg, raise_exception=1) msgprint(msg, raise_exception=NegativeStockError)
else: else:
raise webnotes.ValidationError, msg raise NegativeStockError, msg
def get_previous_sle(args, for_update=False): def get_previous_sle(args, for_update=False):
""" """

View File

@ -69,7 +69,7 @@ def get_incoming_rate(args):
if valuation_method == 'FIFO': if valuation_method == 'FIFO':
if not previous_sle: if not previous_sle:
return 0.0 return 0.0
previous_stock_queue = json.loads(previous_sle.get('stock_queue', '[]')) previous_stock_queue = json.loads(previous_sle.get('stock_queue', '[]') or '[]')
in_rate = previous_stock_queue and \ in_rate = previous_stock_queue and \
get_fifo_rate(previous_stock_queue, args.get("qty") or 0) or 0 get_fifo_rate(previous_stock_queue, args.get("qty") or 0) or 0
elif valuation_method == 'Moving Average': elif valuation_method == 'Moving Average':

View File

@ -19,15 +19,18 @@ runs_for = 20
prob = { prob = {
"Quotation": { "make": 0.5, "qty": (1,5) }, "Quotation": { "make": 0.5, "qty": (1,5) },
"Sales Order": { "make": 0.5, "qty": (1,4) }, "Sales Order": { "make": 0.5, "qty": (1,4) },
"Purchase Order": { "make": 0.7, "qty": (1,4) },
"Purchase Receipt": { "make": 0.7, "qty": (1,4) },
"Supplier Quotation": { "make": 0.5, "qty": (1, 3) } "Supplier Quotation": { "make": 0.5, "qty": (1, 3) }
} }
def make(): def make(reset=False):
webnotes.connect() webnotes.connect()
webnotes.print_messages = True webnotes.print_messages = True
webnotes.mute_emails = True webnotes.mute_emails = True
#setup() if reset:
setup()
simulate() simulate()
def setup(): def setup():
@ -68,13 +71,29 @@ def run_sales(current_date):
make_sales_order(current_date) make_sales_order(current_date)
def run_stock(current_date): def run_stock(current_date):
pass
# make purchase requests # make purchase requests
if can_make("Purchase Receipt"):
from buying.doctype.purchase_order.purchase_order import make_purchase_receipt
report = "Purchase Order Items To Be Received"
for po in list(set([r[0] for r in query_report.run(report)["result"] if r[0]!="Total"]))[:how_many("Purchase Receipt")]:
pr = webnotes.bean(make_purchase_receipt(po))
pr.doc.posting_date = current_date
pr.doc.fiscal_year = "2010"
pr.insert()
pr.submit()
# make delivery notes (if possible) # make delivery notes (if possible)
if can_make("Delivery Note"):
from selling.doctype.sales_order.sales_order import make_delivery_note
report = "Ordered Items To Be Delivered"
for so in list(set([r[0] for r in query_report.run(report)["result"] if r[0]!="Total"]))[:how_many("Delivery Note")]:
dn = webnotes.bean(make_delivery_note(so))
dn.doc.posting_date = current_date
dn.doc.fiscal_year = "2010"
dn.insert()
dn.submit()
# make stock entry (from production order)
def run_purchase(current_date): def run_purchase(current_date):
# make supplier quotations # make supplier quotations
if can_make("Supplier Quotation"): if can_make("Supplier Quotation"):
@ -121,6 +140,40 @@ def run_manufacturing(current_date):
b = webnotes.bean("Material Request", pro[0]) b = webnotes.bean("Material Request", pro[0])
b.submit() b.submit()
# stores -> wip
if can_make("Stock Entry for WIP"):
for pro in query_report.run("Open Production Orders")["result"][:how_many("Stock Entry for WIP")]:
make_stock_entry_from_pro(pro[0], "Material Transfer", current_date)
# wip -> fg
if can_make("Stock Entry for FG"):
for pro in query_report.run("Production Orders in Progress")["result"][:how_many("Stock Entry for FG")]:
make_stock_entry_from_pro(pro[0], "Manufacture/Repack", current_date)
# try posting older drafts (if exists)
for st in webnotes.conn.get_values("Stock Entry", {"docstatus":0}):
try:
webnotes.bean("Stock Entry", st[0]).submit()
except NegativeStockError: pass
except IncorrectValuationRateError: pass
def make_stock_entry_from_pro(pro_id, purpose, current_date):
from manufacturing.doctype.production_order.production_order import make_stock_entry
from stock.stock_ledger import NegativeStockError
from stock.doctype.stock_entry.stock_entry import IncorrectValuationRateError
st = webnotes.bean(make_stock_entry(pro_id, purpose))
st.run_method("get_items")
st.doc.posting_date = current_date
st.doc.fiscal_year = "2010"
st.doc.expense_adjustment_account = "Stock in Hand - WP"
try:
st.insert()
st.submit()
except NegativeStockError: pass
except IncorrectValuationRateError: pass
def make_quotation(current_date): def make_quotation(current_date):
b = webnotes.bean([{ b = webnotes.bean([{
"creation": current_date, "creation": current_date,