Merge branch 'master' of github.com:webnotes/erpnext into flat_discount
diff --git a/accounts/doctype/account/account.js b/accounts/doctype/account/account.js
index b6986cf..8837586 100644
--- a/accounts/doctype/account/account.js
+++ b/accounts/doctype/account/account.js
@@ -95,9 +95,10 @@
wn.route_options = {
"account": doc.name,
"from_date": sys_defaults.year_start_date,
- "to_date": sys_defaults.year_end_date
+ "to_date": sys_defaults.year_end_date,
+ "company": doc.company
};
- wn.set_route("general-ledger");
+ wn.set_route("query-report", "General Ledger");
}, "icon-table");
}
}
diff --git a/accounts/doctype/account/account.py b/accounts/doctype/account/account.py
index cae2028..0640ad9 100644
--- a/accounts/doctype/account/account.py
+++ b/accounts/doctype/account/account.py
@@ -211,6 +211,9 @@
# Validate properties before merging
if merge:
+ if not webnotes.conn.exists("Account", new):
+ webnotes.throw(_("Account ") + new +_(" does not exists"))
+
val = list(webnotes.conn.get_value("Account", new_account,
["group_or_ledger", "debit_or_credit", "is_pl_account"]))
diff --git a/accounts/doctype/accounts_settings/accounts_settings.py b/accounts/doctype/accounts_settings/accounts_settings.py
index d55b022..a6e9938 100644
--- a/accounts/doctype/accounts_settings/accounts_settings.py
+++ b/accounts/doctype/accounts_settings/accounts_settings.py
@@ -5,8 +5,7 @@
from __future__ import unicode_literals
import webnotes
-from webnotes.utils import cint, cstr
-from webnotes import msgprint, _
+from webnotes import _
class DocType:
def __init__(self, d, dl):
@@ -16,6 +15,11 @@
webnotes.conn.set_default("auto_accounting_for_stock", self.doc.auto_accounting_for_stock)
if self.doc.auto_accounting_for_stock:
- for wh in webnotes.conn.sql("select name from `tabWarehouse`"):
- wh_bean = webnotes.bean("Warehouse", wh[0])
+ warehouse_list = webnotes.conn.sql("select name, company from tabWarehouse", as_dict=1)
+ warehouse_with_no_company = [d.name for d in warehouse_list if not d.company]
+ if warehouse_with_no_company:
+ webnotes.throw(_("Company is missing in following warehouses") + ": \n" +
+ "\n".join(warehouse_with_no_company))
+ for wh in warehouse_list:
+ wh_bean = webnotes.bean("Warehouse", wh.name)
wh_bean.save()
\ No newline at end of file
diff --git a/accounts/doctype/journal_voucher/journal_voucher.js b/accounts/doctype/journal_voucher/journal_voucher.js
index a5cd06d..6b94ba1 100644
--- a/accounts/doctype/journal_voucher/journal_voucher.js
+++ b/accounts/doctype/journal_voucher/journal_voucher.js
@@ -120,8 +120,9 @@
"voucher_no": doc.name,
"from_date": doc.posting_date,
"to_date": doc.posting_date,
+ "company": doc.company
};
- wn.set_route("general-ledger");
+ wn.set_route("query-report", "General Ledger");
}, "icon-table");
}
}
diff --git a/accounts/doctype/purchase_invoice/purchase_invoice.js b/accounts/doctype/purchase_invoice/purchase_invoice.js
index 9d75264..0bdc70e 100644
--- a/accounts/doctype/purchase_invoice/purchase_invoice.js
+++ b/accounts/doctype/purchase_invoice/purchase_invoice.js
@@ -35,8 +35,9 @@
"voucher_no": doc.name,
"from_date": doc.posting_date,
"to_date": doc.posting_date,
+ "company": doc.company
};
- wn.set_route("general-ledger");
+ wn.set_route("query-report", "General Ledger");
}, "icon-table");
}
diff --git a/accounts/doctype/purchase_invoice/purchase_invoice.py b/accounts/doctype/purchase_invoice/purchase_invoice.py
index 6c71fff..404627a 100644
--- a/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -350,7 +350,7 @@
# item gl entries
stock_item_and_auto_accounting_for_stock = False
stock_items = self.get_stock_items()
- rounding_diff = 0.0
+ # rounding_diff = 0.0
for item in self.doclist.get({"parentfield": "entries"}):
if auto_accounting_for_stock and item.item_code in stock_items:
if flt(item.valuation_rate):
@@ -359,13 +359,12 @@
# expense will be booked in sales invoice
stock_item_and_auto_accounting_for_stock = True
- valuation_amt = flt(flt(item.valuation_rate) * flt(item.qty) * \
- flt(item.conversion_factor), self.precision("valuation_rate", item))
+ valuation_amt = item.amount + item.item_tax_amount + item.rm_supp_cost
- rounding_diff += (flt(item.amount, self.precision("amount", item)) +
- flt(item.item_tax_amount, self.precision("item_tax_amount", item)) +
- flt(item.rm_supp_cost, self.precision("rm_supp_cost", item)) -
- valuation_amt)
+ # rounding_diff += (flt(item.amount, self.precision("amount", item)) +
+ # flt(item.item_tax_amount, self.precision("item_tax_amount", item)) +
+ # flt(item.rm_supp_cost, self.precision("rm_supp_cost", item)) -
+ # valuation_amt)
gl_entries.append(
self.get_gl_dict({
@@ -394,11 +393,11 @@
expenses_included_in_valuation = \
self.get_company_default("expenses_included_in_valuation")
- if rounding_diff:
- import operator
- cost_center_with_max_value = max(valuation_tax.iteritems(),
- key=operator.itemgetter(1))[0]
- valuation_tax[cost_center_with_max_value] -= flt(rounding_diff)
+ # if rounding_diff:
+ # import operator
+ # cost_center_with_max_value = max(valuation_tax.iteritems(),
+ # key=operator.itemgetter(1))[0]
+ # valuation_tax[cost_center_with_max_value] -= flt(rounding_diff)
for cost_center, amount in valuation_tax.items():
gl_entries.append(
diff --git a/accounts/doctype/sales_invoice/sales_invoice.js b/accounts/doctype/sales_invoice/sales_invoice.js
index b3d784b..e9a2dc9 100644
--- a/accounts/doctype/sales_invoice/sales_invoice.js
+++ b/accounts/doctype/sales_invoice/sales_invoice.js
@@ -54,8 +54,9 @@
"voucher_no": doc.name,
"from_date": doc.posting_date,
"to_date": doc.posting_date,
+ "company": doc.company
};
- wn.set_route("general-ledger");
+ wn.set_route("query-report", "General Ledger");
}, "icon-table");
var percent_paid = cint(flt(doc.grand_total - doc.outstanding_amount) / flt(doc.grand_total) * 100);
diff --git a/accounts/general_ledger.py b/accounts/general_ledger.py
index bc0ac1d..575a2b0 100644
--- a/accounts/general_ledger.py
+++ b/accounts/general_ledger.py
@@ -45,7 +45,7 @@
same_head.credit = flt(same_head.credit) + flt(entry.credit)
else:
merged_gl_map.append(entry)
-
+
# filter zero debit and credit entries
merged_gl_map = filter(lambda x: flt(x.debit)!=0 or flt(x.credit)!=0, merged_gl_map)
return merged_gl_map
diff --git a/accounts/page/accounts_browser/accounts_browser.js b/accounts/page/accounts_browser/accounts_browser.js
index d57073a..235e6ab 100644
--- a/accounts/page/accounts_browser/accounts_browser.js
+++ b/accounts/page/accounts_browser/accounts_browser.js
@@ -175,9 +175,10 @@
wn.route_options = {
"account": node.data('label'),
"from_date": sys_defaults.year_start_date,
- "to_date": sys_defaults.year_end_date
+ "to_date": sys_defaults.year_end_date,
+ "company": me.company
};
- wn.set_route("general-ledger");
+ wn.set_route("query-report", "General Ledger");
},
rename: function() {
var node = this.selected_node();
diff --git a/accounts/report/general_ledger/general_ledger.py b/accounts/report/general_ledger/general_ledger.py
index fcf8010..b88d5bc 100644
--- a/accounts/report/general_ledger/general_ledger.py
+++ b/accounts/report/general_ledger/general_ledger.py
@@ -3,36 +3,59 @@
from __future__ import unicode_literals
import webnotes
-from webnotes.utils import flt
+from webnotes.utils import flt, add_days
from webnotes import _
+from accounts.utils import get_balance_on
def execute(filters=None):
- validate_filters(filters)
- columns = get_columns()
+ account_details = webnotes.conn.get_value("Account", filters["account"],
+ ["debit_or_credit", "group_or_ledger"], as_dict=True) if filters.get("account") else None
+ validate_filters(filters, account_details)
+ columns = get_columns()
+ data = []
if filters.get("group_by"):
- data = get_grouped_gle(filters)
+ data += get_grouped_gle(filters)
else:
- data = get_gl_entries(filters)
+ data += get_gl_entries(filters)
if data:
data.append(get_total_row(data))
+ if account_details:
+ data = [get_opening_balance_row(filters, account_details.debit_or_credit)] + data + \
+ [get_closing_balance_row(filters, account_details.debit_or_credit)]
+
return columns, data
-def validate_filters(filters):
- if filters.get("account") and filters.get("group_by") == "Group by Account":
+def validate_filters(filters, account_details):
+ if account_details and account_details.group_or_ledger == "Ledger" \
+ and filters.get("group_by") == "Group by Account":
webnotes.throw(_("Can not filter based on Account, if grouped by Account"))
if filters.get("voucher_no") and filters.get("group_by") == "Group by Voucher":
webnotes.throw(_("Can not filter based on Voucher No, if grouped by Voucher"))
def get_columns():
- return ["Posting Date:Date:100", "Account:Link/Account:200", "Debit:Currency:100",
- "Credit:Currency:100", "Voucher Type::120", "Voucher No::160",
+ return ["Posting Date:Date:100", "Account:Link/Account:200", "Debit:Float:100",
+ "Credit:Float:100", "Voucher Type::120", "Voucher No::160", "Link::20",
"Cost Center:Link/Cost Center:100", "Remarks::200"]
+def get_opening_balance_row(filters, debit_or_credit):
+ opening_balance = get_balance_on(filters["account"], add_days(filters["from_date"], -1))
+ return get_balance_row(opening_balance, debit_or_credit, "Opening Balance")
+
+def get_closing_balance_row(filters, debit_or_credit):
+ closing_balance = get_balance_on(filters["account"], filters["to_date"])
+ return get_balance_row(closing_balance, debit_or_credit, "Closing Balance")
+
+def get_balance_row(balance, debit_or_credit, balance_label):
+ if debit_or_credit == "Debit":
+ return ["", balance_label, balance, 0.0, "", "", ""]
+ else:
+ return ["", balance_label, 0.0, balance, "", "", ""]
+
def get_gl_entries(filters):
- return webnotes.conn.sql("""select
+ gl_entries = webnotes.conn.sql("""select
posting_date, account, debit, credit, voucher_type, voucher_no, cost_center, remarks
from `tabGL Entry`
where company=%(company)s
@@ -40,11 +63,20 @@
{conditions}
order by posting_date, account"""\
.format(conditions=get_conditions(filters)), filters, as_list=1)
+
+ for d in gl_entries:
+ icon = """<a href="%s"><i class="icon icon-share" style="cursor: pointer;"></i></a>""" \
+ % ("/".join(["#Form", d[4], d[5]]),)
+ d.insert(6, icon)
+
+ return gl_entries
def get_conditions(filters):
conditions = []
if filters.get("account"):
- conditions.append("account=%(account)s")
+ lft, rgt = webnotes.conn.get_value("Account", filters["account"], ["lft", "rgt"])
+ conditions.append("""account in (select name from tabAccount
+ where lft>=%s and rgt<=%s and docstatus<2)""" % (lft, rgt))
if filters.get("voucher_no"):
conditions.append("voucher_no=%(voucher_no)s")
diff --git a/accounts/utils.py b/accounts/utils.py
index caad793..8971c80 100644
--- a/accounts/utils.py
+++ b/accounts/utils.py
@@ -16,7 +16,7 @@
def get_fiscal_year(date=None, fiscal_year=None, label="Date", verbose=1):
- return get_fiscal_years(date, fiscal_year, label, verbose=1)[0]
+ return get_fiscal_years(date, fiscal_year, label, verbose)[0]
def get_fiscal_years(date=None, fiscal_year=None, label="Date", verbose=1):
# if year start date is 2012-04-01, year end date should be 2013-03-31 (hence subdate)
diff --git a/config.json b/config.json
index 68782da..b57ba61 100644
--- a/config.json
+++ b/config.json
@@ -1,6 +1,6 @@
{
"app_name": "ERPNext",
- "app_version": "3.3.2",
+ "app_version": "3.3.8",
"base_template": "app/portal/templates/base.html",
"modules": {
"Accounts": {
@@ -74,5 +74,5 @@
"type": "module"
}
},
- "requires_framework_version": "==3.3.1"
+ "requires_framework_version": "==3.3.2"
}
\ No newline at end of file
diff --git a/controllers/buying_controller.py b/controllers/buying_controller.py
index 5e265e7..ec2e574 100644
--- a/controllers/buying_controller.py
+++ b/controllers/buying_controller.py
@@ -107,10 +107,11 @@
item.import_amount = flt(item.import_rate * item.qty,
self.precision("import_amount", item))
item.item_tax_amount = 0.0;
-
+
+ self._set_in_company_currency(item, "import_amount", "amount")
self._set_in_company_currency(item, "import_ref_rate", "purchase_ref_rate")
self._set_in_company_currency(item, "import_rate", "rate")
- self._set_in_company_currency(item, "import_amount", "amount")
+
def calculate_net_total(self):
self.doc.net_total = self.doc.net_total_import = 0.0
@@ -186,14 +187,12 @@
if item.item_code and item.qty:
self.round_floats_in(item)
-
- purchase_rate = item.rate if self.doc.doctype == "Purchase Invoice" else item.purchase_rate
-
+
# if no item code, which is sometimes the case in purchase invoice,
# then it is not possible to track valuation against it
- item.valuation_rate = flt((purchase_rate +
- (item.item_tax_amount + item.rm_supp_cost) / item.qty) / item.conversion_factor,
- self.precision("valuation_rate", item))
+ qty_in_stock_uom = flt(item.qty * item.conversion_factor)
+ item.valuation_rate = ((item.amount + item.item_tax_amount + item.rm_supp_cost)
+ / qty_in_stock_uom)
else:
item.valuation_rate = 0.0
diff --git a/manufacturing/doctype/production_order/production_order.js b/manufacturing/doctype/production_order/production_order.js
index 2277262..31900ea 100644
--- a/manufacturing/doctype/production_order/production_order.js
+++ b/manufacturing/doctype/production_order/production_order.js
@@ -1,28 +1,56 @@
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
-cur_frm.cscript.onload = function(doc, dt, dn) {
- if (!doc.status) doc.status = 'Draft';
- cfn_set_fields(doc, dt, dn);
-}
+$.extend(cur_frm.cscript, {
+ onload: function (doc, dt, dn) {
-cur_frm.cscript.refresh = function(doc, dt, dn) {
- cur_frm.dashboard.reset();
- erpnext.hide_naming_series();
- cur_frm.set_intro("");
- cfn_set_fields(doc, dt, dn);
+ if (!doc.status) doc.status = 'Draft';
+ cfn_set_fields(doc, dt, dn);
- if(doc.docstatus===0 && !doc.__islocal) {
- cur_frm.set_intro(wn._("Submit this Production Order for further processing."));
- } else if(doc.docstatus===1) {
- var percent = flt(doc.produced_qty) / flt(doc.qty) * 100;
- cur_frm.dashboard.add_progress(cint(percent) + "% " + wn._("Complete"), percent);
+ this.frm.add_fetch("sales_order", "delivery_date", "expected_delivery_date");
+ },
- if(doc.status === "Stopped") {
- cur_frm.dashboard.set_headline_alert(wn._("Stopped"), "alert-danger", "icon-stop");
+ refresh: function(doc, dt, dn) {
+ this.frm.dashboard.reset();
+ erpnext.hide_naming_series();
+ this.frm.set_intro("");
+ cfn_set_fields(doc, dt, dn);
+
+ if (doc.docstatus === 0 && !doc.__islocal) {
+ this.frm.set_intro(wn._("Submit this Production Order for further processing."));
+ } else if (doc.docstatus === 1) {
+ var percent = flt(doc.produced_qty) / flt(doc.qty) * 100;
+ this.frm.dashboard.add_progress(cint(percent) + "% " + wn._("Complete"), percent);
+
+ if(doc.status === "Stopped") {
+ this.frm.dashboard.set_headline_alert(wn._("Stopped"), "alert-danger", "icon-stop");
+ }
}
+ },
+
+ production_item: function(doc) {
+ return this.frm.call({
+ method: "get_item_details",
+ args: { item: doc.production_item }
+ });
+ },
+
+ make_se: function(purpose) {
+ var me = this;
+
+ wn.call({
+ method:"manufacturing.doctype.production_order.production_order.make_stock_entry",
+ args: {
+ "production_order_id": me.frm.doc.name,
+ "purpose": purpose
+ },
+ callback: function(r) {
+ var doclist = wn.model.sync(r.message);
+ wn.set_route("Form", doclist[0].doctype, doclist[0].name);
+ }
+ });
}
-}
+});
var cfn_set_fields = function(doc, dt, dn) {
if (doc.docstatus == 1) {
@@ -38,13 +66,6 @@
}
}
-cur_frm.cscript.production_item = function(doc) {
- return cur_frm.call({
- method: "get_item_details",
- args: { item: doc.production_item }
- });
-}
-
cur_frm.cscript['Stop Production Order'] = function() {
var doc = cur_frm.doc;
var check = confirm(wn._("Do you really want to stop production order: " + doc.name));
@@ -57,7 +78,7 @@
var doc = cur_frm.doc;
var check = confirm(wn._("Do really want to unstop production order: " + doc.name));
if (check)
- return $c_obj(make_doclist(doc.doctype, doc.name), 'stop_unstop', 'Unstopped', function(r, rt) {cur_frm.refresh();});
+ return $c_obj(make_doclist(doc.doctype, doc.name), 'stop_unstop', 'Unstopped', function(r, rt) {cur_frm.refresh();});
}
cur_frm.cscript['Transfer Raw Materials'] = function() {
@@ -68,20 +89,6 @@
cur_frm.cscript.make_se('Manufacture/Repack');
}
-cur_frm.cscript.make_se = function(purpose) {
- wn.call({
- method:"manufacturing.doctype.production_order.production_order.make_stock_entry",
- args: {
- "production_order_id": cur_frm.doc.name,
- "purpose": purpose
- },
- callback: function(r) {
- var doclist = wn.model.sync(r.message);
- wn.set_route("Form", doclist[0].doctype, doclist[0].name);
- }
- })
-}
-
cur_frm.fields_dict['production_item'].get_query = function(doc) {
return {
filters:[
@@ -98,7 +105,6 @@
}
}
-
cur_frm.set_query("bom_no", function(doc) {
if (doc.production_item) {
return{
diff --git a/manufacturing/doctype/production_order/production_order.py b/manufacturing/doctype/production_order/production_order.py
index c5b2b04..bcb13f8 100644
--- a/manufacturing/doctype/production_order/production_order.py
+++ b/manufacturing/doctype/production_order/production_order.py
@@ -8,7 +8,6 @@
from webnotes.model.code import get_obj
from webnotes import msgprint, _
-
class OverProductionError(webnotes.ValidationError): pass
class DocType:
@@ -37,15 +36,20 @@
and is_active=1 and item=%s"""
, (self.doc.bom_no, self.doc.production_item), as_dict =1)
if not bom:
- msgprint("""Incorrect BOM: %s entered.
+ webnotes.throw("""Incorrect BOM: %s entered.
May be BOM not exists or inactive or not submitted
- or for some other item.""" % cstr(self.doc.bom_no), raise_exception=1)
+ or for some other item.""" % cstr(self.doc.bom_no))
def validate_sales_order(self):
if self.doc.sales_order:
- if not webnotes.conn.sql("""select name from `tabSales Order`
- where name=%s and docstatus = 1""", self.doc.sales_order):
- msgprint("Sales Order: %s is not valid" % self.doc.sales_order, raise_exception=1)
+ so = webnotes.conn.sql("""select name, delivery_date from `tabSales Order`
+ where name=%s and docstatus = 1""", self.doc.sales_order, as_dict=1)[0]
+
+ if not so.name:
+ webnotes.throw("Sales Order: %s is not valid" % self.doc.sales_order)
+
+ if not self.doc.expected_delivery_date:
+ self.doc.expected_delivery_date = so.delivery_date
self.validate_production_order_against_so()
@@ -76,11 +80,11 @@
so_qty = flt(so_item_qty) + flt(dnpi_qty)
if total_qty > so_qty:
- webnotes.msgprint(_("Total production order qty for item") + ": " +
+ webnotes.throw(_("Total production order qty for item") + ": " +
cstr(self.doc.production_item) + _(" against sales order") + ": " +
cstr(self.doc.sales_order) + _(" will be ") + cstr(total_qty) + ", " +
_("which is greater than sales order qty ") + "(" + cstr(so_qty) + ")" +
- _("Please reduce qty."), raise_exception=OverProductionError)
+ _("Please reduce qty."), exc=OverProductionError)
def stop_unstop(self, status):
""" Called from client side on Stop/Unstop event"""
@@ -114,8 +118,8 @@
stock_entry = webnotes.conn.sql("""select name from `tabStock Entry`
where production_order = %s and docstatus = 1""", self.doc.name)
if stock_entry:
- msgprint("""Submitted Stock Entry %s exists against this production order.
- Hence can not be cancelled.""" % stock_entry[0][0], raise_exception=1)
+ webnotes.throw("""Submitted Stock Entry %s exists against this production order.
+ Hence can not be cancelled.""" % stock_entry[0][0])
webnotes.conn.set(self.doc,'status', 'Cancelled')
self.update_planned_qty(-self.doc.qty)
diff --git a/manufacturing/doctype/production_order/production_order.txt b/manufacturing/doctype/production_order/production_order.txt
index 85c7c83..5e76c0e 100644
--- a/manufacturing/doctype/production_order/production_order.txt
+++ b/manufacturing/doctype/production_order/production_order.txt
@@ -2,7 +2,7 @@
{
"creation": "2013-01-10 16:34:16",
"docstatus": 0,
- "modified": "2013-11-02 14:05:44",
+ "modified": "2013-12-18 13:22:14",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -137,6 +137,14 @@
"read_only": 1
},
{
+ "depends_on": "sales_order",
+ "doctype": "DocField",
+ "fieldname": "expected_delivery_date",
+ "fieldtype": "Date",
+ "label": "Expected Delivery Date",
+ "read_only": 1
+ },
+ {
"doctype": "DocField",
"fieldname": "warehouses",
"fieldtype": "Section Break",
diff --git a/manufacturing/doctype/production_planning_tool/production_planning_tool.py b/manufacturing/doctype/production_planning_tool/production_planning_tool.py
index c1185a1..29232f5 100644
--- a/manufacturing/doctype/production_planning_tool/production_planning_tool.py
+++ b/manufacturing/doctype/production_planning_tool/production_planning_tool.py
@@ -186,7 +186,6 @@
else :
msgprint(_("No Production Order created."))
-
def get_distinct_items_and_boms(self):
""" Club similar BOM and item for processing
bom_dict {
diff --git a/patches/1311/p07_scheduler_errors_digest.py b/patches/1311/p07_scheduler_errors_digest.py
index 6811571..4527f18 100644
--- a/patches/1311/p07_scheduler_errors_digest.py
+++ b/patches/1311/p07_scheduler_errors_digest.py
@@ -13,11 +13,9 @@
return
# no default company
- company = webnotes.conn.get_default("company")
- if not company:
- company = webnotes.conn.sql_list("select name from `tabCompany`")
- if company:
- company = company[0]
+ company = webnotes.conn.sql_list("select name from `tabCompany`")
+ if company:
+ company = company[0]
if not company:
return
@@ -31,4 +29,4 @@
"recipient_list": "\n".join(system_managers),
"scheduler_errors": 1
})
- edigest.insert()
\ No newline at end of file
+ edigest.insert()
diff --git a/patches/1312/p02_update_item_details_in_item_price.py b/patches/1312/p02_update_item_details_in_item_price.py
new file mode 100644
index 0000000..c19988c
--- /dev/null
+++ b/patches/1312/p02_update_item_details_in_item_price.py
@@ -0,0 +1,10 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import webnotes
+
+def execute():
+ webnotes.conn.sql("""update `tabItem Price` ip INNER JOIN `tabItem` i
+ ON (ip.item_code = i.name)
+ set ip.item_name = i.item_name, ip.item_description = i.description""")
\ No newline at end of file
diff --git a/patches/july_2013/p07_repost_billed_amt_in_sales_cycle.py b/patches/july_2013/p07_repost_billed_amt_in_sales_cycle.py
index 6419315..95004c0 100644
--- a/patches/july_2013/p07_repost_billed_amt_in_sales_cycle.py
+++ b/patches/july_2013/p07_repost_billed_amt_in_sales_cycle.py
@@ -5,7 +5,8 @@
def execute():
import webnotes
+ webnotes.reload_doc('stock', 'doctype', 'packed_item')
for si in webnotes.conn.sql("""select name from `tabSales Invoice` where docstatus = 1"""):
webnotes.get_obj("Sales Invoice", si[0],
with_children=1).update_qty(change_modified=False)
- webnotes.conn.commit()
\ No newline at end of file
+ webnotes.conn.commit()
diff --git a/patches/patch_list.py b/patches/patch_list.py
index 2782c58..608ba77 100644
--- a/patches/patch_list.py
+++ b/patches/patch_list.py
@@ -261,4 +261,6 @@
"execute:webnotes.delete_doc('Report', 'Payment Made With Ageing')",
"patches.1311.p07_scheduler_errors_digest",
"patches.1311.p08_email_digest_recipients",
+ "execute:webnotes.delete_doc('DocType', 'Warehouse Type')",
+ "patches.1312.p02_update_item_details_in_item_price",
]
\ No newline at end of file
diff --git a/public/js/account_tree_grid.js b/public/js/account_tree_grid.js
index 44bef57..1cd9aa6 100644
--- a/public/js/account_tree_grid.js
+++ b/public/js/account_tree_grid.js
@@ -26,9 +26,10 @@
show: true,
parent_field: "parent_account",
formatter: function(item) {
- return repl('<a href="#general-ledger/account=%(enc_value)s">%(value)s</a>', {
+ return repl("<a \
+ onclick='wn.cur_grid_report.show_general_ledger(\"%(value)s\")'>\
+ %(value)s</a>", {
value: item.name,
- enc_value: encodeURIComponent(item.name)
});
}
},
@@ -211,4 +212,14 @@
return;
}
},
+
+ show_general_ledger: function(account) {
+ wn.route_options = {
+ account: account,
+ company: this.company,
+ from_date: this.from_date,
+ to_date: this.to_date
+ };
+ wn.set_route("query-report", "General Ledger");
+ }
});
\ No newline at end of file
diff --git a/public/js/controllers/stock_controller.js b/public/js/controllers/stock_controller.js
index ee5c497..d2fb904 100644
--- a/public/js/controllers/stock_controller.js
+++ b/public/js/controllers/stock_controller.js
@@ -11,9 +11,10 @@
wn.route_options = {
voucher_no: me.frm.doc.name,
from_date: me.frm.doc.posting_date,
- to_date: me.frm.doc.posting_date
+ to_date: me.frm.doc.posting_date,
+ company: me.frm.doc.company
};
- wn.set_route('stock-ledger');
+ wn.set_route("query-report", "Stock Ledger");
}, "icon-bar-chart");
}
@@ -24,11 +25,12 @@
if(this.frm.doc.docstatus===1 && cint(wn.defaults.get_default("auto_accounting_for_stock"))) {
cur_frm.appframe.add_button(wn._('Accounting Ledger'), function() {
wn.route_options = {
- "voucher_no": me.frm.doc.name,
- "from_date": me.frm.doc.posting_date,
- "to_date": me.frm.doc.posting_date,
+ voucher_no: me.frm.doc.name,
+ from_date: me.frm.doc.posting_date,
+ to_date: me.frm.doc.posting_date,
+ company: me.frm.doc.company
};
- wn.set_route("general-ledger");
+ wn.set_route("query-report", "General Ledger");
}, "icon-table");
}
},
diff --git a/public/js/stock_analytics.js b/public/js/stock_analytics.js
index 832cac5..8b68d39 100644
--- a/public/js/stock_analytics.js
+++ b/public/js/stock_analytics.js
@@ -17,10 +17,10 @@
parent_field: "parent_item_group",
formatter: function(item) {
if(!item.is_group) {
- return repl('<a href="#stock-ledger/item_code=%(enc_value)s">%(value)s</a>',
- {
+ return repl("<a \
+ onclick='wn.cur_grid_report.show_stock_ledger(\"%(value)s\")'>\
+ %(value)s</a>", {
value: item.name,
- enc_value: encodeURIComponent(item.name)
});
} else {
return item.name;
@@ -183,5 +183,13 @@
},
get_plot_points: function(item, col, idx) {
return [[dateutil.user_to_obj(col.name).getTime(), item[col.field]]]
+ },
+ show_stock_ledger: function(item_code) {
+ wn.route_options = {
+ item_code: item_code,
+ from_date: this.from_date,
+ to_date: this.to_date
+ };
+ wn.set_route("query-report", "Stock Ledger");
}
});
\ No newline at end of file
diff --git a/setup/doctype/email_digest/email_digest.py b/setup/doctype/email_digest/email_digest.py
index 082b92f..f01c8a8 100644
--- a/setup/doctype/email_digest/email_digest.py
+++ b/setup/doctype/email_digest/email_digest.py
@@ -9,6 +9,7 @@
from webnotes.utils.dateutils import datetime_in_user_format
from datetime import timedelta
from dateutil.relativedelta import relativedelta
+from webnotes.utils.email_lib import sendmail
content_sequence = [
["Income / Expenses", ["income_year_to_date", "bank_balance",
@@ -80,15 +81,15 @@
for user_id in recipients:
msg_for_this_receipient = self.get_msg_html(self.get_user_specific_content(user_id) + \
common_msg)
- from webnotes.utils.email_lib import sendmail
- sendmail(recipients=user_id,
- subject="[ERPNext] [{frequency} Digest] {name}".format(
- frequency=self.doc.frequency, name=self.doc.name),
- msg=msg_for_this_receipient)
+ if msg_for_this_receipient:
+ sendmail(recipients=user_id,
+ subject="[ERPNext] [{frequency} Digest] {name}".format(
+ frequency=self.doc.frequency, name=self.doc.name),
+ msg=msg_for_this_receipient)
def get_digest_msg(self):
return self.get_msg_html(self.get_user_specific_content(webnotes.session.user) + \
- self.get_common_content())
+ self.get_common_content(), send_only_if_updates=False)
def get_common_content(self):
out = []
@@ -119,14 +120,19 @@
return out
- def get_msg_html(self, out):
+ def get_msg_html(self, out, send_only_if_updates=True):
with_value = [o[1] for o in out if o[0]]
if with_value:
+ has_updates = True
with_value = "\n".join(with_value)
else:
+ has_updates = False
with_value = "<p>There were no updates in the items selected for this digest.</p><hr>"
+ if not has_updates and send_only_if_updates:
+ return
+
# seperate out no value items
no_value = [o[1] for o in out if not o[0]]
if no_value:
diff --git a/setup/doctype/features_setup/features_setup.txt b/setup/doctype/features_setup/features_setup.txt
index 3f73ee2..d68f489 100644
--- a/setup/doctype/features_setup/features_setup.txt
+++ b/setup/doctype/features_setup/features_setup.txt
@@ -2,7 +2,7 @@
{
"creation": "2012-12-20 12:50:49",
"docstatus": 0,
- "modified": "2013-11-03 14:20:18",
+ "modified": "2013-12-24 11:40:19",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -90,7 +90,7 @@
"doctype": "DocField",
"fieldname": "fs_packing_details",
"fieldtype": "Check",
- "label": "Packing Detials"
+ "label": "Packing Details"
},
{
"description": "To get Item Group in details table",
diff --git a/setup/page/setup_wizard/setup_wizard.py b/setup/page/setup_wizard/setup_wizard.py
index 962f600..c1d3571 100644
--- a/setup/page/setup_wizard/setup_wizard.py
+++ b/setup/page/setup_wizard/setup_wizard.py
@@ -158,6 +158,10 @@
hr_settings.doc.emp_created_by = "Naming Series"
hr_settings.save()
+ email_settings = webnotes.bean("Email Settings")
+ email_settings.doc.send_print_in_body_and_attachment = 1
+ email_settings.save()
+
# control panel
cp = webnotes.doc("Control Panel", "Control Panel")
cp.company_name = args["company_name"]
diff --git a/stock/doctype/delivery_note/test_delivery_note.py b/stock/doctype/delivery_note/test_delivery_note.py
index 64ddff8..ae69db6 100644
--- a/stock/doctype/delivery_note/test_delivery_note.py
+++ b/stock/doctype/delivery_note/test_delivery_note.py
@@ -58,11 +58,6 @@
self.assertEqual(stock_value, 0)
self.assertEqual(stock_value_difference, -375)
-
- gl_entries = webnotes.conn.sql("""select account, debit, credit
- from `tabGL Entry` where voucher_type='Delivery Note' and voucher_no=%s
- order by account desc""", dn.doc.name, as_dict=1)
-
self.assertFalse(get_gl_entries("Delivery Note", dn.doc.name))
def test_delivery_note_gl_entry(self):
@@ -111,8 +106,8 @@
gl_entries = get_gl_entries("Delivery Note", dn.doc.name)
self.assertTrue(gl_entries)
expected_values = {
- stock_in_hand_account: [0.0, 666.65],
- "Cost of Goods Sold - _TC": [666.65, 0.0]
+ stock_in_hand_account: [0.0, 666.67],
+ "Cost of Goods Sold - _TC": [666.67, 0.0]
}
for i, gle in enumerate(gl_entries):
self.assertEquals([gle.debit, gle.credit], expected_values.get(gle.account))
diff --git a/stock/doctype/item/item.py b/stock/doctype/item/item.py
index 0c4d5dd..531876e 100644
--- a/stock/doctype/item/item.py
+++ b/stock/doctype/item/item.py
@@ -49,6 +49,7 @@
def on_update(self):
self.validate_name_with_item_group()
self.update_website()
+ self.update_item_price()
def check_warehouse_is_set_for_stock_item(self):
if self.doc.is_stock_item=="Yes" and not self.doc.default_warehouse:
@@ -123,14 +124,14 @@
msgprint("'Has Serial No' can not be 'Yes' for non-stock item", raise_exception=1)
def check_for_active_boms(self):
- if self.doc.is_active != "Yes" or self.doc.is_purchase_item != "Yes":
+ if self.doc.is_purchase_item != "Yes":
bom_mat = webnotes.conn.sql("""select distinct t1.parent
from `tabBOM Item` t1, `tabBOM` t2 where t2.name = t1.parent
and t1.item_code =%s and ifnull(t1.bom_no, '') = '' and t2.is_active = 1
and t2.docstatus = 1 and t1.docstatus =1 """, self.doc.name)
if bom_mat and bom_mat[0][0]:
- webnotes.throw(_("Item must be active and purchase item, \
+ webnotes.throw(_("Item must be a purchase item, \
as it is present in one or many Active BOMs"))
if self.doc.is_manufactured_item != "Yes":
@@ -210,6 +211,11 @@
WebsiteGenerator.on_update(self)
+ def update_item_price(self):
+ webnotes.conn.sql("""update `tabItem Price` set item_name=%s,
+ item_description=%s, modified=NOW() where item_code=%s""",
+ (self.doc.item_name, self.doc.description, self.doc.name))
+
def get_page_title(self):
if self.doc.name==self.doc.item_name:
page_name_from = self.doc.name
@@ -246,6 +252,9 @@
def before_rename(self, olddn, newdn, merge=False):
if merge:
# Validate properties before merging
+ if not webnotes.conn.exists("Item", newdn):
+ webnotes.throw(_("Item ") + newdn +_(" does not exists"))
+
field_list = ["stock_uom", "is_stock_item", "has_serial_no", "has_batch_no"]
new_properties = [cstr(d) for d in webnotes.conn.get_value("Item", newdn, field_list)]
if new_properties != [cstr(self.doc.fields[fld]) for fld in field_list]:
diff --git a/stock/doctype/material_request_item/material_request_item.txt b/stock/doctype/material_request_item/material_request_item.txt
index 15884a3..e0b9330 100644
--- a/stock/doctype/material_request_item/material_request_item.txt
+++ b/stock/doctype/material_request_item/material_request_item.txt
@@ -2,7 +2,7 @@
{
"creation": "2013-02-22 01:28:02",
"docstatus": 0,
- "modified": "2013-11-03 20:36:45",
+ "modified": "2013-12-18 14:52:02",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -219,7 +219,7 @@
"fieldname": "sales_order_no",
"fieldtype": "Link",
"label": "Sales Order No",
- "no_copy": 1,
+ "no_copy": 0,
"options": "Sales Order",
"print_hide": 1,
"read_only": 1
diff --git a/stock/doctype/price_list/price_list.py b/stock/doctype/price_list/price_list.py
index 226b9da..d0e5d2b 100644
--- a/stock/doctype/price_list/price_list.py
+++ b/stock/doctype/price_list/price_list.py
@@ -44,5 +44,5 @@
def update_item_price(self):
webnotes.conn.sql("""update `tabItem Price` set currency=%s,
- buying_or_selling=%s where price_list=%s""",
+ buying_or_selling=%s, modified=NOW() where price_list=%s""",
(self.doc.currency, self.doc.buying_or_selling, self.doc.name))
\ No newline at end of file
diff --git a/stock/doctype/stock_entry/stock_entry.py b/stock/doctype/stock_entry/stock_entry.py
index ba0e724..2e7e2a4 100644
--- a/stock/doctype/stock_entry/stock_entry.py
+++ b/stock/doctype/stock_entry/stock_entry.py
@@ -287,9 +287,15 @@
# validate quantity <= ref item's qty - qty already returned
ref_item = ref.doclist.getone({"item_code": item.item_code})
returnable_qty = ref_item.qty - flt(already_returned_item_qty.get(item.item_code))
- self.validate_value("transfer_qty", "<=", returnable_qty, item,
- raise_exception=StockOverReturnError)
-
+ if not returnable_qty:
+ webnotes.throw("{item}: {item_code} {returned}".format(
+ item=_("Item"), item_code=item.item_code,
+ returned=_("already returned though some other documents")))
+ elif item.transfer_qty > returnable_qty:
+ webnotes.throw("{item}: {item_code}, {returned}: {qty}".format(
+ item=_("Item"), item_code=item.item_code,
+ returned=_("Max Returnable Qty"), qty=returnable_qty))
+
def get_already_returned_item_qty(self, ref_fieldname):
return dict(webnotes.conn.sql("""select item_code, sum(transfer_qty) as qty
from `tabStock Entry Detail` where parent in (
diff --git a/stock/doctype/warehouse/warehouse.py b/stock/doctype/warehouse/warehouse.py
index 3559960..db4ee40 100644
--- a/stock/doctype/warehouse/warehouse.py
+++ b/stock/doctype/warehouse/warehouse.py
@@ -20,6 +20,19 @@
if self.doc.email_id and not validate_email_add(self.doc.email_id):
msgprint("Please enter valid Email Id", raise_exception=1)
+ self.update_parent_account()
+
+ def update_parent_account(self):
+ if not self.doc.__islocal and (self.doc.create_account_under !=
+ webnotes.conn.get_value("Warehouse", self.doc.name, "create_account_under")):
+ warehouse_account = webnotes.conn.get_value("Account",
+ {"account_type": "Warehouse", "company": self.doc.company,
+ "master_name": self.doc.name}, ["name", "parent_account"])
+ if warehouse_account and warehouse_account[1] != self.doc.create_account_under:
+ acc_bean = webnotes.bean("Account", warehouse_account[0])
+ acc_bean.doc.parent_account = self.doc.create_account_under
+ acc_bean.save()
+
def on_update(self):
self.create_account_head()
@@ -84,6 +97,9 @@
new_warehouse = get_name_with_abbr(newdn, self.doc.company)
if merge:
+ if not webnotes.conn.exists("Warehouse", newdn):
+ webnotes.throw(_("Warehouse ") + newdn +_(" does not exists"))
+
if self.doc.company != webnotes.conn.get_value("Warehouse", new_warehouse, "company"):
webnotes.throw(_("Both Warehouse must belong to same Company"))
diff --git a/stock/report/item_prices/item_prices.py b/stock/report/item_prices/item_prices.py
index 9a25c13..da8b500 100644
--- a/stock/report/item_prices/item_prices.py
+++ b/stock/report/item_prices/item_prices.py
@@ -15,8 +15,8 @@
bom_rate = get_item_bom_rate()
val_rate_map = get_valuation_rate()
- precision = webnotes.conn.get_value("Global Defaults", None, "float_precision") or 2
-
+ precision = get_currency_precision or 2
+
data = []
for item in sorted(item_map):
data.append([item, item_map[item]["item_name"],
@@ -30,6 +30,14 @@
])
return columns, data
+
+def get_currency_precision():
+ company_currency = webnotes.conn.get_value("Company",
+ webnotes.conn.get_default("company"), "default_currency")
+ currency_format = webnotes.conn.get_value("Currency", company_currency, "number_format")
+
+ from webnotes.utils import get_number_format_info
+ return get_number_format_info(currency_format)[2]
def get_columns(filters):
"""return columns based on filters"""
diff --git a/stock/report/stock_ageing/stock_ageing.py b/stock/report/stock_ageing/stock_ageing.py
index defe724..1a84f93 100644
--- a/stock/report/stock_ageing/stock_ageing.py
+++ b/stock/report/stock_ageing/stock_ageing.py
@@ -67,7 +67,7 @@
item.name, item.item_name, item_group, brand, description, item.stock_uom,
actual_qty, posting_date
from `tabStock Ledger Entry` sle,
- (select name, item_name, description, stock_uom, brand
+ (select name, item_name, description, stock_uom, brand, item_group
from `tabItem` {item_conditions}) item
where item_code = item.name and
company = %(company)s and
diff --git a/stock/report/stock_ledger/stock_ledger.py b/stock/report/stock_ledger/stock_ledger.py
index ea1a60f..38308c2 100644
--- a/stock/report/stock_ledger/stock_ledger.py
+++ b/stock/report/stock_ledger/stock_ledger.py
@@ -3,7 +3,6 @@
from __future__ import unicode_literals
import webnotes
-from webnotes import _
def execute(filters=None):
columns = get_columns()
@@ -13,10 +12,14 @@
data = []
for sle in sl_entries:
item_detail = item_details[sle.item_code]
+ voucher_link_icon = """<a href="%s"><i class="icon icon-share"
+ style="cursor: pointer;"></i></a>""" \
+ % ("/".join(["#Form", sle.voucher_type, sle.voucher_no]),)
+
data.append([sle.date, sle.item_code, item_detail.item_name, item_detail.item_group,
item_detail.brand, item_detail.description, sle.warehouse, item_detail.stock_uom,
sle.actual_qty, sle.qty_after_transaction, sle.stock_value, sle.voucher_type,
- sle.voucher_no, sle.batch_no, sle.serial_no, sle.company])
+ sle.voucher_no, voucher_link_icon, sle.batch_no, sle.serial_no, sle.company])
return columns, data
@@ -25,17 +28,10 @@
"Item Group:Link/Item Group:100", "Brand:Link/Brand:100",
"Description::200", "Warehouse:Link/Warehouse:100",
"Stock UOM:Link/UOM:100", "Qty:Float:50", "Balance Qty:Float:80",
- "Balance Value:Currency:100", "Voucher Type::100", "Voucher #::100",
+ "Balance Value:Currency:100", "Voucher Type::100", "Voucher #::100", "Link::30",
"Batch:Link/Batch:100", "Serial #:Link/Serial No:100", "Company:Link/Company:100"]
def get_stock_ledger_entries(filters):
- if not filters.get("company"):
- webnotes.throw(_("Company is mandatory"))
- if not filters.get("from_date"):
- webnotes.throw(_("From Date is mandatory"))
- if not filters.get("to_date"):
- webnotes.throw(_("To Date is mandatory"))
-
return webnotes.conn.sql("""select concat_ws(" ", posting_date, posting_time) as date,
item_code, warehouse, actual_qty, qty_after_transaction,
stock_value, voucher_type, voucher_no, batch_no, serial_no, company
@@ -66,6 +62,10 @@
def get_sle_conditions(filters):
conditions = []
+ item_conditions=get_item_conditions(filters)
+ if item_conditions:
+ conditions.append("""item_code in (select name from tabItem
+ {item_conditions})""".format(item_conditions=item_conditions))
if filters.get("warehouse"):
conditions.append("warehouse=%(warehouse)s")
if filters.get("voucher_no"):
diff --git a/stock/report/stock_projected_qty/stock_projected_qty.js b/stock/report/stock_projected_qty/stock_projected_qty.js
index a0ad755..8c25e5d 100644
--- a/stock/report/stock_projected_qty/stock_projected_qty.js
+++ b/stock/report/stock_projected_qty/stock_projected_qty.js
@@ -7,9 +7,7 @@
"fieldname":"company",
"label": wn._("Company"),
"fieldtype": "Link",
- "options": "Company",
- "default": wn.defaults.get_user_default("company"),
- "reqd": 1
+ "options": "Company"
},
{
"fieldname":"warehouse",
diff --git a/stock/report/stock_projected_qty/stock_projected_qty.py b/stock/report/stock_projected_qty/stock_projected_qty.py
index 11d5195..d335ebf 100644
--- a/stock/report/stock_projected_qty/stock_projected_qty.py
+++ b/stock/report/stock_projected_qty/stock_projected_qty.py
@@ -13,23 +13,23 @@
projected_qty, item.re_order_level, item.re_order_qty
from `tabBin` bin,
(select name, company from tabWarehouse
- where company=%(company)s {warehouse_conditions}) wh,
+ {warehouse_conditions}) wh,
(select name, item_name, description, stock_uom, item_group,
brand, re_order_level, re_order_qty
from `tabItem` {item_conditions}) item
where item_code = item.name and warehouse = wh.name
order by item.name, wh.name"""\
.format(item_conditions=get_item_conditions(filters),
- warehouse_conditions=get_warehouse_conditions(filters)), filters, debug=1)
+ warehouse_conditions=get_warehouse_conditions(filters)), filters)
return columns, data
def get_columns():
return ["Item Code:Link/Item:140", "Item Name::100", "Description::200",
- "Brand:Link/Brand:100", "Warehouse:Link/Warehouse:120", "UOM:Link/UOM:100",
- "Actual Qty:Float:100", "Planned Qty:Float:100", "Requested Qty:Float:110",
- "Ordered Qty:Float:100", "Reserved Qty:Float:100", "Projected Qty:Float:100",
- "Reorder Level:Float:100", "Reorder Qty:Float:100"]
+ "Item Group:Link/Item Group:100", "Brand:Link/Brand:100", "Warehouse:Link/Warehouse:120",
+ "UOM:Link/UOM:100", "Actual Qty:Float:100", "Planned Qty:Float:100",
+ "Requested Qty:Float:110", "Ordered Qty:Float:100", "Reserved Qty:Float:100",
+ "Projected Qty:Float:100", "Reorder Level:Float:100", "Reorder Qty:Float:100"]
def get_item_conditions(filters):
conditions = []
@@ -41,4 +41,10 @@
return "where {}".format(" and ".join(conditions)) if conditions else ""
def get_warehouse_conditions(filters):
- return " and name=%(warehouse)s" if filters.get("warehouse") else ""
\ No newline at end of file
+ conditions = []
+ if filters.get("company"):
+ conditions.append("company=%(company)s")
+ if filters.get("warehouse"):
+ conditions.append("name=%(warehouse)s")
+
+ return "where {}".format(" and ".join(conditions)) if conditions else ""
\ No newline at end of file
diff --git a/stock/stock_ledger.py b/stock/stock_ledger.py
index 8702744..980dcd6 100644
--- a/stock/stock_ledger.py
+++ b/stock/stock_ledger.py
@@ -113,7 +113,14 @@
(qty_after_transaction * valuation_rate) or 0
else:
stock_value = sum((flt(batch[0]) * flt(batch[1]) for batch in stock_queue))
-
+
+ # rounding as per precision
+ from webnotes.model.meta import get_field_precision
+ meta = webnotes.get_doctype("Stock Ledger Entry")
+
+ stock_value = flt(stock_value, get_field_precision(meta.get_field("stock_value"),
+ webnotes._dict({"fields": sle})))
+
stock_value_difference = stock_value - prev_stock_value
prev_stock_value = stock_value