Merge pull request #1691 from nabinhait/v4-hotfix

build_filter_conditions and pricing rules
This commit is contained in:
Anand Doshi 2014-05-30 11:51:36 +05:30
commit 852e7cdb41
54 changed files with 653 additions and 529 deletions

View File

@ -64,13 +64,3 @@ class CForm(Document):
'net_total' : inv.net_total,
'grand_total' : inv.grand_total
}
def get_invoice_nos(doctype, txt, searchfield, start, page_len, filters):
from erpnext.utilities import build_filter_conditions
conditions, filter_values = build_filter_conditions(filters)
return frappe.db.sql("""select name from `tabSales Invoice` where docstatus = 1
and c_form_applicable = 'Yes' and ifnull(c_form_no, '') = '' %s
and %s like %s order by name limit %s, %s""" %
(conditions, searchfield, "%s", "%s", "%s"),
tuple(filter_values + ["%%%s%%" % txt, start, page_len]))

View File

@ -0,0 +1,64 @@
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
frappe.ui.form.on("Pricing Rule", "refresh", function(frm) {
var help_content = ['<table class="table table-bordered" style="background-color: #f9f9f9;">',
'<tr><td>',
'<h4><i class="icon-hand-right"></i> ',
__('Notes'),
':</h4>',
'<ul>',
'<li>',
__("Pricing Rule is made to overwrite Price List / define discount percentage, based on some criteria."),
'</li>',
'<li>',
__("If selected Pricing Rule is made for 'Price', it will overwrite Price List. Pricing Rule price is the final price, so no further discount should be applied. Hence, in transactions like Sales Order, Purchase Order etc, it will be fetched in 'Rate' field, rather than 'Price List Rate' field."),
'</li>',
'<li>',
__('Discount Percentage can be applied either against a Price List or for all Price List.'),
'</li>',
'<li>',
__('To not apply Pricing Rule in a particular transaction, all applicable Pricing Rules should be disabled.'),
'</li>',
'</ul>',
'</td></tr>',
'<tr><td>',
'<h4><i class="icon-question-sign"></i> ',
__('How Pricing Rule is applied?'),
'</h4>',
'<ol>',
'<li>',
__("Pricing Rule is first selected based on 'Apply On' field, which can be Item, Item Group or Brand."),
'</li>',
'<li>',
__("Then Pricing Rules are filtered out based on Customer, Customer Group, Territory, Supplier, Supplier Type, Campaign, Sales Partner etc."),
'</li>',
'<li>',
__('Pricing Rules are further filtered based on quantity.'),
'</li>',
'<li>',
__('If two or more Pricing Rules are found based on the above conditions, Priority is applied. Priority is a number between 0 to 20 while default value is zero (blank). Higher number means it will take precedence if there are multiple Pricing Rules with same conditions.'),
'</li>',
'<li>',
__('Even if there are multiple Pricing Rules with highest priority, then following internal priorities are applied:'),
'<ul>',
'<li>',
__('Item Code > Item Group > Brand'),
'</li>',
'<li>',
__('Customer > Customer Group > Territory'),
'</li>',
'<li>',
__('Supplier > Supplier Type'),
'</li>',
'</ul>',
'</li>',
'<li>',
__('If multiple Pricing Rules continue to prevail, users are asked to set Priority manually to resolve conflict.'),
'</li>',
'</ol>',
'</td></tr>',
'</table>'].join("\n");
set_field_options("pricing_rule_help", help_content);
});

View File

@ -131,6 +131,13 @@
"fieldtype": "Column Break",
"permlevel": 0
},
{
"fieldname": "company",
"fieldtype": "Link",
"label": "Company",
"options": "Company",
"permlevel": 0
},
{
"default": "Today",
"fieldname": "valid_from",
@ -198,12 +205,25 @@
"label": "For Price List",
"options": "Price List",
"permlevel": 0
},
{
"fieldname": "help_section",
"fieldtype": "Section Break",
"label": "",
"options": "Simple",
"permlevel": 0
},
{
"fieldname": "pricing_rule_help",
"fieldtype": "HTML",
"label": "Pricing Rule Help",
"permlevel": 0
}
],
"icon": "icon-gift",
"idx": 1,
"istable": 0,
"modified": "2014-05-12 16:24:52.005162",
"modified": "2014-05-28 15:36:29.403659",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Pricing Rule",

View File

@ -15,7 +15,6 @@ class PricingRule(Document):
self.validate_min_max_qty()
self.cleanup_fields_value()
def validate_mandatory(self):
for field in ["apply_on", "applicable_for", "price_or_discount"]:
tocheck = frappe.scrub(self.get(field) or "")
@ -26,7 +25,6 @@ class PricingRule(Document):
if self.min_qty and self.max_qty and flt(self.min_qty) > flt(self.max_qty):
throw(_("Min Qty can not be greater than Max Qty"))
def cleanup_fields_value(self):
for logic_field in ["apply_on", "applicable_for", "price_or_discount"]:
fieldname = frappe.scrub(self.get(logic_field) or "")

View File

@ -20,6 +20,7 @@ class TestPricingRule(unittest.TestCase):
"price_or_discount": "Discount Percentage",
"price": 0,
"discount_percentage": 10,
"company": "_Test Company"
}
frappe.get_doc(test_record.copy()).insert()
@ -36,7 +37,6 @@ class TestPricingRule(unittest.TestCase):
"transaction_type": "selling",
"customer": "_Test Customer",
})
details = get_item_details(args)
self.assertEquals(details.get("discount_percentage"), 10)
@ -72,7 +72,7 @@ class TestPricingRule(unittest.TestCase):
frappe.db.sql("update `tabPricing Rule` set priority=NULL where campaign='_Test Campaign'")
from erpnext.stock.get_item_details import MultiplePricingRuleConflict
self.assertRaises (MultiplePricingRuleConflict, get_item_details, args)
self.assertRaises(MultiplePricingRuleConflict, get_item_details, args)
args.item_code = "_Test Item 2"
details = get_item_details(args)

View File

@ -77,16 +77,19 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
},
supplier: function() {
var me = this;
if(this.frm.updating_party_details)
return;
erpnext.utils.get_party_details(this.frm,
"erpnext.accounts.party.get_party_details", {
erpnext.utils.get_party_details(this.frm, "erpnext.accounts.party.get_party_details",
{
posting_date: this.frm.doc.posting_date,
party: this.frm.doc.supplier,
party_type: "Supplier",
account: this.frm.doc.debit_to,
price_list: this.frm.doc.buying_price_list,
})
}, function() {
me.apply_pricing_rule();
})
},
credit_to: function() {

View File

@ -1,6 +1,6 @@
{
"autoname": "EVD.######",
"creation": "2013-05-22 12:43:10.000000",
"creation": "2013-05-22 12:43:10",
"docstatus": 0,
"doctype": "DocType",
"fields": [
@ -193,17 +193,9 @@
"reqd": 1
},
{
"fieldname": "pricing_rule_for_price",
"fieldname": "pricing_rule",
"fieldtype": "Link",
"label": "Pricing Rule For Price",
"options": "Pricing Rule",
"permlevel": 0,
"read_only": 1
},
{
"fieldname": "pricing_rule_for_discount",
"fieldtype": "Link",
"label": "Pricing Rule For Discount",
"label": "Pricing Rule",
"options": "Pricing Rule",
"permlevel": 0,
"read_only": 1
@ -429,9 +421,12 @@
],
"idx": 1,
"istable": 1,
"modified": "2014-02-28 11:27:53.000000",
"modified": "2014-05-28 12:43:40.647183",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice Item",
"owner": "Administrator"
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",
"sort_order": "DESC"
}

View File

@ -155,8 +155,9 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
},
customer: function() {
if(this.frm.updating_party_details)
return;
var me = this;
if(this.frm.updating_party_details) return;
erpnext.utils.get_party_details(this.frm,
"erpnext.accounts.party.get_party_details", {
posting_date: this.frm.doc.posting_date,
@ -164,7 +165,9 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
party_type: "Customer",
account: this.frm.doc.debit_to,
price_list: this.frm.doc.selling_price_list,
})
}, function() {
me.apply_pricing_rule();
})
},
debit_to: function() {

View File

@ -1,6 +1,6 @@
{
"autoname": "INVD.######",
"creation": "2013-06-04 11:02:19.000000",
"creation": "2013-06-04 11:02:19",
"docstatus": 0,
"doctype": "DocType",
"fields": [
@ -201,17 +201,9 @@
"reqd": 1
},
{
"fieldname": "pricing_rule_for_price",
"fieldname": "pricing_rule",
"fieldtype": "Link",
"label": "Pricing Rule For Price",
"options": "Pricing Rule",
"permlevel": 0,
"read_only": 1
},
{
"fieldname": "pricing_rule_for_discount",
"fieldtype": "Link",
"label": "Pricing Rule For Discount",
"label": "Pricing Rule",
"options": "Pricing Rule",
"permlevel": 0,
"read_only": 1
@ -456,9 +448,12 @@
],
"idx": 1,
"istable": 1,
"modified": "2014-02-28 11:04:19.000000",
"modified": "2014-05-28 12:42:28.209942",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice Item",
"owner": "Administrator"
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",
"sort_order": "DESC"
}

View File

@ -18,7 +18,7 @@ frappe.query_reports["Accounts Payable"] = {
"get_query": function() {
var company = frappe.query_report.filters_by_name.company.get_value();
return {
"query": "erpnext.accounts.utils.get_account_list",
"query": "erpnext.controllers.queries.get_account_list",
"filters": {
"report_type": "Balance Sheet",
"company": company,

View File

@ -18,7 +18,7 @@ frappe.query_reports["Accounts Receivable"] = {
"get_query": function() {
var company = frappe.query_report.filters_by_name.company.get_value();
return {
"query": "erpnext.accounts.utils.get_account_list",
"query": "erpnext.controllers.queries.get_account_list",
"filters": {
"report_type": "Balance Sheet",
"company": company,

View File

@ -23,7 +23,7 @@ frappe.query_reports["Bank Clearance Summary"] = {
"options": "Account",
"get_query": function() {
return {
"query": "erpnext.accounts.utils.get_account_list",
"query": "erpnext.controllers.queries.get_account_list",
"filters": [
['Account', 'account_type', 'in', 'Bank, Cash'],
['Account', 'group_or_ledger', '=', 'Ledger'],

View File

@ -11,7 +11,7 @@ frappe.query_reports["Bank Reconciliation Statement"] = {
"reqd": 1,
"get_query": function() {
return {
"query": "erpnext.accounts.utils.get_account_list",
"query": "erpnext.controllers.queries.get_account_list",
"filters": [
['Account', 'account_type', 'in', 'Bank, Cash'],
['Account', 'group_or_ledger', '=', 'Ledger'],
@ -27,4 +27,4 @@ frappe.query_reports["Bank Reconciliation Statement"] = {
"reqd": 1
},
]
}
}

View File

@ -7,10 +7,13 @@ from frappe.utils import flt
def execute(filters=None):
if not filters: filters = {}
columns = get_columns()
if not filters.get("account"): return columns, []
data = get_entries(filters)
from erpnext.accounts.utils import get_balance_on
balance_as_per_company = get_balance_on(filters["account"], filters["report_date"])
@ -20,33 +23,33 @@ def execute(filters=None):
total_credit += flt(d[5])
bank_bal = flt(balance_as_per_company) + flt(total_debit) - flt(total_credit)
data += [
get_balance_row("Balance as per company books", balance_as_per_company),
["", "", "", "Amounts not reflected in bank", total_debit, total_credit],
["", "", "", "Amounts not reflected in bank", total_debit, total_credit],
get_balance_row("Balance as per bank", bank_bal)
]
return columns, data
def get_columns():
return ["Journal Voucher:Link/Journal Voucher:140", "Posting Date:Date:100",
"Clearance Date:Date:110", "Against Account:Link/Account:200",
return ["Journal Voucher:Link/Journal Voucher:140", "Posting Date:Date:100",
"Clearance Date:Date:110", "Against Account:Link/Account:200",
"Debit:Currency:120", "Credit:Currency:120"
]
def get_entries(filters):
entries = frappe.db.sql("""select
entries = frappe.db.sql("""select
jv.name, jv.posting_date, jv.clearance_date, jvd.against_account, jvd.debit, jvd.credit
from
`tabJournal Voucher Detail` jvd, `tabJournal Voucher` jv
where jvd.parent = jv.name and jv.docstatus=1
and jvd.account = %(account)s and jv.posting_date <= %(report_date)s
from
`tabJournal Voucher Detail` jvd, `tabJournal Voucher` jv
where jvd.parent = jv.name and jv.docstatus=1
and jvd.account = %(account)s and jv.posting_date <= %(report_date)s
and ifnull(jv.clearance_date, '4000-01-01') > %(report_date)s
order by jv.name DESC""", filters, as_list=1)
return entries
def get_balance_row(label, amount):
if amount > 0:
return ["", "", "", label, amount, 0]

View File

@ -30,7 +30,7 @@ frappe.query_reports["Item-wise Purchase Register"] = {
"get_query": function() {
var company = frappe.query_report.filters_by_name.company.get_value();
return {
"query": "erpnext.accounts.utils.get_account_list",
"query": "erpnext.controllers.queries.get_account_list",
"filters": {
"report_type": "Balance Sheet",
"company": company,

View File

@ -24,7 +24,7 @@ frappe.query_reports["Item-wise Sales Register"] = frappe.query_reports["Sales R
"get_query": function() {
var company = frappe.query_report.filters_by_name.company.get_value();
return {
"query": "erpnext.accounts.utils.get_account_list",
"query": "erpnext.controllers.queries.get_account_list",
"filters": {
"report_type": "Balance Sheet",
"company": company,

View File

@ -29,7 +29,7 @@ frappe.query_reports["Payment Period Based On Invoice Date"] = {
options: "Account",
get_query: function() {
return {
query: "erpnext.accounts.utils.get_account_list",
query: "erpnext.controllers.queries.get_account_list",
filters: {
"report_type": "Balance Sheet",
company: frappe.query_report.filters_by_name.company.get_value()

View File

@ -24,7 +24,7 @@ frappe.query_reports["Purchase Register"] = {
"get_query": function() {
var company = frappe.query_report.filters_by_name.company.get_value();
return {
"query": "erpnext.accounts.utils.get_account_list",
"query": "erpnext.controllers.queries.get_account_list",
"filters": {
"report_type": "Balance Sheet",
"company": company,

View File

@ -24,7 +24,7 @@ frappe.query_reports["Sales Register"] = {
"get_query": function() {
var company = frappe.query_report.filters_by_name.company.get_value();
return {
"query": "erpnext.accounts.utils.get_account_list",
"query": "erpnext.controllers.queries.get_account_list",
"filters": {
"report_type": "Balance Sheet",
"company": company,

View File

@ -7,8 +7,7 @@ import frappe
from frappe.utils import nowdate, cstr, flt, now, getdate, add_months
from frappe import throw, _
from frappe.utils import formatdate
from erpnext.utilities import build_filter_conditions
import frappe.widgets.reportview
class FiscalYearError(frappe.ValidationError): pass
class BudgetError(frappe.ValidationError): pass
@ -196,28 +195,6 @@ def update_against_doc(d, jv_obj):
jv_obj.ignore_validate_update_after_submit = True
jv_obj.save()
def get_account_list(doctype, txt, searchfield, start, page_len, filters):
if not filters.get("group_or_ledger"):
filters["group_or_ledger"] = "Ledger"
conditions, filter_values = build_filter_conditions(filters)
return frappe.db.sql("""select name, parent_account from `tabAccount`
where docstatus < 2 %s and %s like %s order by name limit %s, %s""" %
(conditions, searchfield, "%s", "%s", "%s"),
tuple(filter_values + ["%%%s%%" % txt, start, page_len]))
def get_cost_center_list(doctype, txt, searchfield, start, page_len, filters):
if not filters.get("group_or_ledger"):
filters["group_or_ledger"] = "Ledger"
conditions, filter_values = build_filter_conditions(filters)
return frappe.db.sql("""select name, parent_cost_center from `tabCost Center`
where docstatus < 2 %s and %s like %s order by name limit %s, %s""" %
(conditions, searchfield, "%s", "%s", "%s"),
tuple(filter_values + ["%%%s%%" % txt, start, page_len]))
def remove_against_link_from_jv(ref_type, ref_no, against_field):
linked_jv = frappe.db.sql_list("""select parent from `tabJournal Voucher Detail`
where `%s`=%s and docstatus < 2""" % (against_field, "%s"), (ref_no))

View File

@ -62,7 +62,8 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
},
supplier: function() {
erpnext.utils.get_party_details(this.frm);
var me = this;
erpnext.utils.get_party_details(this.frm, null, null, function(){me.apply_pricing_rule()});
},
supplier_address: function() {

View File

@ -1,6 +1,6 @@
{
"autoname": "POD/.#####",
"creation": "2013-05-24 19:29:06.000000",
"creation": "2013-05-24 19:29:06",
"docstatus": 0,
"doctype": "DocType",
"fields": [
@ -252,17 +252,9 @@
"reqd": 1
},
{
"fieldname": "pricing_rule_for_price",
"fieldname": "pricing_rule",
"fieldtype": "Link",
"label": "Pricing Rule For Price",
"options": "Pricing Rule",
"permlevel": 0,
"read_only": 1
},
{
"fieldname": "pricing_rule_for_discount",
"fieldtype": "Link",
"label": "Pricing Rule For Discount",
"label": "Pricing Rule",
"options": "Pricing Rule",
"permlevel": 0,
"read_only": 1
@ -474,9 +466,12 @@
],
"idx": 1,
"istable": 1,
"modified": "2014-02-28 11:26:25.000000",
"modified": "2014-05-28 12:42:53.018610",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Order Item",
"owner": "Administrator"
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",
"sort_order": "DESC"
}

View File

@ -1,6 +1,6 @@
{
"autoname": "SQI-.#####",
"creation": "2013-05-22 12:43:10.000000",
"creation": "2013-05-22 12:43:10",
"docstatus": 0,
"doctype": "DocType",
"fields": [
@ -195,17 +195,9 @@
"reqd": 1
},
{
"fieldname": "pricing_rule_for_price",
"fieldname": "pricing_rule",
"fieldtype": "Link",
"label": "Pricing Rule For Price",
"options": "Pricing Rule",
"permlevel": 0,
"read_only": 1
},
{
"fieldname": "pricing_rule_for_discount",
"fieldtype": "Link",
"label": "Pricing Rule For Discount",
"label": "Pricing Rule",
"options": "Pricing Rule",
"permlevel": 0,
"read_only": 1
@ -354,9 +346,12 @@
],
"idx": 1,
"istable": 1,
"modified": "2014-02-28 11:25:38.000000",
"modified": "2014-05-28 12:44:17.347236",
"modified_by": "Administrator",
"module": "Buying",
"name": "Supplier Quotation Item",
"owner": "Administrator"
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",
"sort_order": "DESC"
}

View File

@ -1,18 +1,18 @@
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
frappe.pages['purchase-analytics'].onload = function(wrapper) {
frappe.pages['purchase-analytics'].onload = function(wrapper) {
frappe.ui.make_app_page({
parent: wrapper,
title: __('Purchase Analytics'),
single_column: true
});
});
new erpnext.PurchaseAnalytics(wrapper);
wrapper.appframe.add_module_icon("Buying")
}
erpnext.PurchaseAnalytics = frappe.views.TreeGridReport.extend({
@ -22,19 +22,19 @@ erpnext.PurchaseAnalytics = frappe.views.TreeGridReport.extend({
page: wrapper,
parent: $(wrapper).find('.layout-main'),
appframe: wrapper.appframe,
doctypes: ["Item", "Item Group", "Supplier", "Supplier Type", "Company", "Fiscal Year",
"Purchase Invoice", "Purchase Invoice Item",
"Purchase Order", "Purchase Order Item[Purchase Analytics]",
doctypes: ["Item", "Item Group", "Supplier", "Supplier Type", "Company", "Fiscal Year",
"Purchase Invoice", "Purchase Invoice Item",
"Purchase Order", "Purchase Order Item[Purchase Analytics]",
"Purchase Receipt", "Purchase Receipt Item[Purchase Analytics]"],
tree_grid: { show: true }
});
this.tree_grids = {
"Supplier Type": {
label: __("Supplier Type / Supplier"),
show: true,
show: true,
item_key: "supplier",
parent_field: "parent_supplier_type",
parent_field: "parent_supplier_type",
formatter: function(item) {
// return repl('<a href="#Report/stock-invoices/customer=%(enc_value)s">%(value)s</a>', {
// value: item.name,
@ -45,29 +45,29 @@ erpnext.PurchaseAnalytics = frappe.views.TreeGridReport.extend({
},
"Supplier": {
label: __("Supplier"),
show: false,
show: false,
item_key: "supplier",
formatter: function(item) {
return item.name;
}
},
},
"Item Group": {
label: "Item",
show: true,
parent_field: "parent_item_group",
show: true,
parent_field: "parent_item_group",
item_key: "item_code",
formatter: function(item) {
return item.name;
}
},
},
"Item": {
label: "Item",
show: false,
show: false,
item_key: "item_code",
formatter: function(item) {
return item.name;
}
},
},
}
},
setup_columns: function() {
@ -82,24 +82,24 @@ erpnext.PurchaseAnalytics = frappe.views.TreeGridReport.extend({
formatter: this.currency_formatter}
];
this.make_date_range_columns();
this.make_date_range_columns();
this.columns = std_columns.concat(this.columns);
},
filters: [
{fieldtype:"Select", label: __("Tree Type"), options:["Supplier Type", "Supplier",
{fieldtype:"Select", label: __("Tree Type"), options:["Supplier Type", "Supplier",
"Item Group", "Item"],
filter: function(val, item, opts, me) {
return me.apply_zero_filter(val, item, opts, me);
}},
{fieldtype:"Select", label: __("Based On"), options:["Purchase Invoice",
{fieldtype:"Select", label: __("Based On"), options:["Purchase Invoice",
"Purchase Order", "Purchase Receipt"]},
{fieldtype:"Select", label: __("Value or Qty"), options:["Value", "Quantity"]},
{fieldtype:"Select", label: __("Company"), link:"Company",
{fieldtype:"Select", label: __("Company"), link:"Company",
default_value: "Select Company..."},
{fieldtype:"Date", label: __("From Date")},
{fieldtype:"Label", label: __("To")},
{fieldtype:"Date", label: __("To Date")},
{fieldtype:"Select", label: __("Range"),
{fieldtype:"Select", label: __("Range"),
options:["Daily", "Weekly", "Monthly", "Quarterly", "Yearly"]},
{fieldtype:"Button", label: __("Refresh"), icon:"icon-refresh icon-white"},
{fieldtype:"Button", label: __("Reset Filters"), icon: "icon-filter"}
@ -107,10 +107,10 @@ erpnext.PurchaseAnalytics = frappe.views.TreeGridReport.extend({
setup_filters: function() {
var me = this;
this._super();
this.trigger_refresh_on_change(["value_or_qty", "tree_type", "based_on", "company"]);
this.show_zero_check()
this.show_zero_check()
this.setup_plot_check();
},
init_filter_values: function() {
@ -124,34 +124,34 @@ erpnext.PurchaseAnalytics = frappe.views.TreeGridReport.extend({
// Add 'All Supplier Types' Supplier Type
// (Supplier / Item are not mandatory!!)
// Set parent supplier type for tree view
$.each(frappe.report_dump.data["Supplier Type"], function(i, v) {
v['parent_supplier_type'] = "All Supplier Types"
})
frappe.report_dump.data["Supplier Type"] = [{
name: __("All Supplier Types"),
name: __("All Supplier Types"),
id: "All Supplier Types",
}].concat(frappe.report_dump.data["Supplier Type"]);
frappe.report_dump.data["Supplier"].push({
name: __("Not Set"),
name: __("Not Set"),
parent_supplier_type: "All Supplier Types",
id: "Not Set",
});
frappe.report_dump.data["Item"].push({
name: __("Not Set"),
name: __("Not Set"),
parent_item_group: "All Item Groups",
id: "Not Set",
});
}
if (!this.tl || !this.tl[this.based_on]) {
this.make_transaction_list(this.based_on, this.based_on + " Item");
}
if(!this.data || me.item_type != me.tree_type) {
if(me.tree_type=='Supplier') {
var items = frappe.report_dump.data["Supplier"];
@ -177,20 +177,20 @@ erpnext.PurchaseAnalytics = frappe.views.TreeGridReport.extend({
me.parent_map[d.name] = d[me.tree_grid.parent_field];
}
me.reset_item_values(d);
});
});
this.set_indent();
} else {
// otherwise, only reset values
$.each(this.data, function(i, d) {
me.reset_item_values(d);
});
}
this.prepare_balances();
if(me.tree_grid.show) {
this.set_totals(false);
this.set_totals(false);
this.update_groups();
} else {
this.set_totals(true);
@ -201,14 +201,14 @@ erpnext.PurchaseAnalytics = frappe.views.TreeGridReport.extend({
var from_date = dateutil.str_to_obj(this.from_date);
var to_date = dateutil.str_to_obj(this.to_date);
var is_val = this.value_or_qty == 'Value';
$.each(this.tl[this.based_on], function(i, tl) {
if (me.is_default('company') ? true : tl.company === me.company) {
if (me.is_default('company') ? true : tl.company === me.company) {
var posting_date = dateutil.str_to_obj(tl.posting_date);
if (posting_date >= from_date && posting_date <= to_date) {
var item = me.item_by_name[tl[me.tree_grid.item_key]] ||
var item = me.item_by_name[tl[me.tree_grid.item_key]] ||
me.item_by_name['Not Set'];
item[me.column_map[tl.posting_date].field] += (is_val ? tl.amount : tl.qty);
item[me.column_map[tl.posting_date].field] += (is_val ? tl.base_amount : tl.qty);
}
}
});
@ -220,10 +220,10 @@ erpnext.PurchaseAnalytics = frappe.views.TreeGridReport.extend({
var parent = me.parent_map[item.name];
while(parent) {
parent_group = me.item_by_name[parent];
$.each(me.columns, function(c, col) {
if (col.formatter == me.currency_formatter) {
parent_group[col.field] =
parent_group[col.field] =
flt(parent_group[col.field])
+ flt(item[col.field]);
}
@ -235,10 +235,10 @@ erpnext.PurchaseAnalytics = frappe.views.TreeGridReport.extend({
set_totals: function(sort) {
var me = this;
var checked = false;
$.each(this.data, function(i, d) {
$.each(this.data, function(i, d) {
d.total = 0.0;
$.each(me.columns, function(i, col) {
if(col.formatter==me.currency_formatter && !col.hidden && col.field!="total")
if(col.formatter==me.currency_formatter && !col.hidden && col.field!="total")
d.total += d[col.field];
if(d.checked) checked = true;
})
@ -251,7 +251,7 @@ erpnext.PurchaseAnalytics = frappe.views.TreeGridReport.extend({
}
},
get_plot_points: function(item, col, idx) {
return [[dateutil.str_to_obj(col.id).getTime(), item[col.field]],
return [[dateutil.str_to_obj(col.id).getTime(), item[col.field]],
[dateutil.user_to_obj(col.name).getTime(), item[col.field]]];
}
});
});

View File

@ -156,6 +156,11 @@ def get_data():
"name": "Industry Type",
"description": _("Track Leads by Industry Type.")
},
{
"type": "doctype",
"name": "SMS Settings",
"description": _("Setup SMS gateway settings")
},
]
},
{

View File

@ -83,6 +83,11 @@ def get_data():
"name": "Jobs Email Settings",
"description": _("Setup incoming server for jobs email id. (e.g. jobs@example.com)")
},
{
"type": "doctype",
"name": "SMS Settings",
"description": _("Setup SMS gateway settings")
},
]
},
{

View File

@ -97,11 +97,17 @@ class AccountsController(TransactionBase):
args = item.as_dict()
args.update(parent_dict)
ret = get_item_details(args)
for fieldname, value in ret.items():
if item.meta.get_field(fieldname) and \
item.get(fieldname) is None and value is not None:
item.set(fieldname, value)
if ret.get("pricing_rule"):
for field in ["base_price_list_rate", "price_list_rate",
"discount_percentage", "base_rate", "rate"]:
item.set(field, ret.get(field))
def set_taxes(self, tax_parentfield, tax_master_field):
if not self.meta.get_field(tax_parentfield):
return
@ -409,7 +415,7 @@ class AccountsController(TransactionBase):
if total_billed_amt - max_allowed_amt > 0.01:
reduce_by = total_billed_amt - max_allowed_amt
frappe.throw(_("Cannot overbill for Item {0} in row {0} more than {1}. To allow overbilling, please set in 'Setup' > 'Global Defaults'").format(item.item_code, item.row, max_allowed_amt))
frappe.throw(_("Cannot overbill for Item {0} in row {0} more than {1}. To allow overbilling, please set in Stock Settings").format(item.item_code, item.idx, max_allowed_amt))
def get_company_default(self, fieldname):
from erpnext.accounts.utils import get_company_default

View File

@ -234,3 +234,15 @@ def get_batch_no(doctype, txt, searchfield, start, page_len, filters):
'posting_date': filters['posting_date'], 'txt': "%%%s%%" % txt,
'mcond':get_match_cond(doctype),'start': start,
'page_len': page_len})
def get_account_list(doctype, txt, searchfield, start, page_len, filters):
if isinstance(filters, dict):
if not filters.get("group_or_ledger"):
filters["group_or_ledger"] = "Ledger"
elif isinstance(filters, list):
if "group_or_ledger" not in [d[0] for d in filters]:
filters.append(["Account", "group_or_ledger", "=", "Ledger"])
return frappe.widgets.reportview.execute("Account", filters = filters,
fields = ["name", "parent_account"],
limit_start=start, limit_page_length=page_len, as_list=True)

View File

@ -156,8 +156,8 @@ class StatusUpdater(Document):
item['max_allowed'] = flt(item[args['target_ref_field']] * (100+tolerance)/100)
item['reduce_by'] = item[args['target_field']] - item['max_allowed']
msgprint(_("Allowance for over-delivery / over-billing crossed for Item {0}").format(item["item_code"]))
throw(_("{0} must be less than or equal to {1}").format(_(item["target_ref_field"]), item[args["max_allowed"]]))
msgprint(_("Allowance for over-delivery / over-billing crossed for Item {0}.").format(item["item_code"]))
throw(_("{0} must be less than or equal to {1}").format(item["target_ref_field"].title(), item["max_allowed"]))
def update_qty(self, change_modified=True):
"""

View File

@ -4,11 +4,11 @@
// On REFRESH
cur_frm.cscript.refresh = function(doc,dt,dn){
cur_frm.toggle_enable("item", doc.__islocal);
if (!doc.__islocal && doc.docstatus<2) {
cur_frm.add_custom_button(__("Update Cost"), cur_frm.cscript.update_cost);
}
cur_frm.cscript.with_operations(doc);
set_operation_no(doc);
}
@ -41,14 +41,14 @@ var set_operation_no = function(doc) {
var op = op_table[i].operation_no;
if (op && !inList(operations, op)) operations.push(op);
}
frappe.meta.get_docfield("BOM Item", "operation_no",
frappe.meta.get_docfield("BOM Item", "operation_no",
cur_frm.docname).options = operations.join("\n");
$.each(doc.bom_materials || [], function(i, v) {
if(!inList(operations, cstr(v.operation_no))) v.operation_no = null;
});
refresh_field("bom_materials");
}
@ -97,7 +97,7 @@ var get_bom_material_detail= function(doc, cdt, cdn) {
doc: cur_frm.doc,
method: "get_bom_material_detail",
args: {
'item_code': d.item_code,
'item_code': d.item_code,
'bom_no': d.bom_no != null ? d.bom_no: '',
'qty': d.qty
},
@ -131,7 +131,7 @@ cur_frm.cscript.rate = function(doc, cdt, cdn) {
}
}
var calculate_op_cost = function(doc) {
var calculate_op_cost = function(doc) {
var op = doc.bom_operations || [];
total_op_cost = 0;
for(var i=0;i<op.length;i++) {
@ -143,13 +143,13 @@ var calculate_op_cost = function(doc) {
refresh_field('operating_cost');
}
var calculate_rm_cost = function(doc) {
var calculate_rm_cost = function(doc) {
var rm = doc.bom_materials || [];
total_rm_cost = 0;
for(var i=0;i<rm.length;i++) {
amt = flt(rm[i].rate) * flt(rm[i].qty);
set_multiple('BOM Item',rm[i].name, {'amount': amt}, 'bom_materials');
set_multiple('BOM Item',rm[i].name,
set_multiple('BOM Item',rm[i].name,
{'qty_consumed_per_unit': flt(rm[i].qty)/flt(doc.quantity)}, 'bom_materials');
total_rm_cost += amt;
}
@ -196,7 +196,7 @@ cur_frm.fields_dict['bom_materials'].grid.get_field('bom_no').get_query = functi
'is_active': 1,
'docstatus': 1
}
}
}
}
cur_frm.cscript.validate = function(doc, dt, dn) {

View File

@ -3,7 +3,7 @@
from __future__ import unicode_literals
import frappe
from frappe.utils import cint, cstr, flt, now, nowdate
from frappe.utils import cint, cstr, flt
from frappe import _
from frappe.model.document import Document
@ -54,7 +54,7 @@ class BOM(Document):
def get_item_det(self, item_code):
item = frappe.db.sql("""select name, is_asset_item, is_purchase_item,
docstatus, description, is_sub_contracted_item, stock_uom, default_bom,
last_purchase_rate, standard_rate, is_manufactured_item
last_purchase_rate, is_manufactured_item
from `tabItem` where name=%s""", item_code, as_dict = 1)
return item
@ -111,8 +111,6 @@ class BOM(Document):
frappe.throw(_("Please select Price List"))
rate = frappe.db.get_value("Item Price", {"price_list": self.buying_price_list,
"item_code": arg["item_code"]}, "price_list_rate") or 0
elif self.rm_cost_as_per == 'Standard Rate':
rate = arg['standard_rate']
return rate
@ -134,26 +132,15 @@ class BOM(Document):
return bom and bom[0]['unit_cost'] or 0
def get_valuation_rate(self, args):
""" Get average valuation rate of relevant warehouses
as per valuation method (MAR/FIFO)
as on costing date
"""
from erpnext.stock.utils import get_incoming_rate
posting_date, posting_time = nowdate(), now().split()[1]
warehouse = frappe.db.sql("select warehouse from `tabBin` where item_code = %s", args['item_code'])
rate = []
for wh in warehouse:
r = get_incoming_rate({
"item_code": args.get("item_code"),
"warehouse": wh[0],
"posting_date": posting_date,
"posting_time": posting_time,
"qty": args.get("qty") or 0
})
if r:
rate.append(r)
""" Get weighted average of valuation rate from all warehouses """
return rate and flt(sum(rate))/len(rate) or 0
total_qty, total_value = 0.0, 0.0
for d in frappe.db.sql("""select actual_qty, stock_value from `tabBin`
where item_code=%s and actual_qty > 0""", args['item_code'], as_dict=1):
total_qty += flt(d.actual_qty)
total_value += flt(d.stock_value)
return total_value / total_qty if total_qty else 0.0
def manage_default_bom(self):
""" Uncheck others if current one is selected as default,

View File

@ -1,5 +1,5 @@
{
"creation": "2013-02-22 01:27:49.000000",
"creation": "2013-02-22 01:27:49",
"docstatus": 0,
"doctype": "DocType",
"fields": [
@ -79,7 +79,8 @@
"fieldtype": "Float",
"in_list_view": 1,
"label": "Rate",
"permlevel": 0
"permlevel": 0,
"reqd": 1
},
{
"fieldname": "col_break2",
@ -133,9 +134,12 @@
],
"idx": 1,
"istable": 1,
"modified": "2014-02-03 12:47:39.000000",
"modified": "2014-05-29 15:56:31.859868",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "BOM Item",
"owner": "Administrator"
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",
"sort_order": "DESC"
}

View File

@ -194,6 +194,7 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({
}
this.frm.script_manager.trigger("currency");
this.apply_pricing_rule()
}
},
@ -225,7 +226,9 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({
this.frm.doc.plc_conversion_rate !== this.frm.doc.conversion_rate) {
this.frm.set_value("plc_conversion_rate", this.frm.doc.conversion_rate);
}
if(flt(this.frm.doc.conversion_rate)>0.0) this.calculate_taxes_and_totals();
if(flt(this.frm.doc.conversion_rate)>0.0) {
this.apply_pricing_rule();
}
},
get_price_list_currency: function(buying_or_selling) {
@ -278,12 +281,12 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({
}
if(this.frm.doc.price_list_currency === this.frm.doc.currency) {
this.frm.set_value("conversion_rate", this.frm.doc.plc_conversion_rate);
this.calculate_taxes_and_totals();
this.apply_pricing_rule();
}
},
qty: function(doc, cdt, cdn) {
this.calculate_taxes_and_totals();
this.apply_pricing_rule(frappe.get_doc(cdt, cdn));
},
// tax rate
@ -326,6 +329,53 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({
this.calculate_taxes_and_totals();
},
apply_pricing_rule: function(item) {
var me = this;
var _apply_pricing_rule = function(item) {
return me.frm.call({
method: "erpnext.stock.get_item_details.apply_pricing_rule",
child: item,
args: {
args: {
item_code: item.item_code,
item_group: item.item_group,
brand: item.brand,
qty: item.qty,
customer: me.frm.doc.customer,
customer_group: me.frm.doc.customer_group,
territory: me.frm.doc.territory,
supplier: me.frm.doc.supplier,
supplier_type: me.frm.doc.supplier_type,
currency: me.frm.doc.currency,
conversion_rate: me.frm.doc.conversion_rate,
price_list: me.frm.doc.selling_price_list ||
me.frm.doc.buying_price_list,
plc_conversion_rate: me.frm.doc.plc_conversion_rate,
company: me.frm.doc.company,
transaction_date: me.frm.doc.transaction_date || me.frm.doc.posting_date,
campaign: me.frm.doc.campaign,
sales_partner: me.frm.doc.sales_partner
}
},
callback: function(r) {
if(!r.exc) {
me.frm.script_manager.trigger("price_list_rate", item.doctype, item.name);
}
}
});
}
if(item) {
_apply_pricing_rule(item);
} else {
$.each(this.get_item_doclist(), function(n, item) {
_apply_pricing_rule(item);
});
}
},
included_in_print_rate: function(doc, cdt, cdn) {
var tax = frappe.get_doc(cdt, cdn);
try {

View File

@ -2,7 +2,7 @@
// License: GNU General Public License v3. See license.txt
frappe.provide("erpnext.utils");
erpnext.utils.get_party_details = function(frm, method, args) {
erpnext.utils.get_party_details = function(frm, method, args, callback) {
if(!method) {
method = "erpnext.accounts.party.get_party_details";
}
@ -33,6 +33,7 @@ erpnext.utils.get_party_details = function(frm, method, args) {
frm.updating_party_details = true;
frm.set_value(r.message);
frm.updating_party_details = false;
if(callback) callback();
}
}
});

View File

@ -1,6 +1,6 @@
{
"autoname": "QUOD/.#####",
"creation": "2013-03-07 11:42:57.000000",
"creation": "2013-03-07 11:42:57",
"docstatus": 0,
"doctype": "DocType",
"fields": [
@ -231,17 +231,9 @@
"width": "100px"
},
{
"fieldname": "pricing_rule_for_price",
"fieldname": "pricing_rule",
"fieldtype": "Link",
"label": "Pricing Rule For Price",
"options": "Pricing Rule",
"permlevel": 0,
"read_only": 1
},
{
"fieldname": "pricing_rule_for_discount",
"fieldtype": "Link",
"label": "Pricing Rule For Discount",
"label": "Pricing Rule",
"options": "Pricing Rule",
"permlevel": 0,
"read_only": 1
@ -353,9 +345,12 @@
],
"idx": 1,
"istable": 1,
"modified": "2014-02-28 11:20:34.000000",
"modified": "2014-05-28 12:41:40.811916",
"modified_by": "Administrator",
"module": "Selling",
"name": "Quotation Item",
"owner": "Administrator"
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",
"sort_order": "DESC"
}

View File

@ -1,6 +1,6 @@
{
"autoname": "SOD/.#####",
"creation": "2013-03-07 11:42:58.000000",
"creation": "2013-03-07 11:42:58",
"docstatus": 0,
"doctype": "DocType",
"fields": [
@ -217,17 +217,9 @@
"width": "100px"
},
{
"fieldname": "pricing_rule_for_price",
"fieldname": "pricing_rule",
"fieldtype": "Link",
"label": "Pricing Rule For Price",
"options": "Pricing Rule",
"permlevel": 0,
"read_only": 1
},
{
"fieldname": "pricing_rule_for_discount",
"fieldtype": "Link",
"label": "Pricing Rule For Discount",
"label": "Pricing Rule",
"options": "Pricing Rule",
"permlevel": 0,
"read_only": 1
@ -439,9 +431,12 @@
],
"idx": 1,
"istable": 1,
"modified": "2014-02-28 11:20:05.000000",
"modified": "2014-05-27 14:41:14.996650",
"modified_by": "Administrator",
"module": "Selling",
"name": "Sales Order Item",
"owner": "Administrator"
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",
"sort_order": "DESC"
}

View File

@ -1,17 +1,17 @@
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
frappe.pages['sales-analytics'].onload = function(wrapper) {
frappe.pages['sales-analytics'].onload = function(wrapper) {
frappe.ui.make_app_page({
parent: wrapper,
title: __('Sales Analytics'),
single_column: true
});
new erpnext.SalesAnalytics(wrapper);
wrapper.appframe.add_module_icon("Selling")
}
erpnext.SalesAnalytics = frappe.views.TreeGridReport.extend({
@ -21,55 +21,55 @@ erpnext.SalesAnalytics = frappe.views.TreeGridReport.extend({
page: wrapper,
parent: $(wrapper).find('.layout-main'),
appframe: wrapper.appframe,
doctypes: ["Item", "Item Group", "Customer", "Customer Group", "Company", "Territory",
"Fiscal Year", "Sales Invoice", "Sales Invoice Item",
"Sales Order", "Sales Order Item[Sales Analytics]",
doctypes: ["Item", "Item Group", "Customer", "Customer Group", "Company", "Territory",
"Fiscal Year", "Sales Invoice", "Sales Invoice Item",
"Sales Order", "Sales Order Item[Sales Analytics]",
"Delivery Note", "Delivery Note Item[Sales Analytics]"],
tree_grid: { show: true }
});
this.tree_grids = {
"Customer Group": {
label: __("Customer Group / Customer"),
show: true,
show: true,
item_key: "customer",
parent_field: "parent_customer_group",
parent_field: "parent_customer_group",
formatter: function(item) { return item.name; }
},
"Customer": {
label: __("Customer"),
show: false,
show: false,
item_key: "customer",
formatter: function(item) {
return item.name;
}
},
},
"Item Group": {
label: __("Item"),
show: true,
parent_field: "parent_item_group",
show: true,
parent_field: "parent_item_group",
item_key: "item_code",
formatter: function(item) {
return item.name;
}
},
},
"Item": {
label: __("Item"),
show: false,
show: false,
item_key: "item_code",
formatter: function(item) {
return item.name;
}
},
},
"Territory": {
label: __("Territory / Customer"),
show: true,
show: true,
item_key: "customer",
parent_field: "parent_territory",
parent_field: "parent_territory",
formatter: function(item) {
return item.name;
}
}
}
}
},
setup_columns: function() {
@ -84,24 +84,24 @@ erpnext.SalesAnalytics = frappe.views.TreeGridReport.extend({
formatter: this.currency_formatter}
];
this.make_date_range_columns();
this.make_date_range_columns();
this.columns = std_columns.concat(this.columns);
},
filters: [
{fieldtype:"Select", fieldname: "tree_type", label: __("Tree Type"), options:["Customer Group", "Customer",
{fieldtype:"Select", fieldname: "tree_type", label: __("Tree Type"), options:["Customer Group", "Customer",
"Item Group", "Item", "Territory"],
filter: function(val, item, opts, me) {
return me.apply_zero_filter(val, item, opts, me);
}},
{fieldtype:"Select", fieldname: "based_on", label: __("Based On"), options:["Sales Invoice",
{fieldtype:"Select", fieldname: "based_on", label: __("Based On"), options:["Sales Invoice",
"Sales Order", "Delivery Note"]},
{fieldtype:"Select", fieldname: "value_or_qty", label: __("Value or Qty"), options:["Value", "Quantity"]},
{fieldtype:"Select", fieldname: "company", label: __("Company"), link:"Company",
{fieldtype:"Select", fieldname: "company", label: __("Company"), link:"Company",
default_value: "Select Company..."},
{fieldtype:"Date", fieldname: "from_date", label: __("From Date")},
{fieldtype:"Label", fieldname: "to", label: __("To")},
{fieldtype:"Date", fieldname: "to_date", label: __("To Date")},
{fieldtype:"Select", fieldname: "range", label: __("Range"),
{fieldtype:"Select", fieldname: "range", label: __("Range"),
options:["Daily", "Weekly", "Monthly", "Quarterly", "Yearly"]},
{fieldtype:"Button", fieldname: "refresh", label: __("Refresh"), icon:"icon-refresh"},
{fieldtype:"Button", fieldname: "reset_filters", label: __("Reset Filters"), icon:"icon-filter"}
@ -109,10 +109,10 @@ erpnext.SalesAnalytics = frappe.views.TreeGridReport.extend({
setup_filters: function() {
var me = this;
this._super();
this.trigger_refresh_on_change(["value_or_qty", "tree_type", "based_on", "company"]);
this.show_zero_check()
this.show_zero_check()
this.setup_plot_check();
},
init_filter_values: function() {
@ -125,14 +125,14 @@ erpnext.SalesAnalytics = frappe.views.TreeGridReport.extend({
// add 'Not Set' Customer & Item
// (Customer / Item are not mandatory!!)
frappe.report_dump.data["Customer"].push({
name: "Not Set",
name: "Not Set",
parent_customer_group: "All Customer Groups",
parent_territory: "All Territories",
id: "Not Set",
});
frappe.report_dump.data["Item"].push({
name: "Not Set",
name: "Not Set",
parent_item_group: "All Item Groups",
id: "Not Set",
});
@ -141,7 +141,7 @@ erpnext.SalesAnalytics = frappe.views.TreeGridReport.extend({
if (!this.tl || !this.tl[this.based_on]) {
this.make_transaction_list(this.based_on, this.based_on + " Item");
}
if(!this.data || me.item_type != me.tree_type) {
if(me.tree_type=='Customer') {
var items = frappe.report_dump.data["Customer"];
@ -159,7 +159,7 @@ erpnext.SalesAnalytics = frappe.views.TreeGridReport.extend({
me.parent_map = {};
me.item_by_name = {};
me.data = [];
$.each(items, function(i, v) {
var d = copy_dict(v);
@ -169,20 +169,20 @@ erpnext.SalesAnalytics = frappe.views.TreeGridReport.extend({
me.parent_map[d.name] = d[me.tree_grid.parent_field];
}
me.reset_item_values(d);
});
});
this.set_indent();
} else {
// otherwise, only reset values
$.each(this.data, function(i, d) {
me.reset_item_values(d);
});
}
this.prepare_balances();
if(me.tree_grid.show) {
this.set_totals(false);
this.set_totals(false);
this.update_groups();
} else {
this.set_totals(true);
@ -194,14 +194,14 @@ erpnext.SalesAnalytics = frappe.views.TreeGridReport.extend({
var from_date = dateutil.str_to_obj(this.from_date);
var to_date = dateutil.str_to_obj(this.to_date);
var is_val = this.value_or_qty == 'Value';
$.each(this.tl[this.based_on], function(i, tl) {
if (me.is_default('company') ? true : tl.company === me.company) {
if (me.is_default('company') ? true : tl.company === me.company) {
var posting_date = dateutil.str_to_obj(tl.posting_date);
if (posting_date >= from_date && posting_date <= to_date) {
var item = me.item_by_name[tl[me.tree_grid.item_key]] ||
var item = me.item_by_name[tl[me.tree_grid.item_key]] ||
me.item_by_name['Not Set'];
item[me.column_map[tl.posting_date].field] += (is_val ? tl.amount : tl.qty);
item[me.column_map[tl.posting_date].field] += (is_val ? tl.base_amount : tl.qty);
}
}
});
@ -213,10 +213,10 @@ erpnext.SalesAnalytics = frappe.views.TreeGridReport.extend({
var parent = me.parent_map[item.name];
while(parent) {
parent_group = me.item_by_name[parent];
$.each(me.columns, function(c, col) {
if (col.formatter == me.currency_formatter) {
parent_group[col.field] =
parent_group[col.field] =
flt(parent_group[col.field])
+ flt(item[col.field]);
}
@ -228,10 +228,10 @@ erpnext.SalesAnalytics = frappe.views.TreeGridReport.extend({
set_totals: function(sort) {
var me = this;
var checked = false;
$.each(this.data, function(i, d) {
$.each(this.data, function(i, d) {
d.total = 0.0;
$.each(me.columns, function(i, col) {
if(col.formatter==me.currency_formatter && !col.hidden && col.field!="total")
if(col.formatter==me.currency_formatter && !col.hidden && col.field!="total")
d.total += d[col.field];
if(d.checked) checked = true;
})
@ -244,7 +244,7 @@ erpnext.SalesAnalytics = frappe.views.TreeGridReport.extend({
}
},
get_plot_points: function(item, col, idx) {
return [[dateutil.str_to_obj(col.id).getTime(), item[col.field]],
return [[dateutil.str_to_obj(col.id).getTime(), item[col.field]],
[dateutil.user_to_obj(col.name).getTime(), item[col.field]]];
}
});

View File

@ -104,7 +104,8 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({
},
customer: function() {
erpnext.utils.get_party_details(this.frm);
var me = this;
erpnext.utils.get_party_details(this.frm, null, null, function(){me.apply_pricing_rule()});
},
customer_address: function() {
@ -119,6 +120,14 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({
erpnext.utils.get_contact_details(this.frm);
},
sales_partner: function() {
this.apply_pricing_rule();
},
campaign: function() {
this.apply_pricing_rule();
},
barcode: function(doc, cdt, cdn) {
this.item_code(doc, cdt, cdn);
},

View File

@ -18,14 +18,14 @@ data_map = {
# Accounts
"Account": {
"columns": ["name", "parent_account", "lft", "rgt", "report_type",
"columns": ["name", "parent_account", "lft", "rgt", "report_type",
"company", "group_or_ledger"],
"conditions": ["docstatus < 2"],
"order_by": "lft",
"links": {
"company": ["Company", "name"],
}
},
"Cost Center": {
"columns": ["name", "lft", "rgt"],
@ -33,7 +33,7 @@ data_map = {
"order_by": "lft"
},
"GL Entry": {
"columns": ["name", "account", "posting_date", "cost_center", "debit", "credit",
"columns": ["name", "account", "posting_date", "cost_center", "debit", "credit",
"is_opening", "company", "voucher_type", "voucher_no", "remarks"],
"order_by": "posting_date, account",
"links": {
@ -45,8 +45,8 @@ data_map = {
# Stock
"Item": {
"columns": ["name", "if(item_name=name, '', item_name) as item_name", "description",
"item_group as parent_item_group", "stock_uom", "brand", "valuation_method",
"columns": ["name", "if(item_name=name, '', item_name) as item_name", "description",
"item_group as parent_item_group", "stock_uom", "brand", "valuation_method",
"re_order_level", "re_order_qty"],
# "conditions": ["docstatus < 2"],
"order_by": "name",
@ -76,7 +76,7 @@ data_map = {
"order_by": "name"
},
"Stock Ledger Entry": {
"columns": ["name", "posting_date", "posting_time", "item_code", "warehouse",
"columns": ["name", "posting_date", "posting_time", "item_code", "warehouse",
"actual_qty as qty", "voucher_type", "voucher_no", "project",
"ifnull(incoming_rate,0) as incoming_rate", "stock_uom", "serial_no"],
"order_by": "posting_date, posting_time, name",
@ -98,8 +98,8 @@ data_map = {
"order_by": "posting_date, posting_time, name",
},
"Production Order": {
"columns": ["name", "production_item as item_code",
"(ifnull(qty, 0) - ifnull(produced_qty, 0)) as qty",
"columns": ["name", "production_item as item_code",
"(ifnull(qty, 0) - ifnull(produced_qty, 0)) as qty",
"fg_warehouse as warehouse"],
"conditions": ["docstatus=1", "status != 'Stopped'", "ifnull(fg_warehouse, '')!=''",
"ifnull(qty, 0) > ifnull(produced_qty, 0)"],
@ -109,7 +109,7 @@ data_map = {
},
},
"Material Request Item": {
"columns": ["item.name as name", "item_code", "warehouse",
"columns": ["item.name as name", "item_code", "warehouse",
"(ifnull(qty, 0) - ifnull(ordered_qty, 0)) as qty"],
"from": "`tabMaterial Request Item` item, `tabMaterial Request` main",
"conditions": ["item.parent = main.name", "main.docstatus=1", "main.status != 'Stopped'",
@ -120,21 +120,21 @@ data_map = {
},
},
"Purchase Order Item": {
"columns": ["item.name as name", "item_code", "warehouse",
"columns": ["item.name as name", "item_code", "warehouse",
"(ifnull(qty, 0) - ifnull(received_qty, 0)) as qty"],
"from": "`tabPurchase Order Item` item, `tabPurchase Order` main",
"conditions": ["item.parent = main.name", "main.docstatus=1", "main.status != 'Stopped'",
"conditions": ["item.parent = main.name", "main.docstatus=1", "main.status != 'Stopped'",
"ifnull(warehouse, '')!=''", "ifnull(qty, 0) > ifnull(received_qty, 0)"],
"links": {
"item_code": ["Item", "name"],
"warehouse": ["Warehouse", "name"]
},
},
"Sales Order Item": {
"columns": ["item.name as name", "item_code", "(ifnull(qty, 0) - ifnull(delivered_qty, 0)) as qty", "warehouse"],
"from": "`tabSales Order Item` item, `tabSales Order` main",
"conditions": ["item.parent = main.name", "main.docstatus=1", "main.status != 'Stopped'",
"conditions": ["item.parent = main.name", "main.docstatus=1", "main.status != 'Stopped'",
"ifnull(warehouse, '')!=''", "ifnull(qty, 0) > ifnull(delivered_qty, 0)"],
"links": {
"item_code": ["Item", "name"],
@ -144,7 +144,7 @@ data_map = {
# Sales
"Customer": {
"columns": ["name", "if(customer_name=name, '', customer_name) as customer_name",
"columns": ["name", "if(customer_name=name, '', customer_name) as customer_name",
"customer_group as parent_customer_group", "territory as parent_territory"],
"conditions": ["docstatus < 2"],
"order_by": "name",
@ -218,7 +218,7 @@ data_map = {
}
},
"Supplier": {
"columns": ["name", "if(supplier_name=name, '', supplier_name) as supplier_name",
"columns": ["name", "if(supplier_name=name, '', supplier_name) as supplier_name",
"supplier_type as parent_supplier_type"],
"conditions": ["docstatus < 2"],
"order_by": "name",
@ -291,5 +291,5 @@ data_map = {
"conditions": ["docstatus < 2"],
"order_by": "creation"
}
}

View File

@ -1,6 +1,6 @@
{
"autoname": "DND/.#######",
"creation": "2013-04-22 13:15:44.000000",
"creation": "2013-04-22 13:15:44",
"docstatus": 0,
"doctype": "DocType",
"fields": [
@ -225,17 +225,9 @@
"width": "100px"
},
{
"fieldname": "pricing_rule_for_price",
"fieldname": "pricing_rule",
"fieldtype": "Link",
"label": "Pricing Rule For Price",
"options": "Pricing Rule",
"permlevel": 0,
"read_only": 1
},
{
"fieldname": "pricing_rule_for_discount",
"fieldtype": "Link",
"label": "Pricing Rule For Discount",
"label": "Pricing Rule",
"options": "Pricing Rule",
"permlevel": 0,
"read_only": 1
@ -437,9 +429,12 @@
],
"idx": 1,
"istable": 1,
"modified": "2014-02-28 11:20:58.000000",
"modified": "2014-05-28 12:42:05.788579",
"modified_by": "Administrator",
"module": "Stock",
"name": "Delivery Note Item",
"owner": "Administrator"
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",
"sort_order": "DESC"
}

View File

@ -439,16 +439,6 @@
"permlevel": 0,
"read_only": 1
},
{
"depends_on": "eval:doc.is_purchase_item==\"Yes\"",
"fieldname": "standard_rate",
"fieldtype": "Float",
"label": "Standard Rate",
"oldfieldname": "standard_rate",
"oldfieldtype": "Currency",
"permlevel": 0,
"read_only": 0
},
{
"depends_on": "eval:doc.is_purchase_item==\"Yes\"",
"fieldname": "column_break2",
@ -835,7 +825,7 @@
"icon": "icon-tag",
"idx": 1,
"max_attachments": 1,
"modified": "2014-05-21 15:37:30.124881",
"modified": "2014-05-29 16:05:53.126214",
"modified_by": "Administrator",
"module": "Stock",
"name": "Item",

View File

@ -137,7 +137,6 @@ class Item(WebsiteGenerator):
bom = frappe.db.sql("""select name from `tabBOM` where item = %s
and is_active = 1""", (self.name,))
if bom and bom[0][0]:
print self.name
frappe.throw(_("""Allow Bill of Materials should be 'Yes'. Because one or many active BOMs present for this item"""))
def fill_customer_code(self):

View File

@ -141,7 +141,7 @@ class PackingSlip(Document):
note
"""
recommended_case_no = frappe.db.sql("""SELECT MAX(to_case_no) FROM `tabPacking Slip`
WHERE delivery_note = %(delivery_note)s AND docstatus=1""", self.as_dict())
WHERE delivery_note = %s AND docstatus=1""", self.delivery_note)
return cint(recommended_case_no[0][0]) + 1

View File

@ -1,6 +1,6 @@
{
"autoname": "GRND/.#######",
"creation": "2013-05-24 19:29:10.000000",
"creation": "2013-05-24 19:29:10",
"docstatus": 0,
"doctype": "DocType",
"fields": [
@ -256,17 +256,9 @@
"width": "100px"
},
{
"fieldname": "pricing_rule_for_price",
"fieldname": "pricing_rule",
"fieldtype": "Link",
"label": "Pricing Rule For Price",
"options": "Pricing Rule",
"permlevel": 0,
"read_only": 1
},
{
"fieldname": "pricing_rule_for_discount",
"fieldtype": "Link",
"label": "Pricing Rule For Discount",
"label": "Pricing Rule",
"options": "Pricing Rule",
"permlevel": 0,
"read_only": 1
@ -553,9 +545,12 @@
],
"idx": 1,
"istable": 1,
"modified": "2014-02-28 11:27:09.000000",
"modified": "2014-05-28 12:43:16.669040",
"modified_by": "Administrator",
"module": "Stock",
"name": "Purchase Receipt Item",
"owner": "Administrator"
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",
"sort_order": "DESC"
}

View File

@ -41,6 +41,7 @@ class StockEntry(StockController):
self.validate_return_reference_doc()
self.validate_with_material_request()
self.validate_fiscal_year()
self.validate_valuation_rate()
self.set_total_amount()
def on_submit(self):
@ -170,11 +171,26 @@ class StockEntry(StockController):
frappe.throw(_("Stock Entries already created for Production Order ")
+ self.production_order + ":" + ", ".join(other_ste), DuplicateEntryForProductionOrderError)
def validate_valuation_rate(self):
if self.purpose == "Manufacture/Repack":
valuation_at_source, valuation_at_target = 0, 0
for d in self.get("mtn_details"):
if d.s_warehouse and not d.t_warehouse:
valuation_at_source += flt(d.amount)
if d.t_warehouse and not d.s_warehouse:
valuation_at_target += flt(d.amount)
if valuation_at_target < valuation_at_source:
frappe.throw(_("Total valuation for manufactured or repacked item(s) can not be less than total valuation of raw materials"))
def set_total_amount(self):
self.total_amount = sum([flt(item.amount) for item in self.get("mtn_details")])
def get_stock_and_rate(self):
"""get stock and incoming rate on posting date"""
raw_material_cost = 0.0
for d in self.get('mtn_details'):
args = frappe._dict({
"item_code": d.item_code,
@ -182,17 +198,28 @@ class StockEntry(StockController):
"posting_date": self.posting_date,
"posting_time": self.posting_time,
"qty": d.s_warehouse and -1*d.transfer_qty or d.transfer_qty,
"serial_no": d.serial_no,
"bom_no": d.bom_no,
"serial_no": d.serial_no
})
# get actual stock at source warehouse
d.actual_qty = get_previous_sle(args).get("qty_after_transaction") or 0
# get incoming rate
if not flt(d.incoming_rate):
d.incoming_rate = self.get_incoming_rate(args)
if not d.bom_no:
if not flt(d.incoming_rate):
d.incoming_rate = self.get_incoming_rate(args)
d.amount = flt(d.transfer_qty) * flt(d.incoming_rate)
d.amount = flt(d.transfer_qty) * flt(d.incoming_rate)
raw_material_cost += flt(d.amount)
# set incoming rate for fg item
if self.production_order and self.purpose == "Manufacture/Repack":
for d in self.get("mtn_details"):
if d.bom_no:
if not flt(d.incoming_rate):
bom = frappe.db.get_value("BOM", d.bom_no, ["operating_cost", "quantity"], as_dict=1)
operation_cost_per_unit = flt(bom.operating_cost) / flt(bom.quantity)
d.incoming_rate = operation_cost_per_unit + (raw_material_cost / flt(d.transfer_qty))
d.amount = flt(d.transfer_qty) * flt(d.incoming_rate)
def get_incoming_rate(self, args):
incoming_rate = 0
@ -365,17 +392,18 @@ class StockEntry(StockController):
ret.update(stock_and_rate)
return ret
def get_uom_details(self, arg = ''):
arg, ret = eval(arg), {}
uom = frappe.db.sql("""select conversion_factor from `tabUOM Conversion Detail`
where parent = %s and uom = %s""", (arg['item_code'], arg['uom']), as_dict = 1)
if not uom or not flt(uom[0].conversion_factor):
frappe.msgprint(_("UOM coversion factor required for UOM {0} in Item {1}").format(arg["uom"], arg["item_code"]))
def get_uom_details(self, args):
conversion_factor = frappe.db.get_value("UOM Conversion Detail", {"parent": args.get("item_code"),
"uom": args.get("uom")}, "conversion_factor")
if not conversion_factor:
frappe.msgprint(_("UOM coversion factor required for UOM: {0} in Item: {1}")
.format(args.get("uom"), args.get("item_code")))
ret = {'uom' : ''}
else:
ret = {
'conversion_factor' : flt(uom[0]['conversion_factor']),
'transfer_qty' : flt(arg['qty']) * flt(uom[0]['conversion_factor']),
'conversion_factor' : flt(conversion_factor),
'transfer_qty' : flt(args.get("qty")) * flt(conversion_factor)
}
return ret

View File

@ -11,8 +11,11 @@ from erpnext.stock.stock_ledger import update_entries_after
from erpnext.controllers.stock_controller import StockController
class StockReconciliation(StockController):
def validate(self):
def __init__(self, arg1, arg2=None):
super(StockReconciliation, self).__init__(arg1, arg2)
self.head_row = ["Item Code", "Warehouse", "Quantity", "Valuation Rate"]
def validate(self):
self.entries = []
self.validate_data()
@ -300,4 +303,5 @@ class StockReconciliation(StockController):
@frappe.whitelist()
def upload():
from frappe.utils.datautils import read_csv_content_from_uploaded_file
return read_csv_content_from_uploaded_file()
csv_content = read_csv_content_from_uploaded_file()
return filter(lambda x: x and any(x), csv_content)

View File

@ -68,7 +68,12 @@ def get_item_details(args):
if args.transaction_type == "selling" and cint(args.is_pos):
out.update(get_pos_settings_item_details(args.company, args))
apply_pricing_rule(out, args)
# update args with out, if key or value not exists
for key, value in out.iteritems():
if args.get(key) is None:
args[key] = value
out.update(apply_pricing_rule(args))
if args.get("doctype") in ("Sales Invoice", "Delivery Note"):
if item_doc.has_serial_no == "Yes" and not args.serial_no:
@ -243,36 +248,51 @@ def get_pos_settings(company):
return pos_settings and pos_settings[0] or None
def apply_pricing_rule(out, args):
args_dict = frappe._dict().update(args)
args_dict.update(out)
all_pricing_rules = get_pricing_rules(args_dict)
@frappe.whitelist()
def apply_pricing_rule(args):
if isinstance(args, basestring):
args = json.loads(args)
rule_for_price = False
for rule_for in ["price", "discount_percentage"]:
pricing_rules = filter(lambda x: x[rule_for] > 0.0, all_pricing_rules)
if rule_for_price:
pricing_rules = filter(lambda x: not x["for_price_list"], pricing_rules)
args = frappe._dict(args)
out = frappe._dict()
pricing_rule = filter_pricing_rules(args_dict, pricing_rules)
if not args.get("item_group") or not args.get("brand"):
args.item_group, args.brand = frappe.db.get_value("Item",
args.item_code, ["item_group", "brand"])
if pricing_rule:
if rule_for == "discount_percentage":
out["discount_percentage"] = pricing_rule["discount_percentage"]
out["pricing_rule_for_discount"] = pricing_rule["name"]
else:
out["base_price_list_rate"] = pricing_rule["price"]
out["price_list_rate"] = pricing_rule["price"] * \
flt(args_dict.plc_conversion_rate) / flt(args_dict.conversion_rate)
out["pricing_rule_for_price"] = pricing_rule["name"]
rule_for_price = True
if args.get("customer") and (not args.get("customer_group") or not args.get("territory")):
args.customer_group, args.territory = frappe.db.get_value("Customer",
args.customer, ["customer_group", "territory"])
def get_pricing_rules(args_dict):
if args.get("supplier") and not args.get("supplier_type"):
args.supplier_type = frappe.db.get_value("Supplier", args.supplier, "supplier_type")
pricing_rules = get_pricing_rules(args)
pricing_rule = filter_pricing_rules(args, pricing_rules)
if pricing_rule:
out.pricing_rule = pricing_rule.name
if pricing_rule.price_or_discount == "Price":
out.base_price_list_rate = pricing_rule.price
out.price_list_rate = pricing_rule.price*flt(args.plc_conversion_rate)/flt(args.conversion_rate)
out.base_rate = out.base_price_list_rate
out.rate = out.price_list_rate
out.discount_percentage = 0.0
else:
out.discount_percentage = pricing_rule.discount_percentage
else:
out.pricing_rule = None
return out
def get_pricing_rules(args):
def _get_tree_conditions(doctype, allow_blank=True):
field = frappe.scrub(doctype)
condition = ""
if args_dict.get(field):
lft, rgt = frappe.db.get_value(doctype, args_dict[field], ["lft", "rgt"])
if args.get(field):
lft, rgt = frappe.db.get_value(doctype, args[field], ["lft", "rgt"])
parent_groups = frappe.db.sql_list("""select name from `tab%s`
where lft<=%s and rgt>=%s""" % (doctype, '%s', '%s'), (lft, rgt))
@ -284,8 +304,8 @@ def get_pricing_rules(args_dict):
conditions = ""
for field in ["customer", "supplier", "supplier_type", "campaign", "sales_partner"]:
if args_dict.get(field):
for field in ["company", "customer", "supplier", "supplier_type", "campaign", "sales_partner"]:
if args.get(field):
conditions += " and ifnull("+field+", '') in (%("+field+")s, '')"
else:
conditions += " and ifnull("+field+", '') = ''"
@ -297,8 +317,7 @@ def get_pricing_rules(args_dict):
conditions += " and ifnull(for_price_list, '') in (%(price_list)s, '')"
if args_dict.get("transaction_date"):
if args.get("transaction_date"):
conditions += """ and %(transaction_date)s between ifnull(valid_from, '2000-01-01')
and ifnull(valid_upto, '2500-12-31')"""
@ -307,13 +326,13 @@ def get_pricing_rules(args_dict):
and docstatus < 2 and ifnull(disable, 0) = 0 {conditions}
order by priority desc, name desc""".format(
item_group_condition=_get_tree_conditions("Item Group", False), conditions=conditions),
args_dict, as_dict=1)
args, as_dict=1)
def filter_pricing_rules(args_dict, pricing_rules):
def filter_pricing_rules(args, pricing_rules):
# filter for qty
if pricing_rules and args_dict.get("qty"):
pricing_rules = filter(lambda x: (args_dict.qty>=flt(x.min_qty)
and (args_dict.qty<=x.max_qty if x.max_qty else True)), pricing_rules)
if pricing_rules and args.get("qty"):
pricing_rules = filter(lambda x: (args.qty>=flt(x.min_qty)
and (args.qty<=x.max_qty if x.max_qty else True)), pricing_rules)
# find pricing rule with highest priority
if pricing_rules:
@ -323,16 +342,22 @@ def filter_pricing_rules(args_dict, pricing_rules):
# apply internal priority
all_fields = ["item_code", "item_group", "brand", "customer", "customer_group", "territory",
"supplier", "supplier_type", "campaign", "for_price_list", "sales_partner"]
"supplier", "supplier_type", "campaign", "sales_partner"]
if len(pricing_rules) > 1:
for field_set in [["item_code", "item_group", "brand"],
["customer", "customer_group", "territory"], ["supplier", "supplier_type"]]:
remaining_fields = list(set(all_fields) - set(field_set))
if if_all_rules_same(pricing_rules, remaining_fields):
pricing_rules = apply_internal_priority(pricing_rules, field_set, args_dict)
pricing_rules = apply_internal_priority(pricing_rules, field_set, args)
break
if len(pricing_rules) > 1:
price_or_discount = list(set([d.price_or_discount for d in pricing_rules]))
if len(price_or_discount) == 1 and price_or_discount[0] == "Discount Percentage":
pricing_rules = filter(lambda x: x.for_price_list==args.price_list, pricing_rules) \
or pricing_rules
if len(pricing_rules) > 1:
frappe.throw(_("Multiple Price Rule exists with same criteria, please resolve \
conflict by assigning priority. Price Rules: {0}")
@ -350,11 +375,11 @@ def if_all_rules_same(pricing_rules, fields):
return all_rules_same
def apply_internal_priority(pricing_rules, field_set, args_dict):
def apply_internal_priority(pricing_rules, field_set, args):
filtered_rules = []
for field in field_set:
if args_dict.get(field):
filtered_rules = filter(lambda x: x[field]==args_dict[field], pricing_rules)
if args.get(field):
filtered_rules = filter(lambda x: x[field]==args[field], pricing_rules)
if filtered_rules: break
return filtered_rules or pricing_rules

View File

@ -3,18 +3,18 @@
frappe.require("assets/erpnext/js/stock_analytics.js");
frappe.pages['stock-balance'].onload = function(wrapper) {
frappe.pages['stock-balance'].onload = function(wrapper) {
frappe.ui.make_app_page({
parent: wrapper,
title: __('Stock Balance'),
single_column: true
});
new erpnext.StockBalance(wrapper);
wrapper.appframe.add_module_icon("Stock")
}
erpnext.StockBalance = erpnext.StockAnalytics.extend({
@ -30,40 +30,42 @@ erpnext.StockBalance = erpnext.StockAnalytics.extend({
{id: "name", name: __("Item"), field: "name", width: 300,
formatter: this.tree_formatter},
{id: "item_name", name: __("Item Name"), field: "item_name", width: 100},
{id: "description", name: __("Description"), field: "description", width: 200,
{id: "description", name: __("Description"), field: "description", width: 200,
formatter: this.text_formatter},
{id: "brand", name: __("Brand"), field: "brand", width: 100},
{id: "stock_uom", name: __("UOM"), field: "stock_uom", width: 100},
{id: "opening_qty", name: __("Opening Qty"), field: "opening_qty", width: 100,
{id: "opening_qty", name: __("Opening Qty"), field: "opening_qty", width: 100,
formatter: this.currency_formatter},
{id: "inflow_qty", name: __("In Qty"), field: "inflow_qty", width: 100,
{id: "inflow_qty", name: __("In Qty"), field: "inflow_qty", width: 100,
formatter: this.currency_formatter},
{id: "outflow_qty", name: __("Out Qty"), field: "outflow_qty", width: 100,
{id: "outflow_qty", name: __("Out Qty"), field: "outflow_qty", width: 100,
formatter: this.currency_formatter},
{id: "closing_qty", name: __("Closing Qty"), field: "closing_qty", width: 100,
{id: "closing_qty", name: __("Closing Qty"), field: "closing_qty", width: 100,
formatter: this.currency_formatter},
{id: "opening_value", name: __("Opening Value"), field: "opening_value", width: 100,
{id: "opening_value", name: __("Opening Value"), field: "opening_value", width: 100,
formatter: this.currency_formatter},
{id: "inflow_value", name: __("In Value"), field: "inflow_value", width: 100,
{id: "inflow_value", name: __("In Value"), field: "inflow_value", width: 100,
formatter: this.currency_formatter},
{id: "outflow_value", name: __("Out Value"), field: "outflow_value", width: 100,
{id: "outflow_value", name: __("Out Value"), field: "outflow_value", width: 100,
formatter: this.currency_formatter},
{id: "closing_value", name: __("Closing Value"), field: "closing_value", width: 100,
{id: "closing_value", name: __("Closing Value"), field: "closing_value", width: 100,
formatter: this.currency_formatter},
{id: "valuation_rate", name: __("Valuation Rate"), field: "valuation_rate", width: 100,
formatter: this.currency_formatter},
];
},
filters: [
{fieldtype:"Select", label: __("Brand"), link:"Brand",
{fieldtype:"Select", label: __("Brand"), link:"Brand",
default_value: "Select Brand...", filter: function(val, item, opts) {
return val == opts.default_value || item.brand == val || item._show;
}, link_formatter: {filter_input: "brand"}},
{fieldtype:"Select", label: __("Warehouse"), link:"Warehouse",
{fieldtype:"Select", label: __("Warehouse"), link:"Warehouse",
default_value: "Select Warehouse...", filter: function(val, item, opts, me) {
return me.apply_zero_filter(val, item, opts, me);
}},
{fieldtype:"Select", label: __("Project"), link:"Project",
{fieldtype:"Select", label: __("Project"), link:"Project",
default_value: "Select Project...", filter: function(val, item, opts, me) {
return me.apply_zero_filter(val, item, opts, me);
}, link_formatter: {filter_input: "project"}},
@ -73,16 +75,16 @@ erpnext.StockBalance = erpnext.StockAnalytics.extend({
{fieldtype:"Button", label: __("Refresh"), icon:"icon-refresh icon-white"},
{fieldtype:"Button", label: __("Reset Filters"), icon: "icon-filter"}
],
setup_plot_check: function() {
return;
},
prepare_data: function() {
this.stock_entry_map = this.make_name_map(frappe.report_dump.data["Stock Entry"], "name");
this._super();
},
prepare_balances: function() {
var me = this;
var from_date = dateutil.str_to_obj(this.from_date);
@ -95,26 +97,26 @@ erpnext.StockBalance = erpnext.StockAnalytics.extend({
for(var i=0, j=data.length; i<j; i++) {
var sl = data[i];
var sl_posting_date = dateutil.str_to_obj(sl.posting_date);
if((me.is_default("warehouse") ? true : me.warehouse == sl.warehouse) &&
(me.is_default("project") ? true : me.project == sl.project)) {
var item = me.item_by_name[sl.item_code];
var wh = me.get_item_warehouse(sl.warehouse, sl.item_code);
var valuation_method = item.valuation_method ?
var valuation_method = item.valuation_method ?
item.valuation_method : sys_defaults.valuation_method;
var is_fifo = valuation_method == "FIFO";
var qty_diff = sl.qty;
var value_diff = me.get_value_diff(wh, sl, is_fifo);
if(sl_posting_date < from_date) {
item.opening_qty += qty_diff;
item.opening_value += value_diff;
} else if(sl_posting_date <= to_date) {
var ignore_inflow_outflow = this.is_default("warehouse")
&& sl.voucher_type=="Stock Entry"
&& sl.voucher_type=="Stock Entry"
&& this.stock_entry_map[sl.voucher_no].purpose=="Material Transfer";
if(!ignore_inflow_outflow) {
if(qty_diff < 0) {
item.outflow_qty += Math.abs(qty_diff);
@ -126,7 +128,7 @@ erpnext.StockBalance = erpnext.StockAnalytics.extend({
} else {
item.inflow_value += value_diff;
}
item.closing_qty += qty_diff;
item.closing_value += value_diff;
}
@ -138,13 +140,18 @@ erpnext.StockBalance = erpnext.StockAnalytics.extend({
}
// opening + diff = closing
// adding opening, since diff already added to closing
// adding opening, since diff already added to closing
$.each(me.item_by_name, function(key, item) {
item.closing_qty += item.opening_qty;
item.closing_value += item.opening_value;
// valuation rate
if(!item.is_group && flt(item.closing_qty) > 0)
item.valuation_rate = flt(item.closing_value) / flt(item.closing_qty);
else item.valuation_rate = 0.0
});
},
update_groups: function() {
var me = this;
@ -155,24 +162,22 @@ erpnext.StockBalance = erpnext.StockAnalytics.extend({
while(parent) {
parent_group = me.item_by_name[parent];
$.each(me.columns, function(c, col) {
if (col.formatter == me.currency_formatter) {
parent_group[col.field] =
flt(parent_group[col.field])
+ flt(item[col.field]);
if (col.formatter == me.currency_formatter && col.field != "valuation_rate") {
parent_group[col.field] = flt(parent_group[col.field]) + flt(item[col.field]);
}
});
// show parent if filtered by brand
if(item.brand == me.brand)
parent_group._show = true;
parent = me.parent_map[parent];
}
}
});
},
get_plot_data: function() {
return;
}
});
});

View File

@ -1,13 +1,13 @@
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
frappe.pages['stock-ledger'].onload = function(wrapper) {
frappe.pages['stock-ledger'].onload = function(wrapper) {
frappe.ui.make_app_page({
parent: wrapper,
title: __('Stock Ledger'),
single_column: true
});
new erpnext.StockLedger(wrapper);
wrapper.appframe.add_module_icon("Stock")
}
@ -30,7 +30,7 @@ erpnext.StockLedger = erpnext.StockGridReport.extend({
this.columns = [
{id: "posting_datetime", name: __("Posting Date"), field: "posting_datetime", width: 120,
formatter: this.date_formatter},
{id: "item_code", name: __("Item Code"), field: "item_code", width: 160,
{id: "item_code", name: __("Item Code"), field: "item_code", width: 160,
link_formatter: {
filter_input: "item_code",
open_btn: true,
@ -57,10 +57,10 @@ erpnext.StockLedger = erpnext.StockGridReport.extend({
doctype: "dataContext.voucher_type"
}},
];
},
filters: [
{fieldtype:"Select", label: __("Warehouse"), link:"Warehouse",
{fieldtype:"Select", label: __("Warehouse"), link:"Warehouse",
default_value: "Select Warehouse...", filter: function(val, item, opts) {
return item.warehouse == val || val == opts.default_value;
}},
@ -68,7 +68,7 @@ erpnext.StockLedger = erpnext.StockGridReport.extend({
filter: function(val, item, opts) {
return item.item_code == val || !val;
}},
{fieldtype:"Select", label: "Brand", link:"Brand",
{fieldtype:"Select", label: "Brand", link:"Brand",
default_value: "Select Brand...", filter: function(val, item, opts) {
return val == opts.default_value || item.brand == val || item._show;
}, link_formatter: {filter_input: "brand"}},
@ -87,17 +87,17 @@ erpnext.StockLedger = erpnext.StockGridReport.extend({
{fieldtype:"Button", label: __("Refresh"), icon:"icon-refresh icon-white"},
{fieldtype:"Button", label: __("Reset Filters"), icon: "icon-filter"}
],
setup_filters: function() {
var me = this;
this._super();
this.wrapper.bind("apply_filters_from_route", function() { me.toggle_enable_brand(); });
this.filter_inputs.item_code.change(function() { me.toggle_enable_brand(); });
this.trigger_refresh_on_change(["item_code", "warehouse", "brand"]);
},
toggle_enable_brand: function() {
if(!this.filter_inputs.item_code.val()) {
this.filter_inputs.brand.prop("disabled", false);
@ -107,7 +107,7 @@ erpnext.StockLedger = erpnext.StockGridReport.extend({
.prop("disabled", true);
}
},
init_filter_values: function() {
this._super();
this.filter_inputs.warehouse.get(0).selectedIndex = 0;
@ -131,19 +131,19 @@ erpnext.StockLedger = erpnext.StockGridReport.extend({
item_code: "Total Out", qty: 0.0, balance: 0.0, balance_value: 0.0,
id:"_total_out", _show: true, _style: "font-weight: bold"
}
// clear balance
$.each(frappe.report_dump.data["Item"], function(i, item) {
item.balance = item.balance_value = 0.0;
item.balance = item.balance_value = 0.0;
});
// initialize warehouse-item map
this.item_warehouse = {};
this.serialized_buying_rates = this.get_serialized_buying_rates();
var from_datetime = dateutil.str_to_obj(me.from_date + " 00:00:00");
var to_datetime = dateutil.str_to_obj(me.to_date + " 23:59:59");
//
//
for(var i=0, j=data.length; i<j; i++) {
var sl = data[i];
var item = me.item_by_name[sl.item_code]
@ -152,8 +152,8 @@ erpnext.StockLedger = erpnext.StockGridReport.extend({
sl.posting_datetime = sl.posting_date + " " + (sl.posting_time || "00:00:00");
sl.brand = item.brand;
var posting_datetime = dateutil.str_to_obj(sl.posting_datetime);
var is_fifo = item.valuation_method ? item.valuation_method=="FIFO"
var is_fifo = item.valuation_method ? item.valuation_method=="FIFO"
: sys_defaults.valuation_method=="FIFO";
var value_diff = me.get_value_diff(wh, sl, is_fifo);
@ -175,27 +175,27 @@ erpnext.StockLedger = erpnext.StockGridReport.extend({
}
}
}
if(!before_end) break;
// apply filters
if(me.apply_filters(sl)) {
out.push(sl);
}
// update balance
if((!me.is_default("warehouse") ? me.apply_filter(sl, "warehouse") : true)) {
sl.balance = me.item_by_name[sl.item_code].balance + sl.qty;
me.item_by_name[sl.item_code].balance = sl.balance;
sl.balance_value = me.item_by_name[sl.item_code].balance_value + value_diff;
me.item_by_name[sl.item_code].balance_value = sl.balance_value;
me.item_by_name[sl.item_code].balance_value = sl.balance_value;
}
}
if(me.item_code && !me.voucher_no) {
var closing = {
item_code: "On " + dateutil.str_to_user(this.to_date),
item_code: "On " + dateutil.str_to_user(this.to_date),
balance: (out.length ? out[out.length-1].balance : 0), qty: 0,
balance_value: (out.length ? out[out.length-1].balance_value : 0),
id:"_closing", _show: true, _style: "font-weight: bold"
@ -203,7 +203,7 @@ erpnext.StockLedger = erpnext.StockGridReport.extend({
total_out.balance_value = -total_out.balance_value;
var out = [opening].concat(out).concat([total_in, total_out, closing]);
}
this.data = out;
},
get_plot_data: function() {
@ -231,7 +231,7 @@ erpnext.StockLedger = erpnext.StockGridReport.extend({
get_plot_options: function() {
return {
grid: { hoverable: true, clickable: true },
xaxis: { mode: "time",
xaxis: { mode: "time",
min: dateutil.str_to_obj(this.from_date).getTime(),
max: dateutil.str_to_obj(this.to_date).getTime(),
},
@ -244,4 +244,4 @@ erpnext.StockLedger = erpnext.StockGridReport.extend({
var value = format_number(y);
return value.bold() + " on " + date;
}
});
});

View File

@ -1,13 +0,0 @@
{
"creation": "2011-08-23 16:49:40",
"docstatus": 0,
"doctype": "Print Format",
"html": "<html>\n<head>\n\n<script>\nfunction replaceAll(s,s1,s2){ return s.split(s1).join(s2);}\n\nfunction get_letter_head() {\n\t// add letter head\n\tvar cp = locals['Control Panel']['Control Panel'];\n\tif(doc.letter_head)\n\t\tvar lh= cstr(_p.letter_heads[doc.letter_head]);\n\telse if(cp.letter_head)\n\t\tvar lh= cp.letter_head;\n\telse \n\t\tvar lh= '';\n\t\t\n\treturn lh;\n}\n\n</script>\n<style>\n.cust_tbl { border-collapse:collapse; }\n.cust_tbl td { border:1px solid #848484; font-size: 13px}\n.large_font td {font-size: 13px}\n</style>\n</head>\n\n<body>\n<script>\nreplaceAll(doc.print_packing_slip,'[HEADER GOES HERE]',get_letter_head());\n</script>\n</body>\n</html>",
"idx": 1,
"modified": "2014-05-13 16:07:19.133031",
"modified_by": "Administrator",
"module": "Stock",
"name": "Delivery Note Packing List Wise",
"owner": "Administrator",
"standard": "Yes"
}

View File

@ -7,7 +7,7 @@ from frappe.utils import flt
def execute(filters=None):
if not filters: filters = {}
columns = get_columns(filters)
item_map = get_item_details()
pl = get_price_list()
@ -19,34 +19,33 @@ def execute(filters=None):
precision = get_currency_precision() or 2
data = []
for item in sorted(item_map):
data.append([item, item_map[item]["item_name"],
item_map[item]["description"], item_map[item]["stock_uom"],
flt(last_purchase_rate.get(item, 0), precision),
flt(val_rate_map.get(item, 0), precision),
pl.get(item, {}).get("Selling"),
pl.get(item, {}).get("Buying"),
flt(bom_rate.get(item, 0), precision),
flt(item_map[item]["standard_rate"], precision)
data.append([item, item_map[item]["item_name"],
item_map[item]["description"], item_map[item]["stock_uom"],
flt(last_purchase_rate.get(item, 0), precision),
flt(val_rate_map.get(item, 0), precision),
pl.get(item, {}).get("Selling"),
pl.get(item, {}).get("Buying"),
flt(bom_rate.get(item, 0), precision)
])
return columns, data
def get_columns(filters):
"""return columns based on filters"""
columns = ["Item:Link/Item:100", "Item Name::150", "Description::150", "UOM:Link/UOM:80",
"Last Purchase Rate:Currency:90", "Valuation Rate:Currency:80", "Sales Price List::80",
"Purchase Price List::80", "BOM Rate:Currency:90", "Standard Rate:Currency:100"]
columns = ["Item:Link/Item:100", "Item Name::150", "Description::150", "UOM:Link/UOM:80",
"Last Purchase Rate:Currency:90", "Valuation Rate:Currency:80", "Sales Price List::80",
"Purchase Price List::80", "BOM Rate:Currency:90"]
return columns
def get_item_details():
"""returns all items details"""
item_map = {}
for i in frappe.db.sql("select name, item_name, description, \
stock_uom, standard_rate from tabItem \
stock_uom from tabItem \
order by item_code", as_dict=1):
item_map.setdefault(i.name, i)
@ -57,42 +56,42 @@ def get_price_list():
rate = {}
price_list = frappe.db.sql("""select ip.item_code, ip.buying, ip.selling,
concat(ip.price_list, " - ", ip.currency, " ", ip.price_list_rate) as price
from `tabItem Price` ip, `tabPrice List` pl
price_list = frappe.db.sql("""select ip.item_code, ip.buying, ip.selling,
concat(ip.price_list, " - ", ip.currency, " ", ip.price_list_rate) as price
from `tabItem Price` ip, `tabPrice List` pl
where ip.price_list=pl.name and pl.enabled=1""", as_dict=1)
for j in price_list:
if j.price:
rate.setdefault(j.item_code, {}).setdefault("Buying" if j.buying else "Selling", []).append(j.price)
item_rate_map = {}
for item in rate:
for buying_or_selling in rate[item]:
item_rate_map.setdefault(item, {}).setdefault(buying_or_selling,
item_rate_map.setdefault(item, {}).setdefault(buying_or_selling,
", ".join(rate[item].get(buying_or_selling, [])))
return item_rate_map
def get_last_purchase_rate():
item_last_purchase_rate_map = {}
query = """select * from (select
query = """select * from (select
result.item_code,
result.base_rate
from (
(select
(select
po_item.item_code,
po_item.item_name,
po.transaction_date as posting_date,
po_item.base_price_list_rate,
po_item.discount_percentage,
po_item.base_price_list_rate,
po_item.discount_percentage,
po_item.base_rate
from `tabPurchase Order` po, `tabPurchase Order Item` po_item
where po.name = po_item.parent and po.docstatus = 1)
union
(select
(select
pr_item.item_code,
pr_item.item_name,
pr.posting_date,
@ -114,8 +113,8 @@ def get_item_bom_rate():
"""Get BOM rate of an item from BOM"""
item_bom_map = {}
for b in frappe.db.sql("""select item, (total_cost/quantity) as bom_rate
for b in frappe.db.sql("""select item, (total_cost/quantity) as bom_rate
from `tabBOM` where is_active=1 and is_default=1""", as_dict=1):
item_bom_map.setdefault(b.item, flt(b.bom_rate))
@ -125,8 +124,8 @@ def get_valuation_rate():
"""Get an average valuation rate of an item from all warehouses"""
item_val_rate_map = {}
for d in frappe.db.sql("""select item_code,
for d in frappe.db.sql("""select item_code,
sum(actual_qty*valuation_rate)/sum(actual_qty) as val_rate
from tabBin where actual_qty > 0 group by item_code""", as_dict=1):
item_val_rate_map.setdefault(d.item_code, d.val_rate)

View File

@ -17,23 +17,25 @@ def execute(filters=None):
% ("/".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.valuation_rate, sle.stock_value,
sle.voucher_type, sle.voucher_no, voucher_link_icon, sle.batch_no, sle.serial_no, sle.company])
item_detail.brand, item_detail.description, sle.warehouse,
item_detail.stock_uom, sle.actual_qty, sle.qty_after_transaction,
(sle.incoming_rate if sle.actual_qty > 0 else 0.0),
sle.valuation_rate, sle.stock_value, sle.voucher_type, sle.voucher_no,
voucher_link_icon, sle.batch_no, sle.serial_no, sle.company])
return columns, data
def get_columns():
return ["Date:Datetime:95", "Item:Link/Item:130", "Item Name::100",
"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:100", "Valuation Rate:Currency:110",
"Balance Value:Currency:110", "Voucher Type::110", "Voucher #::100", "Link::30",
"Batch:Link/Batch:100", "Serial #:Link/Serial No:100", "Company:Link/Company:100"]
return ["Date:Datetime:95", "Item:Link/Item:130", "Item Name::100", "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:100",
"Incoming Rate:Currency:110", "Valuation Rate:Currency:110", "Balance Value:Currency:110",
"Voucher Type::110", "Voucher #::100", "Link::30", "Batch:Link/Batch:100",
"Serial #:Link/Serial No:100", "Company:Link/Company:100"]
def get_stock_ledger_entries(filters):
return frappe.db.sql("""select concat_ws(" ", posting_date, posting_time) as date,
item_code, warehouse, actual_qty, qty_after_transaction, valuation_rate,
item_code, warehouse, actual_qty, qty_after_transaction, incoming_rate, valuation_rate,
stock_value, voucher_type, voucher_no, batch_no, serial_no, company
from `tabStock Ledger Entry`
where company = %(company)s and

View File

@ -68,10 +68,6 @@ def get_incoming_rate(args):
in_rate = 0
if args.get("serial_no"):
in_rate = get_avg_purchase_rate(args.get("serial_no"))
elif args.get("bom_no"):
result = frappe.db.sql("""select ifnull(total_cost, 0) / ifnull(quantity, 1)
from `tabBOM` where name = %s and docstatus=1 and is_active=1""", args.get("bom_no"))
in_rate = result and flt(result[0][0]) or 0
else:
valuation_method = get_valuation_method(args.get("item_code"))
previous_sle = get_previous_sle(args)

View File

@ -22,12 +22,3 @@ from frappe.utils import cint, comma_or
def validate_status(status, options):
if status not in options:
frappe.throw(_("Status must be one of {0}").format(comma_or(options)))
def build_filter_conditions(filters):
conditions, filter_values = [], []
for key in filters:
conditions.append('`' + key + '` = %s')
filter_values.append(filters[key])
conditions = conditions and " and " + " and ".join(conditions) or ""
return conditions, filter_values