Merge pull request #2864 from neilLasrado/delivery-note
Delivery note - Batch Id made Link Feild
diff --git a/erpnext/accounts/doctype/purchase_taxes_and_charges_master/purchase_taxes_and_charges_master.js b/erpnext/accounts/doctype/purchase_taxes_and_charges_master/purchase_taxes_and_charges_master.js
index 09f2e8d..f53581a 100644
--- a/erpnext/accounts/doctype/purchase_taxes_and_charges_master/purchase_taxes_and_charges_master.js
+++ b/erpnext/accounts/doctype/purchase_taxes_and_charges_master/purchase_taxes_and_charges_master.js
@@ -1,6 +1,8 @@
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
+cur_frm.cscript.tax_table = "Purchase Taxes and Charges";
+
{% include "public/js/controllers/accounts.js" %}
frappe.ui.form.on("Purchase Taxes and Charges", "add_deduct_tax", function(doc, cdt, cdn) {
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
index 4670303..220e171 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
@@ -199,8 +199,8 @@
this.hide_fields(this.frm.doc);
},
- items_on_form_rendered: function(doc, grid_row) {
- erpnext.setup_serial_no(grid_row)
+ items_on_form_rendered: function() {
+ erpnext.setup_serial_no();
}
});
diff --git a/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json b/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json
index 2322550..67f7ab4 100644
--- a/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json
+++ b/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json
@@ -56,7 +56,7 @@
"oldfieldname": "rate",
"oldfieldtype": "Currency",
"permlevel": 0,
- "reqd": 1
+ "reqd": 0
},
{
"fieldname": "col_break_1",
@@ -186,7 +186,7 @@
"hide_heading": 1,
"idx": 1,
"istable": 1,
- "modified": "2015-02-23 12:36:02.213508",
+ "modified": "2015-02-25 02:50:44.152307",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Taxes and Charges",
diff --git a/erpnext/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.js b/erpnext/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.js
index b5dc38f..3ad573a 100644
--- a/erpnext/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.js
+++ b/erpnext/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.js
@@ -1,9 +1,10 @@
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
+cur_frm.cscript.tax_table = "Sales Taxes and Charges";
+
{% include "public/js/controllers/accounts.js" %}
-cur_frm.cscript.onload = function(doc, cdt, cdn) {
- if(doc.doctype === "Sales Taxes and Charges Master")
- erpnext.add_applicable_territory();
-}
+frappe.ui.form.on("Sales Taxes and Charges Master", "onload", function(frm) {
+ erpnext.add_applicable_territory();
+});
diff --git a/erpnext/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.py b/erpnext/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.py
index 4317f2a..907c7c6 100644
--- a/erpnext/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.py
+++ b/erpnext/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.py
@@ -20,3 +20,4 @@
validate_taxes_and_charges(tax)
validate_inclusive_tax(tax, self)
+
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index bbab88f..fd17fd6 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -370,6 +370,15 @@
elif tax.row_id and cint(tax.row_id) >= cint(tax.idx):
frappe.throw(_("Cannot refer row number greater than or equal to current row number for this Charge type"))
+ if tax.charge_type == "Actual":
+ if not tax.tax_amount:
+ frappe.throw(_("Amount is mandatory for charge type 'Actual'"))
+ tax.rate = None
+ else:
+ if not tax.rate:
+ frappe.throw(_("Rate is mandatory for charge type '{0}'").format(tax.charge_type))
+ tax.tax_amount = None
+
def validate_inclusive_tax(tax, doc):
def _on_previous_row_error(row_range):
throw(_("To include tax in row {0} in Item rate, taxes in rows {1} must also be included").format(tax.idx,
diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py
index 28499c1..2d5df15 100644
--- a/erpnext/controllers/taxes_and_totals.py
+++ b/erpnext/controllers/taxes_and_totals.py
@@ -217,7 +217,7 @@
tax.grand_total_for_current_item = flt(item.net_amount + current_tax_amount, tax.precision("total"))
else:
tax.grand_total_for_current_item = \
- flt(self.doc.get("taxes")[i-1].grand_total_for_current_item + current_tax_amount, tax.precision("total_taxes_and_charges"))
+ flt(self.doc.get("taxes")[i-1].grand_total_for_current_item + current_tax_amount, tax.precision("total"))
# in tax.total, accumulate grand total of each item
tax.total += tax.grand_total_for_current_item
diff --git a/erpnext/manufacturing/doctype/production_order/production_order.json b/erpnext/manufacturing/doctype/production_order/production_order.json
index 089fd38..2962c48 100644
--- a/erpnext/manufacturing/doctype/production_order/production_order.json
+++ b/erpnext/manufacturing/doctype/production_order/production_order.json
@@ -351,7 +351,7 @@
"idx": 1,
"in_create": 0,
"is_submittable": 1,
- "modified": "2015-02-23 07:42:05.639225",
+ "modified": "2015-02-25 01:01:11.550217",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Production Order",
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 1a88177..30ffd1a 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -126,3 +126,5 @@
erpnext.patches.v5_0.update_journal_entry_title
erpnext.patches.v5_0.taxes_and_totals_in_party_currency
erpnext.patches.v5_0.replace_renamed_fields_in_custom_scripts_and_print_formats
+erpnext.patches.v5_0.update_from_bom
+erpnext.patches.v5_0.update_account_types
diff --git a/erpnext/patches/v5_0/update_account_types.py b/erpnext/patches/v5_0/update_account_types.py
new file mode 100644
index 0000000..4686a5f
--- /dev/null
+++ b/erpnext/patches/v5_0/update_account_types.py
@@ -0,0 +1,20 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import frappe
+
+def execute():
+ for company in frappe.db.get_all("Company"):
+ company = frappe.get_doc("Company", company.name)
+
+ match_types = ("Stock Received But Not Billed", "Stock Adjustment", "Expenses Included In Valuation",
+ "Cost of Goods Sold")
+
+ for account_type in match_types:
+ account_name = "{0} - {1}".format(account_type, company.abbr)
+ current_account_type = frappe.db.get_value("Account", account_name, "account_type")
+ if current_account_type != account_type:
+ frappe.db.set_value("Account", account_name, "account_type", account_type)
+
+ company.set_default_accounts()
diff --git a/erpnext/patches/v5_0/update_from_bom.py b/erpnext/patches/v5_0/update_from_bom.py
new file mode 100644
index 0000000..660b3e4
--- /dev/null
+++ b/erpnext/patches/v5_0/update_from_bom.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import frappe
+
+def execute():
+ frappe.reload_doctype("Stock Entry")
+ frappe.db.sql("update `tabStock Entry` set from_bom = if(ifnull(bom_no, '')='', 0, 1)")
diff --git a/erpnext/public/js/controllers/accounts.js b/erpnext/public/js/controllers/accounts.js
index 35d8c66..283ec74 100644
--- a/erpnext/public/js/controllers/accounts.js
+++ b/erpnext/public/js/controllers/accounts.js
@@ -2,6 +2,10 @@
// License: GNU General Public License v3. See license.txt
// get tax rate
+frappe.provide("erpnext.taxes");
+frappe.provide("erpnext.taxes.flags");
+
+
cur_frm.cscript.account_head = function(doc, cdt, cdn) {
var d = locals[cdt][cdn];
if(!d.charge_type && d.account_head){
@@ -82,34 +86,60 @@
}
}
-frappe.ui.form.on(cur_frm.cscript.tax_table, "row_id", function(frm, cdt, cdn) {
- cur_frm.cscript.validate_taxes_and_charges(cdt, cdn);
-});
+if(!erpnext.taxes.flags[cur_frm.cscript.tax_table]) {
+ erpnext.taxes.flags[cur_frm.cscript.tax_table] = true;
-frappe.ui.form.on(cur_frm.cscript.tax_table, "rate", function(frm, cdt, cdn) {
- cur_frm.cscript.validate_taxes_and_charges(cdt, cdn);
-});
-
-frappe.ui.form.on(cur_frm.cscript.tax_table, "tax_amount", function(frm, cdt, cdn) {
- cur_frm.cscript.validate_taxes_and_charges(cdt, cdn);
-});
-
-frappe.ui.form.on(cur_frm.cscript.tax_table, "charge_type", function(frm, cdt, cdn) {
- cur_frm.cscript.validate_taxes_and_charges(cdt, cdn);
-});
-
-frappe.ui.form.on(cur_frm.cscript.tax_table, "included_in_print_rate", function(frm, cdt, cdn) {
- var tax = frappe.get_doc(cdt, cdn);
- try {
+ frappe.ui.form.on(cur_frm.cscript.tax_table, "row_id", function(frm, cdt, cdn) {
cur_frm.cscript.validate_taxes_and_charges(cdt, cdn);
- cur_frm.cscript.validate_inclusive_tax(tax);
- } catch(e) {
- tax.included_in_print_rate = 0;
- refresh_field("included_in_print_rate", tax.name, tax.parentfield);
- throw e;
+ });
+
+ frappe.ui.form.on(cur_frm.cscript.tax_table, "rate", function(frm, cdt, cdn) {
+ cur_frm.cscript.validate_taxes_and_charges(cdt, cdn);
+ });
+
+ frappe.ui.form.on(cur_frm.cscript.tax_table, "tax_amount", function(frm, cdt, cdn) {
+ cur_frm.cscript.validate_taxes_and_charges(cdt, cdn);
+ });
+
+ frappe.ui.form.on(cur_frm.cscript.tax_table, "charge_type", function(frm, cdt, cdn) {
+ cur_frm.cscript.validate_taxes_and_charges(cdt, cdn);
+ erpnext.taxes.set_conditional_mandatory_rate_or_amount(frm);
+ });
+
+ frappe.ui.form.on(cur_frm.cscript.tax_table, "included_in_print_rate", function(frm, cdt, cdn) {
+ var tax = frappe.get_doc(cdt, cdn);
+ try {
+ cur_frm.cscript.validate_taxes_and_charges(cdt, cdn);
+ cur_frm.cscript.validate_inclusive_tax(tax);
+ } catch(e) {
+ tax.included_in_print_rate = 0;
+ refresh_field("included_in_print_rate", tax.name, tax.parentfield);
+ throw e;
+ }
+ });
+}
+
+erpnext.taxes.set_conditional_mandatory_rate_or_amount = function(frm) {
+ var grid_row = frm.open_grid_row();
+ if(grid_row.doc.charge_type==="Actual") {
+ grid_row.toggle_display("tax_amount", true);
+ grid_row.toggle_reqd("tax_amount", true);
+ grid_row.toggle_display("rate", false);
+ grid_row.toggle_reqd("rate", false);
+ } else {
+ grid_row.toggle_display("rate", true);
+ grid_row.toggle_reqd("rate", true);
+ grid_row.toggle_display("tax_amount", false);
+ grid_row.toggle_reqd("tax_amount", false);
}
+}
+
+// setup conditional mandatory for tax and rates
+frappe.ui.form.on(cur_frm.doctype, "taxes_on_form_rendered", function(frm) {
+ erpnext.taxes.set_conditional_mandatory_rate_or_amount(frm);
});
+
cur_frm.set_query("account_head", "taxes", function(doc) {
if(cur_frm.cscript.tax_table == "Sales Taxes and Charges") {
var account_type = ["Tax", "Chargeable", "Expense Account"];
diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js
index 67d8eaa..0d97a94 100644
--- a/erpnext/public/js/utils.js
+++ b/erpnext/public/js/utils.js
@@ -57,13 +57,15 @@
}
},
- setup_serial_no: function(grid_row) {
+ setup_serial_no: function() {
+ var grid_row = cur_frm.open_grid_row();
+
if(!grid_row.fields_dict.serial_no ||
grid_row.fields_dict.serial_no.get_status()!=="Write") return;
var $btn = $('<button class="btn btn-sm btn-default">'+__("Add Serial No")+'</button>')
.appendTo($("<div>")
- .css({"margin-bottom": "10px", "margin-left": "15px"})
+ .css({"margin-bottom": "10px", "margin-top": "10px"})
.appendTo(grid_row.fields_dict.serial_no.$wrapper));
$btn.on("click", function() {
@@ -99,7 +101,7 @@
d.show();
});
},
-
+
get_letter_head: function(company) {
frappe.call({
type:"GET",
@@ -112,7 +114,7 @@
}
});
},
-
+
});
diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json
index 67e1ba8..cede702 100644
--- a/erpnext/setup/doctype/company/company.json
+++ b/erpnext/setup/doctype/company/company.json
@@ -188,7 +188,7 @@
"fieldname": "default_expense_account",
"fieldtype": "Link",
"ignore_user_permissions": 1,
- "label": "Default Expense Account",
+ "label": "Default Cost of Goods Sold Account",
"no_copy": 1,
"options": "Account",
"permlevel": 0
@@ -399,7 +399,7 @@
],
"icon": "icon-building",
"idx": 1,
- "modified": "2015-02-21 10:32:38.523900",
+ "modified": "2015-02-25 06:28:13.565128",
"modified_by": "Administrator",
"module": "Setup",
"name": "Company",
diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py
index 6247afd..56e0243 100644
--- a/erpnext/setup/doctype/company/company.py
+++ b/erpnext/setup/doctype/company/company.py
@@ -106,28 +106,30 @@
account.insert()
def set_default_accounts(self):
- def _set_default_account(fieldname, account_type):
- if self.get(fieldname):
- return
+ self._set_default_account("default_cash_account", "Cash")
+ self._set_default_account("default_bank_account", "Bank")
- account = frappe.db.get_value("Account", {"account_type": account_type,
- "group_or_ledger": "Ledger", "company": self.name})
-
- if account:
- self.db_set(fieldname, account)
-
- _set_default_account("default_cash_account", "Cash")
- _set_default_account("default_bank_account", "Bank")
-
- if cint(frappe.db.get_value("Accounts Settings", None, "auto_accounting_for_stock")):
- _set_default_account("stock_received_but_not_billed", "Stock Received But Not Billed")
- _set_default_account("stock_adjustment_account", "Stock Adjustment")
- _set_default_account("expenses_included_in_valuation", "Expenses Included In Valuation")
+ if cint(frappe.db.get_single_value("Accounts Settings", "auto_accounting_for_stock")):
+ self._set_default_account("stock_received_but_not_billed", "Stock Received But Not Billed")
+ self._set_default_account("stock_adjustment_account", "Stock Adjustment")
+ self._set_default_account("expenses_included_in_valuation", "Expenses Included In Valuation")
+ self._set_default_account("default_expense_account", "Cost of Goods Sold")
if not self.default_income_account:
self.db_set("default_income_account", frappe.db.get_value("Account",
{"account_name": _("Sales"), "company": self.name}))
+
+ def _set_default_account(self, fieldname, account_type):
+ if self.get(fieldname):
+ return
+
+ account = frappe.db.get_value("Account", {"account_type": account_type,
+ "group_or_ledger": "Ledger", "company": self.name})
+
+ if account:
+ self.db_set(fieldname, account)
+
def create_default_cost_center(self):
cc_list = [
{
diff --git a/erpnext/setup/doctype/item_group/item_group.js b/erpnext/setup/doctype/item_group/item_group.js
index 717c7d6..67e4e99 100644
--- a/erpnext/setup/doctype/item_group/item_group.js
+++ b/erpnext/setup/doctype/item_group/item_group.js
@@ -1,30 +1,36 @@
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
-cur_frm.list_route = "Sales Browser/Item Group";
+frappe.ui.form.on("Item Group", {
+ onload: function(frm) {
+ frm.list_route = "Sales Browser/Item Group";
-cur_frm.cscript.refresh = function(doc, cdt, cdn) {
- cur_frm.cscript.set_root_readonly(doc);
- cur_frm.add_custom_button(__("Item Group Tree"), function() {
- frappe.set_route("Sales Browser", "Item Group");
- }, "icon-sitemap")
-}
+ //get query select item group
+ frm.fields_dict['parent_item_group'].get_query = function(doc,cdt,cdn) {
+ return{
+ filters:[
+ ['Item Group', 'is_group', '=', 'Yes'],
+ ['Item Group', 'name', '!=', doc.item_group_name]
+ ]
+ }
+ }
+ },
-cur_frm.cscript.set_root_readonly = function(doc) {
- // read-only for root item group
- cur_frm.set_intro("");
- if(!doc.parent_item_group) {
- cur_frm.set_read_only();
- cur_frm.set_intro(__("This is a root item group and cannot be edited."), true);
- }
-}
+ refresh: function(frm) {
+ frm.trigger("set_root_readonly");
+ frm.add_custom_button(__("Item Group Tree"), function() {
+ frappe.set_route("Sales Browser", "Item Group");
+ }, "icon-sitemap");
+ },
-//get query select item group
-cur_frm.fields_dict['parent_item_group'].get_query = function(doc,cdt,cdn) {
- return{
- filters:[
- ['Item Group', 'is_group', '=', 'Yes'],
- ['Item Group', 'name', '!=', doc.item_group_name]
- ]
- }
-}
+ set_root_readonly: function(frm) {
+ // read-only for root item group
+ frm.set_intro("");
+ if(!frm.doc.parent_item_group) {
+ frm.set_read_only();
+ frm.set_intro(__("This is a root item group and cannot be edited."), true);
+ }
+ },
+
+ page_name: frappe.utils.warn_page_name_change
+});
diff --git a/erpnext/startup/notifications.py b/erpnext/startup/notifications.py
index 66bc83d..7d6109d 100644
--- a/erpnext/startup/notifications.py
+++ b/erpnext/startup/notifications.py
@@ -5,7 +5,7 @@
import frappe
def get_notification_config():
- return { "for_doctype":
+ return { "for_doctype":
{
"Issue": {"status":"Open"},
"Warranty Claim": {"status":"Open"},
@@ -14,9 +14,9 @@
"Contact": {"status":"Open"},
"Opportunity": {"docstatus":0},
"Quotation": {"docstatus":0},
- "Sales Order": {"docstatus":0},
+ "Sales Order": { "per_delivered": ("<", 100) },
"Journal Entry": {"docstatus":0},
- "Sales Invoice": {"docstatus":0},
+ "Sales Invoice": { "outstanding_amount": (">", 0) },
"Purchase Invoice": {"docstatus":0},
"Leave Application": {"status":"Open"},
"Expense Claim": {"approval_status":"Draft"},
@@ -25,11 +25,11 @@
"Delivery Note": {"docstatus":0},
"Stock Entry": {"docstatus":0},
"Material Request": {"docstatus":0},
- "Purchase Order": {"docstatus":0},
- "Production Order": {"docstatus":0},
+ "Purchase Order": { "per_received": ("<", 100) },
+ "Production Order": { "status": "In Process" },
"BOM": {"docstatus":0},
"Timesheet": {"docstatus":0},
"Time Log": {"status":"Draft"},
"Time Log Batch": {"status":"Draft"},
}
- }
\ No newline at end of file
+ }
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.js b/erpnext/stock/doctype/delivery_note/delivery_note.js
index 185c242..1c68339 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.js
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.js
@@ -79,7 +79,7 @@
},
items_on_form_rendered: function(doc, grid_row) {
- erpnext.setup_serial_no(grid_row)
+ erpnext.setup_serial_no();
}
});
diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js
index 33b1720..eec8ec2 100644
--- a/erpnext/stock/doctype/item/item.js
+++ b/erpnext/stock/doctype/item/item.js
@@ -3,200 +3,207 @@
frappe.provide("erpnext.item");
-frappe.ui.form.on("Item", "refresh", function(frm) {
- if(frm.doc.is_stock_item) {
- frm.add_custom_button(__("Show Balance"), function() {
- frappe.route_options = {
- "item_code": frm.doc.name
- }
- frappe.set_route("query-report", "Stock Balance");
- });
- }
-})
-
-cur_frm.cscript.refresh = function(doc) {
- // make sensitive fields(has_serial_no, is_stock_item, valuation_method)
- // read only if any stock ledger entry exists
-
- cur_frm.cscript.make_dashboard();
-
- if (cur_frm.doc.has_variants) {
- cur_frm.set_intro(__("This Item is a Template and cannot be used in transactions. Item attributes will be copied over into the variants unless 'No Copy' is set"), true);
- cur_frm.add_custom_button(__("Show Variants"), function() {
- frappe.set_route("List", "Item", {"variant_of": cur_frm.doc.name});
- }, "icon-list", "btn-default");
- }
- if (cur_frm.doc.variant_of) {
- cur_frm.set_intro(__("This Item is a Variant of {0} (Template). Attributes will be copied over from the template unless 'No Copy' is set", [cur_frm.doc.variant_of]), true);
- }
-
- if (frappe.defaults.get_default("item_naming_by")!="Naming Series") {
- cur_frm.toggle_display("naming_series", false);
- } else {
- erpnext.toggle_naming_series();
- }
-
-
- cur_frm.cscript.edit_prices_button();
-
- if (!doc.__islocal && doc.is_stock_item == 'Yes') {
- cur_frm.toggle_enable(['has_serial_no', 'is_stock_item', 'valuation_method', 'has_batch_no'],
- (doc.__onload && doc.__onload.sle_exists=="exists") ? false : true);
- }
-
- erpnext.item.toggle_reqd(cur_frm);
-}
-
-erpnext.item.toggle_reqd = function(frm) {
- frm.toggle_reqd("default_warehouse", frm.doc.is_stock_item==="Yes");
-};
-
-frappe.ui.form.on("Item", "onload", function(frm) {
- var df = frappe.meta.get_docfield("Item Variant", "item_attribute_value");
- df.on_make = function(field) {
- field.$input.autocomplete({
- minLength: 0,
- minChars: 0,
- source: function(request, response) {
- frappe.call({
- method:"frappe.client.get_list",
- args:{
- doctype:"Item Attribute Value",
- filters: [
- ["parent","=", field.doc.item_attribute],
- ["attribute_value", "like", request.term + "%"]
- ],
- fields: ["attribute_value"]
- },
- callback: function(r) {
- response($.map(r.message, function(d) { return d.attribute_value; }));
+frappe.ui.form.on("Item", {
+ onload: function(frm) {
+ var df = frappe.meta.get_docfield("Item Variant", "item_attribute_value");
+ df.on_make = function(field) {
+ field.$input.autocomplete({
+ minLength: 0,
+ minChars: 0,
+ source: function(request, response) {
+ frappe.call({
+ method:"frappe.client.get_list",
+ args:{
+ doctype:"Item Attribute Value",
+ filters: [
+ ["parent","=", field.doc.item_attribute],
+ ["attribute_value", "like", request.term + "%"]
+ ],
+ fields: ["attribute_value"]
+ },
+ callback: function(r) {
+ response($.map(r.message, function(d) { return d.attribute_value; }));
+ }
+ });
+ },
+ select: function(event, ui) {
+ field.$input.val(ui.item.value);
+ field.$input.trigger("change");
+ },
+ focus: function( event, ui ) {
+ if(ui.item.action) {
+ return false;
}
- });
- },
- select: function(event, ui) {
- field.$input.val(ui.item.value);
- field.$input.trigger("change");
- },
- focus: function( event, ui ) {
- if(ui.item.action) {
- return false;
+ },
+ });
+ }
+
+ erpnext.item.setup_queries(frm);
+ },
+
+ refresh: function(frm) {
+ if(frm.doc.is_stock_item) {
+ frm.add_custom_button(__("Show Balance"), function() {
+ frappe.route_options = {
+ "item_code": frm.doc.name
}
- },
+ frappe.set_route("query-report", "Stock Balance");
+ });
+ }
+
+ // make sensitive fields(has_serial_no, is_stock_item, valuation_method)
+ // read only if any stock ledger entry exists
+ erpnext.item.make_dashboard(frm);
+
+ if (frm.doc.has_variants) {
+ frm.set_intro(__("This Item is a Template and cannot be used in transactions. Item attributes will be copied over into the variants unless 'No Copy' is set"), true);
+ frm.add_custom_button(__("Show Variants"), function() {
+ frappe.set_route("List", "Item", {"variant_of": frm.doc.name});
+ }, "icon-list", "btn-default");
+ }
+ if (frm.doc.variant_of) {
+ frm.set_intro(__("This Item is a Variant of {0} (Template). Attributes will be copied over from the template unless 'No Copy' is set", [frm.doc.variant_of]), true);
+ }
+
+ if (frappe.defaults.get_default("item_naming_by")!="Naming Series") {
+ frm.toggle_display("naming_series", false);
+ } else {
+ erpnext.toggle_naming_series();
+ }
+
+ erpnext.item.edit_prices_button(frm);
+
+ if (!frm.doc.__islocal && frm.doc.is_stock_item == 'Yes') {
+ frm.toggle_enable(['has_serial_no', 'is_stock_item', 'valuation_method', 'has_batch_no'],
+ (frm.doc.__onload && frm.doc.__onload.sle_exists=="exists") ? false : true);
+ }
+
+ erpnext.item.toggle_reqd(frm);
+ },
+
+ validate: function(frm){
+ erpnext.item.weight_to_validate(frm);
+ },
+
+ image: function(frm) {
+ refresh_field("image_view");
+ },
+
+ page_name: frappe.utils.warn_page_name_change,
+
+ item_code: function(frm) {
+ if(!frm.doc.item_name)
+ frm.set_value("item_name", frm.doc.item_code);
+ if(!frm.doc.description)
+ frm.set_value("description", frm.doc.item_code);
+ },
+
+ tax_type: function(frm, cdt, cdn){
+ var d = locals[cdt][cdn];
+ return get_server_fields('get_tax_rate', d.tax_type, 'taxes', doc, cdt, cdn, 1);
+ },
+
+ copy_from_item_group: function(frm) {
+ return frm.call({
+ doc: frm.doc,
+ method: "copy_specification_from_item_group"
});
- }
+ },
});
-cur_frm.cscript.make_dashboard = function() {
- cur_frm.dashboard.reset();
- if(cur_frm.doc.__islocal)
- return;
-}
-
-cur_frm.cscript.edit_prices_button = function() {
- cur_frm.add_custom_button(__("Add / Edit Prices"), function() {
- frappe.set_route("Report", "Item Price", {"item_code": cur_frm.doc.name});
- }, "icon-money", "btn-default");
-}
-
-cur_frm.cscript.item_code = function(doc) {
- if(!doc.item_name)
- cur_frm.set_value("item_name", doc.item_code);
- if(!doc.description)
- cur_frm.set_value("description", doc.item_code);
-}
-
-// Expense Account
-// ---------------------------------
-cur_frm.fields_dict['expense_account'].get_query = function(doc) {
- return {
- filters: {
- "report_type": "Profit and Loss",
- "group_or_ledger": "Ledger"
+$.extend(erpnext.item, {
+ setup_queries: function(frm) {
+ // Expense Account
+ // ---------------------------------
+ frm.fields_dict['expense_account'].get_query = function(doc) {
+ return {
+ filters: {
+ "report_type": "Profit and Loss",
+ "group_or_ledger": "Ledger"
+ }
+ }
}
- }
-}
-// Income Account
-// --------------------------------
-cur_frm.fields_dict['income_account'].get_query = function(doc) {
- return {
- filters: {
- "report_type": "Profit and Loss",
- 'group_or_ledger': "Ledger",
- 'account_type': "Income Account"
+ // Income Account
+ // --------------------------------
+ frm.fields_dict['income_account'].get_query = function(doc) {
+ return {
+ filters: {
+ "report_type": "Profit and Loss",
+ 'group_or_ledger': "Ledger",
+ 'account_type': "Income Account"
+ }
+ }
}
- }
-}
-// Purchase Cost Center
-// -----------------------------
-cur_frm.fields_dict['buying_cost_center'].get_query = function(doc) {
- return {
- filters:{ 'group_or_ledger': "Ledger" }
- }
-}
+ // Purchase Cost Center
+ // -----------------------------
+ frm.fields_dict['buying_cost_center'].get_query = function(doc) {
+ return {
+ filters:{ 'group_or_ledger': "Ledger" }
+ }
+ }
-// Sales Cost Center
-// -----------------------------
-cur_frm.fields_dict['selling_cost_center'].get_query = function(doc) {
- return {
- filters:{ 'group_or_ledger': "Ledger" }
- }
-}
+ // Sales Cost Center
+ // -----------------------------
+ frm.fields_dict['selling_cost_center'].get_query = function(doc) {
+ return {
+ filters:{ 'group_or_ledger': "Ledger" }
+ }
+ }
-cur_frm.fields_dict['taxes'].grid.get_field("tax_type").get_query = function(doc, cdt, cdn) {
- return {
- filters: [
- ['Account', 'account_type', 'in',
- 'Tax, Chargeable, Income Account, Expense Account'],
- ['Account', 'docstatus', '!=', 2]
- ]
- }
-}
+ frm.fields_dict['taxes'].grid.get_field("tax_type").get_query = function(doc, cdt, cdn) {
+ return {
+ filters: [
+ ['Account', 'account_type', 'in',
+ 'Tax, Chargeable, Income Account, Expense Account'],
+ ['Account', 'docstatus', '!=', 2]
+ ]
+ }
+ }
-cur_frm.cscript.tax_type = function(doc, cdt, cdn){
- var d = locals[cdt][cdn];
- return get_server_fields('get_tax_rate', d.tax_type, 'taxes', doc, cdt, cdn, 1);
-}
+ frm.fields_dict['item_group'].get_query = function(doc,cdt,cdn) {
+ return {
+ filters: [
+ ['Item Group', 'docstatus', '!=', 2]
+ ]
+ }
+ }
-cur_frm.fields_dict['item_group'].get_query = function(doc,cdt,cdn) {
- return {
- filters: [
- ['Item Group', 'docstatus', '!=', 2]
- ]
- }
-}
+ frm.fields_dict.customer_items.grid.get_field("customer_name").get_query = function(doc, cdt, cdn) {
+ return { query: "erpnext.controllers.queries.customer_query" }
+ }
-// Quotation to validation - either customer or lead mandatory
-cur_frm.cscript.weight_to_validate = function(doc, cdt, cdn){
- if((doc.nett_weight || doc.gross_weight) && !doc.weight_uom) {
- msgprint(__('Weight is mentioned,\nPlease mention "Weight UOM" too'));
- validated = 0;
- }
-}
+ frm.fields_dict.supplier_items.grid.get_field("supplier").get_query = function(doc, cdt, cdn) {
+ return { query: "erpnext.controllers.queries.supplier_query" }
+ }
-cur_frm.cscript.validate = function(doc, cdt, cdn){
- cur_frm.cscript.weight_to_validate(doc, cdt, cdn);
-}
+ },
-cur_frm.fields_dict.customer_items.grid.get_field("customer_name").get_query = function(doc, cdt, cdn) {
- return { query: "erpnext.controllers.queries.customer_query" }
-}
+ toggle_reqd: function(frm) {
+ frm.toggle_reqd("default_warehouse", frm.doc.is_stock_item==="Yes");
+ },
-cur_frm.fields_dict.supplier_items.grid.get_field("supplier").get_query = function(doc, cdt, cdn) {
- return { query: "erpnext.controllers.queries.supplier_query" }
-}
+ make_dashboard: function(frm) {
+ frm.dashboard.reset();
+ if(frm.doc.__islocal)
+ return;
+ },
-cur_frm.cscript.copy_from_item_group = function(doc) {
- return cur_frm.call({
- doc: doc,
- method: "copy_specification_from_item_group"
- });
-}
+ edit_prices_button: function(frm) {
+ frm.add_custom_button(__("Add / Edit Prices"), function() {
+ frappe.set_route("Report", "Item Price", {"item_code": frm.doc.name});
+ }, "icon-money", "btn-default");
+ },
-cur_frm.cscript.image = function() {
- refresh_field("image_view");
-}
+ weight_to_validate: function(frm){
+ if((frm.doc.nett_weight || frm.doc.gross_weight) && !frm.doc.weight_uom) {
+ msgprint(__('Weight is mentioned,\nPlease mention "Weight UOM" too'));
+ validated = 0;
+ }
+ },
+
+});
diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json
index 70826fb..28bfb5a 100644
--- a/erpnext/stock/doctype/item/item.json
+++ b/erpnext/stock/doctype/item/item.json
@@ -772,7 +772,7 @@
"label": "Page Name",
"no_copy": 1,
"permlevel": 0,
- "read_only": 1
+ "read_only": 0
},
{
"depends_on": "show_in_website",
@@ -876,7 +876,7 @@
"icon": "icon-tag",
"idx": 1,
"max_attachments": 1,
- "modified": "2015-02-24 05:19:07.382483",
+ "modified": "2015-02-25 02:46:14.483577",
"modified_by": "Administrator",
"module": "Stock",
"name": "Item",
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js
index 8795524..716b4a8 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.js
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.js
@@ -274,7 +274,7 @@
},
items_on_form_rendered: function(doc, grid_row) {
- erpnext.setup_serial_no(grid_row)
+ erpnext.setup_serial_no();
},
customer: function() {
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.json b/erpnext/stock/doctype/stock_entry/stock_entry.json
index db40708..16e7a89 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.json
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.json
@@ -8,6 +8,14 @@
"doctype": "DocType",
"fields": [
{
+ "fieldname": "items_section",
+ "fieldtype": "Section Break",
+ "label": "",
+ "oldfieldtype": "Section Break",
+ "permlevel": 0,
+ "read_only": 0
+ },
+ {
"fieldname": "col1",
"fieldtype": "Column Break",
"oldfieldtype": "Column Break",
@@ -74,15 +82,6 @@
"search_index": 1
},
{
- "depends_on": "eval:!inList([\"Sales Return\", \"Purchase Return\"], doc.purpose)",
- "fieldname": "bom_no",
- "fieldtype": "Link",
- "label": "BOM No",
- "options": "BOM",
- "permlevel": 0,
- "read_only": 0
- },
- {
"allow_on_submit": 0,
"depends_on": "eval:doc.purpose==\"Sales Return\"",
"fieldname": "delivery_note_no",
@@ -133,6 +132,13 @@
"search_index": 1
},
{
+ "fieldname": "from_bom",
+ "fieldtype": "Check",
+ "label": "From BOM",
+ "permlevel": 0,
+ "precision": ""
+ },
+ {
"fieldname": "col2",
"fieldtype": "Column Break",
"oldfieldtype": "Column Break",
@@ -178,14 +184,92 @@
"search_index": 0
},
{
- "fieldname": "items_section",
+ "depends_on": "eval: doc.from_bom && (doc.purpose!==\"Sales Return\" && doc.purpose!==\"Purchase Return\")",
+ "fieldname": "sb1",
"fieldtype": "Section Break",
"label": "",
- "oldfieldtype": "Section Break",
"permlevel": 0,
"read_only": 0
},
{
+ "depends_on": "eval:!inList([\"Sales Return\", \"Purchase Return\"], doc.purpose)",
+ "fieldname": "bom_no",
+ "fieldtype": "Link",
+ "label": "BOM No",
+ "options": "BOM",
+ "permlevel": 0,
+ "read_only": 0
+ },
+ {
+ "depends_on": "eval:inList([\"Manufacture\", \"Repack\"], doc.purpose)",
+ "fieldname": "additional_operating_cost",
+ "fieldtype": "Currency",
+ "label": "Additional Operating Cost",
+ "no_copy": 1,
+ "options": "Company:company:default_currency",
+ "permlevel": 0,
+ "read_only": 0
+ },
+ {
+ "fieldname": "cb1",
+ "fieldtype": "Column Break",
+ "permlevel": 0,
+ "read_only": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "depends_on": "eval:!inList([\"Sales Return\", \"Purchase Return\"], doc.purpose)",
+ "description": "As per Stock UOM",
+ "fieldname": "fg_completed_qty",
+ "fieldtype": "Float",
+ "hidden": 0,
+ "in_filter": 0,
+ "label": "Manufacturing Quantity",
+ "no_copy": 0,
+ "oldfieldname": "fg_completed_qty",
+ "oldfieldtype": "Currency",
+ "permlevel": 0,
+ "print_hide": 1,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0
+ },
+ {
+ "default": "1",
+ "depends_on": "eval:!inList([\"Sales Return\", \"Purchase Return\"], doc.purpose)",
+ "description": "Including items for sub assemblies",
+ "fieldname": "use_multi_level_bom",
+ "fieldtype": "Check",
+ "label": "Use Multi-Level BOM",
+ "permlevel": 0,
+ "print_hide": 1,
+ "read_only": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "depends_on": "eval:!inList([\"Sales Return\", \"Purchase Return\"], doc.purpose)",
+ "fieldname": "get_items",
+ "fieldtype": "Button",
+ "hidden": 0,
+ "in_filter": 0,
+ "label": "Get Items",
+ "no_copy": 0,
+ "oldfieldtype": "Button",
+ "permlevel": 0,
+ "print_hide": 1,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0
+ },
+ {
+ "fieldname": "section_break_12",
+ "fieldtype": "Section Break",
+ "permlevel": 0,
+ "precision": ""
+ },
+ {
"allow_on_submit": 0,
"fieldname": "from_warehouse",
"fieldtype": "Link",
@@ -302,75 +386,13 @@
"read_only": 1
},
{
- "depends_on": "eval:(doc.purpose!==\"Sales Return\" && doc.purpose!==\"Purchase Return\")",
- "fieldname": "sb1",
- "fieldtype": "Section Break",
- "label": "From Bill of Materials",
+ "description": "This will override Difference Account in Item",
+ "fieldname": "difference_account",
+ "fieldtype": "Link",
+ "label": "Difference Account",
+ "options": "Account",
"permlevel": 0,
- "read_only": 0
- },
- {
- "allow_on_submit": 0,
- "depends_on": "eval:!inList([\"Sales Return\", \"Purchase Return\"], doc.purpose)",
- "description": "As per Stock UOM",
- "fieldname": "fg_completed_qty",
- "fieldtype": "Float",
- "hidden": 0,
- "in_filter": 0,
- "label": "Manufacturing Quantity",
- "no_copy": 0,
- "oldfieldname": "fg_completed_qty",
- "oldfieldtype": "Currency",
- "permlevel": 0,
- "print_hide": 1,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0
- },
- {
- "depends_on": "eval:inList([\"Manufacture\", \"Repack\"], doc.purpose)",
- "fieldname": "additional_operating_cost",
- "fieldtype": "Currency",
- "label": "Additional Operating Cost",
- "no_copy": 1,
- "options": "Company:company:default_currency",
- "permlevel": 0,
- "read_only": 0
- },
- {
- "fieldname": "cb1",
- "fieldtype": "Column Break",
- "permlevel": 0,
- "read_only": 0
- },
- {
- "default": "1",
- "depends_on": "eval:!inList([\"Sales Return\", \"Purchase Return\"], doc.purpose)",
- "description": "If checked, BOM for sub-assembly items will be considered for getting raw materials. Otherwise, all sub-assembly items will be treated as a raw material.",
- "fieldname": "use_multi_level_bom",
- "fieldtype": "Check",
- "label": "Use Multi-Level BOM",
- "permlevel": 0,
- "print_hide": 1,
- "read_only": 0
- },
- {
- "allow_on_submit": 0,
- "depends_on": "eval:!inList([\"Sales Return\", \"Purchase Return\"], doc.purpose)",
- "fieldname": "get_items",
- "fieldtype": "Button",
- "hidden": 0,
- "in_filter": 0,
- "label": "Get Items",
- "no_copy": 0,
- "oldfieldtype": "Button",
- "permlevel": 0,
- "print_hide": 1,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0
+ "precision": ""
},
{
"fieldname": "fold",
@@ -632,7 +654,7 @@
"is_submittable": 1,
"issingle": 0,
"max_attachments": 0,
- "modified": "2015-02-20 05:04:09.060180",
+ "modified": "2015-02-25 06:13:11.899840",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock Entry",
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index a48c5f3..f4f1eec 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -45,25 +45,25 @@
self.get("__onload").credit_debit_note_exists = 1 if count else 0
def validate(self):
+ self.pro_doc = None
+ if self.production_order:
+ self.pro_doc = frappe.get_doc('Production Order', self.production_order)
+
self.validate_posting_time()
self.validate_purpose()
- pro_obj = self.production_order and \
- frappe.get_doc('Production Order', self.production_order) or None
-
validate_fiscal_year(self.posting_date, self.fiscal_year, self.meta.get_label("posting_date"), self)
-
self.validate_item()
self.set_transfer_qty()
self.validate_uom_is_integer("uom", "qty")
self.validate_uom_is_integer("stock_uom", "transfer_qty")
- self.validate_warehouse(pro_obj)
+ self.validate_warehouse()
self.validate_production_order()
self.get_stock_and_rate()
self.validate_bom()
self.validate_finished_goods()
self.validate_return_reference_doc()
self.validate_with_material_request()
- #self.validate_valuation_rate()
+ self.validate_valuation_rate()
self.set_total_incoming_outgoing_value()
self.set_total_amount()
@@ -86,6 +86,12 @@
if self.purpose not in valid_purposes:
frappe.throw(_("Purpose must be one of {0}").format(comma_or(valid_purposes)))
+ if self.purpose in ("Manufacture", "Repack", "Sales Return") and not self.difference_account:
+ self.difference_account = frappe.db.get_value("Company", self.company, "default_expense_account")
+
+ if self.purpose in ("Purchase Return") and not self.difference_account:
+ frappe.throw(_("Difference Account mandatory for purpose '{0}'").format(self.purpose))
+
def set_transfer_qty(self):
for item in self.get("items"):
if not flt(item.qty):
@@ -108,6 +114,9 @@
if f not in ["expense_account", "cost_center"] or not item.get(f):
item.set(f, item_details.get(f))
+ if self.difference_account:
+ item.expense_account = self.difference_account
+
if not item.transfer_qty:
item.transfer_qty = item.qty * item.conversion_factor
@@ -117,7 +126,7 @@
frappe.throw(_("Row #{0}: Please specify Serial No for Item {1}").format(item.idx, item.item_code),
frappe.MandatoryError)
- def validate_warehouse(self, pro_obj):
+ def validate_warehouse(self):
"""perform various (sometimes conditional) validations on warehouse"""
source_mandatory = ["Material Issue", "Material Transfer", "Purchase Return", "Subcontract", "Material Transfer for Manufacture"]
@@ -156,7 +165,7 @@
if not d.t_warehouse:
frappe.throw(_("Target warehouse is mandatory for row {0}").format(d.idx))
- elif pro_obj and cstr(d.t_warehouse) != pro_obj.fg_warehouse:
+ elif self.pro_doc and cstr(d.t_warehouse) != self.pro_doc.fg_warehouse:
frappe.throw(_("Target warehouse in row {0} must be same as Production Order").format(d.idx))
else:
@@ -272,37 +281,45 @@
incoming_rate = flt(self.get_incoming_rate(args), self.precision("incoming_rate", d))
if incoming_rate > 0:
d.incoming_rate = incoming_rate
+
d.amount = flt(d.transfer_qty) * flt(d.incoming_rate)
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):
- if not flt(d.incoming_rate) or force:
- 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
+ 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.t_warehouse and number_of_fg_items == 1):
+ operation_cost_per_unit = 0.0
+ if self.production_order:
+ 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:
- pro_order = frappe.get_doc("Production Order", self.production_order)
- for d in pro_order.get("operations"):
+ 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(pro_order.qty)
+ 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 + flt(self.additional_operating_cost) / flt(qty)
+ return operation_cost_per_unit + (flt(self.additional_operating_cost) / flt(qty))
def get_incoming_rate(self, args):
incoming_rate = 0
@@ -503,12 +520,16 @@
self.set('items', [])
self.validate_production_order()
- pro_obj = None
+ if not getattr(self, "pro_doc", None):
+ self.pro_doc = None
+
if self.production_order:
# common validations
- pro_obj = frappe.get_doc('Production Order', self.production_order)
- if pro_obj:
- self.bom_no = pro_obj.bom_no
+ if not self.pro_doc:
+ self.pro_doc = frappe.get_doc('Production Order', self.production_order)
+
+ if self.pro_doc:
+ self.bom_no = self.pro_doc.bom_no
else:
# invalid production order
self.production_order = None
@@ -517,18 +538,18 @@
if self.purpose in ["Material Issue", "Material Transfer", "Manufacture", "Repack",
"Subcontract", "Material Transfer for Manufacture"]:
if self.production_order and self.purpose == "Material Transfer for Manufacture":
- item_dict = self.get_pending_raw_materials(pro_obj)
- if self.to_warehouse and pro_obj:
+ item_dict = self.get_pending_raw_materials()
+ if self.to_warehouse and self.pro_doc:
for item in item_dict.values():
- item["to_warehouse"] = pro_obj.wip_warehouse
+ item["to_warehouse"] = self.pro_doc.wip_warehouse
else:
if not self.fg_completed_qty:
frappe.throw(_("Manufacturing Quantity is mandatory"))
item_dict = self.get_bom_raw_materials(self.fg_completed_qty)
for item in item_dict.values():
- if pro_obj:
- item["from_warehouse"] = pro_obj.wip_warehouse
+ if self.pro_doc:
+ item["from_warehouse"] = self.pro_doc.wip_warehouse
item["to_warehouse"] = self.to_warehouse if self.purpose=="Subcontract" else ""
@@ -537,31 +558,34 @@
# add finished goods item
if self.purpose in ("Manufacture", "Repack"):
- if self.production_order:
- item_code = pro_obj.production_item
- to_warehouse = pro_obj.fg_warehouse
- else:
- item_code = frappe.db.get_value("BOM", self.bom_no, "item")
- to_warehouse = ""
-
- item = frappe.db.get_value("Item", item_code, ["item_name",
- "description", "stock_uom", "expense_account", "buying_cost_center", "name"], as_dict=1)
-
- self.add_to_stock_entry_detail({
- item.name: {
- "to_warehouse": to_warehouse,
- "from_warehouse": "",
- "qty": self.fg_completed_qty,
- "item_name": item.item_name,
- "description": item.description,
- "stock_uom": item.stock_uom,
- "expense_account": item.expense_account,
- "cost_center": item.buying_cost_center,
- }
- }, bom_no = self.bom_no)
+ self.load_items_from_bom()
self.get_stock_and_rate()
+ def load_items_from_bom(self):
+ if self.production_order:
+ item_code = self.pro_doc.production_item
+ to_warehouse = self.pro_doc.fg_warehouse
+ else:
+ item_code = frappe.db.get_value("BOM", self.bom_no, "item")
+ to_warehouse = ""
+
+ item = frappe.db.get_value("Item", item_code, ["item_name",
+ "description", "stock_uom", "expense_account", "buying_cost_center", "name"], as_dict=1)
+
+ self.add_to_stock_entry_detail({
+ item.name: {
+ "to_warehouse": to_warehouse,
+ "from_warehouse": "",
+ "qty": self.fg_completed_qty,
+ "item_name": item.item_name,
+ "description": item.description,
+ "stock_uom": item.stock_uom,
+ "expense_account": item.expense_account,
+ "cost_center": item.buying_cost_center,
+ }
+ }, bom_no = self.bom_no)
+
def get_bom_raw_materials(self, qty):
from erpnext.manufacturing.doctype.bom.bom import get_bom_items_as_dict
@@ -573,7 +597,7 @@
return item_dict
- def get_pending_raw_materials(self, pro_obj):
+ def get_pending_raw_materials(self):
"""
issue (item quantity) that is pending to issue or desire to transfer,
whichever is less
@@ -581,7 +605,7 @@
item_dict = self.get_bom_raw_materials(1)
issued_item_qty = self.get_issued_qty()
- max_qty = flt(pro_obj.qty)
+ max_qty = flt(self.pro_doc.qty)
only_pending_fetched = []
for item in item_dict:
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
index 14a54e9..0845a6b 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
@@ -10,12 +10,19 @@
from erpnext.controllers.stock_controller import StockController
from erpnext.stock.utils import get_stock_balance
+class OpeningEntryAccountError(frappe.ValidationError): pass
+
class StockReconciliation(StockController):
def __init__(self, arg1, arg2=None):
super(StockReconciliation, self).__init__(arg1, arg2)
self.head_row = ["Item Code", "Warehouse", "Quantity", "Valuation Rate"]
def validate(self):
+ if not self.expense_account:
+ self.expense_account = frappe.db.get_value("Company", self.company, "stock_adjustment_account")
+ if not self.cost_center:
+ self.cost_center = frappe.db.get_value("Company", self.company, "cost_center")
+ self.validate_posting_time()
self.remove_items_with_no_change()
self.validate_data()
self.validate_expense_account()
@@ -214,7 +221,12 @@
msgprint(_("Please enter Expense Account"), raise_exception=1)
elif not frappe.db.sql("""select name from `tabStock Ledger Entry` limit 1"""):
if frappe.db.get_value("Account", self.expense_account, "report_type") == "Profit and Loss":
- frappe.throw(_("Difference Account must be a 'Liability' type account, since this Stock Reconciliation is an Opening Entry"))
+ frappe.throw(_("Difference Account must be a 'Liability' type account, since this Stock Reconciliation is an Opening Entry"), OpeningEntryAccountError)
+
+ def get_items_for(self, warehouse):
+ self.items = []
+ for item in get_items(warehouse, self.posting_date, self.posting_time):
+ self.append("items", item)
@frappe.whitelist()
def get_items(warehouse, posting_date, posting_time):
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index 84743ae..d51a89b 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -150,7 +150,7 @@
return
if sle.serial_no:
- self.valuation_rate = self.get_serialized_values(sle)
+ self.get_serialized_values(sle)
self.qty_after_transaction += flt(sle.actual_qty)
self.stock_value = flt(self.qty_after_transaction) * flt(self.valuation_rate)
else:
@@ -208,12 +208,14 @@
if incoming_rate < 0:
# wrong incoming rate
incoming_rate = self.valuation_rate
- elif incoming_rate == 0 or flt(sle.actual_qty) < 0:
- # In case of delivery/stock issue, get average purchase rate
- # of serial nos of current entry
- incoming_rate = flt(frappe.db.sql("""select avg(ifnull(purchase_rate, 0))
- from `tabSerial No` where name in (%s)""" % (", ".join(["%s"]*len(serial_no))),
- tuple(serial_no))[0][0])
+
+ elif incoming_rate == 0:
+ if flt(sle.actual_qty) < 0:
+ # In case of delivery/stock issue, get average purchase rate
+ # of serial nos of current entry
+ incoming_rate = flt(frappe.db.sql("""select avg(ifnull(purchase_rate, 0))
+ from `tabSerial No` where name in (%s)""" % (", ".join(["%s"]*len(serial_no))),
+ tuple(serial_no))[0][0])
if incoming_rate and not self.valuation_rate:
self.valuation_rate = incoming_rate
diff --git a/erpnext/templates/form_grid/stock_entry_grid.html b/erpnext/templates/form_grid/stock_entry_grid.html
index 2527ef3..efb4ab6 100644
--- a/erpnext/templates/form_grid/stock_entry_grid.html
+++ b/erpnext/templates/form_grid/stock_entry_grid.html
@@ -15,12 +15,10 @@
<div class="col-sm-5 col-xs-4"><strong>{%= doc.item_code %}</strong>
{% if(doc.item_name != doc.item_code) { %}
<br>{%= doc.item_name %}{% } %}
- <!-- {% if(doc.item_name != doc.description) { %}
- <p>{%= doc.description %}</p>{% } %} -->
{% include "templates/form_grid/includes/visible_cols.html" %}
{% if(frm.doc.docstatus==0 && doc.s_warehouse && doc.actual_qty < doc.qty) { %}
<span class="text-danger small" style="margin-left: 15px;">
- <span class="octicon octicon-stop" style="font-size: 12px;"></span> Not in Stock
+ Not in Stock
</span>
{% } %}
</div>
diff --git a/test_sites/test_site/site_config.json b/test_sites/test_site/site_config.json
index 31a445b..96f3446 100644
--- a/test_sites/test_site/site_config.json
+++ b/test_sites/test_site/site_config.json
@@ -5,5 +5,6 @@
"auto_email_id": "admin@example.com",
"host_name": "http://localhost:8888",
"auto_email_id": "admin@example.com",
- "mute_emails": 1
+ "mute_emails": 1,
+ "install_apps": ["erpnext"]
}