fixed conflict
diff --git a/erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py b/erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py
index 02ffb99..6fd980e 100644
--- a/erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py
+++ b/erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py
@@ -97,7 +97,7 @@
with open(os.path.join(os.path.dirname(__file__), "syscohada_syscohada_chart_template.json"), "r") as f:
_get_chart_name(f.read())
- if len(charts) > 1:
+ if len(charts) != 1:
charts.append("Standard")
return charts
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py
index 79eeb70..66e4854 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.py
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py
@@ -39,6 +39,7 @@
self.check_credit_days()
self.validate_expense_claim()
self.validate_credit_debit_note()
+ self.validate_empty_accounts_table()
def on_submit(self):
self.check_credit_limit()
@@ -450,6 +451,10 @@
})
if count:
frappe.throw(_("{0} already made against stock entry {1}".format(self.voucher_type, self.stock_entry)))
+
+ def validate_empty_accounts_table(self):
+ if not self.get('accounts'):
+ frappe.throw("Accounts table cannot be blank.")
@frappe.whitelist()
def get_default_bank_cash_account(company, voucher_type, mode_of_payment=None):
diff --git a/erpnext/accounts/page/financial_analytics/financial_analytics.js b/erpnext/accounts/page/financial_analytics/financial_analytics.js
index 4e50711..a895aad 100644
--- a/erpnext/accounts/page/financial_analytics/financial_analytics.js
+++ b/erpnext/accounts/page/financial_analytics/financial_analytics.js
@@ -1,8 +1,6 @@
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
-frappe.require("assets/erpnext/js/account_tree_grid.js");
-
frappe.pages['financial-analytics'].on_page_load = function(wrapper) {
frappe.ui.make_app_page({
parent: wrapper,
@@ -10,9 +8,11 @@
single_column: true
});
erpnext.financial_analytics = new erpnext.FinancialAnalytics(wrapper, 'Financial Analytics');
- frappe.add_breadcrumbs("Accounts")
+ frappe.add_breadcrumbs("Accounts");
-}
+};
+
+frappe.require("assets/erpnext/js/account_tree_grid.js");
erpnext.FinancialAnalytics = erpnext.AccountTreeGrid.extend({
filters: [
diff --git a/erpnext/buying/page/purchase_analytics/purchase_analytics.js b/erpnext/buying/page/purchase_analytics/purchase_analytics.js
index 33fb9b2..470e44c 100644
--- a/erpnext/buying/page/purchase_analytics/purchase_analytics.js
+++ b/erpnext/buying/page/purchase_analytics/purchase_analytics.js
@@ -11,10 +11,11 @@
new erpnext.PurchaseAnalytics(wrapper);
- frappe.add_breadcrumbs("Buying")
-
+ frappe.add_breadcrumbs("Buying");
}
+frappe.assets.views["Report"]();
+
erpnext.PurchaseAnalytics = frappe.views.TreeGridReport.extend({
init: function(wrapper) {
this._super({
diff --git a/erpnext/manufacturing/doctype/bom_operation/bom_operation.json b/erpnext/manufacturing/doctype/bom_operation/bom_operation.json
index a3678b5..aedc074 100644
--- a/erpnext/manufacturing/doctype/bom_operation/bom_operation.json
+++ b/erpnext/manufacturing/doctype/bom_operation/bom_operation.json
@@ -60,7 +60,7 @@
"oldfieldtype": "Currency",
"options": "",
"permlevel": 0,
- "reqd": 0
+ "reqd": 1
},
{
"allow_on_submit": 0,
@@ -77,7 +77,7 @@
],
"idx": 1,
"istable": 1,
- "modified": "2014-12-23 15:01:54.340605",
+ "modified": "2015-02-12 17:49:00.126034",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "BOM Operation",
diff --git a/erpnext/manufacturing/doctype/production_order/production_order.js b/erpnext/manufacturing/doctype/production_order/production_order.js
index f7ebaa9..8ab6237 100644
--- a/erpnext/manufacturing/doctype/production_order/production_order.js
+++ b/erpnext/manufacturing/doctype/production_order/production_order.js
@@ -7,7 +7,7 @@
cfn_set_fields(doc, dt, dn);
this.frm.add_fetch("sales_order", "delivery_date", "expected_delivery_date");
-
+
if(doc.__islocal) {
cur_frm.set_value({
"actual_start_date": "",
@@ -70,7 +70,7 @@
method: "set_production_order_operations"
});
},
-
+
planned_start_date: function() {
return this.frm.call({
doc: this.frm.doc,
@@ -144,7 +144,7 @@
}
cur_frm.cscript['Transfer Raw Materials'] = function() {
- cur_frm.cscript.make_se('Material Transfer');
+ cur_frm.cscript.make_se('Material Transfer for Manufacture');
}
cur_frm.cscript['Update Finished Goods'] = function() {
@@ -176,7 +176,58 @@
} else msgprint(__("Please enter Production Item first"));
});
-frappe.ui.form.on("Production Order", "additional_operating_cost", function(frm) {
+
+var calculate_total_cost = function(frm) {
var variable_cost = frm.doc.actual_operating_cost ? flt(frm.doc.actual_operating_cost) : flt(frm.doc.planned_operating_cost)
frm.set_value("total_operating_cost", (flt(frm.doc.additional_operating_cost) + variable_cost))
-})
+}
+
+frappe.ui.form.on("Production Order", "additional_operating_cost", function(frm) {
+ calculate_total_cost(frm);
+});
+
+frappe.ui.form.on("Production Order Operation", "workstation", function(frm, cdt, cdn) {
+ var d = locals[cdt][cdn];
+ frappe.call({
+ "method": "frappe.client.get",
+ args: {
+ doctype: "Workstation",
+ name: d.workstation
+ },
+ callback: function (data) {
+ frappe.model.set_value(d.doctype, d.name, "hour_rate", data.message.hour_rate);
+ calculate_cost(frm.doc);
+ calculate_total_cost(frm);
+ }
+ })
+});
+
+var calculate_cost = function(doc) {
+ if (doc.operations){
+ var op = doc.operations;
+ doc.planned_operating_cost = 0.0;
+ for(var i=0;i<op.length;i++) {
+ planned_operating_cost = flt(flt(op[i].hour_rate) * flt(op[i].time_in_mins) / 60, 2);
+ frappe.model.set_value('Production Order Operation',op[i].name, "planned_operating_cost", planned_operating_cost);
+
+ doc.planned_operating_cost += planned_operating_cost;
+ }
+ refresh_field('planned_operating_cost');
+ }
+}
+
+frappe.ui.form.on("Production Order Operation", "time_in_mins", function(frm, cdt, cdn) {
+ calculate_cost(frm.doc);
+ calculate_total_cost(frm)
+});
+
+var company_filter = function(doc) {
+ return{
+ filters: {
+ 'company': doc.company
+ }
+ }
+}
+
+cur_frm.fields_dict.fg_warehouse.get_query = company_filter
+cur_frm.fields_dict.wip_warehouse.get_query = company_filter
diff --git a/erpnext/manufacturing/doctype/production_order/production_order.py b/erpnext/manufacturing/doctype/production_order/production_order.py
index 67b53f6..3e9b710 100644
--- a/erpnext/manufacturing/doctype/production_order/production_order.py
+++ b/erpnext/manufacturing/doctype/production_order/production_order.py
@@ -210,7 +210,7 @@
d.status = "Completed"
else:
frappe.throw(_("Completed Qty can not be greater than 'Qty to Manufacture'"))
-
+
def set_actual_dates(self):
if self.get("operations"):
actual_date = frappe.db.sql("""select min(actual_start_time) as start_date, max(actual_end_time) as end_date from `tabProduction Order Operation`
@@ -220,13 +220,16 @@
else:
self.actual_start_date = None
self.actual_end_date = None
-
+
def validate_delivery_date(self):
- if self.planned_start_date and self.expected_delivery_date and getdate(self.expected_delivery_date) < getdate(self.planned_start_date):
- frappe.throw(_("Expected Delivery Date cannot be greater than Planned Start Date"))
-
- if self.planned_end_date and self.expected_delivery_date and getdate(self.expected_delivery_date) < getdate(self.planned_end_date):
- frappe.msgprint(_("Production might not be able to finish by the Expected Delivery Date."))
+ if self.docstatus==1:
+ if self.planned_start_date and self.expected_delivery_date \
+ and getdate(self.expected_delivery_date) < getdate(self.planned_start_date):
+ frappe.throw(_("Expected Delivery Date cannot be greater than Planned Start Date"))
+
+ if self.planned_end_date and self.expected_delivery_date \
+ and getdate(self.expected_delivery_date) < getdate(self.planned_end_date):
+ frappe.msgprint(_("Production might not be able to finish by the Expected Delivery Date."))
@frappe.whitelist()
def get_item_details(item):
@@ -254,7 +257,7 @@
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))
- if purpose=="Material Transfer":
+ if purpose=="Material Transfer for Manufacture":
stock_entry.to_warehouse = production_order.wip_warehouse
else:
stock_entry.from_warehouse = production_order.wip_warehouse
diff --git a/erpnext/manufacturing/doctype/production_order_operation/production_order_operation.json b/erpnext/manufacturing/doctype/production_order_operation/production_order_operation.json
index f495b0b..cd62ad2 100644
--- a/erpnext/manufacturing/doctype/production_order_operation/production_order_operation.json
+++ b/erpnext/manufacturing/doctype/production_order_operation/production_order_operation.json
@@ -117,7 +117,7 @@
"permlevel": 0,
"precision": "",
"print_hide": 0,
- "read_only": 1,
+ "read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
@@ -173,7 +173,7 @@
"permlevel": 0,
"precision": "",
"print_hide": 0,
- "read_only": 1,
+ "read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
@@ -282,7 +282,7 @@
"is_submittable": 0,
"issingle": 0,
"istable": 1,
- "modified": "2014-12-23 15:42:34.892964",
+ "modified": "2015-02-13 16:26:30.441657",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Production Order Operation",
diff --git a/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.py b/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.py
index ed4ee76..6eddb3f 100644
--- a/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.py
+++ b/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.py
@@ -3,7 +3,7 @@
from __future__ import unicode_literals
import frappe
-from frappe.utils import cstr, flt, cint, nowdate, add_days, comma_and
+from frappe.utils import cstr, flt, cint, nowdate, now, add_days, comma_and
from frappe import msgprint, _
@@ -218,6 +218,9 @@
for key in items:
pro = frappe.new_doc("Production Order")
pro.update(items[key])
+
+ pro.planned_start_date = now()
+ pro.set_production_order_operations()
frappe.flags.mute_messages = True
try:
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 5fcf8bf..6ea16db 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -113,3 +113,7 @@
erpnext.patches.v5_0.rename_customer_issue
erpnext.patches.v5_0.rename_total_fields
erpnext.patches.v5_0.replace_renamed_fields_in_custom_script_and_print_formats
+erpnext.patches.v5_0.new_crm_module
+erpnext.patches.v5_0.rename_customer_issue
+erpnext.patches.v5_0.update_material_transfer_for_manufacture
+erpnext.patches.v5_0.manufacturing_activity_type
diff --git a/erpnext/patches/v5_0/manufacturing_activity_type.py b/erpnext/patches/v5_0/manufacturing_activity_type.py
new file mode 100644
index 0000000..d4e7873
--- /dev/null
+++ b/erpnext/patches/v5_0/manufacturing_activity_type.py
@@ -0,0 +1,13 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+import frappe
+
+def execute():
+ if not frappe.db.exists('Activity Type','Manufacturing') {
+ doc = frappe.new_doc('Activity Type')
+ doc.update({
+ 'activity_type' : 'Manufacturing'
+ })
+ doc.save()
+ }
\ No newline at end of file
diff --git a/erpnext/patches/v5_0/new_crm_module.py b/erpnext/patches/v5_0/new_crm_module.py
new file mode 100644
index 0000000..ecf20e5
--- /dev/null
+++ b/erpnext/patches/v5_0/new_crm_module.py
@@ -0,0 +1,24 @@
+# Copyright (c) 2015, Web Notes Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+import json
+import frappe
+
+def execute():
+ frappe.reload_doc('crm', 'doctype', 'lead')
+ frappe.reload_doc('crm', 'doctype', 'opportunity')
+
+ add_crm_to_user_desktop_items()
+
+def add_crm_to_user_desktop_items():
+ key = "_user_desktop_items"
+ for user in frappe.get_all("User", filters={"enabled": 1, "user_type": "System User"}):
+ user = user.name
+ user_desktop_items = frappe.db.get_defaults(key, parent=user)
+ if user_desktop_items:
+ user_desktop_items = json.loads(user_desktop_items)
+ if "CRM" not in user_desktop_items:
+ user_desktop_items.append("CRM")
+ frappe.db.set_default(key, json.dumps(user_desktop_items), parent=user)
+
+
diff --git a/erpnext/patches/v5_0/update_material_transfer_for_manufacture.py b/erpnext/patches/v5_0/update_material_transfer_for_manufacture.py
new file mode 100644
index 0000000..d862ad3
--- /dev/null
+++ b/erpnext/patches/v5_0/update_material_transfer_for_manufacture.py
@@ -0,0 +1,5 @@
+import frappe
+
+def execute():
+ frappe.db.sql("""update `tabStock Entry` set purpose='Material Transfer for Manufacture'
+ where ifnull(production_order, '')!='' and purpose='Material Transfer'""")
diff --git a/erpnext/projects/doctype/activity_type/activity_type.py b/erpnext/projects/doctype/activity_type/activity_type.py
index a98d8cf..7e316b5 100644
--- a/erpnext/projects/doctype/activity_type/activity_type.py
+++ b/erpnext/projects/doctype/activity_type/activity_type.py
@@ -7,4 +7,10 @@
from frappe.model.document import Document
class ActivityType(Document):
- pass
\ No newline at end of file
+
+ def on_trash(self):
+ self.validate_manufacturing_type()
+
+ def validate_manufacturing_type(self):
+ if self.activity_type == 'Manufacturing':
+ frappe.throw(_("Activity Type 'Manufacturing' cannot be deleted."))
\ No newline at end of file
diff --git a/erpnext/public/js/account_tree_grid.js b/erpnext/public/js/account_tree_grid.js
index dde943a..729e88d 100644
--- a/erpnext/public/js/account_tree_grid.js
+++ b/erpnext/public/js/account_tree_grid.js
@@ -14,6 +14,8 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
+frappe.assets.views["Report"]();
+
erpnext.AccountTreeGrid = frappe.views.TreeGridReport.extend({
init: function(wrapper, title) {
this._super({
diff --git a/erpnext/public/js/stock_grid_report.js b/erpnext/public/js/stock_grid_report.js
index 726852f..e1f97f8 100644
--- a/erpnext/public/js/stock_grid_report.js
+++ b/erpnext/public/js/stock_grid_report.js
@@ -1,6 +1,8 @@
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
+frappe.assets.views["Report"]();
+
erpnext.StockGridReport = frappe.views.TreeGridReport.extend({
get_item_warehouse: function(warehouse, item) {
if(!this.item_warehouse[item]) this.item_warehouse[item] = {};
diff --git a/erpnext/selling/page/sales_analytics/sales_analytics.js b/erpnext/selling/page/sales_analytics/sales_analytics.js
index ad2102a..bbe69d7 100644
--- a/erpnext/selling/page/sales_analytics/sales_analytics.js
+++ b/erpnext/selling/page/sales_analytics/sales_analytics.js
@@ -12,7 +12,9 @@
frappe.add_breadcrumbs("Selling")
-}
+};
+
+frappe.assets.views["Report"]();
erpnext.SalesAnalytics = frappe.views.TreeGridReport.extend({
init: function(wrapper) {
diff --git a/erpnext/setup/page/setup_wizard/install_fixtures.py b/erpnext/setup/page/setup_wizard/install_fixtures.py
index 96ea0d9..b24eaa6 100644
--- a/erpnext/setup/page/setup_wizard/install_fixtures.py
+++ b/erpnext/setup/page/setup_wizard/install_fixtures.py
@@ -135,7 +135,7 @@
{'doctype': 'Activity Type', 'activity_type': _('Proposal Writing')},
{'doctype': 'Activity Type', 'activity_type': _('Execution')},
{'doctype': 'Activity Type', 'activity_type': _('Communication')},
- {'doctype': 'Activity Type', 'activity_type': _('Manufacturing')},
+ {'doctype': 'Activity Type', 'activity_type': 'Manufacturing'},
# Industry Type
{'doctype': 'Industry Type', 'industry': _('Accounting')},
diff --git a/erpnext/setup/page/setup_wizard/setup_wizard.js b/erpnext/setup/page/setup_wizard/setup_wizard.js
index a10dc80..2dc93e0 100644
--- a/erpnext/setup/page/setup_wizard/setup_wizard.js
+++ b/erpnext/setup/page/setup_wizard/setup_wizard.js
@@ -83,11 +83,11 @@
this.hide_current_slide();
this.current_slide = this.slide_dict[id];
- this.current_slide.$wrapper.toggle(true);
+ this.current_slide.$wrapper.removeClass("hidden");
},
hide_current_slide: function() {
if(this.current_slide) {
- this.current_slide.$wrapper.toggle(false);
+ this.current_slide.$wrapper.addClass("hidden");
this.current_slide = null;
}
},
@@ -103,7 +103,7 @@
erpnext.wiz.WizardSlide = Class.extend({
init: function(opts) {
$.extend(this, opts);
- this.$wrapper = $("<div>")
+ this.$wrapper = $('<div class="slide-wrapper hidden"></div>')
.appendTo(this.wiz.parent)
.attr("data-slide-id", this.id);
},
@@ -305,10 +305,11 @@
$timezone.empty();
// add country specific timezones first
- if(country){
+ if(country) {
var timezone_list = data.country_info[country].timezones || [];
$timezone.add_options(timezone_list.sort());
slide.get_field("currency").set_input(data.country_info[country].currency);
+ slide.get_field("currency").$input.trigger("change");
}
// add all timezones at the end, so that user has the option to change it to any timezone
@@ -320,6 +321,23 @@
frappe.boot.sysdefaults.date_format = (data.country_info[country].date_format
|| "dd-mm-yyyy");
});
+
+ slide.get_input("currency").on("change", function() {
+ var currency = slide.get_input("currency").val();
+ frappe.model.with_doc("Currency", currency, function() {
+ frappe.provide("locals.:Currency." + currency);
+ var currency_doc = frappe.model.get_doc("Currency", currency);
+ var number_format = currency_doc.number_format;
+ if (number_format==="#.###") {
+ number_format = "#.###,##";
+ } else if (number_format==="#,###") {
+ number_format = "#,###.##"
+ }
+
+ frappe.boot.sysdefaults.number_format = number_format;
+ locals[":Currency"][currency] = $.extend({}, currency_doc);
+ });
+ });
}
},
@@ -585,14 +603,7 @@
callback: function(r) {
wiz.show_complete();
setTimeout(function() {
- if(user==="Administrator") {
- msgprint(__("Login with your new User ID") + ": " + values.email);
- setTimeout(function() {
- frappe.app.logout();
- }, 2000);
- } else {
- window.location = "/desk";
- }
+ window.location = "/desk";
}, 2000);
},
error: function(r) {
diff --git a/erpnext/setup/page/setup_wizard/setup_wizard.py b/erpnext/setup/page/setup_wizard/setup_wizard.py
index 445aa3d..31dd03d 100644
--- a/erpnext/setup/page/setup_wizard/setup_wizard.py
+++ b/erpnext/setup/page/setup_wizard/setup_wizard.py
@@ -75,6 +75,8 @@
website_maker(args.company_name.strip(), args.company_tagline, args.name)
create_logo(args)
+ login_as_first_user(args)
+
frappe.clear_cache()
frappe.db.commit()
@@ -432,6 +434,11 @@
"is_group": "No"
}).insert()
+def login_as_first_user(args):
+ if args.get("email") and hasattr(frappe.local, "login_manager"):
+ frappe.local.login_manager.user = args.get("email")
+ frappe.local.login_manager.post_login()
+
@frappe.whitelist()
def load_messages(language):
frappe.clear_cache()
@@ -444,8 +451,6 @@
@frappe.whitelist()
def load_languages():
- from frappe.sessions import get_geo_from_ip
-
return {
"default_language": get_language_from_code(frappe.local.lang),
"languages": sorted(get_lang_dict().keys())
diff --git a/erpnext/stock/doctype/material_request/material_request_list.js b/erpnext/stock/doctype/material_request/material_request_list.js
index 173ed67..1b9bca3 100644
--- a/erpnext/stock/doctype/material_request/material_request_list.js
+++ b/erpnext/stock/doctype/material_request/material_request_list.js
@@ -1,12 +1,13 @@
frappe.listview_settings['Material Request'] = {
add_fields: ["material_request_type", "status", "per_ordered"],
- get_status: function(doc) {
+ get_indicator: function(doc) {
+ console.log()
if(doc.status=="Stopped") {
return [__("Stopped"), "red", "status,=,Stopped"];
- } if(doc.docstatus==1 && doc.per_ordered < 100) {
+ } else if(doc.docstatus==1 && doc.per_ordered < 100) {
return [__("Pending"), "orange", "per_ordered,<,100"];
- } else if(doc.status==1 && doc.per_ordered == 100) {
- return [__("Completed"), "green", "per_ordered,=,100"];
+ } else if(doc.docstatus==1 && doc.per_ordered == 100) {
+ return [__("Ordered"), "green", "per_ordered,=,100"];
}
}
};
diff --git a/erpnext/stock/doctype/price_list/price_list.json b/erpnext/stock/doctype/price_list/price_list.json
index 3349559..a944d20 100644
--- a/erpnext/stock/doctype/price_list/price_list.json
+++ b/erpnext/stock/doctype/price_list/price_list.json
@@ -27,6 +27,7 @@
"fieldname": "price_list_name",
"fieldtype": "Data",
"label": "Price List Name",
+ "no_copy": 1,
"oldfieldname": "price_list_name",
"oldfieldtype": "Data",
"permlevel": 0,
@@ -74,7 +75,7 @@
"icon": "icon-tags",
"idx": 1,
"max_attachments": 1,
- "modified": "2015-02-05 05:11:42.450750",
+ "modified": "2015-02-12 17:39:02.825767",
"modified_by": "Administrator",
"module": "Stock",
"name": "Price List",
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index bc1b94a..9395e84 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -286,7 +286,7 @@
if d.item_code in stock_items and flt(d.valuation_rate) and flt(d.qty):
if warehouse_account.get(d.warehouse):
- val_rate_db_precision = 6 if cint(self.precision("valuation_rate")) <= 6 else 9
+ val_rate_db_precision = 6 if cint(d.precision("valuation_rate")) <= 6 else 9
# warehouse account
gl_entries.append(self.get_gl_dict({
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js
index cc7169b..a77aa74 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.js
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.js
@@ -122,7 +122,7 @@
clean_up: function() {
// Clear Production Order record from locals, because it is updated via Stock Entry
if(this.frm.doc.production_order &&
- this.frm.doc.purpose == "Manufacture") {
+ in_list(["Manufacture", "Material Transfer for Manufacture"], this.frm.doc.purpose)) {
frappe.model.remove_from_locals("Production Order",
this.frm.doc.production_order);
}
@@ -164,7 +164,7 @@
},
toggle_enable_bom: function() {
- this.frm.toggle_enable("bom_no", this.frm.doc.purpose!="Manufacture");
+ this.frm.toggle_enable("bom_no", !in_list(["Manufacture", "Material Transfer for Manufacture"], this.frm.doc.purpose));
},
get_doctype_docname: function() {
@@ -233,8 +233,10 @@
if(!row.t_warehouse) row.t_warehouse = this.frm.doc.to_warehouse;
},
- source_mandatory: ["Material Issue", "Material Transfer", "Purchase Return", "Subcontract"],
- target_mandatory: ["Material Receipt", "Material Transfer", "Sales Return", "Subcontract"],
+ source_mandatory: ["Material Issue", "Material Transfer", "Purchase Return", "Subcontract",
+ "Material Transfer for Manufacture"],
+ target_mandatory: ["Material Receipt", "Material Transfer", "Sales Return", "Subcontract",
+ "Material Transfer for Manufacture"],
from_warehouse: function(doc) {
var me = this;
@@ -251,6 +253,7 @@
},
set_warehouse_if_missing: function(fieldname, value, condition) {
+ var changed = false;
for (var i=0, l=(this.frm.doc.items || []).length; i<l; i++) {
var row = this.frm.doc.items[i];
if (!row[fieldname]) {
@@ -259,8 +262,10 @@
}
frappe.model.set_value(row.doctype, row.name, fieldname, value, "Link");
+ changed = true;
}
}
+ refresh_field("items");
},
items_on_form_rendered: function(doc, grid_row) {
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.json b/erpnext/stock/doctype/stock_entry/stock_entry.json
index 7746d98..ac6e94b 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.json
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.json
@@ -1,674 +1,674 @@
{
- "allow_copy": 0,
- "allow_import": 1,
- "allow_rename": 0,
- "autoname": "naming_series:",
- "creation": "2013-04-09 11:43:55",
- "docstatus": 0,
- "doctype": "DocType",
+ "allow_copy": 0,
+ "allow_import": 1,
+ "allow_rename": 0,
+ "autoname": "naming_series:",
+ "creation": "2013-04-09 11:43:55",
+ "docstatus": 0,
+ "doctype": "DocType",
"fields": [
{
- "fieldname": "col1",
- "fieldtype": "Column Break",
- "oldfieldtype": "Column Break",
- "permlevel": 0,
- "print_width": "50%",
- "read_only": 0,
+ "fieldname": "col1",
+ "fieldtype": "Column Break",
+ "oldfieldtype": "Column Break",
+ "permlevel": 0,
+ "print_width": "50%",
+ "read_only": 0,
"width": "50%"
- },
+ },
{
- "allow_on_submit": 0,
- "fieldname": "naming_series",
- "fieldtype": "Select",
- "hidden": 0,
- "in_filter": 0,
- "label": "Series",
- "no_copy": 1,
- "oldfieldname": "naming_series",
- "oldfieldtype": "Select",
- "options": "STE-",
- "permlevel": 0,
- "print_hide": 1,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 1,
+ "allow_on_submit": 0,
+ "fieldname": "naming_series",
+ "fieldtype": "Select",
+ "hidden": 0,
+ "in_filter": 0,
+ "label": "Series",
+ "no_copy": 1,
+ "oldfieldname": "naming_series",
+ "oldfieldtype": "Select",
+ "options": "STE-",
+ "permlevel": 0,
+ "print_hide": 1,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 1,
"search_index": 0
- },
+ },
{
- "allow_on_submit": 0,
- "default": "Material Issue",
- "fieldname": "purpose",
- "fieldtype": "Select",
- "hidden": 0,
- "in_filter": 1,
- "in_list_view": 1,
- "label": "Purpose",
- "no_copy": 0,
- "oldfieldname": "purpose",
- "oldfieldtype": "Select",
- "options": "Material Issue\nMaterial Receipt\nMaterial Transfer\nManufacture\nRepack\nSubcontract\nSales Return\nPurchase Return",
- "permlevel": 0,
- "print_hide": 0,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 1,
+ "allow_on_submit": 0,
+ "default": "Material Issue",
+ "fieldname": "purpose",
+ "fieldtype": "Select",
+ "hidden": 0,
+ "in_filter": 1,
+ "in_list_view": 1,
+ "label": "Purpose",
+ "no_copy": 0,
+ "oldfieldname": "purpose",
+ "oldfieldtype": "Select",
+ "options": "Material Issue\nMaterial Receipt\nMaterial Transfer\nMaterial Transfer for Manufacture\nManufacture\nRepack\nSubcontract\nSales Return\nPurchase Return",
+ "permlevel": 0,
+ "print_hide": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 1,
"search_index": 0
- },
+ },
{
- "allow_on_submit": 0,
- "depends_on": "eval:doc.purpose==\"Sales Return\"",
- "fieldname": "delivery_note_no",
- "fieldtype": "Link",
- "hidden": 0,
- "in_filter": 0,
- "label": "Delivery Note No",
- "no_copy": 1,
- "oldfieldname": "delivery_note_no",
- "oldfieldtype": "Link",
- "options": "Delivery Note",
- "permlevel": 0,
- "print_hide": 1,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
+ "allow_on_submit": 0,
+ "depends_on": "eval:in_list([\"Material Transfer for Manufacture\", \"Manufacture\"], doc.purpose)",
+ "fieldname": "production_order",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "in_filter": 1,
+ "label": "Production Order",
+ "no_copy": 0,
+ "oldfieldname": "production_order",
+ "oldfieldtype": "Link",
+ "options": "Production Order",
+ "permlevel": 0,
+ "print_hide": 1,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
"search_index": 1
- },
+ },
{
- "depends_on": "eval:doc.purpose==\"Sales Return\"",
- "fieldname": "sales_invoice_no",
- "fieldtype": "Link",
- "hidden": 0,
- "label": "Sales Invoice No",
- "no_copy": 1,
- "options": "Sales Invoice",
- "permlevel": 0,
- "print_hide": 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==\"Purchase Return\"",
- "fieldname": "purchase_receipt_no",
- "fieldtype": "Link",
- "hidden": 0,
- "in_filter": 0,
- "label": "Purchase Receipt No",
- "no_copy": 1,
- "oldfieldname": "purchase_receipt_no",
- "oldfieldtype": "Link",
- "options": "Purchase Receipt",
- "permlevel": 0,
- "print_hide": 1,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
+ "allow_on_submit": 0,
+ "depends_on": "eval:doc.purpose==\"Sales Return\"",
+ "fieldname": "delivery_note_no",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "in_filter": 0,
+ "label": "Delivery Note No",
+ "no_copy": 1,
+ "oldfieldname": "delivery_note_no",
+ "oldfieldtype": "Link",
+ "options": "Delivery Note",
+ "permlevel": 0,
+ "print_hide": 1,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
"search_index": 1
- },
+ },
{
- "fieldname": "col2",
- "fieldtype": "Column Break",
- "oldfieldtype": "Column Break",
- "permlevel": 0,
- "print_width": "50%",
- "read_only": 0,
+ "depends_on": "eval:doc.purpose==\"Sales Return\"",
+ "fieldname": "sales_invoice_no",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "label": "Sales Invoice No",
+ "no_copy": 1,
+ "options": "Sales Invoice",
+ "permlevel": 0,
+ "print_hide": 1,
+ "read_only": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "depends_on": "eval:doc.purpose==\"Purchase Return\"",
+ "fieldname": "purchase_receipt_no",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "in_filter": 0,
+ "label": "Purchase Receipt No",
+ "no_copy": 1,
+ "oldfieldname": "purchase_receipt_no",
+ "oldfieldtype": "Link",
+ "options": "Purchase Receipt",
+ "permlevel": 0,
+ "print_hide": 1,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 1
+ },
+ {
+ "fieldname": "col2",
+ "fieldtype": "Column Break",
+ "oldfieldtype": "Column Break",
+ "permlevel": 0,
+ "print_width": "50%",
+ "read_only": 0,
"width": "50%"
- },
+ },
{
- "allow_on_submit": 0,
- "default": "Today",
- "fieldname": "posting_date",
- "fieldtype": "Date",
- "hidden": 0,
- "in_filter": 1,
- "in_list_view": 0,
- "label": "Posting Date",
- "no_copy": 1,
- "oldfieldname": "posting_date",
- "oldfieldtype": "Date",
- "permlevel": 0,
- "print_hide": 0,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 1,
+ "allow_on_submit": 0,
+ "default": "Today",
+ "fieldname": "posting_date",
+ "fieldtype": "Date",
+ "hidden": 0,
+ "in_filter": 1,
+ "in_list_view": 0,
+ "label": "Posting Date",
+ "no_copy": 1,
+ "oldfieldname": "posting_date",
+ "oldfieldtype": "Date",
+ "permlevel": 0,
+ "print_hide": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 1,
"search_index": 1
- },
+ },
{
- "allow_on_submit": 0,
- "fieldname": "posting_time",
- "fieldtype": "Time",
- "hidden": 0,
- "in_filter": 0,
- "label": "Posting Time",
- "no_copy": 1,
- "oldfieldname": "posting_time",
- "oldfieldtype": "Time",
- "permlevel": 0,
- "print_hide": 1,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 1,
+ "allow_on_submit": 0,
+ "fieldname": "posting_time",
+ "fieldtype": "Time",
+ "hidden": 0,
+ "in_filter": 0,
+ "label": "Posting Time",
+ "no_copy": 1,
+ "oldfieldname": "posting_time",
+ "oldfieldtype": "Time",
+ "permlevel": 0,
+ "print_hide": 1,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 1,
"search_index": 0
- },
+ },
{
- "fieldname": "items_section",
- "fieldtype": "Section Break",
- "label": "Items",
- "oldfieldtype": "Section Break",
- "permlevel": 0,
+ "fieldname": "items_section",
+ "fieldtype": "Section Break",
+ "label": "Items",
+ "oldfieldtype": "Section Break",
+ "permlevel": 0,
"read_only": 0
- },
+ },
{
- "allow_on_submit": 0,
- "fieldname": "from_warehouse",
- "fieldtype": "Link",
- "hidden": 0,
- "in_filter": 0,
- "in_list_view": 1,
- "label": "Default Source Warehouse",
- "no_copy": 1,
- "oldfieldname": "from_warehouse",
- "oldfieldtype": "Link",
- "options": "Warehouse",
- "permlevel": 0,
- "print_hide": 1,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
+ "allow_on_submit": 0,
+ "fieldname": "from_warehouse",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "label": "Default Source Warehouse",
+ "no_copy": 1,
+ "oldfieldname": "from_warehouse",
+ "oldfieldtype": "Link",
+ "options": "Warehouse",
+ "permlevel": 0,
+ "print_hide": 1,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
"search_index": 0
- },
+ },
{
- "fieldname": "cb0",
- "fieldtype": "Column Break",
- "permlevel": 0,
+ "fieldname": "cb0",
+ "fieldtype": "Column Break",
+ "permlevel": 0,
"read_only": 0
- },
+ },
{
- "allow_on_submit": 0,
- "fieldname": "to_warehouse",
- "fieldtype": "Link",
- "hidden": 0,
- "in_filter": 0,
- "in_list_view": 1,
- "label": "Default Target Warehouse",
- "no_copy": 1,
- "oldfieldname": "to_warehouse",
- "oldfieldtype": "Link",
- "options": "Warehouse",
- "permlevel": 0,
- "print_hide": 1,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
+ "allow_on_submit": 0,
+ "fieldname": "to_warehouse",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "label": "Default Target Warehouse",
+ "no_copy": 1,
+ "oldfieldname": "to_warehouse",
+ "oldfieldtype": "Link",
+ "options": "Warehouse",
+ "permlevel": 0,
+ "print_hide": 1,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
"search_index": 0
- },
+ },
{
- "fieldname": "sb0",
- "fieldtype": "Section Break",
- "options": "Simple",
- "permlevel": 0,
+ "fieldname": "sb0",
+ "fieldtype": "Section Break",
+ "options": "Simple",
+ "permlevel": 0,
"read_only": 0
- },
+ },
{
- "allow_on_submit": 0,
- "fieldname": "items",
- "fieldtype": "Table",
- "hidden": 0,
- "in_filter": 0,
- "label": "Items",
- "no_copy": 0,
- "oldfieldname": "mtn_details",
- "oldfieldtype": "Table",
- "options": "Stock Entry Detail",
- "permlevel": 0,
- "print_hide": 0,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
+ "allow_on_submit": 0,
+ "fieldname": "items",
+ "fieldtype": "Table",
+ "hidden": 0,
+ "in_filter": 0,
+ "label": "Items",
+ "no_copy": 0,
+ "oldfieldname": "mtn_details",
+ "oldfieldtype": "Table",
+ "options": "Stock Entry Detail",
+ "permlevel": 0,
+ "print_hide": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
"search_index": 0
- },
+ },
{
- "description": "Get valuation rate and available stock at source/target warehouse on mentioned posting date-time. If serialized item, please press this button after entering serial nos.",
- "fieldname": "get_stock_and_rate",
- "fieldtype": "Button",
- "label": "Get Stock and Rate",
- "oldfieldtype": "Button",
- "options": "get_stock_and_rate",
- "permlevel": 0,
- "print_hide": 1,
+ "description": "Get valuation rate and available stock at source/target warehouse on mentioned posting date-time. If serialized item, please press this button after entering serial nos.",
+ "fieldname": "get_stock_and_rate",
+ "fieldtype": "Button",
+ "label": "Get Stock and Rate",
+ "oldfieldtype": "Button",
+ "options": "get_stock_and_rate",
+ "permlevel": 0,
+ "print_hide": 1,
"read_only": 0
- },
+ },
{
- "depends_on": "eval:(doc.purpose!==\"Sales Return\" && doc.purpose!==\"Purchase Return\")",
- "fieldname": "sb1",
- "fieldtype": "Section Break",
- "label": "From Bill of Materials",
- "permlevel": 0,
+ "depends_on": "eval:(doc.purpose!==\"Sales Return\" && doc.purpose!==\"Purchase Return\")",
+ "fieldname": "sb1",
+ "fieldtype": "Section Break",
+ "label": "From Bill of Materials",
+ "permlevel": 0,
"read_only": 0
- },
+ },
{
- "allow_on_submit": 0,
- "depends_on": "eval:inList([\"Material Transfer\", \"Manufacture\"], doc.purpose)",
- "fieldname": "production_order",
- "fieldtype": "Link",
- "hidden": 0,
- "in_filter": 1,
- "label": "Production Order",
- "no_copy": 0,
- "oldfieldname": "production_order",
- "oldfieldtype": "Link",
- "options": "Production Order",
- "permlevel": 0,
- "print_hide": 1,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
- "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:!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,
+ "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,
+ "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,
+ "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,
+ "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,
+ "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": "fold",
- "fieldtype": "Fold",
+ "fieldname": "fold",
+ "fieldtype": "Fold",
"permlevel": 0
- },
+ },
{
- "depends_on": "eval:(doc.purpose==\"Sales Return\" || doc.purpose==\"Purchase Return\")",
- "fieldname": "contact_section",
- "fieldtype": "Section Break",
- "label": "Contact Info",
- "permlevel": 0,
+ "depends_on": "eval:(doc.purpose==\"Sales Return\" || doc.purpose==\"Purchase Return\")",
+ "fieldname": "contact_section",
+ "fieldtype": "Section Break",
+ "label": "Contact Info",
+ "permlevel": 0,
"read_only": 0
- },
+ },
{
- "allow_on_submit": 0,
- "depends_on": "eval:doc.purpose==\"Purchase Return\"",
- "fieldname": "supplier",
- "fieldtype": "Link",
- "hidden": 0,
- "in_filter": 0,
- "label": "Supplier",
- "no_copy": 1,
- "oldfieldname": "supplier",
- "oldfieldtype": "Link",
- "options": "Supplier",
- "permlevel": 0,
- "print_hide": 1,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
+ "allow_on_submit": 0,
+ "depends_on": "eval:doc.purpose==\"Purchase Return\"",
+ "fieldname": "supplier",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "in_filter": 0,
+ "label": "Supplier",
+ "no_copy": 1,
+ "oldfieldname": "supplier",
+ "oldfieldtype": "Link",
+ "options": "Supplier",
+ "permlevel": 0,
+ "print_hide": 1,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
"search_index": 0
- },
+ },
{
- "allow_on_submit": 0,
- "depends_on": "eval:doc.purpose==\"Purchase Return\"",
- "fieldname": "supplier_name",
- "fieldtype": "Data",
- "hidden": 0,
- "in_filter": 0,
- "label": "Supplier Name",
- "no_copy": 1,
- "oldfieldname": "supplier_name",
- "oldfieldtype": "Data",
- "permlevel": 0,
- "print_hide": 0,
- "read_only": 1,
- "report_hide": 0,
- "reqd": 0,
+ "allow_on_submit": 0,
+ "depends_on": "eval:doc.purpose==\"Purchase Return\"",
+ "fieldname": "supplier_name",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "in_filter": 0,
+ "label": "Supplier Name",
+ "no_copy": 1,
+ "oldfieldname": "supplier_name",
+ "oldfieldtype": "Data",
+ "permlevel": 0,
+ "print_hide": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
"search_index": 0
- },
+ },
{
- "allow_on_submit": 0,
- "depends_on": "eval:doc.purpose==\"Purchase Return\"",
- "fieldname": "supplier_address",
- "fieldtype": "Small Text",
- "hidden": 0,
- "in_filter": 0,
- "label": "Supplier Address",
- "no_copy": 1,
- "oldfieldname": "supplier_address",
- "oldfieldtype": "Small Text",
- "permlevel": 0,
- "print_hide": 0,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
+ "allow_on_submit": 0,
+ "depends_on": "eval:doc.purpose==\"Purchase Return\"",
+ "fieldname": "supplier_address",
+ "fieldtype": "Small Text",
+ "hidden": 0,
+ "in_filter": 0,
+ "label": "Supplier Address",
+ "no_copy": 1,
+ "oldfieldname": "supplier_address",
+ "oldfieldtype": "Small Text",
+ "permlevel": 0,
+ "print_hide": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
"search_index": 0
- },
+ },
{
- "allow_on_submit": 0,
- "depends_on": "eval:doc.purpose==\"Sales Return\"",
- "fieldname": "customer",
- "fieldtype": "Link",
- "hidden": 0,
- "in_filter": 0,
- "label": "Customer",
- "no_copy": 1,
- "oldfieldname": "customer",
- "oldfieldtype": "Link",
- "options": "Customer",
- "permlevel": 0,
- "print_hide": 1,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
+ "allow_on_submit": 0,
+ "depends_on": "eval:doc.purpose==\"Sales Return\"",
+ "fieldname": "customer",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "in_filter": 0,
+ "label": "Customer",
+ "no_copy": 1,
+ "oldfieldname": "customer",
+ "oldfieldtype": "Link",
+ "options": "Customer",
+ "permlevel": 0,
+ "print_hide": 1,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
"search_index": 0
- },
+ },
{
- "allow_on_submit": 0,
- "depends_on": "eval:doc.purpose==\"Sales Return\"",
- "fieldname": "customer_name",
- "fieldtype": "Data",
- "hidden": 0,
- "in_filter": 0,
- "label": "Customer Name",
- "no_copy": 1,
- "oldfieldname": "customer_name",
- "oldfieldtype": "Data",
- "permlevel": 0,
- "print_hide": 0,
- "read_only": 1,
- "report_hide": 0,
- "reqd": 0,
+ "allow_on_submit": 0,
+ "depends_on": "eval:doc.purpose==\"Sales Return\"",
+ "fieldname": "customer_name",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "in_filter": 0,
+ "label": "Customer Name",
+ "no_copy": 1,
+ "oldfieldname": "customer_name",
+ "oldfieldtype": "Data",
+ "permlevel": 0,
+ "print_hide": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
"search_index": 0
- },
+ },
{
- "allow_on_submit": 0,
- "depends_on": "eval:doc.purpose==\"Sales Return\"",
- "fieldname": "customer_address",
- "fieldtype": "Small Text",
- "hidden": 0,
- "in_filter": 0,
- "label": "Customer Address",
- "no_copy": 1,
- "oldfieldname": "customer_address",
- "oldfieldtype": "Small Text",
- "permlevel": 0,
- "print_hide": 0,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
+ "allow_on_submit": 0,
+ "depends_on": "eval:doc.purpose==\"Sales Return\"",
+ "fieldname": "customer_address",
+ "fieldtype": "Small Text",
+ "hidden": 0,
+ "in_filter": 0,
+ "label": "Customer Address",
+ "no_copy": 1,
+ "oldfieldname": "customer_address",
+ "oldfieldtype": "Small Text",
+ "permlevel": 0,
+ "print_hide": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
"search_index": 0
- },
+ },
{
- "fieldname": "more_info",
- "fieldtype": "Section Break",
- "label": "More Info",
- "oldfieldtype": "Section Break",
- "permlevel": 0,
+ "fieldname": "more_info",
+ "fieldtype": "Section Break",
+ "label": "More Info",
+ "oldfieldtype": "Section Break",
+ "permlevel": 0,
"read_only": 0
- },
+ },
{
- "fieldname": "project_name",
- "fieldtype": "Link",
- "in_filter": 1,
- "label": "Project Name",
- "oldfieldname": "project_name",
- "oldfieldtype": "Link",
- "options": "Project",
- "permlevel": 0,
+ "fieldname": "project_name",
+ "fieldtype": "Link",
+ "in_filter": 1,
+ "label": "Project Name",
+ "oldfieldname": "project_name",
+ "oldfieldtype": "Link",
+ "options": "Project",
+ "permlevel": 0,
"read_only": 0
- },
+ },
{
- "allow_on_submit": 0,
- "fieldname": "remarks",
- "fieldtype": "Text",
- "hidden": 0,
- "in_filter": 0,
- "label": "Remarks",
- "no_copy": 1,
- "oldfieldname": "remarks",
- "oldfieldtype": "Text",
- "permlevel": 0,
- "print_hide": 1,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
+ "allow_on_submit": 0,
+ "fieldname": "remarks",
+ "fieldtype": "Text",
+ "hidden": 0,
+ "in_filter": 0,
+ "label": "Remarks",
+ "no_copy": 1,
+ "oldfieldname": "remarks",
+ "oldfieldtype": "Text",
+ "permlevel": 0,
+ "print_hide": 1,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
"search_index": 0
- },
+ },
{
- "fieldname": "col5",
- "fieldtype": "Column Break",
- "permlevel": 0,
- "print_width": "50%",
- "read_only": 0,
+ "fieldname": "col5",
+ "fieldtype": "Column Break",
+ "permlevel": 0,
+ "print_width": "50%",
+ "read_only": 0,
"width": "50%"
- },
+ },
{
- "fieldname": "total_amount",
- "fieldtype": "Currency",
- "label": "Total Amount",
- "options": "Company:company:default_currency",
- "permlevel": 0,
+ "fieldname": "total_amount",
+ "fieldtype": "Currency",
+ "label": "Total Amount",
+ "options": "Company:company:default_currency",
+ "permlevel": 0,
"read_only": 1
- },
+ },
{
- "allow_on_submit": 0,
- "fieldname": "company",
- "fieldtype": "Link",
- "hidden": 0,
- "in_filter": 1,
- "label": "Company",
- "no_copy": 0,
- "oldfieldname": "company",
- "oldfieldtype": "Link",
- "options": "Company",
- "permlevel": 0,
- "print_hide": 1,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 1,
+ "allow_on_submit": 0,
+ "fieldname": "company",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "in_filter": 1,
+ "label": "Company",
+ "no_copy": 0,
+ "oldfieldname": "company",
+ "oldfieldtype": "Link",
+ "options": "Company",
+ "permlevel": 0,
+ "print_hide": 1,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 1,
"search_index": 0
- },
+ },
{
- "fieldname": "fiscal_year",
- "fieldtype": "Link",
- "in_filter": 0,
- "label": "Fiscal Year",
- "options": "Fiscal Year",
- "permlevel": 0,
- "print_hide": 1,
- "read_only": 0,
+ "fieldname": "fiscal_year",
+ "fieldtype": "Link",
+ "in_filter": 0,
+ "label": "Fiscal Year",
+ "options": "Fiscal Year",
+ "permlevel": 0,
+ "print_hide": 1,
+ "read_only": 0,
"reqd": 1
- },
+ },
{
- "allow_on_submit": 1,
- "fieldname": "select_print_heading",
- "fieldtype": "Link",
- "hidden": 0,
- "in_filter": 0,
- "label": "Print Heading",
- "no_copy": 0,
- "oldfieldname": "select_print_heading",
- "oldfieldtype": "Link",
- "options": "Print Heading",
- "permlevel": 0,
- "print_hide": 0,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
+ "allow_on_submit": 1,
+ "fieldname": "select_print_heading",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "in_filter": 0,
+ "label": "Print Heading",
+ "no_copy": 0,
+ "oldfieldname": "select_print_heading",
+ "oldfieldtype": "Link",
+ "options": "Print Heading",
+ "permlevel": 0,
+ "print_hide": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
"search_index": 0
- },
+ },
{
- "allow_on_submit": 0,
- "fieldname": "amended_from",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 1,
- "in_filter": 0,
- "label": "Amended From",
- "no_copy": 1,
- "oldfieldname": "amended_from",
- "oldfieldtype": "Link",
- "options": "Stock Entry",
- "permlevel": 0,
- "print_hide": 1,
- "read_only": 1,
- "report_hide": 0,
- "reqd": 0,
+ "allow_on_submit": 0,
+ "fieldname": "amended_from",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 1,
+ "in_filter": 0,
+ "label": "Amended From",
+ "no_copy": 1,
+ "oldfieldname": "amended_from",
+ "oldfieldtype": "Link",
+ "options": "Stock Entry",
+ "permlevel": 0,
+ "print_hide": 1,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
"search_index": 0
- },
+ },
{
- "fieldname": "credit_note",
- "fieldtype": "Link",
- "hidden": 1,
- "label": "Credit Note",
- "options": "Journal Entry",
- "permlevel": 0,
+ "fieldname": "credit_note",
+ "fieldtype": "Link",
+ "hidden": 1,
+ "label": "Credit Note",
+ "options": "Journal Entry",
+ "permlevel": 0,
"precision": ""
}
- ],
- "hide_heading": 0,
- "hide_toolbar": 0,
- "icon": "icon-file-text",
- "idx": 1,
- "in_create": 0,
- "in_dialog": 0,
- "is_submittable": 1,
- "issingle": 0,
- "max_attachments": 0,
- "modified": "2015-02-05 05:11:46.986620",
- "modified_by": "Administrator",
- "module": "Stock",
- "name": "Stock Entry",
- "owner": "Administrator",
+ ],
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "icon": "icon-file-text",
+ "idx": 1,
+ "in_create": 0,
+ "in_dialog": 0,
+ "is_submittable": 1,
+ "issingle": 0,
+ "max_attachments": 0,
+ "modified": "2015-02-17 00:49:04.294855",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Stock Entry",
+ "owner": "Administrator",
"permissions": [
{
- "amend": 1,
- "apply_user_permissions": 1,
- "cancel": 1,
- "create": 1,
- "delete": 1,
- "email": 1,
- "permlevel": 0,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Material User",
- "share": 1,
- "submit": 1,
+ "amend": 1,
+ "apply_user_permissions": 1,
+ "cancel": 1,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Material User",
+ "share": 1,
+ "submit": 1,
"write": 1
- },
+ },
{
- "amend": 1,
- "apply_user_permissions": 1,
- "cancel": 1,
- "create": 1,
- "delete": 1,
- "email": 1,
- "permlevel": 0,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Manufacturing User",
- "share": 1,
- "submit": 1,
+ "amend": 1,
+ "apply_user_permissions": 1,
+ "cancel": 1,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Manufacturing User",
+ "share": 1,
+ "submit": 1,
"write": 1
- },
+ },
{
- "amend": 1,
- "cancel": 1,
- "create": 1,
- "delete": 1,
- "email": 1,
- "permlevel": 0,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Manufacturing Manager",
- "share": 1,
- "submit": 1,
+ "amend": 1,
+ "cancel": 1,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Manufacturing Manager",
+ "share": 1,
+ "submit": 1,
"write": 1
- },
+ },
{
- "amend": 1,
- "cancel": 1,
- "create": 1,
- "delete": 1,
- "email": 1,
- "permlevel": 0,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Material Manager",
- "share": 1,
- "submit": 1,
+ "amend": 1,
+ "cancel": 1,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Material Manager",
+ "share": 1,
+ "submit": 1,
"write": 1
}
- ],
- "read_only": 0,
- "read_only_onload": 0,
- "search_fields": "posting_date, from_warehouse, to_warehouse, purpose, remarks",
- "sort_field": "modified",
- "sort_order": "DESC",
+ ],
+ "read_only": 0,
+ "read_only_onload": 0,
+ "search_fields": "posting_date, from_warehouse, to_warehouse, purpose, remarks",
+ "sort_field": "modified",
+ "sort_order": "DESC",
"title_field": "purpose"
-}
+}
\ No newline at end of file
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 4faa644..ab90170 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -9,7 +9,7 @@
from frappe import _
from erpnext.stock.utils import get_incoming_rate
-from erpnext.stock.stock_ledger import get_previous_sle
+from erpnext.stock.stock_ledger import get_previous_sle, NegativeStockError
from erpnext.controllers.queries import get_match_cond
from erpnext.stock.get_item_details import get_available_qty, get_default_cost_center, get_conversion_factor
from erpnext.manufacturing.doctype.bom.bom import validate_bom_no
@@ -82,7 +82,7 @@
self.meta.get_label("posting_date"))
def validate_purpose(self):
- valid_purposes = ["Material Issue", "Material Receipt", "Material Transfer",
+ valid_purposes = ["Material Issue", "Material Receipt", "Material Transfer", "Material Transfer for Manufacture",
"Manufacture", "Repack", "Subcontract", "Sales Return", "Purchase Return"]
if self.purpose not in valid_purposes:
frappe.throw(_("Purpose must be one of {0}").format(comma_or(valid_purposes)))
@@ -112,7 +112,7 @@
if not item.transfer_qty:
item.transfer_qty = item.qty * item.conversion_factor
- if (self.purpose in ("Material Transfer", "Sales Return", "Purchase Return")
+ if (self.purpose in ("Material Transfer", "Sales Return", "Purchase Return", "Material Transfer for Manufacture")
and not item.serial_no
and item.item_code in serialized_items):
frappe.throw(_("Row #{0}: Please specify Serial No for Item {1}").format(item.idx, item.item_code),
@@ -121,8 +121,8 @@
def validate_warehouse(self, pro_obj):
"""perform various (sometimes conditional) validations on warehouse"""
- source_mandatory = ["Material Issue", "Material Transfer", "Purchase Return", "Subcontract"]
- target_mandatory = ["Material Receipt", "Material Transfer", "Sales Return", "Subcontract"]
+ source_mandatory = ["Material Issue", "Material Transfer", "Purchase Return", "Subcontract", "Material Transfer for Manufacture"]
+ target_mandatory = ["Material Receipt", "Material Transfer", "Sales Return", "Subcontract", "Material Transfer for Manufacture"]
validate_for_manufacture_repack = any([d.bom_no for d in self.get("items")])
@@ -169,13 +169,14 @@
frappe.throw(_("Source and target warehouse cannot be same for row {0}").format(d.idx))
def validate_production_order(self):
- if self.purpose == "Manufacture":
+ if self.purpose in ("Manufacture", "Material Transfer for Manufacture"):
# check if production order is entered
if not self.production_order:
frappe.throw(_("Production order number is mandatory for stock entry purpose manufacture"))
# check for double entry
- self.check_if_operations_completed()
- self.check_duplicate_entry_for_production_order()
+ if self.purpose=="Manufacture":
+ self.check_if_operations_completed()
+ self.check_duplicate_entry_for_production_order()
elif self.purpose != "Material Transfer":
self.production_order = None
@@ -254,7 +255,7 @@
if d.docstatus==1 and d.s_warehouse and not allow_negative_stock and d.actual_qty < d.transfer_qty:
frappe.throw(_("""Row {0}: Qty not avalable in warehouse {1} on {2} {3}.
Available Qty: {4}, Transfer Qty: {5}""").format(d.idx, d.s_warehouse,
- self.posting_date, self.posting_time, d.actual_qty, d.transfer_qty))
+ self.posting_date, self.posting_time, d.actual_qty, d.transfer_qty), NegativeStockError)
# get incoming rate
if not flt(d.incoming_rate) or d.s_warehouse or self.purpose == "Sales Return" or force:
@@ -285,7 +286,7 @@
if flt(d.completed_qty):
operation_cost_per_unit += flt(d.actual_operating_cost) / flt(d.completed_qty)
else:
- operation_cost_per_unit += flt(d.planned_operating_cost) / flt(self.qty)
+ operation_cost_per_unit += flt(d.planned_operating_cost) / flt(pro_order.qty)
if not operation_cost_per_unit and bom_no:
bom = frappe.db.get_value("BOM", bom_no, ["operating_cost", "quantity"], as_dict=1)
@@ -503,8 +504,8 @@
if self.bom_no:
if self.purpose in ["Material Issue", "Material Transfer", "Manufacture", "Repack",
- "Subcontract"]:
- if self.production_order and self.purpose == "Material Transfer":
+ "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:
for item in item_dict.values():
@@ -601,7 +602,7 @@
result = frappe.db.sql("""select t1.item_code, sum(t1.qty)
from `tabStock Entry Detail` t1, `tabStock Entry` t2
where t1.parent = t2.name and t2.production_order = %s and t2.docstatus = 1
- and t2.purpose = 'Material Transfer'
+ and t2.purpose = 'Material Transfer for Manufacture'
group by t1.item_code""", self.production_order)
for t in result:
issued_item_qty[t[0]] = flt(t[1])
@@ -662,7 +663,7 @@
def get_production_order_details(production_order):
res = frappe.db.sql("""select bom_no, use_multi_level_bom, wip_warehouse,
ifnull(qty, 0) - ifnull(produced_qty, 0) as fg_completed_qty,
- (infull(additional_operating_cost, 0) / qty)*(ifnull(qty, 0) - ifnull(produced_qty, 0)) as additional_operating_cost
+ (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 {}
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js
index 27ed872..535a548 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js
@@ -5,6 +5,26 @@
frappe.require("assets/erpnext/js/utils.js");
frappe.provide("erpnext.stock");
+frappe.ui.form.on("Stock Reconciliation", "get_items", function(frm) {
+ frappe.prompt({label:"Warehouse", fieldtype:"Link", options:"Warehouse", reqd: 1},
+ function(data) {
+ frappe.call({
+ method:"erpnext.stock.doctype.stock_reconciliation.stock_reconciliation.get_items",
+ args: {warehouse: data.warehouse},
+ callback: function(r) {
+ var items = [];
+ frm.clear_table("items");
+ for(var i=0; i< r.message.length; i++) {
+ var d = frm.add_child("items");
+ $.extend(d, r.message[i]);
+ }
+ frm.refresh_field("items");
+ }
+ });
+ }
+ , __("Get Items"), __("Update"));
+});
+
erpnext.stock.StockReconciliation = erpnext.stock.StockController.extend({
onload: function() {
this.set_default_expense_account();
@@ -31,6 +51,8 @@
setup: function() {
var me = this;
+ this.frm.get_docfield("items").allow_bulk_edit = 1;
+
if (sys_defaults.auto_accounting_for_stock) {
this.frm.add_fetch("company", "stock_adjustment_account", "expense_account");
this.frm.add_fetch("company", "cost_center", "cost_center");
@@ -55,108 +77,12 @@
},
refresh: function() {
- if(this.frm.doc.docstatus===0) {
- this.show_download_template();
- this.show_upload();
- if(this.frm.doc.reconciliation_json) {
- this.frm.set_intro(__("You can submit this Stock Reconciliation."));
- } else {
- this.frm.set_intro(__("Download the Template, fill appropriate data and attach the modified file."));
- }
- } else if(this.frm.doc.docstatus == 1) {
- this.frm.set_intro(__("Cancelling this Stock Reconciliation will nullify its effect."));
+ if(this.frm.doc.docstatus==1) {
this.show_stock_ledger();
this.show_general_ledger();
- } else {
- this.frm.set_intro("");
- }
- this.show_reconciliation_data();
- this.show_download_reconciliation_data();
- },
-
- show_download_template: function() {
- var me = this;
- this.frm.add_custom_button(__("Download Template"), function() {
- this.title = __("Stock Reconcilation Template");
- frappe.tools.downloadify([[__("Stock Reconciliation")],
- ["----"],
- [__("Stock Reconciliation can be used to update the stock on a particular date, usually as per physical inventory.")],
- [__("When submitted, the system creates difference entries to set the given stock and valuation on this date.")],
- [__("It can also be used to create opening stock entries and to fix stock value.")],
- ["----"],
- [__("Notes:")],
- [__("Item Code and Warehouse should already exist.")],
- [__("You can update either Quantity or Valuation Rate or both.")],
- [__("If no change in either Quantity or Valuation Rate, leave the cell blank.")],
- ["----"],
- ["Item Code", "Warehouse", "Quantity", "Valuation Rate"]], null, this);
- return false;
- }, "icon-download");
- },
-
- show_upload: function() {
- var me = this;
- var $wrapper = $(cur_frm.fields_dict.upload_html.wrapper).empty();
-
- // upload
- frappe.upload.make({
- parent: $wrapper,
- args: {
- method: 'erpnext.stock.doctype.stock_reconciliation.stock_reconciliation.upload'
- },
- sample_url: "e.g. http://example.com/somefile.csv",
- callback: function(attachment, r) {
- me.frm.set_value("reconciliation_json", JSON.stringify(r.message));
- me.show_reconciliation_data();
- me.frm.save();
- }
- });
-
- // rename button
- $wrapper.find('form input[type="submit"]')
- .attr('value', 'Upload')
-
- },
-
- show_download_reconciliation_data: function() {
- var me = this;
- if(this.frm.doc.reconciliation_json) {
- this.frm.add_custom_button(__("Download Reconcilation Data"), function() {
- this.title = __("Stock Reconcilation Data");
- frappe.tools.downloadify(JSON.parse(me.frm.doc.reconciliation_json), null, this);
- return false;
- }, "icon-download", "btn-default");
}
},
- show_reconciliation_data: function() {
- var $wrapper = $(cur_frm.fields_dict.reconciliation_html.wrapper).empty();
- if(this.frm.doc.reconciliation_json) {
- var reconciliation_data = JSON.parse(this.frm.doc.reconciliation_json);
-
- var _make = function(data, header) {
- var result = "";
-
- var _render = header
- ? function(col) { return "<th>" + col + "</th>"; }
- : function(col) { return "<td>" + col + "</td>"; };
-
- $.each(data, function(i, row) {
- result += "<tr>"
- + $.map(row, _render).join("")
- + "</tr>";
- });
- return result;
- };
-
- var $reconciliation_table = $("<div style='overflow-x: auto;'>\
- <table class='table table-striped table-bordered'>\
- <thead>" + _make([reconciliation_data[0]], true) + "</thead>\
- <tbody>" + _make(reconciliation_data.splice(1)) + "</tbody>\
- </table>\
- </div>").appendTo($wrapper);
- }
- },
});
cur_frm.cscript = new erpnext.stock.StockReconciliation({frm: cur_frm});
@@ -167,4 +93,4 @@
cur_frm.cscript.posting_date = function(doc, cdt, cdn){
erpnext.get_fiscal_year(doc.company, doc.posting_date);
-}
\ No newline at end of file
+}
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.json b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.json
index a6c09ee..6ca212d 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.json
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.json
@@ -1,5 +1,5 @@
{
- "allow_copy": 1,
+ "allow_copy": 0,
"autoname": "SR/.######",
"creation": "2013-03-28 10:35:31",
"description": "This tool helps you to update or fix the quantity and valuation of stock in the system. It is typically used to synchronise the system values and what actually exists in your warehouses.",
@@ -32,6 +32,14 @@
"reqd": 1
},
{
+ "depends_on": "eval:cint(sys_defaults.auto_accounting_for_stock)",
+ "fieldname": "expense_account",
+ "fieldtype": "Link",
+ "label": "Difference Account",
+ "options": "Account",
+ "permlevel": 0
+ },
+ {
"fieldname": "amended_from",
"fieldtype": "Link",
"ignore_user_permissions": 1,
@@ -43,6 +51,11 @@
"read_only": 1
},
{
+ "fieldname": "col1",
+ "fieldtype": "Column Break",
+ "permlevel": 0
+ },
+ {
"fieldname": "company",
"fieldtype": "Link",
"label": "Company",
@@ -61,14 +74,6 @@
},
{
"depends_on": "eval:cint(sys_defaults.auto_accounting_for_stock)",
- "fieldname": "expense_account",
- "fieldtype": "Link",
- "label": "Difference Account",
- "options": "Account",
- "permlevel": 0
- },
- {
- "depends_on": "eval:cint(sys_defaults.auto_accounting_for_stock)",
"fieldname": "cost_center",
"fieldtype": "Link",
"label": "Cost Center",
@@ -76,9 +81,31 @@
"permlevel": 0
},
{
- "fieldname": "col1",
- "fieldtype": "Column Break",
- "permlevel": 0
+ "fieldname": "sb9",
+ "fieldtype": "Section Break",
+ "permlevel": 0,
+ "precision": ""
+ },
+ {
+ "fieldname": "items",
+ "fieldtype": "Table",
+ "label": "Items",
+ "options": "Stock Reconciliation Item",
+ "permlevel": 0,
+ "precision": ""
+ },
+ {
+ "fieldname": "get_items",
+ "fieldtype": "Button",
+ "label": "Get Items",
+ "permlevel": 0,
+ "precision": ""
+ },
+ {
+ "fieldname": "section_break_9",
+ "fieldtype": "Section Break",
+ "permlevel": 0,
+ "precision": ""
},
{
"fieldname": "upload_html",
@@ -119,7 +146,7 @@
"idx": 1,
"is_submittable": 1,
"max_attachments": 1,
- "modified": "2015-02-05 05:11:47.153367",
+ "modified": "2015-02-17 02:09:17.483016",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock Reconciliation",
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
index 9c85277..ef4403f 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
@@ -4,7 +4,6 @@
from __future__ import unicode_literals
import frappe
import frappe.defaults
-import json
from frappe import msgprint, _
from frappe.utils import cstr, flt, cint
from erpnext.stock.stock_ledger import update_entries_after
@@ -28,64 +27,52 @@
self.make_gl_entries_on_cancel()
def validate_data(self):
- if not self.reconciliation_json:
- return
-
- data = json.loads(self.reconciliation_json)
-
- # strip out extra columns (if any)
- data = [row[:4] for row in data]
-
- if self.head_row not in data:
- msgprint(_("""Wrong Template: Unable to find head row."""),
- raise_exception=1)
-
- # remove the help part and save the json
- head_row_no = 0
- if data.index(self.head_row) != 0:
- head_row_no = data.index(self.head_row)
- data = data[head_row_no:]
- self.reconciliation_json = json.dumps(data)
-
def _get_msg(row_num, msg):
- return _("Row # {0}: ").format(row_num+head_row_no+2) + msg
+ return _("Row # {0}: ").format(row_num+1) + msg
self.validation_messages = []
item_warehouse_combinations = []
+ default_currency = frappe.db.get_default("currency")
+
# validate no of rows
- rows = data[1:]
- if len(rows) > 100:
- msgprint(_("""Sorry! We can only allow upto 100 rows for Stock Reconciliation."""),
- raise_exception=True)
- for row_num, row in enumerate(rows):
+ if len(self.items) > 100:
+ frappe.throw(_("""Max 100 rows for Stock Reconciliation."""))
+ for row_num, row in enumerate(self.items):
# find duplicates
- if [row[0], row[1]] in item_warehouse_combinations:
+ if [row.item_code, row.warehouse] in item_warehouse_combinations:
self.validation_messages.append(_get_msg(row_num, _("Duplicate entry")))
else:
- item_warehouse_combinations.append([row[0], row[1]])
+ item_warehouse_combinations.append([row.item_code, row.warehouse])
- self.validate_item(row[0], row_num+head_row_no+2)
+ self.validate_item(row.item_code, row_num+1)
# validate warehouse
- if not frappe.db.get_value("Warehouse", row[1]):
+ if not frappe.db.get_value("Warehouse", row.warehouse):
self.validation_messages.append(_get_msg(row_num, _("Warehouse not found in the system")))
# if both not specified
- if row[2] in ["", None] and row[3] in ["", None]:
+ if row.qty in ["", None] and row.valuation_rate in ["", None]:
self.validation_messages.append(_get_msg(row_num,
_("Please specify either Quantity or Valuation Rate or both")))
# do not allow negative quantity
- if flt(row[2]) < 0:
+ if flt(row.qty) < 0:
self.validation_messages.append(_get_msg(row_num,
_("Negative Quantity is not allowed")))
# do not allow negative valuation
- if flt(row[3]) < 0:
+ if flt(row.valuation_rate) < 0:
self.validation_messages.append(_get_msg(row_num,
_("Negative Valuation Rate is not allowed")))
+ if row.qty and not row.valuation_rate:
+ # try if there is a buying price list in default currency
+ buying_rate = frappe.db.get_value("Item Price", {"item_code": row.item_code,
+ "buying": 1, "currency": default_currency}, "price_list_rate")
+ if buying_rate:
+ row.valuation_rate = buying_rate
+
# throw all validation messages
if self.validation_messages:
for msg in self.validation_messages:
@@ -129,16 +116,7 @@
and create stock ledger entries based on the difference"""
from erpnext.stock.stock_ledger import get_previous_sle
- row_template = ["item_code", "warehouse", "qty", "valuation_rate"]
-
- if not self.reconciliation_json:
- msgprint(_("""Stock Reconciliation file not uploaded"""), raise_exception=1)
-
- data = json.loads(self.reconciliation_json)
- for row_num, row in enumerate(data[data.index(self.head_row)+1:]):
- row = frappe._dict(zip(row_template, row))
- row["row_num"] = row_num
-
+ for row in self.items:
if row.qty in ("", None) or row.valuation_rate in ("", None):
previous_sle = get_previous_sle({
"item_code": row.item_code,
@@ -216,7 +194,14 @@
frappe.throw(_("Difference Account must be a 'Liability' type account, since this Stock Reconciliation is an Opening Entry"))
@frappe.whitelist()
-def upload():
- from frappe.utils.csvutils import read_csv_content_from_uploaded_file
- csv_content = read_csv_content_from_uploaded_file()
- return filter(lambda x: x and any(x), csv_content)
+def get_items(warehouse):
+ from erpnext.stock.utils import get_stock_balance
+ items = frappe.get_list("Item", fields=["name"], filters=
+ {"is_stock_item": "Yes", "has_serial_no": "No", "has_batch_no": "No"})
+ for item in items:
+ item.item_code = item.name
+ item.warehouse = warehouse
+ del item["name"]
+ item.qty, item.valuation_rate = get_stock_balance(item.name, warehouse, with_valuation_rate=True)
+
+ return items
diff --git a/erpnext/stock/doctype/stock_reconciliation_item/__init__.py b/erpnext/stock/doctype/stock_reconciliation_item/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/stock/doctype/stock_reconciliation_item/__init__.py
diff --git a/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json b/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json
new file mode 100644
index 0000000..aa55031
--- /dev/null
+++ b/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json
@@ -0,0 +1,110 @@
+{
+ "allow_copy": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "creation": "2015-02-17 01:06:05.072764",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "Other",
+ "fields": [
+ {
+ "allow_on_submit": 0,
+ "fieldname": "item_code",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "label": "Item Code",
+ "no_copy": 0,
+ "options": "Item",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "fieldname": "warehouse",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "label": "Warehouse",
+ "no_copy": 0,
+ "options": "Warehouse",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "description": "Leave blank if no change",
+ "fieldname": "qty",
+ "fieldtype": "Float",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "label": "Quantity",
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "description": "Leave blank if no change",
+ "fieldname": "valuation_rate",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "label": "Valuation Rate",
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0
+ }
+ ],
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "in_create": 0,
+ "in_dialog": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 1,
+ "modified": "2015-02-17 01:07:50.200649",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Stock Reconciliation Item",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [],
+ "read_only": 0,
+ "read_only_onload": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC"
+}
\ No newline at end of file
diff --git a/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.py b/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.py
new file mode 100644
index 0000000..6597efd
--- /dev/null
+++ b/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.model.document import Document
+
+class StockReconciliationItem(Document):
+ pass
diff --git a/erpnext/stock/page/stock_analytics/stock_analytics.js b/erpnext/stock/page/stock_analytics/stock_analytics.js
index 346750b..0b7acb8 100644
--- a/erpnext/stock/page/stock_analytics/stock_analytics.js
+++ b/erpnext/stock/page/stock_analytics/stock_analytics.js
@@ -2,7 +2,7 @@
// License: GNU General Public License v3. See license.txt
-frappe.pages['stock-analytics'].on_page_load = function(wrapper) {
+frappe.pages['stock-analytics'].on_page_load = function(wrapper) {
frappe.ui.make_app_page({
parent: wrapper,
title: __('Stock Analytics'),
@@ -13,7 +13,8 @@
frappe.add_breadcrumbs("Stock")
-
-}
-frappe.require("assets/erpnext/js/stock_analytics.js");
\ No newline at end of file
+};
+
+frappe.assets.views["Report"]();
+frappe.require("assets/erpnext/js/stock_analytics.js");
diff --git a/erpnext/stock/page/stock_ledger/stock_ledger.js b/erpnext/stock/page/stock_ledger/stock_ledger.js
index 3f6a9e3..27525e9 100644
--- a/erpnext/stock/page/stock_ledger/stock_ledger.js
+++ b/erpnext/stock/page/stock_ledger/stock_ledger.js
@@ -10,7 +10,7 @@
new erpnext.StockLedger(wrapper);
frappe.add_breadcrumbs("Stock")
-}
+};
frappe.require("assets/erpnext/js/stock_grid_report.js");
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index a77a629..a4c54b4 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -59,278 +59,256 @@
frappe.db.sql("""delete from `tabStock Ledger Entry`
where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no))
-def update_entries_after(args, allow_zero_rate=False, allow_negative_stock=False, verbose=1):
+class update_entries_after(object):
"""
update valution rate and qty after transaction
from the current time-bucket onwards
- args = {
- "item_code": "ABC",
- "warehouse": "XYZ",
- "posting_date": "2012-12-12",
- "posting_time": "12:00"
- }
+ :param args: args as dict
+
+ args = {
+ "item_code": "ABC",
+ "warehouse": "XYZ",
+ "posting_date": "2012-12-12",
+ "posting_time": "12:00"
+ }
"""
- if not _exceptions:
- frappe.local.stockledger_exceptions = []
+ def __init__(self, args, allow_zero_rate=False, allow_negative_stock=None, verbose=1):
+ from frappe.model.meta import get_field_precision
- if not allow_negative_stock:
- allow_negative_stock = cint(frappe.db.get_default("allow_negative_stock"))
+ self.exceptions = []
+ self.verbose = verbose
+ self.allow_zero_rate = allow_zero_rate
+ self.allow_negative_stock = allow_negative_stock
- previous_sle = get_sle_before_datetime(args)
+ if self.allow_negative_stock==None:
+ self.allow_negative_stock = cint(frappe.db.get_single_value("Stock Settings",
+ "allow_negative_stock"))
- qty_after_transaction = flt(previous_sle.get("qty_after_transaction"))
- valuation_rate = flt(previous_sle.get("valuation_rate"))
- stock_queue = json.loads(previous_sle.get("stock_queue") or "[]")
- stock_value = flt(previous_sle.get("stock_value"))
- prev_stock_value = flt(previous_sle.get("stock_value"))
+ self.args = args
+ for key, value in args.iteritems():
+ setattr(self, key, value)
- entries_to_fix = get_sle_after_datetime(previous_sle or \
- {"item_code": args["item_code"], "warehouse": args["warehouse"]}, for_update=True)
- valuation_method = get_valuation_method(args["item_code"])
- stock_value_difference = 0.0
+ self.previous_sle = self.get_sle_before_datetime()
+ self.previous_sle = self.previous_sle[0] if self.previous_sle else frappe._dict()
- for sle in entries_to_fix:
- if sle.serial_no or not cint(frappe.db.get_value("Stock Settings", None, "allow_negative_stock")):
+ for key in ("qty_after_transaction", "valuation_rate", "stock_value"):
+ setattr(self, key, flt(self.previous_sle.get(key)))
+
+ self.company = frappe.db.get_value("Warehouse", self.warehouse, "company")
+ self.precision = get_field_precision(frappe.get_meta("Stock Ledger Entry").get_field("stock_value"),
+ currency=frappe.db.get_value("Company", self.company, "default_currency"))
+
+ self.prev_stock_value = self.stock_value
+ self.stock_queue = json.loads(self.previous_sle.stock_queue or "[]")
+ self.valuation_method = get_valuation_method(self.item_code)
+ self.stock_value_difference = 0.0
+ self.build()
+
+ def build(self):
+ entries_to_fix = self.get_sle_after_datetime()
+
+ for sle in entries_to_fix:
+ self.process_sle(sle)
+
+ if self.exceptions:
+ self.raise_exceptions()
+
+ self.update_bin()
+
+ def update_bin(self):
+ # update bin
+ bin_name = frappe.db.get_value("Bin", {
+ "item_code": self.item_code,
+ "warehouse": self.warehouse
+ })
+
+ if not bin_name:
+ bin_doc = frappe.get_doc({
+ "doctype": "Bin",
+ "item_code": self.item_code,
+ "warehouse": self.warehouse
+ })
+ bin_doc.insert(ignore_permissions=True)
+ else:
+ bin_doc = frappe.get_doc("Bin", bin_name)
+
+ bin_doc.update({
+ "valuation_rate": self.valuation_rate,
+ "actual_qty": self.qty_after_transaction,
+ "stock_value": self.stock_value
+ })
+ bin_doc.save(ignore_permissions=True)
+
+ def process_sle(self, sle):
+ if sle.serial_no or not cint(self.allow_negative_stock):
# validate negative stock for serialized items, fifo valuation
# or when negative stock is not allowed for moving average
- if not validate_negative_stock(qty_after_transaction, sle):
- qty_after_transaction += flt(sle.actual_qty)
- continue
-
+ if not self.validate_negative_stock(sle):
+ self.qty_after_transaction += flt(sle.actual_qty)
+ return
if sle.serial_no:
- valuation_rate = get_serialized_values(qty_after_transaction, sle, valuation_rate)
- qty_after_transaction += flt(sle.actual_qty)
-
+ self.valuation_rate = self.get_serialized_values(sle)
+ self.qty_after_transaction += flt(sle.actual_qty)
+ self.stock_value = self.qty_after_transaction * self.valuation_rate
else:
if sle.voucher_type=="Stock Reconciliation":
- valuation_rate = sle.valuation_rate
- qty_after_transaction = sle.qty_after_transaction
- stock_queue = [[qty_after_transaction, valuation_rate]]
+ # assert
+ self.valuation_rate = sle.valuation_rate
+ self.qty_after_transaction = sle.qty_after_transaction
+ self.stock_queue = [[self.qty_after_transaction, self.valuation_rate]]
+ self.stock_value = self.qty_after_transaction * self.valuation_rate
else:
- if valuation_method == "Moving Average":
- valuation_rate = get_moving_average_values(qty_after_transaction, sle, valuation_rate, allow_zero_rate)
+ if self.valuation_method == "Moving Average":
+ self.get_moving_average_values(sle)
+ self.qty_after_transaction += flt(sle.actual_qty)
+ self.stock_value = self.qty_after_transaction * self.valuation_rate
else:
- valuation_rate = get_fifo_values(qty_after_transaction, sle, stock_queue, allow_zero_rate)
+ self.get_fifo_values(sle)
+ self.qty_after_transaction += flt(sle.actual_qty)
+ self.stock_value = sum((flt(batch[0]) * flt(batch[1]) for batch in self.stock_queue))
- qty_after_transaction += flt(sle.actual_qty)
-
- # get stock value
- if sle.serial_no:
- stock_value = qty_after_transaction * valuation_rate
- elif valuation_method == "Moving Average":
- stock_value = qty_after_transaction * valuation_rate
- else:
- stock_value = sum((flt(batch[0]) * flt(batch[1]) for batch in stock_queue))
-
# rounding as per precision
- from frappe.model.meta import get_field_precision
- meta = frappe.get_meta("Stock Ledger Entry")
+ self.stock_value = flt(self.stock_value, self.precision)
- stock_value = flt(stock_value, get_field_precision(meta.get_field("stock_value"),
- frappe._dict({"fields": sle})))
-
- stock_value_difference = stock_value - prev_stock_value
- prev_stock_value = stock_value
+ stock_value_difference = self.stock_value - self.prev_stock_value
+ self.prev_stock_value = self.stock_value
# update current sle
- frappe.db.sql("""update `tabStock Ledger Entry`
- set qty_after_transaction=%s, valuation_rate=%s, stock_queue=%s,
- stock_value=%s, stock_value_difference=%s where name=%s""",
- (qty_after_transaction, valuation_rate,
- json.dumps(stock_queue), stock_value, stock_value_difference, sle.name))
+ sle.qty_after_transaction = self.qty_after_transaction
+ sle.valuation_rate = self.valuation_rate
+ sle.stock_queue = json.dumps(self.stock_queue)
+ sle.stock_value_difference = stock_value_difference
+ sle.save()
- if _exceptions:
- _raise_exceptions(args, verbose)
+ def validate_negative_stock(self, sle):
+ """
+ validate negative stock for entries current datetime onwards
+ will not consider cancelled entries
+ """
+ diff = self.qty_after_transaction + flt(sle.actual_qty)
- # update bin
- if not frappe.db.exists({"doctype": "Bin", "item_code": args["item_code"],
- "warehouse": args["warehouse"]}):
- bin_wrapper = frappe.get_doc({
- "doctype": "Bin",
- "item_code": args["item_code"],
- "warehouse": args["warehouse"],
- })
- bin_wrapper.flags.ignore_permissions = 1
- bin_wrapper.insert()
-
- frappe.db.sql("""update `tabBin` set valuation_rate=%s, actual_qty=%s,
- stock_value=%s,
- projected_qty = (actual_qty + indented_qty + ordered_qty + planned_qty - reserved_qty)
- where item_code=%s and warehouse=%s""", (valuation_rate, qty_after_transaction,
- stock_value, args["item_code"], args["warehouse"]))
-
-def get_sle_before_datetime(args, for_update=False):
- """
- get previous stock ledger entry before current time-bucket
-
- Details:
- get the last sle before the current time-bucket, so that all values
- are reposted from the current time-bucket onwards.
- this is necessary because at the time of cancellation, there may be
- entries between the cancelled entries in the same time-bucket
- """
- sle = get_stock_ledger_entries(args,
- ["timestamp(posting_date, posting_time) < timestamp(%(posting_date)s, %(posting_time)s)"],
- "desc", "limit 1", for_update=for_update)
-
- return sle and sle[0] or frappe._dict()
-
-def get_sle_after_datetime(args, for_update=False):
- """get Stock Ledger Entries after a particular datetime, for reposting"""
- # NOTE: using for update of
- conditions = ["timestamp(posting_date, posting_time) > timestamp(%(posting_date)s, %(posting_time)s)"]
-
- # Excluding name: Workaround for MariaDB timestamp() floating microsecond issue
- if args.get("name"):
- conditions.append("name!=%(name)s")
-
- return get_stock_ledger_entries(args, conditions, "asc", for_update=for_update)
-
-def get_stock_ledger_entries(args, conditions=None, order="desc", limit=None, for_update=False):
- """get stock ledger entries filtered by specific posting datetime conditions"""
- if not args.get("posting_date"):
- args["posting_date"] = "1900-01-01"
- if not args.get("posting_time"):
- args["posting_time"] = "00:00"
-
- return frappe.db.sql("""select *, timestamp(posting_date, posting_time) as "timestamp" from `tabStock Ledger Entry`
- where item_code = %%(item_code)s
- and warehouse = %%(warehouse)s
- and ifnull(is_cancelled, 'No')='No'
- %(conditions)s
- order by timestamp(posting_date, posting_time) %(order)s, name %(order)s
- %(limit)s %(for_update)s""" % {
- "conditions": conditions and ("and " + " and ".join(conditions)) or "",
- "limit": limit or "",
- "for_update": for_update and "for update" or "",
- "order": order
- }, args, as_dict=1)
-
-def validate_negative_stock(qty_after_transaction, sle):
- """
- validate negative stock for entries current datetime onwards
- will not consider cancelled entries
- """
- diff = qty_after_transaction + flt(sle.actual_qty)
-
- if not _exceptions:
- frappe.local.stockledger_exceptions = []
-
- if diff < 0 and abs(diff) > 0.0001:
- # negative stock!
- exc = sle.copy().update({"diff": diff})
- _exceptions.append(exc)
- return False
- else:
- return True
-
-def get_serialized_values(qty_after_transaction, sle, valuation_rate):
- incoming_rate = flt(sle.incoming_rate)
- actual_qty = flt(sle.actual_qty)
- serial_no = cstr(sle.serial_no).split("\n")
-
- if incoming_rate < 0:
- # wrong incoming rate
- incoming_rate = 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])
-
- if incoming_rate and not valuation_rate:
- valuation_rate = incoming_rate
- else:
- new_stock_qty = qty_after_transaction + actual_qty
- if new_stock_qty > 0:
- new_stock_value = qty_after_transaction * valuation_rate + actual_qty * incoming_rate
- if new_stock_value > 0:
- # calculate new valuation rate only if stock value is positive
- # else it remains the same as that of previous entry
- valuation_rate = new_stock_value / new_stock_qty
-
- return valuation_rate
-
-def get_moving_average_values(qty_after_transaction, sle, valuation_rate, allow_zero_rate):
- incoming_rate = flt(sle.incoming_rate)
- actual_qty = flt(sle.actual_qty)
-
- if flt(sle.actual_qty) > 0:
- if qty_after_transaction < 0 and not valuation_rate:
- # if negative stock, take current valuation rate as incoming rate
- valuation_rate = incoming_rate
-
- new_stock_qty = abs(qty_after_transaction) + actual_qty
- new_stock_value = (abs(qty_after_transaction) * valuation_rate) + (actual_qty * incoming_rate)
-
- if new_stock_qty:
- valuation_rate = new_stock_value / flt(new_stock_qty)
- elif not valuation_rate and qty_after_transaction <= 0:
- valuation_rate = get_valuation_rate(sle.item_code, sle.warehouse, allow_zero_rate)
-
- return abs(flt(valuation_rate))
-
-def get_fifo_values(qty_after_transaction, sle, stock_queue, allow_zero_rate):
- incoming_rate = flt(sle.incoming_rate)
- actual_qty = flt(sle.actual_qty)
-
- if actual_qty > 0:
- if not stock_queue:
- stock_queue.append([0, 0])
-
- if stock_queue[-1][0] > 0:
- stock_queue.append([actual_qty, incoming_rate])
+ if diff < 0 and abs(diff) > 0.0001:
+ # negative stock!
+ exc = sle.copy().update({"diff": diff})
+ self.exceptions.append(exc)
+ return False
else:
- qty = stock_queue[-1][0] + actual_qty
- if qty == 0:
- stock_queue.pop(-1)
+ return True
+
+ def get_serialized_values(self, sle):
+ incoming_rate = flt(sle.incoming_rate)
+ actual_qty = flt(sle.actual_qty)
+ serial_no = cstr(sle.serial_no).split("\n")
+
+ 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])
+
+ if incoming_rate and not self.valuation_rate:
+ self.valuation_rate = incoming_rate
+ else:
+ new_stock_qty = self.qty_after_transaction + actual_qty
+ if new_stock_qty > 0:
+ new_stock_value = self.qty_after_transaction * self.valuation_rate + actual_qty * incoming_rate
+ if new_stock_value > 0:
+ # calculate new valuation rate only if stock value is positive
+ # else it remains the same as that of previous entry
+ self.valuation_rate = new_stock_value / new_stock_qty
+
+ def get_moving_average_values(self, sle):
+ incoming_rate = flt(sle.incoming_rate)
+ actual_qty = flt(sle.actual_qty)
+
+ if flt(sle.actual_qty) > 0:
+ if self.qty_after_transaction < 0 and not self.valuation_rate:
+ # if negative stock, take current valuation rate as incoming rate
+ self.valuation_rate = incoming_rate
+
+ new_stock_qty = abs(self.qty_after_transaction) + actual_qty
+ new_stock_value = (abs(self.qty_after_transaction) * self.valuation_rate) + (actual_qty * incoming_rate)
+
+ if new_stock_qty:
+ self.valuation_rate = new_stock_value / flt(new_stock_qty)
+ elif not self.valuation_rate and self.qty_after_transaction <= 0:
+ valuation_rate = get_valuation_rate(sle.item_code, sle.warehouse, self.allow_zero_rate)
+
+ return abs(flt(valuation_rate))
+
+ def get_fifo_values(self, sle):
+ incoming_rate = flt(sle.incoming_rate)
+ actual_qty = flt(sle.actual_qty)
+
+ if actual_qty > 0:
+ if not self.stock_queue:
+ self.stock_queue.append([0, 0])
+
+ if self.stock_queue[-1][0] > 0:
+ self.stock_queue.append([actual_qty, incoming_rate])
else:
- stock_queue[-1] = [qty, incoming_rate]
- else:
- qty_to_pop = abs(actual_qty)
- while qty_to_pop:
- if not stock_queue:
- stock_queue.append([0, get_valuation_rate(sle.item_code, sle.warehouse, allow_zero_rate)
- if qty_after_transaction <= 0 else 0])
+ qty = self.stock_queue[-1][0] + actual_qty
+ if qty == 0:
+ self.stock_queue.pop(-1)
+ else:
+ self.stock_queue[-1] = [qty, incoming_rate]
+ else:
+ qty_to_pop = abs(actual_qty)
+ while qty_to_pop:
+ if not self.stock_queue:
+ if self.qty_after_transaction > 0:
+ _rate = get_valuation_rate(sle.item_code, sle.warehouse, self.allow_zero_rate)
+ else:
+ _rate = 0
+ self.stock_queue.append([0, _rate])
- batch = stock_queue[0]
+ batch = self.stock_queue[0]
- if qty_to_pop >= batch[0]:
- # consume current batch
- qty_to_pop = qty_to_pop - batch[0]
- stock_queue.pop(0)
- if not stock_queue and qty_to_pop:
- # stock finished, qty still remains to be withdrawn
- # negative stock, keep in as a negative batch
- stock_queue.append([-qty_to_pop, batch[1]])
- break
+ if qty_to_pop >= batch[0]:
+ # consume current batch
+ qty_to_pop = qty_to_pop - batch[0]
+ self.stock_queue.pop(0)
+ if not self.stock_queue and qty_to_pop:
+ # stock finished, qty still remains to be withdrawn
+ # negative stock, keep in as a negative batch
+ self.stock_queue.append([-qty_to_pop, batch[1]])
+ break
- else:
- # qty found in current batch
- # consume it and exit
- batch[0] = batch[0] - qty_to_pop
- qty_to_pop = 0
+ else:
+ # qty found in current batch
+ # consume it and exit
+ batch[0] = batch[0] - qty_to_pop
+ qty_to_pop = 0
- stock_value = sum((flt(batch[0]) * flt(batch[1]) for batch in stock_queue))
- stock_qty = sum((flt(batch[0]) for batch in stock_queue))
+ stock_value = sum((flt(batch[0]) * flt(batch[1]) for batch in self.stock_queue))
+ stock_qty = sum((flt(batch[0]) for batch in self.stock_queue))
- valuation_rate = (stock_value / flt(stock_qty)) if stock_qty else 0
+ self.valuation_rate = (stock_value / flt(stock_qty)) if stock_qty else 0
- return abs(valuation_rate)
+ def get_sle_before_datetime(self):
+ """get previous stock ledger entry before current time-bucket"""
+ return get_stock_ledger_entries(self.args, "<", "desc", "limit 1", for_update=False)
-def _raise_exceptions(args, verbose=1):
- deficiency = min(e["diff"] for e in _exceptions)
- msg = _("Negative Stock Error ({6}) for Item {0} in Warehouse {1} on {2} {3} in {4} {5}").format(args["item_code"],
- args.get("warehouse"), _exceptions[0]["posting_date"], _exceptions[0]["posting_time"],
- _(_exceptions[0]["voucher_type"]), _exceptions[0]["voucher_no"], deficiency)
- if verbose:
- frappe.throw(msg, NegativeStockError)
- else:
- raise NegativeStockError, msg
+ def get_sle_after_datetime(self):
+ """get Stock Ledger Entries after a particular datetime, for reposting"""
+ return get_stock_ledger_entries(self.previous_sle or self.args, ">", "asc", for_update=True)
+
+ def raise_exceptions(self):
+ deficiency = min(e["diff"] for e in self.exceptions)
+ msg = _("Negative Stock Error ({6}) for Item {0} in Warehouse {1} on {2} {3} in {4} {5}").format(self.item_code,
+ self.warehouse, self.exceptions[0]["posting_date"], self.exceptions[0]["posting_time"],
+ _(self.exceptions[0]["voucher_type"]), self.exceptions[0]["voucher_no"], deficiency)
+ if self.verbose:
+ frappe.throw(msg, NegativeStockError)
+ else:
+ raise NegativeStockError, msg
def get_previous_sle(args, for_update=False):
"""
@@ -346,13 +324,34 @@
"sle": "name of reference Stock Ledger Entry"
}
"""
- if not args.get("sle"): args["sle"] = ""
-
- sle = get_stock_ledger_entries(args, ["name != %(sle)s",
- "timestamp(posting_date, posting_time) <= timestamp(%(posting_date)s, %(posting_time)s)"],
- "desc", "limit 1", for_update=for_update)
+ args["name"] = args.get("sle", None) or ""
+ sle = get_stock_ledger_entries(args, "<=", "desc", "limit 1", for_update=for_update)
return sle and sle[0] or {}
+def get_stock_ledger_entries(previous_sle, operator=None, order="desc", limit=None, for_update=False):
+ """get stock ledger entries filtered by specific posting datetime conditions"""
+ conditions = "timestamp(posting_date, posting_time) {0} timestamp(%(posting_date)s, %(posting_time)s)".format(operator)
+ if not previous_sle.get("posting_date"):
+ previous_sle["posting_date"] = "1900-01-01"
+ if not previous_sle.get("posting_time"):
+ previous_sle["posting_time"] = "00:00"
+
+ if operator in (">", "<=") and previous_sle.get("name"):
+ conditions += " and name!=%(name)s"
+
+ return frappe.db.sql("""select *, timestamp(posting_date, posting_time) as "timestamp" from `tabStock Ledger Entry`
+ where item_code = %%(item_code)s
+ and warehouse = %%(warehouse)s
+ and ifnull(is_cancelled, 'No')='No'
+ and %(conditions)s
+ order by timestamp(posting_date, posting_time) %(order)s, name %(order)s
+ %(limit)s %(for_update)s""" % {
+ "conditions": conditions,
+ "limit": limit or "",
+ "for_update": for_update and "for update" or "",
+ "order": order
+ }, previous_sle, as_dict=1)
+
def get_valuation_rate(item_code, warehouse, allow_zero_rate=False):
last_valuation_rate = frappe.db.sql("""select valuation_rate
from `tabStock Ledger Entry`
diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py
index ceb0ebf..9a33735 100644
--- a/erpnext/stock/utils.py
+++ b/erpnext/stock/utils.py
@@ -34,19 +34,22 @@
return sum(sle_map.values())
-def get_stock_balance(item_code, warehouse, posting_date=None, posting_time=None):
+def get_stock_balance(item_code, warehouse, posting_date=None, posting_time=None, with_valuation_rate=False):
+ """Returns stock balance quantity at given warehouse on given posting date or current date.
+
+ If `with_valuation_rate` is True, will return tuple (qty, rate)"""
if not posting_date: posting_date = nowdate()
if not posting_time: posting_time = nowtime()
- last_entry = frappe.db.sql("""select qty_after_transaction from `tabStock Ledger Entry`
+ last_entry = frappe.db.sql("""select qty_after_transaction, valuation_rate from `tabStock Ledger Entry`
where item_code=%s and warehouse=%s
and timestamp(posting_date, posting_time) < timestamp(%s, %s)
order by timestamp(posting_date, posting_time) limit 1""",
(item_code, warehouse, posting_date, posting_time))
- if last_entry:
- return last_entry[0][0]
+ if with_valuation_rate:
+ return (last_entry[0][0], last_entry[0][1]) if last_entry else (0.0, 0.0)
else:
- return 0.0
+ return last_entry[0][0] if last_entry else 0.0
def get_latest_stock_balance():
bin_map = {}
diff --git a/erpnext/support/page/support_analytics/support_analytics.js b/erpnext/support/page/support_analytics/support_analytics.js
index 4ca15a2..057aebc 100644
--- a/erpnext/support/page/support_analytics/support_analytics.js
+++ b/erpnext/support/page/support_analytics/support_analytics.js
@@ -15,6 +15,8 @@
}
+frappe.assets.views["Report"]();
+
erpnext.SupportAnalytics = frappe.views.GridReportWithPlot.extend({
init: function(wrapper) {
this._super({
diff --git a/erpnext/templates/form_grid/item_grid.html b/erpnext/templates/form_grid/item_grid.html
index e792da0..6bd5d48 100644
--- a/erpnext/templates/form_grid/item_grid.html
+++ b/erpnext/templates/form_grid/item_grid.html
@@ -14,9 +14,9 @@
<br>{%= doc.item_name %}{% } %}
{% if(doc.item_name != doc.description) { %}
<p>{%= doc.description %}</p>{% } %}
- <div>
+ <p>
{% if(doc.sales_order || doc.against_sales_order) { %}
- <span class="label label-default"
+ <span class="label label-default" style="margin-right: 10px;"
title="{%= __("Sales Order") %}">
<i class="icon-file"></i>
{%= doc.sales_order || doc.against_sales_order %}
@@ -36,22 +36,28 @@
}
}
%}
- <span style="margin-left: 10px;"
+ <span style="margin-right: 10px;"
title="{%= title %}">
<span class="label {%= label_class %}">
{%= doc.warehouse %}
</span>
</span>
{% } %}
- </div>
+ </p>
{% include "templates/form_grid/includes/visible_cols.html" %}
{% if(doc.schedule_date) { %}
- <div><span title="{%= __("Reqd By Date") %}" class="label {%=
- (frappe.datetime.get_diff(doc.schedule_date) < 1
- && doc.received_qty < doc.qty)
- ? "label-danger" : "label-default" %}">
- {%= doc.get_formatted("schedule_date") %}</span>
- </div>
+ <div class="clearfix"></div>
+ <div>
+ {% if(frappe.datetime.get_diff(doc.schedule_date) < 1 && doc.received_qty < doc.qty) { %}
+ <span class="text-danger">
+ {%= __("Overdue on {0}", [doc.get_formatted("schedule_date")]) %}
+ </span>
+ {% } else { %}
+ <span class="text-muted">
+ {%= __("Due on {0}", [doc.get_formatted("schedule_date")]) %}
+ </span>
+ {% } %}
+ </div>
{% } %}
</div>
diff --git a/erpnext/templates/form_grid/material_request_grid.html b/erpnext/templates/form_grid/material_request_grid.html
index c30cf58..812245a 100644
--- a/erpnext/templates/form_grid/material_request_grid.html
+++ b/erpnext/templates/form_grid/material_request_grid.html
@@ -13,32 +13,32 @@
<br>{%= doc.item_name %}{% } %}
{% if(doc.item_name != doc.description) { %}
<p>{%= doc.description %}</p>{% } %}
- {% include "templates/form_grid/includes/visible_cols.html" %}
+ <div>
+ {% if(doc.warehouse) { %}
+ <span class="label label-default" title="{%= __("For Warehouse") %}"
+ style="margin-right: 10px;">
+ {%= doc.warehouse %}
+ </span>
+ {% } %}
{% if(doc.schedule_date) { %}
- <br><span title="{%= __("Reqd By Date") %}" class="label {%=
+ <span title="{%= __("Reqd By Date") %}" class="label {%=
(frappe.datetime.get_diff(doc.schedule_date, frappe.datetime.get_today()) < 0
&& doc.ordered_qty < doc.qty)
? "label-danger" : "label-default" %}">
{%= doc.get_formatted("schedule_date") %}</span>
{% } %}
+ </div>
+ {% include "templates/form_grid/includes/visible_cols.html" %}
</div>
<!-- qty -->
<div class="col-sm-3 text-right">
{%= doc.get_formatted("qty") %}
- <small>{%= doc.uom || doc.stock_uom %}</small>
- {% var completed =
- 100 - cint((doc.qty - cint(doc.ordered_qty)) * 100 / doc.qty),
- title = __("Ordered"); %}
- {% include "templates/form_grid/includes/progress.html" %}
- {% if(doc.warehouse) { %}
- <div style="overflow:hidden; min-height: 25px;
- white-space:nowrap; text-overflow: ellipsis;"
- title="{%= __("For Warehouse") %}">
- <span class="label label-default">
- {%= doc.warehouse %}
- </span>
- </div>
+ <div class="small">{%= doc.uom || doc.stock_uom %}</div>
+ {% if(doc.qty == doc.ordered_qty) { %}
+ <div class="small text-success">{%= __("Ordered") %}</div>
+ {% } else { %}
+ <div class="small text-muted">{%= __("{0} Ordered", doc.ordered_qty) %}</div>
{% } %}
</div>
</div>
diff --git a/erpnext/templates/form_grid/stock_entry_grid.html b/erpnext/templates/form_grid/stock_entry_grid.html
index cf9b260..ef465a4 100644
--- a/erpnext/templates/form_grid/stock_entry_grid.html
+++ b/erpnext/templates/form_grid/stock_entry_grid.html
@@ -18,15 +18,18 @@
<p>{%= doc.description %}</p>{% } %}
{% include "templates/form_grid/includes/visible_cols.html" %}
<div>
- {% if(doc.s_warehouse) { %}<span class="label
- {%= (doc.actual_qty >= doc.qty) ? "label-success"
- : "label-danger" %}"
- title="{%= (doc.actual_qty >= doc.qty) ? __("In Stock")
- : __("Not In Stock") %}">
- {%= doc.s_warehouse || "" %}</span>{% } %}
- <i class="icon-long-arrow-right"></i>
+ {% if(doc.s_warehouse) { %}
+ <span class="label label-primary">
+ {%= doc.s_warehouse || "" %}</span>
+ {% } %}
+ <i class="octicon octicon-arrow-small-right"></i>
{% if(doc.t_warehouse) { %}<span class="label label-primary">
{%= doc.t_warehouse || "" %}</span>{% } %}
+ {% if(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
+ </span>
+ {% } %}
</div>
</div>