diff --git a/accounts/doctype/sales_invoice/sales_invoice.py b/accounts/doctype/sales_invoice/sales_invoice.py
index 5ebbd0e7b0..c1556363bc 100644
--- a/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/accounts/doctype/sales_invoice/sales_invoice.py
@@ -251,7 +251,7 @@ class DocType(SellingController):
# fetch charges
if self.doc.charge and not len(self.doclist.get({"parentfield": "other_charges"})):
- self.set_taxes()
+ self.set_taxes("other_charges", "charge")
def get_customer_account(self):
"""Get Account Head to which amount needs to be Debited based on Customer"""
diff --git a/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.py b/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.py
index bc55701f45..27316cae3a 100644
--- a/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.py
+++ b/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.py
@@ -16,12 +16,10 @@
from __future__ import unicode_literals
import webnotes
+from webnotes.utils import cint
+from webnotes.model.controller import DocListController
-class DocType:
- def __init__(self, doc, doclist=[]):
- self.doc = doc
- self.doclist = doclist
-
+class DocType(DocListController):
def get_rate(self, arg):
from webnotes.model.code import get_obj
return get_obj('Sales Common').get_rate(arg)
@@ -30,4 +28,12 @@ class DocType:
if self.doc.is_default == 1:
webnotes.conn.sql("""update `tabSales Taxes and Charges Master` set is_default = 0
where ifnull(is_default,0) = 1 and name != %s and company = %s""",
- (self.doc.name, self.doc.company))
\ No newline at end of file
+ (self.doc.name, self.doc.company))
+
+ # at least one territory
+ self.validate_table_has_rows("valid_for_territories")
+
+ def on_update(self):
+ cart_settings = webnotes.get_obj("Shopping Cart Settings")
+ if cint(cart_settings.doc.enabled):
+ cart_settings.validate_tax_masters()
\ No newline at end of file
diff --git a/accounts/doctype/sales_taxes_and_charges_master/test_sales_taxes_and_charges_master.py b/accounts/doctype/sales_taxes_and_charges_master/test_sales_taxes_and_charges_master.py
new file mode 100644
index 0000000000..9bea271816
--- /dev/null
+++ b/accounts/doctype/sales_taxes_and_charges_master/test_sales_taxes_and_charges_master.py
@@ -0,0 +1,146 @@
+test_records = [
+ [
+ {
+ "doctype": "Sales Taxes and Charges Master",
+ "title": "_Test Sales Taxes and Charges Master",
+ "company": "_Test Company"
+ },
+ {
+ "account_head": "_Test Account VAT - _TC",
+ "charge_type": "On Net Total",
+ "description": "VAT",
+ "doctype": "Sales Taxes and Charges",
+ "parentfield": "other_charges",
+ "rate": 6,
+ },
+ {
+ "account_head": "_Test Account Service Tax - _TC",
+ "charge_type": "On Net Total",
+ "description": "Service Tax",
+ "doctype": "Sales Taxes and Charges",
+ "parentfield": "other_charges",
+ "rate": 6.36,
+ },
+ {
+ "doctype": "For Territory",
+ "parentfield": "valid_for_territories",
+ "territory": "All Territories"
+ }
+ ],
+ [
+ {
+ "doctype": "Sales Taxes and Charges Master",
+ "title": "_Test India Tax Master",
+ "company": "_Test Company"
+ },
+ {
+ "doctype": "Sales Taxes and Charges",
+ "parentfield": "other_charges",
+ "charge_type": "Actual",
+ "account_head": "_Test Account Shipping Charges - _TC",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "Shipping Charges",
+ "rate": 100
+ },
+ {
+ "doctype": "Sales Taxes and Charges",
+ "parentfield": "other_charges",
+ "charge_type": "On Net Total",
+ "account_head": "_Test Account Customs Duty - _TC",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "Customs Duty",
+ "rate": 10
+ },
+ {
+ "doctype": "Sales Taxes and Charges",
+ "parentfield": "other_charges",
+ "charge_type": "On Net Total",
+ "account_head": "_Test Account Excise Duty - _TC",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "Excise Duty",
+ "rate": 12
+ },
+ {
+ "doctype": "Sales Taxes and Charges",
+ "parentfield": "other_charges",
+ "charge_type": "On Previous Row Amount",
+ "account_head": "_Test Account Education Cess - _TC",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "Education Cess",
+ "rate": 2,
+ "row_id": 3
+ },
+ {
+ "doctype": "Sales Taxes and Charges",
+ "parentfield": "other_charges",
+ "charge_type": "On Previous Row Amount",
+ "account_head": "_Test Account S&H Education Cess - _TC",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "S&H Education Cess",
+ "rate": 1,
+ "row_id": 3
+ },
+ {
+ "doctype": "Sales Taxes and Charges",
+ "parentfield": "other_charges",
+ "charge_type": "On Previous Row Total",
+ "account_head": "_Test Account CST - _TC",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "CST",
+ "rate": 2,
+ "row_id": 5
+ },
+ {
+ "doctype": "Sales Taxes and Charges",
+ "parentfield": "other_charges",
+ "charge_type": "On Net Total",
+ "account_head": "_Test Account VAT - _TC",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "VAT",
+ "rate": 12.5
+ },
+ {
+ "doctype": "Sales Taxes and Charges",
+ "parentfield": "other_charges",
+ "charge_type": "On Previous Row Total",
+ "account_head": "_Test Account Discount - _TC",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "Discount",
+ "rate": -10,
+ "row_id": 7
+ },
+ {
+ "doctype": "For Territory",
+ "parentfield": "valid_for_territories",
+ "territory": "_Test Territory India"
+ }
+ ],
+ [
+ {
+ "doctype": "Sales Taxes and Charges Master",
+ "title": "_Test Sales Taxes and Charges Master 2",
+ "company": "_Test Company"
+ },
+ {
+ "account_head": "_Test Account VAT - _TC",
+ "charge_type": "On Net Total",
+ "description": "VAT",
+ "doctype": "Sales Taxes and Charges",
+ "parentfield": "other_charges",
+ "rate": 12,
+ },
+ {
+ "account_head": "_Test Account Service Tax - _TC",
+ "charge_type": "On Net Total",
+ "description": "Service Tax",
+ "doctype": "Sales Taxes and Charges",
+ "parentfield": "other_charges",
+ "rate": 4,
+ },
+ {
+ "doctype": "For Territory",
+ "parentfield": "valid_for_territories",
+ "territory": "All Territories"
+ }
+ ],
+]
\ No newline at end of file
diff --git a/accounts/doctype/shipping_rule/shipping_rule.py b/accounts/doctype/shipping_rule/shipping_rule.py
index 87148656df..a363b18454 100644
--- a/accounts/doctype/shipping_rule/shipping_rule.py
+++ b/accounts/doctype/shipping_rule/shipping_rule.py
@@ -16,6 +16,7 @@ class DocType(DocListController):
self.doc, self.doclist = d, dl
def validate(self):
+ self.validate_value("calculate_based_on", "in", ["Net Total", "Net Weight"])
self.shipping_rule_conditions = self.doclist.get({"parentfield": "shipping_rule_conditions"})
self.validate_from_to_values()
self.sort_shipping_rule_conditions()
@@ -57,7 +58,7 @@ class DocType(DocListController):
then condition y can only be like 50 to 99 or 301 to 400
hence, non-overlapping condition = (x1 <= x2 < y1 <= y2) or (y1 <= y2 < x1 <= x2)
"""
- separate = (x1 <= x2 < y1 <= y2) or (y1 <= y2 < x1 <= x2)
+ separate = (x1 <= x2 <= y1 <= y2) or (y1 <= y2 <= x1 <= x2)
return (not separate)
overlaps = []
diff --git a/accounts/doctype/shipping_rule/shipping_rule.txt b/accounts/doctype/shipping_rule/shipping_rule.txt
index dd4fe8dfed..99886cac29 100644
--- a/accounts/doctype/shipping_rule/shipping_rule.txt
+++ b/accounts/doctype/shipping_rule/shipping_rule.txt
@@ -2,11 +2,12 @@
{
"creation": "2013-06-25 11:48:03",
"docstatus": 0,
- "modified": "2013-06-25 12:15:21",
+ "modified": "2013-07-04 16:28:42",
"modified_by": "Administrator",
"owner": "Administrator"
},
{
+ "autoname": "Prompt",
"description": "Specify conditions to calculate shipping amount",
"doctype": "DocType",
"module": "Accounts",
@@ -37,7 +38,7 @@
{
"description": "example: Next Day Shipping",
"doctype": "DocField",
- "fieldname": "shipping_rule_label",
+ "fieldname": "label",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Shipping Rule Label",
@@ -49,14 +50,16 @@
"fieldtype": "Column Break"
},
{
- "default": "Amount",
+ "default": "Net Total",
"doctype": "DocField",
"fieldname": "calculate_based_on",
"fieldtype": "Select",
+ "hidden": 1,
"in_list_view": 1,
"label": "Calculate Based On",
- "options": "Amount\nNet Weight",
- "reqd": 1
+ "options": "Net Total\nNet Weight",
+ "read_only": 1,
+ "reqd": 0
},
{
"doctype": "DocField",
diff --git a/accounts/doctype/shipping_rule/test_shipping_rule.py b/accounts/doctype/shipping_rule/test_shipping_rule.py
index ff217bc5a7..23d204d040 100644
--- a/accounts/doctype/shipping_rule/test_shipping_rule.py
+++ b/accounts/doctype/shipping_rule/test_shipping_rule.py
@@ -32,7 +32,7 @@ test_records = [
[
{
"doctype": "Shipping Rule",
- "calculate_based_on": "Amount",
+ "calculate_based_on": "Net Total",
"company": "_Test Company",
"account": "_Test Account Shipping Charges - _TC",
"cost_center": "_Test Cost Center - _TC"
diff --git a/accounts/page/accounts_home/accounts_home.js b/accounts/page/accounts_home/accounts_home.js
index 3ca51b8d37..1f41fcc69a 100644
--- a/accounts/page/accounts_home/accounts_home.js
+++ b/accounts/page/accounts_home/accounts_home.js
@@ -106,6 +106,11 @@ wn.module_page["Accounts"] = [
"doctype":"Shipping Rule",
"description": wn._("Rules to calculate shipping amount for a sale")
},
+ {
+ "label": wn._("Currency Exchange"),
+ "doctype":"Currency Exchange",
+ "description": wn._("Manage exchange rates for currency conversion")
+ },
{
"label": wn._("Point-of-Sale Setting"),
"doctype":"POS Setting",
diff --git a/buying/doctype/purchase_common/purchase_common.js b/buying/doctype/purchase_common/purchase_common.js
index c8e4aa3585..0d4a615ec1 100644
--- a/buying/doctype/purchase_common/purchase_common.js
+++ b/buying/doctype/purchase_common/purchase_common.js
@@ -361,7 +361,7 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
var setup_field_label_map = function(fields_list, currency) {
$.each(fields_list, function(i, fname) {
- var docfield = wn.meta.get_docfield(me.frm.doc.doctype, fname);
+ var docfield = wn.meta.docfield_map[me.frm.doc.doctype][fname];
if(docfield) {
var label = wn._(docfield.label || "").replace(/\([^\)]*\)/g, "");
field_label_map[fname] = label.trim() + " (" + currency + ")";
@@ -407,7 +407,7 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
var setup_field_label_map = function(fields_list, currency, parentfield) {
var grid_doctype = me.frm.fields_dict[parentfield].grid.doctype;
$.each(fields_list, function(i, fname) {
- var docfield = wn.meta.get_docfield(grid_doctype, fname);
+ var docfield = wn.meta.docfield_map[grid_doctype][fname];
if(docfield) {
var label = wn._(docfield.label || "").replace(/\([^\)]*\)/g, "");
field_label_map[grid_doctype + "-" + fname] =
diff --git a/buying/doctype/purchase_order/test_purchase_order.py b/buying/doctype/purchase_order/test_purchase_order.py
index be2e2946a9..34286dbd10 100644
--- a/buying/doctype/purchase_order/test_purchase_order.py
+++ b/buying/doctype/purchase_order/test_purchase_order.py
@@ -31,6 +31,7 @@ class TestPurchaseOrder(unittest.TestCase):
from controllers.buying_controller import WrongWarehouseCompany
po = webnotes.bean(copy=test_records[0])
po.doc.company = "_Test Company 1"
+ po.doc.conversion_rate = 0.0167
self.assertRaises(WrongWarehouseCompany, po.insert)
diff --git a/controllers/accounts_controller.py b/controllers/accounts_controller.py
index 69522c4852..5439030306 100644
--- a/controllers/accounts_controller.py
+++ b/controllers/accounts_controller.py
@@ -55,14 +55,18 @@ class AccountsController(TransactionBase):
if self.doc.price_list_currency:
if not self.doc.plc_conversion_rate:
- exchange = self.doc.price_list_currency + "-" + get_company_currency(self.doc.company)
- self.doc.plc_conversion_rate = flt(webnotes.conn.get_value("Currency Exchange",
- exchange, "exchange_rate"))
+ company_currency = get_company_currency(self.doc.company)
+ if self.doc.price_list_currency == company_currency:
+ self.doc.plc_conversion_rate = 1.0
+ else:
+ exchange = self.doc.price_list_currency + "-" + company_currency
+ self.doc.plc_conversion_rate = flt(webnotes.conn.get_value("Currency Exchange",
+ exchange, "exchange_rate"))
if not self.doc.currency:
self.doc.currency = self.doc.price_list_currency
self.doc.conversion_rate = self.doc.plc_conversion_rate
-
+
def set_missing_item_details(self, get_item_details):
"""set missing item values"""
for item in self.doclist.get({"parentfield": self.fname}):
@@ -71,34 +75,44 @@ class AccountsController(TransactionBase):
ret = get_item_details(args)
for fieldname, value in ret.items():
if self.meta.get_field(fieldname, parentfield=self.fname) and \
- item.fields.get(fieldname) is None:
+ item.fields.get(fieldname) is None and value is not None:
item.fields[fieldname] = value
- def set_taxes(self, tax_doctype, tax_parentfield, tax_master_field):
+ def set_taxes(self, tax_parentfield, tax_master_field):
if not self.meta.get_field(tax_parentfield):
return
+ tax_master_doctype = self.meta.get_field(tax_master_field).options
+
if not self.doclist.get({"parentfield": tax_parentfield}):
if not self.doc.fields.get(tax_master_field):
# get the default tax master
self.doc.fields[tax_master_field] = \
- webnotes.conn.get_value(tax_doctype + " Master", {"is_default": 1})
-
- if self.doc.fields.get(tax_master_field):
- from webnotes.model import default_fields
- tax_master = webnotes.bean(tax_doctype + " Master", self.doc.fields.get(tax_master_field))
-
- for i, tax in enumerate(tax_master.doclist.get({"parentfield": tax_parentfield})):
- for fieldname in default_fields:
- tax.fields[fieldname] = None
+ webnotes.conn.get_value(tax_master_doctype, {"is_default": 1})
- tax.fields.update({
- "doctype": tax_doctype,
- "parentfield": tax_parentfield,
- "idx": i+1
- })
-
- self.doclist.append(tax)
+ self.append_taxes_from_master(tax_parentfield, tax_master_field, tax_master_doctype)
+
+ def append_taxes_from_master(self, tax_parentfield, tax_master_field, tax_master_doctype=None):
+ if self.doc.fields.get(tax_master_field):
+ if not tax_master_doctype:
+ tax_master_doctype = self.meta.get_field(tax_master_field).options
+
+ tax_doctype = self.meta.get_field(tax_parentfield).options
+
+ from webnotes.model import default_fields
+ tax_master = webnotes.bean(tax_master_doctype, self.doc.fields.get(tax_master_field))
+
+ for i, tax in enumerate(tax_master.doclist.get({"parentfield": tax_parentfield})):
+ for fieldname in default_fields:
+ tax.fields[fieldname] = None
+
+ tax.fields.update({
+ "doctype": tax_doctype,
+ "parentfield": tax_parentfield,
+ "idx": i+1
+ })
+
+ self.doclist.append(tax)
def calculate_taxes_and_totals(self):
self.doc.conversion_rate = flt(self.doc.conversion_rate)
diff --git a/controllers/buying_controller.py b/controllers/buying_controller.py
index 885f9e54e6..e58eb03ca9 100644
--- a/controllers/buying_controller.py
+++ b/controllers/buying_controller.py
@@ -30,7 +30,7 @@ class BuyingController(StockController):
def onload_post_render(self):
# contact, address, item details
self.set_missing_values()
- self.set_taxes("Purchase Taxes and Charges", "purchase_tax_details", "purchase_other_charges")
+ self.set_taxes("purchase_tax_details", "purchase_other_charges")
def validate(self):
super(BuyingController, self).validate()
@@ -55,7 +55,7 @@ class BuyingController(StockController):
def get_purchase_tax_details(self):
self.doclist = self.doc.clear_table(self.doclist, "purchase_tax_details")
- self.set_taxes("Purchase Taxes and Charges", "purchase_tax_details", "purchase_other_charges")
+ self.set_taxes("purchase_tax_details", "purchase_other_charges")
def validate_warehouse_belongs_to_company(self):
for warehouse, company in webnotes.conn.get_values("Warehouse",
diff --git a/controllers/selling_controller.py b/controllers/selling_controller.py
index 140e0c0088..2f772957fa 100644
--- a/controllers/selling_controller.py
+++ b/controllers/selling_controller.py
@@ -28,48 +28,61 @@ class SellingController(StockController):
# contact, address, item details and pos details (if applicable)
self.set_missing_values()
- self.set_taxes("Sales Taxes and Charges", "other_charges", "charge")
+ self.set_taxes("other_charges", "charge")
def set_missing_values(self, for_validate=False):
super(SellingController, self).set_missing_values(for_validate)
- self.set_price_list_currency("Selling")
-
# set contact and address details for customer, if they are not mentioned
self.set_missing_lead_customer_details()
-
- self.set_missing_item_details(get_item_details)
+
+ self.set_price_list_and_item_details()
def set_missing_lead_customer_details(self):
if self.doc.customer:
- if not (self.doc.contact_person and self.doc.customer_address):
- for fieldname, val in self.get_default_address_and_contact("customer").items():
+ if not (self.doc.contact_person and self.doc.customer_address and self.doc.customer_name):
+ for fieldname, val in self.get_customer_defaults().items():
if not self.doc.fields.get(fieldname) and self.meta.get_field(fieldname):
self.doc.fields[fieldname] = val
- customer_fetch = webnotes.conn.get_value("Customer", self.doc.customer,
- ['customer_name', 'customer_group', 'territory'], as_dict=True)
- for fieldname in ['customer_name', 'customer_group', 'territory']:
- if not self.doc.fields.get(fieldname):
- self.doc.fields[fieldname] = customer_fetch[fieldname]
-
elif self.doc.lead:
- lead_fetch = webnotes.conn.get_value("Lead", self.doc.lead,
- ['company_name', 'lead_name', 'territory'], as_dict=True)
- if not self.doc.customer_name:
- self.doc.customer_name = lead_fetch.company_name or lead_fetch.lead_name
- if not self.doc.territory:
- self.doc.territory = lead_fetch.territory
-
+ if not (self.doc.customer_address and self.doc.customer_name and \
+ self.doc.contact_display):
+ for fieldname, val in self.get_lead_defaults().items():
+ if not self.doc.fields.get(fieldname) and self.meta.get_field(fieldname):
+ self.doc.fields[fieldname] = val
+
+ def set_price_list_and_item_details(self):
+ self.set_price_list_currency("Selling")
+ self.set_missing_item_details(get_item_details)
+
def get_other_charges(self):
self.doclist = self.doc.clear_table(self.doclist, "other_charges")
- self.set_taxes("Sales Taxes and Charges", "other_charges", "charge")
+ self.set_taxes("other_charges", "charge")
- def set_customer_defaults(self):
- self.get_default_customer_address()
-
- if self.meta.get_field("shipping_address"):
- self.doc.fields.update(self.get_shipping_address(self.doc.customer))
+ def apply_shipping_rule(self):
+ if self.doc.shipping_rule:
+ shipping_rule = webnotes.bean("Shipping Rule", self.doc.shipping_rule)
+ value = self.doc.net_total
+
+ # TODO
+ # shipping rule calculation based on item's net weight
+
+ shipping_amount = 0.0
+ for condition in shipping_rule.doclist.get({"parentfield": "shipping_rule_conditions"}):
+ if not condition.to_value or (flt(condition.from_value) <= value <= flt(condition.to_value)):
+ shipping_amount = condition.shipping_amount
+ break
+
+ self.doclist.append({
+ "doctype": "Sales Taxes and Charges",
+ "parentfield": "other_charges",
+ "charge_type": "Actual",
+ "account_head": shipping_rule.doc.account,
+ "cost_center": shipping_rule.doc.cost_center,
+ "description": shipping_rule.doc.label,
+ "rate": shipping_amount
+ })
def set_total_in_words(self):
from webnotes.utils import money_in_words
diff --git a/public/js/complete_setup.js b/public/js/complete_setup.js
index 37c2fba52e..9d859797e2 100644
--- a/public/js/complete_setup.js
+++ b/public/js/complete_setup.js
@@ -84,6 +84,8 @@ $.extend(erpnext.complete_setup, {
wn.container.wntoolbar.set_user_name();
setTimeout(function() { window.location.reload(); }, 3000);
+ } else {
+ $(this).done_working();
}
});
diff --git a/public/js/website_utils.js b/public/js/website_utils.js
index 59614c54a6..e714514e97 100644
--- a/public/js/website_utils.js
+++ b/public/js/website_utils.js
@@ -55,6 +55,15 @@ wn.call = function(opts) {
$(opts.btn).addClass("btn-danger");
setTimeout(function() { $(opts.btn).removeClass("btn-danger"); }, 1000);
}
+ try {
+ var err = JSON.parse(data.exc);
+ if($.isArray(err)) {
+ err = err.join("\n");
+ }
+ console.error ? console.error(err) : console.log(err);
+ } catch(e) {
+ console.log(data.exc);
+ }
} else{
if(opts.btn) {
$(opts.btn).addClass("btn-success");
@@ -66,6 +75,9 @@ wn.call = function(opts) {
}
if(opts.callback)
opts.callback(data);
+ },
+ error: function(response) {
+ console.error ? console.error(response) : console.log(response);
}
});
diff --git a/selling/doctype/installation_note/installation_note.js b/selling/doctype/installation_note/installation_note.js
index b0e005eb0b..61d0484a08 100644
--- a/selling/doctype/installation_note/installation_note.js
+++ b/selling/doctype/installation_note/installation_note.js
@@ -8,69 +8,82 @@
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
-// along with this program. If not, see .
+// along with this program. If not, see .
cur_frm.cscript.tname = "Installation Note Item";
cur_frm.cscript.fname = "installed_item_details";
+wn.provide("erpnext.selling");
+// TODO commonify this code
+erpnext.selling.InstallationNote = wn.ui.form.Controller.extend({
+ customer: function() {
+ var me = this;
+ if(this.frm.doc.customer) {
+ this.frm.call({
+ doc: this.frm.doc,
+ method: "set_customer_defaults",
+ callback: function(r) {
+ if(!r.exc) me.frm.refresh_fields();
+ }
+ });
+
+ // TODO shift this to depends_on
+ unhide_field(['customer_address', 'contact_person', 'customer_name',
+ 'address_display', 'contact_display', 'contact_mobile', 'contact_email',
+ 'territory', 'customer_group']);
+ }
+ }
+});
+
+$.extend(cur_frm.cscript, new erpnext.selling.InstallationNote({frm: cur_frm}));
+
cur_frm.cscript.onload = function(doc, dt, dn) {
- if(!doc.status) set_multiple(dt,dn,{status:'Draft'});
- if(doc.__islocal){
- set_multiple(dt,dn,{inst_date:get_today()});
- hide_field(['customer_address','contact_person','customer_name','address_display','contact_display','contact_mobile','contact_email','territory','customer_group']);
- }
- if (doc.customer) {
- unhide_field(['customer_address','contact_person','customer_name','address_display','contact_display','contact_mobile','contact_email','territory','customer_group']);
- }
+ if(!doc.status) set_multiple(dt,dn,{status:'Draft'});
+ if(doc.__islocal){
+ set_multiple(dt,dn,{inst_date:get_today()});
+ hide_field(['customer_address','contact_person','customer_name','address_display','contact_display','contact_mobile','contact_email','territory','customer_group']);
+ }
+ if (doc.customer) {
+ unhide_field(['customer_address','contact_person','customer_name','address_display','contact_display','contact_mobile','contact_email','territory','customer_group']);
+ }
}
cur_frm.fields_dict['delivery_note_no'].get_query = function(doc) {
- doc = locals[this.doctype][this.docname];
- var cond = '';
- if(doc.customer) {
- cond = '`tabDelivery Note`.customer = "'+doc.customer+'" AND';
- }
- return repl('SELECT DISTINCT `tabDelivery Note`.name, `tabDelivery Note`.customer_name FROM `tabDelivery Note`, `tabDelivery Note Item` WHERE `tabDelivery Note`.company = "%(company)s" AND `tabDelivery Note`.docstatus = 1 AND ifnull(`tabDelivery Note`.per_installed,0) < 99.99 AND %(cond)s `tabDelivery Note`.name LIKE "%s" ORDER BY `tabDelivery Note`.name DESC LIMIT 50', {company:doc.company, cond:cond});
+ doc = locals[this.doctype][this.docname];
+ var cond = '';
+ if(doc.customer) {
+ cond = '`tabDelivery Note`.customer = "'+doc.customer+'" AND';
+ }
+ return repl('SELECT DISTINCT `tabDelivery Note`.name, `tabDelivery Note`.customer_name FROM `tabDelivery Note`, `tabDelivery Note Item` WHERE `tabDelivery Note`.company = "%(company)s" AND `tabDelivery Note`.docstatus = 1 AND ifnull(`tabDelivery Note`.per_installed,0) < 99.99 AND %(cond)s `tabDelivery Note`.name LIKE "%s" ORDER BY `tabDelivery Note`.name DESC LIMIT 50', {company:doc.company, cond:cond});
}
cur_frm.fields_dict['territory'].get_query = function(doc,cdt,cdn) {
- return 'SELECT `tabTerritory`.`name`,`tabTerritory`.`parent_territory` FROM `tabTerritory` WHERE `tabTerritory`.`is_group` = "No" AND `tabTerritory`.`docstatus`!= 2 AND `tabTerritory`.%(key)s LIKE "%s" ORDER BY `tabTerritory`.`name` ASC LIMIT 50';
+ return 'SELECT `tabTerritory`.`name`,`tabTerritory`.`parent_territory` FROM `tabTerritory` WHERE `tabTerritory`.`is_group` = "No" AND `tabTerritory`.`docstatus`!= 2 AND `tabTerritory`.%(key)s LIKE "%s" ORDER BY `tabTerritory`.`name` ASC LIMIT 50';
}
cur_frm.cscript.get_items = function(doc, dt, dn) {
- var callback = function(r,rt) {
- unhide_field(['customer_address','contact_person','customer_name','address_display','contact_display','contact_mobile','contact_email','territory','customer_group']);
- cur_frm.refresh();
- }
- get_server_fields('pull_delivery_note_details','','',doc, dt, dn,1,callback);
+ var callback = function(r,rt) {
+ unhide_field(['customer_address','contact_person','customer_name','address_display','contact_display','contact_mobile','contact_email','territory','customer_group']);
+ cur_frm.refresh();
+ }
+ get_server_fields('pull_delivery_note_details','','',doc, dt, dn,1,callback);
}
-//customer
-cur_frm.cscript.customer = function(doc,dt,dn) {
- var callback = function(r,rt) {
- var doc = locals[cur_frm.doctype][cur_frm.docname];
- cur_frm.refresh();
- }
-
- if(doc.customer) $c_obj(make_doclist(doc.doctype, doc.name), 'get_default_customer_address', '', callback);
- if(doc.customer) unhide_field(['customer_address','contact_person','customer_name','address_display','contact_display','contact_mobile','contact_email','territory','customer_group']);
-}
-
-cur_frm.cscript.customer_address = cur_frm.cscript.contact_person = function(doc,dt,dn) {
- if(doc.customer) get_server_fields('get_customer_address', JSON.stringify({customer: doc.customer, address: doc.customer_address, contact: doc.contact_person}),'', doc, dt, dn, 1);
+cur_frm.cscript.customer_address = cur_frm.cscript.contact_person = function(doc,dt,dn) {
+ if(doc.customer) get_server_fields('get_customer_address', JSON.stringify({customer: doc.customer, address: doc.customer_address, contact: doc.contact_person}),'', doc, dt, dn, 1);
}
cur_frm.fields_dict['customer_address'].get_query = function(doc, cdt, cdn) {
- return 'SELECT name,address_line1,city FROM tabAddress WHERE customer = "'+ doc.customer +'" AND docstatus != 2 AND name LIKE "%s" ORDER BY name ASC LIMIT 50';
+ return 'SELECT name,address_line1,city FROM tabAddress WHERE customer = "'+ doc.customer +'" AND docstatus != 2 AND name LIKE "%s" ORDER BY name ASC LIMIT 50';
}
cur_frm.fields_dict['contact_person'].get_query = function(doc, cdt, cdn) {
- return 'SELECT name,CONCAT(first_name," ",ifnull(last_name,"")) As FullName,department,designation FROM tabContact WHERE customer = "'+ doc.customer +'" AND docstatus != 2 AND name LIKE "%s" ORDER BY name ASC LIMIT 50';
+ return 'SELECT name,CONCAT(first_name," ",ifnull(last_name,"")) As FullName,department,designation FROM tabContact WHERE customer = "'+ doc.customer +'" AND docstatus != 2 AND name LIKE "%s" ORDER BY name ASC LIMIT 50';
}
cur_frm.fields_dict.customer.get_query = erpnext.utils.customer_query;
\ No newline at end of file
diff --git a/selling/doctype/lead/lead.txt b/selling/doctype/lead/lead.txt
index 15f4dc0956..343eb1ee78 100644
--- a/selling/doctype/lead/lead.txt
+++ b/selling/doctype/lead/lead.txt
@@ -2,7 +2,7 @@
{
"creation": "2013-04-10 11:45:37",
"docstatus": 0,
- "modified": "2013-07-03 10:22:31",
+ "modified": "2013-07-03 10:24:00",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -314,13 +314,6 @@
"oldfieldtype": "Select",
"options": "\nProduct Enquiry\nRequest for Information\nSuggestions\nOther"
},
- {
- "doctype": "DocField",
- "fieldname": "default_price_list",
- "fieldtype": "Link",
- "label": "Default Price List",
- "options": "Price List"
- },
{
"doctype": "DocField",
"fieldname": "fiscal_year",
diff --git a/selling/doctype/opportunity/opportunity.js b/selling/doctype/opportunity/opportunity.js
index 583f25adf1..c3843cf466 100644
--- a/selling/doctype/opportunity/opportunity.js
+++ b/selling/doctype/opportunity/opportunity.js
@@ -16,6 +16,30 @@
wn.require('app/utilities/doctype/sms_control/sms_control.js');
+wn.provide("erpnext.selling");
+// TODO commonify this code
+erpnext.selling.Opportunity = wn.ui.form.Controller.extend({
+ customer: function() {
+ var me = this;
+ if(this.frm.doc.customer) {
+ this.frm.call({
+ doc: this.frm.doc,
+ method: "set_customer_defaults",
+ callback: function(r) {
+ if(!r.exc) me.frm.refresh_fields();
+ }
+ });
+
+ // TODO shift this to depends_on
+ unhide_field(['customer_address', 'contact_person', 'customer_name',
+ 'address_display', 'contact_display', 'contact_mobile', 'contact_email',
+ 'territory', 'customer_group']);
+ }
+ }
+});
+
+$.extend(cur_frm.cscript, new erpnext.selling.Opportunity({frm: cur_frm}));
+
cur_frm.cscript.refresh = function(doc, cdt, cdn){
erpnext.hide_naming_series();
@@ -117,28 +141,6 @@ cur_frm.cscript.lead_cust_show = function(doc,cdt,cdn){
}
}
-// customer
-cur_frm.cscript.customer = function(doc,dt,dn) {
- cur_frm.toggle_display("contact_info", doc.customer || doc.lead);
-
- if(doc.customer) {
- cur_frm.call({
- doc: cur_frm.doc,
- method: "get_default_customer_address",
- args: { customer: doc.customer },
- callback: function(r) {
- if(!r.exc) {
- cur_frm.refresh();
- }
- }
- });
-
- unhide_field(["customer_name", "customer_address", "contact_person",
- "address_display", "contact_display", "contact_mobile", "contact_email",
- "territory", "customer_group"]);
- }
-}
-
cur_frm.cscript.customer_address = cur_frm.cscript.contact_person = function(doc,dt,dn) {
if(doc.customer) get_server_fields('get_customer_address', JSON.stringify({customer: doc.customer, address: doc.customer_address, contact: doc.contact_person}),'', doc, dt, dn, 1);
}
@@ -167,7 +169,15 @@ cur_frm.cscript.lead = function(doc, cdt, cdn) {
cur_frm.toggle_display("contact_info", doc.customer || doc.lead);
if(doc.lead) {
- get_server_fields('get_lead_details', doc.lead,'', doc, cdt, cdn, 1);
+ cur_frm.call({
+ doc: cur_frm.doc,
+ method: "set_lead_defaults",
+ callback: function(r) {
+ if(!r.exc) {
+ cur_frm.refresh_fields();
+ }
+ }
+ });
unhide_field(['customer_name', 'address_display','contact_mobile', 'contact_email',
'territory']);
}
diff --git a/selling/doctype/opportunity/opportunity.py b/selling/doctype/opportunity/opportunity.py
index 9fb061b228..c56915754f 100644
--- a/selling/doctype/opportunity/opportunity.py
+++ b/selling/doctype/opportunity/opportunity.py
@@ -24,9 +24,8 @@ from webnotes import msgprint
sql = webnotes.conn.sql
from utilities.transaction_base import TransactionBase
-
class DocType(TransactionBase):
- def __init__(self,doc,doclist=[]):
+ def __init__(self,doc,doclist):
self.doc = doc
self.doclist = doclist
self.fname = 'enq_details'
diff --git a/selling/doctype/quotation/quotation.js b/selling/doctype/quotation/quotation.js
index 259c8109f2..97fd714205 100644
--- a/selling/doctype/quotation/quotation.js
+++ b/selling/doctype/quotation/quotation.js
@@ -99,7 +99,15 @@ cur_frm.fields_dict.lead.get_query = erpnext.utils.lead_query;
cur_frm.cscript.lead = function(doc, cdt, cdn) {
if(doc.lead) {
- get_server_fields('get_lead_details', doc.lead,'', doc, cdt, cdn, 1);
+ cur_frm.call({
+ doc: cur_frm.doc,
+ method: "set_lead_defaults",
+ callback: function(r) {
+ if(!r.exc) {
+ cur_frm.refresh_fields();
+ }
+ }
+ });
unhide_field('territory');
}
}
diff --git a/selling/doctype/sales_common/sales_common.js b/selling/doctype/sales_common/sales_common.js
index 5534369b54..27c82f6cf4 100644
--- a/selling/doctype/sales_common/sales_common.js
+++ b/selling/doctype/sales_common/sales_common.js
@@ -26,10 +26,78 @@ wn.require("app/js/transaction.js");
erpnext.selling.SellingController = erpnext.TransactionController.extend({
setup: function() {
+ var me = this;
+
this.frm.add_fetch("sales_partner", "commission_rate", "commission_rate");
- if(this.frm.fields_dict.shipping_address_name && this.frm.fields_dict.customer_address)
- this.frm.fields_dict.shipping_address_name.get_query = this.frm.fields_dict['customer_address'].get_query;
+ if(this.frm.fields_dict.shipping_address_name && this.frm.fields_dict.customer_address) {
+ this.frm.fields_dict.shipping_address_name.get_query =
+ this.frm.fields_dict['customer_address'].get_query;
+ }
+
+ this.frm.set_query("customer_address", function() {
+ return 'SELECT name, address_line1, city FROM tabAddress \
+ WHERE customer = "'+ me.frm.doc.customer +'" AND docstatus != 2 AND \
+ %(key)s LIKE "%s" ORDER BY name ASC LIMIT 50';
+ });
+
+ this.frm.set_query("contact_person", function() {
+ return 'SELECT name, CONCAT(first_name," ",ifnull(last_name,"")) As FullName, \
+ department, designation FROM tabContact WHERE customer = "'+ me.frm.doc.customer +
+ '" AND docstatus != 2 AND %(key)s LIKE "%s" ORDER BY name ASC LIMIT 50';
+ });
+
+ if(this.frm.fields_dict.charge) {
+ this.frm.set_query("charge", function() {
+ return 'SELECT DISTINCT `tabSales Taxes and Charges Master`.name FROM \
+ `tabSales Taxes and Charges Master` \
+ WHERE `tabSales Taxes and Charges Master`.company = "' + me.frm.doc.company +
+ '" AND `tabSales Taxes and Charges Master`.company is not NULL \
+ AND `tabSales Taxes and Charges Master`.docstatus != 2 \
+ AND `tabSales Taxes and Charges Master`.%(key)s LIKE "%s" \
+ ORDER BY `tabSales Taxes and Charges Master`.name LIMIT 50';
+ });
+ }
+
+ this.frm.fields_dict.customer.get_query = erpnext.utils.customer_query;
+
+ this.frm.fields_dict.lead && this.frm.set_query("lead", erpnext.utils.lead_query);
+
+ if(!this.fname) {
+ return;
+ }
+
+ if(this.frm.fields_dict[this.fname].grid.get_field('item_code')) {
+ this.frm.set_query("item_code", this.fname, function() {
+ return me.frm.doc.order_type === "Maintenance" ?
+ erpnext.queries.item({'ifnull(tabItem.is_service_item, "No")': "Yes"}) :
+ erpnext.queries.item({'ifnull(tabItem.is_sales_item, "No")': "Yes"});
+ });
+ }
+
+ if(this.frm.fields_dict[this.fname].grid.get_field('batch_no')) {
+ this.frm.set_query("batch_no", this.fname, function(doc, cdt, cdn) {
+ var item = wn.model.get_doc(cdt, cdn);
+ if(!item.item_code) {
+ wn.throw("Please enter Item Code to get batch no");
+ } else {
+ if(item.warehouse) {
+ return "select batch_no from `tabStock Ledger Entry` sle \
+ where item_code = '" + item.item_code +
+ "' and warehouse = '" + item.warehouse +
+ "' and ifnull(is_cancelled, 'No') = 'No' and batch_no like '%s' \
+ and exists(select * from `tabBatch` where \
+ name = sle.batch_no and expiry_date >= '" + me.frm.doc.posting_date +
+ "' and docstatus != 2) group by batch_no having sum(actual_qty) > 0 \
+ order by batch_no desc limit 50";
+ } else {
+ return "SELECT name FROM tabBatch WHERE docstatus != 2 AND item = '" +
+ item.item_code + "' and expiry_date >= '" + me.frm.doc.posting_date +
+ "' AND name like '%s' ORDER BY name DESC LIMIT 50";
+ }
+ }
+ });
+ }
},
onload: function() {
@@ -403,7 +471,7 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({
var setup_field_label_map = function(fields_list, currency) {
$.each(fields_list, function(i, fname) {
- var docfield = wn.meta.get_docfield(me.frm.doc.doctype, fname);
+ var docfield = wn.meta.docfield_map[me.frm.doc.doctype][fname];
if(docfield) {
var label = wn._(docfield.label || "").replace(/\([^\)]*\)/g, "");
field_label_map[fname] = label.trim() + " (" + currency + ")";
@@ -448,7 +516,7 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({
var setup_field_label_map = function(fields_list, currency, parentfield) {
var grid_doctype = me.frm.fields_dict[parentfield].grid.doctype;
$.each(fields_list, function(i, fname) {
- var docfield = wn.meta.get_docfield(grid_doctype, fname);
+ var docfield = wn.meta.docfield_map[grid_doctype][fname];
if(docfield) {
var label = wn._(docfield.label || "").replace(/\([^\)]*\)/g, "");
field_label_map[grid_doctype + "-" + fname] =
@@ -508,15 +576,6 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({
}
});
-// to save previous state of cur_frm.cscript
-var prev_cscript = {};
-$.extend(prev_cscript, cur_frm.cscript);
-
-cur_frm.cscript = new erpnext.selling.SellingController({frm: cur_frm});
-
-// for backward compatibility: combine new and previous states
-$.extend(cur_frm.cscript, prev_cscript);
-
// Help for Sales BOM items
var set_sales_bom_help = function(doc) {
if(!cur_frm.fields_dict.packing_list) return;
@@ -540,61 +599,3 @@ var set_sales_bom_help = function(doc) {
}
refresh_field('sales_bom_help');
}
-
-cur_frm.fields_dict[cur_frm.cscript.fname].grid.get_field("item_code").get_query = function(doc, cdt, cdn) {
- if (doc.order_type == "Maintenance") {
- return erpnext.queries.item({
- 'ifnull(tabItem.is_service_item, "No")': 'Yes'
- });
- } else {
- return erpnext.queries.item({
- 'ifnull(tabItem.is_sales_item, "No")': 'Yes'
- });
- }
-}
-
-cur_frm.fields_dict[cur_frm.cscript.fname].grid.get_field('batch_no').get_query =
- function(doc, cdt, cdn) {
- var d = locals[cdt][cdn];
- if(d.item_code) {
- if (d.warehouse) {
- return "select batch_no from `tabStock Ledger Entry` sle \
- where item_code = '" + d.item_code + "' and warehouse = '" + d.warehouse +
- "' and ifnull(is_cancelled, 'No') = 'No' and batch_no like '%s' \
- and exists(select * from `tabBatch` where \
- name = sle.batch_no and expiry_date >= '" + doc.posting_date +
- "' and docstatus != 2) group by batch_no having sum(actual_qty) > 0 \
- order by batch_no desc limit 50";
- } else {
- return "SELECT name FROM tabBatch WHERE docstatus != 2 AND item = '" +
- d.item_code + "' and expiry_date >= '" + doc.posting_date +
- "' AND name like '%s' ORDER BY name DESC LIMIT 50";
- }
- } else {
- msgprint("Please enter Item Code to get batch no");
- }
- }
-
-cur_frm.fields_dict['customer_address'].get_query = function(doc, cdt, cdn) {
- return 'SELECT name, address_line1, city FROM tabAddress \
- WHERE customer = "'+ doc.customer +'" AND docstatus != 2 AND \
- %(key)s LIKE "%s" ORDER BY name ASC LIMIT 50';
-}
-
-cur_frm.fields_dict['contact_person'].get_query = function(doc, cdt, cdn) {
- return 'SELECT name, CONCAT(first_name," ",ifnull(last_name,"")) As FullName, \
- department, designation FROM tabContact WHERE customer = "'+ doc.customer +
- '" AND docstatus != 2 AND %(key)s LIKE "%s" ORDER BY name ASC LIMIT 50';
-}
-
-// ************* GET OTHER CHARGES BASED ON COMPANY *************
-cur_frm.fields_dict.charge.get_query = function(doc) {
- return 'SELECT DISTINCT `tabSales Taxes and Charges Master`.name FROM \
- `tabSales Taxes and Charges Master` WHERE `tabSales Taxes and Charges Master`.company = "'
- +doc.company+'" AND `tabSales Taxes and Charges Master`.company is not NULL \
- AND `tabSales Taxes and Charges Master`.docstatus != 2 \
- AND `tabSales Taxes and Charges Master`.%(key)s LIKE "%s" \
- ORDER BY `tabSales Taxes and Charges Master`.name LIMIT 50';
-}
-
-cur_frm.fields_dict.customer.get_query = erpnext.utils.customer_query;
\ No newline at end of file
diff --git a/setup/doctype/currency_exchange/currency_exchange.js b/setup/doctype/currency_exchange/currency_exchange.js
new file mode 100644
index 0000000000..02cba115f0
--- /dev/null
+++ b/setup/doctype/currency_exchange/currency_exchange.js
@@ -0,0 +1,39 @@
+// ERPNext - web based ERP (http://erpnext.com)
+// Copyright (C) 2012 Web Notes Technologies Pvt Ltd
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+$.extend(cur_frm.cscript, {
+ refresh: function() {
+ cur_frm.cscript.set_exchange_rate_label();
+ },
+
+ from_currency: function() {
+ cur_frm.cscript.set_exchange_rate_label();
+ },
+
+ to_currency: function() {
+ cur_frm.cscript.set_exchange_rate_label();
+ },
+
+ set_exchange_rate_label: function() {
+ if(cur_frm.doc.from_currency && cur_frm.doc.to_currency) {
+ var default_label = wn._(wn.meta.docfield_map[cur_frm.doctype]["exchange_rate"].label);
+ console.log(default_label +
+ repl(" (1 %(from_currency)s = [?] %(to_currency)s)", cur_frm.doc));
+ cur_frm.fields_dict.exchange_rate.set_label(default_label +
+ repl(" (1 %(from_currency)s = [?] %(to_currency)s)", cur_frm.doc));
+ }
+ }
+});
\ No newline at end of file
diff --git a/setup/doctype/currency_exchange/currency_exchange.txt b/setup/doctype/currency_exchange/currency_exchange.txt
index 1a05ebe908..94bb28ab47 100644
--- a/setup/doctype/currency_exchange/currency_exchange.txt
+++ b/setup/doctype/currency_exchange/currency_exchange.txt
@@ -2,7 +2,7 @@
{
"creation": "2013-06-20 15:40:29",
"docstatus": 0,
- "modified": "2013-06-20 15:40:29",
+ "modified": "2013-07-03 17:11:40",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -23,6 +23,16 @@
"permlevel": 0,
"reqd": 1
},
+ {
+ "doctype": "DocPerm",
+ "name": "__common__",
+ "parent": "Currency Exchange",
+ "parentfield": "permissions",
+ "parenttype": "DocType",
+ "permlevel": 0,
+ "read": 1,
+ "report": 1
+ },
{
"doctype": "DocType",
"name": "Currency Exchange"
@@ -46,5 +56,24 @@
"fieldname": "exchange_rate",
"fieldtype": "Float",
"label": "Exchange Rate"
+ },
+ {
+ "cancel": 1,
+ "create": 1,
+ "doctype": "DocPerm",
+ "role": "Accounts Manager",
+ "write": 1
+ },
+ {
+ "doctype": "DocPerm",
+ "role": "Accounts User"
+ },
+ {
+ "doctype": "DocPerm",
+ "role": "Sales User"
+ },
+ {
+ "doctype": "DocPerm",
+ "role": "Purchase User"
}
]
\ No newline at end of file
diff --git a/setup/doctype/currency_exchange/test_currency_exchange.py b/setup/doctype/currency_exchange/test_currency_exchange.py
new file mode 100644
index 0000000000..cd0ac21aa7
--- /dev/null
+++ b/setup/doctype/currency_exchange/test_currency_exchange.py
@@ -0,0 +1,14 @@
+test_records = [
+ [{
+ "doctype": "Currency Exchange",
+ "from_currency": "USD",
+ "to_currency": "INR",
+ "exchange_rate": 1
+ }],
+ [{
+ "doctype": "Currency Exchange",
+ "from_currency": "USD",
+ "to_currency": "EUR",
+ "exchange_rate": 0.773
+ }]
+]
\ No newline at end of file
diff --git a/setup/doctype/price_list/price_list.py b/setup/doctype/price_list/price_list.py
index b40a46f040..a5f22d9da0 100644
--- a/setup/doctype/price_list/price_list.py
+++ b/setup/doctype/price_list/price_list.py
@@ -17,7 +17,7 @@
from __future__ import unicode_literals
import webnotes
from webnotes import msgprint, _
-from webnotes.utils import comma_or
+from webnotes.utils import comma_or, cint
from webnotes.model.controller import DocListController
class DocType(DocListController):
@@ -30,6 +30,14 @@ class DocType(DocListController):
msgprint(_(self.meta.get_label("buying_or_selling")) + " " + _("must be one of") + " " +
comma_or(["Buying", "Selling"]), raise_exception=True)
+ # at least one territory
+ self.validate_table_has_rows("valid_for_territories")
+
+ def on_update(self):
+ cart_settings = webnotes.get_obj("Shopping Cart Settings")
+ if cint(cart_settings.doc.enabled):
+ cart_settings.validate_price_lists()
+
def on_trash(self):
webnotes.conn.sql("""delete from `tabItem Price` where price_list_name = %s""",
self.doc.name)
\ No newline at end of file
diff --git a/setup/doctype/price_list/test_price_list.py b/setup/doctype/price_list/test_price_list.py
index 68d9a35521..c3633e78ec 100644
--- a/setup/doctype/price_list/test_price_list.py
+++ b/setup/doctype/price_list/test_price_list.py
@@ -1,8 +1,59 @@
test_records = [
- [{
- "doctype": "Price List",
- "price_list_name": "_Test Price List",
- "currency": "INR",
- "buying_or_selling": "Selling"
- }]
+ [
+ {
+ "doctype": "Price List",
+ "price_list_name": "_Test Price List",
+ "currency": "INR",
+ "buying_or_selling": "Selling"
+ },
+ {
+ "doctype": "For Territory",
+ "parentfield": "valid_for_territories",
+ "territory": "All Territories"
+ }
+ ],
+ [
+ {
+ "doctype": "Price List",
+ "price_list_name": "_Test Price List 2",
+ "currency": "INR",
+ "buying_or_selling": "Selling"
+ },
+ {
+ "doctype": "For Territory",
+ "parentfield": "valid_for_territories",
+ "territory": "_Test Territory Rest of the World"
+ }
+ ],
+ [
+ {
+ "doctype": "Price List",
+ "price_list_name": "_Test Price List India",
+ "currency": "INR",
+ "buying_or_selling": "Selling"
+ },
+ {
+ "doctype": "For Territory",
+ "parentfield": "valid_for_territories",
+ "territory": "_Test Territory India"
+ }
+ ],
+ [
+ {
+ "doctype": "Price List",
+ "price_list_name": "_Test Price List Rest of the World",
+ "currency": "USD",
+ "buying_or_selling": "Selling"
+ },
+ {
+ "doctype": "For Territory",
+ "parentfield": "valid_for_territories",
+ "territory": "_Test Territory Rest of the World"
+ },
+ {
+ "doctype": "For Territory",
+ "parentfield": "valid_for_territories",
+ "territory": "_Test Territory United States"
+ }
+ ],
]
\ No newline at end of file
diff --git a/setup/doctype/setup_control/setup_control.py b/setup/doctype/setup_control/setup_control.py
index a4af7f891a..c6b0b6ce28 100644
--- a/setup/doctype/setup_control/setup_control.py
+++ b/setup/doctype/setup_control/setup_control.py
@@ -87,18 +87,20 @@ class DocType:
self.curr_fiscal_year = curr_fiscal_year
def create_price_lists(self, args):
- webnotes.bean({
- 'doctype': 'Price List',
- 'price_list_name': 'Standard Selling',
- "buying_or_selling": "Selling",
- "currency": args["currency"]
- }).insert(),
- webnotes.bean({
- 'doctype': 'Price List',
- 'price_list_name': 'Standard Buying',
- "buying_or_selling": "Buying",
- "currency": args["currency"]
- }).insert(),
+ for pl_type in ["Selling", "Buying"]:
+ webnotes.bean([
+ {
+ "doctype": "Price List",
+ "price_list_name": "Standard " + pl_type,
+ "buying_or_selling": pl_type,
+ "currency": args["currency"]
+ },
+ {
+ "doctype": "For Territory",
+ "parentfield": "valid_for_territories",
+ "territory": "All Territories"
+ }
+ ]).insert()
def set_defaults(self, args):
# enable default currency
@@ -207,15 +209,18 @@ class DocType:
# ------------------------
def get_fy_details(self, fy_start, last_year=False):
st = {'1st Jan':'01-01','1st Apr':'04-01','1st Jul':'07-01', '1st Oct': '10-01'}
- curr_year = getdate(nowdate()).year
- if last_year:
- curr_year = curr_year - 1
if cint(getdate(nowdate()).month) < cint((st[fy_start].split('-'))[0]):
curr_year = getdate(nowdate()).year - 1
+ else:
+ curr_year = getdate(nowdate()).year
+
+ if last_year:
+ curr_year = curr_year - 1
+
stdt = cstr(curr_year)+'-'+cstr(st[fy_start])
if(fy_start == '1st Jan'):
- fy = cstr(getdate(nowdate()).year)
+ fy = cstr(curr_year)
abbr = cstr(fy)[-2:]
else:
fy = cstr(curr_year) + '-' + cstr(curr_year+1)
diff --git a/setup/doctype/territory/test_territory.py b/setup/doctype/territory/test_territory.py
index 2124199c12..75003f2b56 100644
--- a/setup/doctype/territory/test_territory.py
+++ b/setup/doctype/territory/test_territory.py
@@ -4,5 +4,29 @@ test_records = [
"territory_name": "_Test Territory",
"parent_territory": "All Territories",
"is_group": "No",
- }]
+ }],
+ [{
+ "doctype": "Territory",
+ "territory_name": "_Test Territory India",
+ "parent_territory": "All Territories",
+ "is_group": "Yes",
+ }],
+ [{
+ "doctype": "Territory",
+ "territory_name": "_Test Territory Maharashtra",
+ "parent_territory": "_Test Territory India",
+ "is_group": "No",
+ }],
+ [{
+ "doctype": "Territory",
+ "territory_name": "_Test Territory Rest of the World",
+ "parent_territory": "All Territories",
+ "is_group": "No",
+ }],
+ [{
+ "doctype": "Territory",
+ "territory_name": "_Test Territory United States",
+ "parent_territory": "All Territories",
+ "is_group": "No",
+ }],
]
\ No newline at end of file
diff --git a/setup/page/setup/setup.py b/setup/page/setup/setup.py
index 348fce968c..baee676ca6 100644
--- a/setup/page/setup/setup.py
+++ b/setup/page/setup/setup.py
@@ -91,6 +91,8 @@ items = [
},
{ "doctype": "Sales Taxes and Charges Master" },
{ "doctype": "Purchase Taxes and Charges Master" },
+ { "doctype": "Shipping Rule" },
+ { "doctype": "Currency Exchange" },
{
"type": "Section",
"title": "Opening Accounts and Stock",
diff --git a/setup/utils.py b/setup/utils.py
index c343ed1db1..e1463fa0d6 100644
--- a/setup/utils.py
+++ b/setup/utils.py
@@ -40,8 +40,8 @@ def get_ancestors_of(doctype, name):
"""Get ancestor elements of a DocType with a tree structure"""
lft, rgt = webnotes.conn.get_value(doctype, name, ["lft", "rgt"])
result = webnotes.conn.sql_list("""select name from `tab%s`
- where lft<%s and rgt>%s and docstatus < 2""" % (doctype, "%s", "%s"), (lft, rgt))
- return result or None
+ where lft<%s and rgt>%s order by lft desc""" % (doctype, "%s", "%s"), (lft, rgt))
+ return result or []
@webnotes.whitelist()
def get_price_list_currency(args):
diff --git a/stock/doctype/delivery_note/delivery_note.py b/stock/doctype/delivery_note/delivery_note.py
index 8b2b55933c..5a3f0ca833 100644
--- a/stock/doctype/delivery_note/delivery_note.py
+++ b/stock/doctype/delivery_note/delivery_note.py
@@ -48,9 +48,6 @@ class DocType(SellingController):
'keyword': 'Delivered'
}]
- def set_customer_defaults(self):
- self.get_default_customer_shipping_address()
-
def validate_fiscal_year(self):
get_obj('Sales Common').validate_fiscal_year(self.doc.fiscal_year,self.doc.posting_date,'Posting Date')
diff --git a/support/doctype/customer_issue/customer_issue.js b/support/doctype/customer_issue/customer_issue.js
index 0f3007ea10..3a602b8bc9 100644
--- a/support/doctype/customer_issue/customer_issue.js
+++ b/support/doctype/customer_issue/customer_issue.js
@@ -14,6 +14,28 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
+wn.provide("erpnext.support");
+// TODO commonify this code
+erpnext.support.CustomerIssue = wn.ui.form.Controller.extend({
+ customer: function() {
+ var me = this;
+ if(this.frm.doc.customer) {
+ this.frm.call({
+ doc: this.frm.doc,
+ method: "set_customer_defaults",
+ callback: function(r) {
+ if(!r.exc) me.frm.refresh_fields();
+ }
+ });
+
+ // TODO shift this to depends_on
+ unhide_field(['customer_address', 'contact_person']);
+ }
+ }
+});
+
+$.extend(cur_frm.cscript, new erpnext.support.CustomerIssue({frm: cur_frm}));
+
cur_frm.cscript.onload = function(doc,cdt,cdn){
if(!doc.status)
set_multiple(dt,dn,{status:'Open'});
@@ -28,18 +50,6 @@ cur_frm.cscript.refresh = function(doc,ct,cdn){
cur_frm.cscript['Make Maintenance Visit']);
}
-
-//customer
-cur_frm.cscript.customer = function(doc,dt,dn) {
- var callback = function(r,rt) {
- var doc = locals[cur_frm.doctype][cur_frm.docname];
- cur_frm.refresh();
- }
-
- if(doc.customer) $c_obj(make_doclist(doc.doctype, doc.name), 'get_default_customer_address', '', callback);
- if(doc.customer) unhide_field(['customer_address','contact_person']);
-}
-
cur_frm.cscript.customer_address = cur_frm.cscript.contact_person = function(doc,dt,dn) {
if(doc.customer)
get_server_fields('get_customer_address',
diff --git a/support/doctype/maintenance_schedule/maintenance_schedule.js b/support/doctype/maintenance_schedule/maintenance_schedule.js
index 30fbecf832..6b3ce10f75 100644
--- a/support/doctype/maintenance_schedule/maintenance_schedule.js
+++ b/support/doctype/maintenance_schedule/maintenance_schedule.js
@@ -14,6 +14,31 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
+wn.provide("erpnext.support");
+// TODO commonify this code
+erpnext.support.MaintenanceSchedule = wn.ui.form.Controller.extend({
+ customer: function() {
+ var me = this;
+ if(this.frm.doc.customer) {
+ this.frm.call({
+ doc: this.frm.doc,
+ method: "set_customer_defaults",
+ callback: function(r) {
+ if(!r.exc) me.frm.refresh_fields();
+ }
+ });
+
+ // TODO shift this to depends_on
+ unhide_field(['customer_address', 'contact_person', 'customer_name',
+ 'address_display', 'contact_display', 'contact_mobile', 'contact_email',
+ 'territory', 'customer_group']);
+
+ }
+ }
+});
+
+$.extend(cur_frm.cscript, new erpnext.support.MaintenanceSchedule({frm: cur_frm}));
+
cur_frm.cscript.onload = function(doc, dt, dn) {
if(!doc.status) set_multiple(dt,dn,{status:'Draft'});
@@ -23,18 +48,6 @@ cur_frm.cscript.onload = function(doc, dt, dn) {
}
}
-
-//customer
-cur_frm.cscript.customer = function(doc,dt,dn) {
- var callback = function(r,rt) {
- var doc = locals[cur_frm.doctype][cur_frm.docname];
- cur_frm.refresh();
- }
-
- if(doc.customer) $c_obj(make_doclist(doc.doctype, doc.name), 'get_default_customer_address', '', callback);
- if(doc.customer) unhide_field(['customer_address','contact_person','customer_name','address_display','contact_display','contact_mobile','contact_email','territory','customer_group']);
-}
-
cur_frm.cscript.customer_address = cur_frm.cscript.contact_person = function(doc,dt,dn) {
if(doc.customer) get_server_fields('get_customer_address', JSON.stringify({customer: doc.customer, address: doc.customer_address, contact: doc.contact_person}),'', doc, dt, dn, 1);
}
diff --git a/support/doctype/maintenance_visit/maintenance_visit.js b/support/doctype/maintenance_visit/maintenance_visit.js
index 3514a3a710..3b8de757c9 100644
--- a/support/doctype/maintenance_visit/maintenance_visit.js
+++ b/support/doctype/maintenance_visit/maintenance_visit.js
@@ -8,16 +8,38 @@
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
-// along with this program. If not, see .
+// along with this program. If not, see .
+
+wn.provide("erpnext.support");
+// TODO commonify this code
+erpnext.support.MaintenanceVisit = wn.ui.form.Controller.extend({
+ customer: function() {
+ var me = this;
+ if(this.frm.doc.customer) {
+ this.frm.call({
+ doc: this.frm.doc,
+ method: "set_customer_defaults",
+ callback: function(r) {
+ if(!r.exc) me.frm.refresh_fields();
+ }
+ });
+
+ // TODO shift this to depends_on
+ hide_contact_info(this.frm.doc);
+ }
+ }
+});
+
+$.extend(cur_frm.cscript, new erpnext.support.MaintenanceVisit({frm: cur_frm}));
cur_frm.cscript.onload = function(doc, dt, dn) {
- if(!doc.status) set_multiple(dt,dn,{status:'Draft'});
- if(doc.__islocal) set_multiple(dt,dn,{mntc_date:get_today()});
- hide_contact_info(doc);
+ if(!doc.status) set_multiple(dt,dn,{status:'Draft'});
+ if(doc.__islocal) set_multiple(dt,dn,{mntc_date:get_today()});
+ hide_contact_info(doc);
}
var hide_contact_info = function(doc) {
@@ -30,79 +52,68 @@ cur_frm.cscript.refresh = function(doc) {
hide_contact_info(doc);
}
-//customer
-cur_frm.cscript.customer = function(doc,dt,dn) {
- var callback = function(r,rt) {
- var doc = locals[cur_frm.doctype][cur_frm.docname];
- cur_frm.refresh();
- }
-
- if(doc.customer) $c_obj(make_doclist(doc.doctype, doc.name), 'get_default_customer_address', '', callback);
- hide_contact_info(doc);
-}
-
-cur_frm.cscript.customer_address = cur_frm.cscript.contact_person = function(doc,dt,dn) {
- if(doc.customer) get_server_fields('get_customer_address', JSON.stringify({customer: doc.customer, address: doc.customer_address, contact: doc.contact_person}),'', doc, dt, dn, 1);
+cur_frm.cscript.customer_address = cur_frm.cscript.contact_person = function(doc,dt,dn) {
+ if(doc.customer) get_server_fields('get_customer_address', JSON.stringify({customer: doc.customer, address: doc.customer_address, contact: doc.contact_person}),'', doc, dt, dn, 1);
}
cur_frm.fields_dict['customer_address'].get_query = function(doc, cdt, cdn) {
- return 'SELECT name,address_line1,city FROM tabAddress WHERE customer = "'+ doc.customer +'" AND docstatus != 2 AND name LIKE "%s" ORDER BY name ASC LIMIT 50';
+ return 'SELECT name,address_line1,city FROM tabAddress WHERE customer = "'+ doc.customer +'" AND docstatus != 2 AND name LIKE "%s" ORDER BY name ASC LIMIT 50';
}
cur_frm.fields_dict['contact_person'].get_query = function(doc, cdt, cdn) {
- return 'SELECT name,CONCAT(first_name," ",ifnull(last_name,"")) As FullName,department,designation FROM tabContact WHERE customer = "'+ doc.customer +'" AND docstatus != 2 AND name LIKE "%s" ORDER BY name ASC LIMIT 50';
+ return 'SELECT name,CONCAT(first_name," ",ifnull(last_name,"")) As FullName,department,designation FROM tabContact WHERE customer = "'+ doc.customer +'" AND docstatus != 2 AND name LIKE "%s" ORDER BY name ASC LIMIT 50';
}
cur_frm.cscript.get_items = function(doc, dt, dn) {
- var callback = function(r,rt) {
- hide_contact_info(doc);
- cur_frm.refresh();
- }
- get_server_fields('fetch_items','','',doc, dt, dn,1,callback);
+ var callback = function(r,rt) {
+ hide_contact_info(doc);
+ cur_frm.refresh();
+ }
+ get_server_fields('fetch_items','','',doc, dt, dn,1,callback);
}
cur_frm.fields_dict['maintenance_visit_details'].grid.get_field('item_code').get_query = function(doc, cdt, cdn) {
- return 'SELECT tabItem.name,tabItem.item_name,tabItem.description FROM tabItem WHERE tabItem.is_service_item="Yes" AND tabItem.docstatus != 2 AND tabItem.%(key)s LIKE "%s" LIMIT 50';
+ return 'SELECT tabItem.name,tabItem.item_name,tabItem.description FROM tabItem WHERE tabItem.is_service_item="Yes" AND tabItem.docstatus != 2 AND tabItem.%(key)s LIKE "%s" LIMIT 50';
}
cur_frm.cscript.item_code = function(doc, cdt, cdn) {
- var fname = cur_frm.cscript.fname;
- var d = locals[cdt][cdn];
- if (d.item_code) {
- get_server_fields('get_item_details',d.item_code, 'maintenance_visit_details',doc,cdt,cdn,1);
- }
+ var fname = cur_frm.cscript.fname;
+ var d = locals[cdt][cdn];
+ if (d.item_code) {
+ get_server_fields('get_item_details',d.item_code, 'maintenance_visit_details',doc,cdt,cdn,1);
+ }
}
cur_frm.fields_dict['sales_order_no'].get_query = function(doc) {
- doc = locals[this.doctype][this.docname];
- var cond = '';
- if(doc.customer) {
- cond = '`tabSales Order`.customer = "'+doc.customer+'" AND';
- }
- return repl('SELECT DISTINCT `tabSales Order`.name FROM `tabSales Order`, `tabSales Order Item`, `tabItem` WHERE `tabSales Order`.company = "%(company)s" AND `tabSales Order`.docstatus = 1 AND `tabSales Order Item`.parent = `tabSales Order`.name AND `tabSales Order Item`.item_code = `tabItem`.name AND `tabItem`.is_service_item = "Yes" AND %(cond)s `tabSales Order`.name LIKE "%s" ORDER BY `tabSales Order`.name DESC LIMIT 50', {company:doc.company, cond:cond});
+ doc = locals[this.doctype][this.docname];
+ var cond = '';
+ if(doc.customer) {
+ cond = '`tabSales Order`.customer = "'+doc.customer+'" AND';
+ }
+ return repl('SELECT DISTINCT `tabSales Order`.name FROM `tabSales Order`, `tabSales Order Item`, `tabItem` WHERE `tabSales Order`.company = "%(company)s" AND `tabSales Order`.docstatus = 1 AND `tabSales Order Item`.parent = `tabSales Order`.name AND `tabSales Order Item`.item_code = `tabItem`.name AND `tabItem`.is_service_item = "Yes" AND %(cond)s `tabSales Order`.name LIKE "%s" ORDER BY `tabSales Order`.name DESC LIMIT 50', {company:doc.company, cond:cond});
}
cur_frm.fields_dict['customer_issue_no'].get_query = function(doc) {
- doc = locals[this.doctype][this.docname];
- var cond = '';
- if(doc.customer) {
- cond = '`tabCustomer Issue`.customer = "'+doc.customer+'" AND';
- }
- return repl('SELECT `tabCustomer Issue`.name FROM `tabCustomer Issue` WHERE `tabCustomer Issue`.company = "%(company)s" AND %(cond)s `tabCustomer Issue`.docstatus = 1 AND (`tabCustomer Issue`.status = "Open" OR `tabCustomer Issue`.status = "Work In Progress") AND `tabCustomer Issue`.name LIKE "%s" ORDER BY `tabCustomer Issue`.name DESC LIMIT 50', {company:doc.company, cond:cond});
+ doc = locals[this.doctype][this.docname];
+ var cond = '';
+ if(doc.customer) {
+ cond = '`tabCustomer Issue`.customer = "'+doc.customer+'" AND';
+ }
+ return repl('SELECT `tabCustomer Issue`.name FROM `tabCustomer Issue` WHERE `tabCustomer Issue`.company = "%(company)s" AND %(cond)s `tabCustomer Issue`.docstatus = 1 AND (`tabCustomer Issue`.status = "Open" OR `tabCustomer Issue`.status = "Work In Progress") AND `tabCustomer Issue`.name LIKE "%s" ORDER BY `tabCustomer Issue`.name DESC LIMIT 50', {company:doc.company, cond:cond});
}
cur_frm.fields_dict['maintenance_schedule'].get_query = function(doc) {
- doc = locals[this.doctype][this.docname];
- var cond = '';
- if(doc.customer) {
- cond = '`tabMaintenance Schedule`.customer = "'+doc.customer+'" AND';
- }
- return repl('SELECT `tabMaintenance Schedule`.name FROM `tabMaintenance Schedule` WHERE `tabMaintenance Schedule`.company = "%(company)s" AND %(cond)s `tabMaintenance Schedule`.docstatus = 1 AND `tabMaintenance Schedule`.name LIKE "%s" ORDER BY `tabMaintenance Schedule`.name DESC LIMIT 50', {company:doc.company, cond:cond});
+ doc = locals[this.doctype][this.docname];
+ var cond = '';
+ if(doc.customer) {
+ cond = '`tabMaintenance Schedule`.customer = "'+doc.customer+'" AND';
+ }
+ return repl('SELECT `tabMaintenance Schedule`.name FROM `tabMaintenance Schedule` WHERE `tabMaintenance Schedule`.company = "%(company)s" AND %(cond)s `tabMaintenance Schedule`.docstatus = 1 AND `tabMaintenance Schedule`.name LIKE "%s" ORDER BY `tabMaintenance Schedule`.name DESC LIMIT 50', {company:doc.company, cond:cond});
}
//get query select Territory
cur_frm.fields_dict['territory'].get_query = function(doc,cdt,cdn) {
- return 'SELECT `tabTerritory`.`name`,`tabTerritory`.`parent_territory` FROM `tabTerritory` WHERE `tabTerritory`.`is_group` = "No" AND `tabTerritory`.`docstatus`!= 2 AND `tabTerritory`.%(key)s LIKE "%s" ORDER BY `tabTerritory`.`name` ASC LIMIT 50';
+ return 'SELECT `tabTerritory`.`name`,`tabTerritory`.`parent_territory` FROM `tabTerritory` WHERE `tabTerritory`.`is_group` = "No" AND `tabTerritory`.`docstatus`!= 2 AND `tabTerritory`.%(key)s LIKE "%s" ORDER BY `tabTerritory`.`name` ASC LIMIT 50';
}
cur_frm.fields_dict.customer.get_query = erpnext.utils.customer_query;
\ No newline at end of file
diff --git a/support/doctype/support_ticket/support_ticket.js b/support/doctype/support_ticket/support_ticket.js
index beff192346..903f41cd58 100644
--- a/support/doctype/support_ticket/support_ticket.js
+++ b/support/doctype/support_ticket/support_ticket.js
@@ -16,6 +16,25 @@
cur_frm.fields_dict.customer.get_query = erpnext.utils.customer_query;
+wn.provide("erpnext.support");
+// TODO commonify this code
+erpnext.support.CustomerIssue = wn.ui.form.Controller.extend({
+ customer: function() {
+ var me = this;
+ if(this.frm.doc.customer) {
+ this.frm.call({
+ doc: this.frm.doc,
+ method: "set_customer_defaults",
+ callback: function(r) {
+ if(!r.exc) me.frm.refresh_fields();
+ }
+ });
+ }
+ }
+});
+
+$.extend(cur_frm.cscript, new erpnext.support.CustomerIssue({frm: cur_frm}));
+
$.extend(cur_frm.cscript, {
onload: function(doc, dt, dn) {
if(in_list(user_roles,'System Manager')) {
@@ -67,17 +86,6 @@ $.extend(cur_frm.cscript, {
},
- customer: function(doc, dt, dn) {
- var callback = function(r,rt) {
- var doc = locals[cur_frm.doctype][cur_frm.docname];
- if(!r.exc) {
- cur_frm.refresh();
- }
- }
- if(doc.customer) $c_obj(make_doclist(doc.doctype, doc.name),
- 'get_default_customer_address', '', callback);
- },
-
'Close Ticket': function() {
cur_frm.cscript.set_status("Closed");
},
diff --git a/utilities/transaction_base.py b/utilities/transaction_base.py
index ab53575aee..7794444e27 100644
--- a/utilities/transaction_base.py
+++ b/utilities/transaction_base.py
@@ -23,46 +23,89 @@ from webnotes.model.doc import addchild
from controllers.status_updater import StatusUpdater
class TransactionBase(StatusUpdater):
- def get_default_address_and_contact(self, party_type):
+ def get_default_address_and_contact(self, party_field, party_name=None):
"""get a dict of default field values of address and contact for a given party type
party_type can be one of: customer, supplier"""
- ret = {}
+ if not party_name:
+ party_name = self.doc.fields.get(party_field)
- # {customer: self.doc.fields.get("customer")}
- args = {party_type: self.doc.fields.get(party_type)}
+ return get_default_address_and_contact(party_field, party_name,
+ fetch_shipping_address=True if self.meta.get_field("shipping_address_name") else False)
+
+ def get_customer_defaults(self):
+ out = self.get_default_address_and_contact("customer")
+
+ customer = webnotes.doc("Customer", self.doc.customer)
+ for f in ['customer_name', 'customer_group', 'territory']:
+ out[f] = customer.fields.get(f)
- address_text, address_name = self.get_address_text(**args)
- ret.update({
- # customer_address
- (party_type + "_address"): address_name,
- "address_display": address_text
- })
- ret.update(self.get_contact_text(**args))
- return ret
+ # fields prepended with default in Customer doctype
+ for f in ['sales_partner', 'commission_rate', 'currency', 'price_list']:
+ out[f] = customer.fields.get("default_" + f)
+
+ return out
+
+ def set_customer_defaults(self):
+ """
+ For a customer:
+ 1. Sets default address and contact
+ 2. Sets values like Territory, Customer Group, etc.
+ 3. Clears existing Sales Team and fetches the one mentioned in Customer
+ """
+ customer_defaults = self.get_customer_defaults()
+
+ # hack! TODO - add shipping_address_field in Delivery Note
+ if self.doc.doctype == "Delivery Note":
+ customer_defaults["customer_address"] = customer_defaults["shipping_address_name"]
+ customer_defaults["address_display"] = customer_defaults["shipping_address"]
+
+ customer_defaults["price_list"] = customer_defaults["price_list"] or \
+ webnotes.conn.get_value("Customer Group", self.doc.customer_group, "default_price_list") or \
+ self.doc.price_list
+
+ self.doc.fields.update(customer_defaults)
+
+ if self.meta.get_field("sales_team"):
+ self.set_sales_team_for_customer()
+
+ def set_sales_team_for_customer(self):
+ from webnotes.model import default_fields
+
+ # clear table
+ self.doclist = self.doc.clear_table(self.doclist, "sales_team")
+
+ sales_team = webnotes.conn.sql("""select * from `tabSales Team`
+ where parenttype="Customer" and parent=%s""", self.doc.customer, as_dict=True)
+ for i, sales_person in enumerate(sales_team):
+ # remove default fields
+ for fieldname in default_fields:
+ if fieldname in sales_person:
+ del sales_person[fieldname]
+
+ sales_person.update({
+ "doctype": "Sales Team",
+ "parentfield": "sales_team",
+ "idx": i+1
+ })
+
+ # add child
+ self.doclist.append(sales_person)
- # Get Customer Default Primary Address - first load
- def get_default_customer_address(self, args=''):
- address_text, address_name = self.get_address_text(customer=self.doc.customer)
- self.doc.customer_address = address_name or ''
- self.doc.address_display = address_text or ''
- self.doc.fields.update(self.get_contact_text(customer=self.doc.customer))
-
- if args != 'onload':
- self.get_customer_details(self.doc.customer)
- self.get_sales_person(self.doc.customer)
+ def get_lead_defaults(self):
+ out = self.get_default_address_and_contact("lead")
- # Get Customer Default Shipping Address - first load
- # -----------------------
- def get_default_customer_shipping_address(self, args=''):
- address_text, address_name = self.get_address_text(customer=self.doc.customer,is_shipping_address=1)
- self.doc.customer_address = address_name or ''
- self.doc.address_display = address_text or ''
- self.doc.fields.update(self.get_contact_text(customer=self.doc.customer))
-
- if self.doc.doctype != 'Quotation' and args != 'onload':
- self.get_customer_details(self.doc.customer)
- self.get_sales_person(self.doc.customer)
+ lead = webnotes.conn.get_value("Lead", self.doc.lead,
+ ["territory", "company_name", "lead_name"], as_dict=True) or {}
+ out["territory"] = lead.get("territory")
+ out["customer_name"] = lead.get("company_name") or lead.get("lead_name")
+
+ return out
+
+ def set_lead_defaults(self):
+ self.doc.fields.update(self.get_lead_defaults())
+
+
# Get Customer Address
# -----------------------
def get_customer_address(self, args):
@@ -91,13 +134,14 @@ class TransactionBase(StatusUpdater):
details = webnotes.conn.sql("select name, address_line1, address_line2, city, country, pincode, state, phone, fax from `tabAddress` where %s and docstatus != 2 order by is_shipping_address desc, is_primary_address desc limit 1" % cond, as_dict = 1)
else:
details = webnotes.conn.sql("select name, address_line1, address_line2, city, country, pincode, state, phone, fax from `tabAddress` where %s and docstatus != 2 order by is_primary_address desc limit 1" % cond, as_dict = 1)
-
- extract = lambda x: details and details[0] and details[0].get(x,'') or ''
- address_fields = [('','address_line1'),('\n','address_line2'),('\n','city'),('\n','state'),(' ','pincode'),('\n','country'),('\nPhone: ','phone'),('\nFax: ', 'fax')]
- address_display = ''.join([a[0]+extract(a[1]) for a in address_fields if extract(a[1])])
- if address_display.startswith('\n'): address_display = address_display[1:]
+
+ address_display = ""
+
+ if details:
+ address_display = get_address_display(details[0])
address_name = details and details[0]['name'] or ''
+
return address_display, address_name
# Get Contact Text
@@ -126,41 +170,13 @@ class TransactionBase(StatusUpdater):
"contact_department": details and details[0]["department"] or "",
}
- def get_customer_details(self, name):
- """
- Get customer details like name, group, territory
- and other such defaults
- """
- customer_details = webnotes.conn.sql("""\
- select
- customer_name, customer_group, territory,
- default_sales_partner, default_commission_rate, default_currency,
- default_price_list
- from `tabCustomer`
- where name = %s and docstatus < 2""", name, as_dict=1)
- if customer_details:
- for f in ['customer_name', 'customer_group', 'territory']:
- self.doc.fields[f] = customer_details[0][f] or self.doc.fields.get(f)
-
- # fields prepended with default in Customer doctype
- for f in ['sales_partner', 'commission_rate', 'currency']:
- self.doc.fields[f] = customer_details[0]["default_%s" % f] or self.doc.fields.get(f)
-
- # optionally fetch default price list from Customer Group
- self.doc.price_list_name = (customer_details[0]['default_price_list']
- or webnotes.conn.get_value('Customer Group', self.doc.customer_group,
- 'default_price_list')
- or self.doc.fields.get('price_list_name'))
-
- # Get Customer Shipping Address
- # -----------------------
+ # TODO deprecate this - used only in sales_order.js
def get_shipping_address(self, name):
details = webnotes.conn.sql("select name, address_line1, address_line2, city, country, pincode, state, phone from `tabAddress` where customer = '%s' and docstatus != 2 order by is_shipping_address desc, is_primary_address desc limit 1" %(name), as_dict = 1)
- extract = lambda x: details and details[0] and details[0].get(x,'') or ''
- address_fields = [('','address_line1'),('\n','address_line2'),('\n','city'),(' ','pincode'),('\n','state'),('\n','country'),('\nPhone: ','phone')]
- address_display = ''.join([a[0]+extract(a[1]) for a in address_fields if extract(a[1])])
- if address_display.startswith('\n'): address_display = address_display[1:]
+ address_display = ""
+ if details:
+ address_display = get_address_display(details[0])
ret = {
'shipping_address_name' : details and details[0]['name'] or '',
@@ -168,36 +184,6 @@ class TransactionBase(StatusUpdater):
}
return ret
- # Get Lead Details
- # -----------------------
- def get_lead_details(self, name):
- details = webnotes.conn.sql("""select name, lead_name, address_line1, address_line2, city, country, state, pincode
- from `tabAddress` where lead=%s""", name, as_dict=True)
- lead = webnotes.conn.get_value("Lead", name,
- ["territory", "phone", "mobile_no", "email_id", "company_name", "lead_name"], as_dict=True) or {}
-
- address_display = ""
- if details:
- details = details[0]
- for separator, fieldname in (('','address_line1'), ('\n','address_line2'), ('\n','city'),
- (' ','pincode'), ('\n','state'), ('\n','country'), ('\nPhone: ', 'phone')):
- if details.get(fieldname):
- address_display += separator + details.get(fieldname)
-
- if address_display.startswith('\n'):
- address_display = address_display[1:]
-
- ret = {
- 'contact_display' : lead.get('lead_name'),
- 'address_display' : address_display,
- 'territory' : lead.get('territory'),
- 'contact_mobile' : lead.get('mobile_no'),
- 'contact_email' : lead.get('email_id'),
- 'customer_name' : lead.get('company_name') or lead.get('lead_name')
- }
- return ret
-
-
# Get Supplier Default Primary Address - first load
# -----------------------
def get_default_supplier_address(self, args):
@@ -312,20 +298,112 @@ class TransactionBase(StatusUpdater):
})
webnotes.bean(event_doclist).insert()
-
+
+def get_default_address_and_contact(party_field, party_name, fetch_shipping_address=False):
+ out = {}
+
+ # get addresses
+ billing_address = get_address_dict(party_field, party_name)
+ if billing_address:
+ out[party_field + "_address"] = billing_address["name"]
+ out["address_display"] = get_address_display(billing_address)
+ else:
+ out[party_field + "_address"] = out["address_display"] = None
+
+ if fetch_shipping_address:
+ shipping_address = get_address_dict(party_field, party_name, is_shipping_address=True)
+ if shipping_address:
+ out["shipping_address_name"] = shipping_address["name"]
+ out["shipping_address"] = get_address_display(shipping_address)
+ else:
+ out["shipping_address_name"] = out["shipping_address"] = None
+
+ # get contact
+ if party_field == "lead":
+ out["customer_address"] = out.get("lead_address")
+ out.update(map_lead_fields(party_name))
+ else:
+ out.update(map_contact_fields(party_field, party_name))
+
+ return out
+
+def get_address_dict(party_field, party_name, is_shipping_address=None):
+ order_by = "is_shipping_address desc, is_primary_address desc, name asc" if \
+ is_shipping_address else "is_primary_address desc, name asc"
+
+ address = webnotes.conn.sql("""select * from `tabAddress` where `%s`=%s order by %s
+ limit 1""" % (party_field, "%s", order_by), party_name, as_dict=True,
+ update={"doctype": "Address"})
+
+ return address[0] if address else None
def get_address_display(address_dict):
+ def _prepare_for_display(a_dict, sequence):
+ display = ""
+ for separator, fieldname in sequence:
+ if a_dict.get(fieldname):
+ display += separator + a_dict.get(fieldname)
+
+ return display.strip()
+
meta = webnotes.get_doctype("Address")
address_sequence = (("", "address_line1"), ("\n", "address_line2"), ("\n", "city"),
("\n", "state"), ("\n" + meta.get_label("pincode") + ": ", "pincode"), ("\n", "country"),
("\n" + meta.get_label("phone") + ": ", "phone"), ("\n" + meta.get_label("fax") + ": ", "fax"))
- address_display = ""
- for separator, fieldname in address_sequence:
- if address_dict.get(fieldname):
- address_display += separator + address_dict.get(fieldname)
-
- return address_display
+ return _prepare_for_display(address_dict, address_sequence)
+
+def map_lead_fields(party_name):
+ out = {}
+ for fieldname in ["contact_display", "contact_email", "contact_mobile", "contact_phone"]:
+ out[fieldname] = None
+
+ lead = webnotes.conn.sql("""select * from `tabLead` where name=%s""", party_name, as_dict=True)
+ if lead:
+ lead = lead[0]
+ out.update({
+ "contact_display": lead.get("lead_name"),
+ "contact_email": lead.get("email_id"),
+ "contact_mobile": lead.get("mobile_no"),
+ "contact_phone": lead.get("phone"),
+ })
+
+ return out
+
+def map_contact_fields(party_field, party_name):
+ out = {}
+ for fieldname in ["contact_person", "contact_display", "contact_email",
+ "contact_mobile", "contact_phone", "contact_designation", "contact_department"]:
+ out[fieldname] = None
+
+ contact = webnotes.conn.sql("""select * from `tabContact` where `%s`=%s
+ order by is_primary_contact desc, name asc limit 1""" % (party_field, "%s"),
+ (party_name,), as_dict=True)
+ if contact:
+ contact = contact[0]
+ out.update({
+ "contact_person": contact.get("name"),
+ "contact_display": " ".join(filter(None,
+ [contact.get("first_name"), contact.get("last_name")])),
+ "contact_email": contact.get("email_id"),
+ "contact_mobile": contact.get("mobile_no"),
+ "contact_phone": contact.get("phone"),
+ "contact_designation": contact.get("designation"),
+ "contact_department": contact.get("department")
+ })
+
+ return out
+
+def get_address_territory(address_doc):
+ territory = None
+ for fieldname in ("city", "state", "country"):
+ value = address_doc.fields.get(fieldname)
+ if value:
+ territory = webnotes.conn.get_value("Territory", value.strip())
+ if territory:
+ break
+
+ return territory
def validate_conversion_rate(currency, conversion_rate, conversion_rate_label, company):
"""common validation for currency and price list currency"""
diff --git a/website/css/website.css b/website/css/website.css
index 34ff983b0b..df37821bfe 100644
--- a/website/css/website.css
+++ b/website/css/website.css
@@ -10,6 +10,10 @@ a {
cursor: pointer;
}
+img {
+ max-width: 100%;
+}
+
.content {
padding-bottom: 30px;
}
diff --git a/website/doctype/shopping_cart_settings/shopping_cart_settings.py b/website/doctype/shopping_cart_settings/shopping_cart_settings.py
index 516c96834f..121422f9be 100644
--- a/website/doctype/shopping_cart_settings/shopping_cart_settings.py
+++ b/website/doctype/shopping_cart_settings/shopping_cart_settings.py
@@ -9,42 +9,70 @@ from webnotes.model.controller import DocListController
class ShoppingCartSetupError(webnotes.ValidationError): pass
class DocType(DocListController):
- def __init__(self, d, dl):
- self.doc, self.doclist = d, dl
-
def validate(self):
if self.doc.enabled:
- self.validate_overlapping_territories("shopping_cart_price_lists", "price_list")
- self.validate_overlapping_territories("shopping_cart_taxes_and_charges_masters",
- "sales_taxes_and_charges_master")
- self.validate_shipping_rules()
+ self.validate_price_lists()
+ self.validate_tax_masters()
self.validate_exchange_rates_exist()
-
- def validate_overlapping_territories(self, parentfield, fieldname):
- names = [doc.fields(fieldname) for doc in self.doclist.get({"parentfield": parentfield})]
- doctype = self.meta.get_field(parentfield).options
- parenttype = self.meta.get_field(fieldname, parentfield=parentfield).options
-
- if not names:
- msgprint(_("Please specify at least one") + " " + _(doctype),
- raise_exception=ShoppingCartSetupError)
-
- territory_name = webnotes.conn.sql("""select territory, parent from `tabFor Territory`
- where parenttype=%s and parent in (%s)""" % ("%s", ", ".join(["%s"]*names)),
- tuple([parenttype] + names))
-
- territory_name_map = {}
- for territory, name in territory_name:
- territory_name_map.setdefault(territory, []).append(name)
+ def validate_overlapping_territories(self, parentfield, fieldname):
+ # for displaying message
+ doctype = self.meta.get_field(parentfield).options
+
+ # specify atleast one entry in the table
+ self.validate_table_has_rows(parentfield, raise_exception=ShoppingCartSetupError)
+
+ territory_name_map = self.get_territory_name_map(parentfield, fieldname)
for territory, names in territory_name_map.items():
if len(names) > 1:
msgprint(_("Error for") + " " + _(doctype) + ": " + comma_and(names) +
" " + _("have a common territory") + ": " + territory,
raise_exception=ShoppingCartSetupError)
- def validate_shipping_rules(self):
- pass
+ return territory_name_map
+
+ def validate_price_lists(self):
+ territory_name_map = self.validate_overlapping_territories("price_lists",
+ "price_list")
+
+ # validate that a Shopping Cart Price List exists for the root territory
+ # as a catch all!
+ from setup.utils import get_root_of
+ root_territory = get_root_of("Territory")
+
+ if root_territory not in territory_name_map.keys():
+ msgprint(_("Please specify a Price List which is valid for Territory") +
+ ": " + root_territory, raise_exception=ShoppingCartSetupError)
+
+ def validate_tax_masters(self):
+ self.validate_overlapping_territories("sales_taxes_and_charges_masters",
+ "sales_taxes_and_charges_master")
+
+ def get_territory_name_map(self, parentfield, fieldname):
+ territory_name_map = {}
+
+ # entries in table
+ names = [doc.fields.get(fieldname) for doc in self.doclist.get({"parentfield": parentfield})]
+
+ if names:
+ # for condition in territory check
+ parenttype = self.meta.get_field(fieldname, parentfield=parentfield).options
+
+ # to validate territory overlap
+ # make a map of territory: [list of names]
+ # if list against each territory has more than one element, raise exception
+ territory_name = webnotes.conn.sql("""select `territory`, `parent`
+ from `tabFor Territory`
+ where `parenttype`=%s and `parent` in (%s)""" %
+ ("%s", ", ".join(["%s"]*len(names))), tuple([parenttype] + names))
+
+ for territory, name in territory_name:
+ territory_name_map.setdefault(territory, []).append(name)
+
+ if len(territory_name_map[territory]) > 1:
+ territory_name_map[territory].sort(key=lambda val: names.index(val))
+
+ return territory_name_map
def validate_exchange_rates_exist(self):
"""check if exchange rates exist for all Price List currencies (to company's currency)"""
@@ -54,7 +82,7 @@ class DocType(DocListController):
raise_exception=ShoppingCartSetupError)
price_list_currency_map = webnotes.conn.get_values("Price List",
- [d.price_list for d in self.doclist.get({"parentfield": "shopping_cart_price_lists"})],
+ [d.price_list for d in self.doclist.get({"parentfield": "price_lists"})],
"currency")
expected_to_exist = [currency + "-" + company_currency
@@ -70,5 +98,41 @@ class DocType(DocListController):
if missing:
msgprint(_("Missing Currency Exchange Rates for" + ": " + comma_and(missing)),
raise_exception=ShoppingCartSetupError)
+
+ def get_name_from_territory(self, territory, parentfield, fieldname):
+ name = None
+ territory_name_map = self.get_territory_name_map(parentfield, fieldname)
-
\ No newline at end of file
+ if territory_name_map.get(territory):
+ name = territory_name_map.get(territory)
+ else:
+ territory_ancestry = self.get_territory_ancestry(territory)
+ for ancestor in territory_ancestry:
+ if territory_name_map.get(ancestor):
+ name = territory_name_map.get(ancestor)
+ break
+
+ return name
+
+ def get_price_list(self, billing_territory):
+ price_list = self.get_name_from_territory(billing_territory, "price_lists", "price_list")
+ return price_list and price_list[0] or None
+
+ def get_tax_master(self, billing_territory):
+ tax_master = self.get_name_from_territory(billing_territory, "sales_taxes_and_charges_masters",
+ "sales_taxes_and_charges_master")
+ return tax_master and tax_master[0] or None
+
+ def get_shipping_rules(self, shipping_territory):
+ return self.get_name_from_territory(shipping_territory, "shipping_rules", "shipping_rule")
+
+ def get_territory_ancestry(self, territory):
+ from setup.utils import get_ancestors_of
+
+ if not hasattr(self, "_territory_ancestry"):
+ self._territory_ancestry = {}
+
+ if not self._territory_ancestry.get(territory):
+ self._territory_ancestry[territory] = get_ancestors_of("Territory", territory)
+
+ return self._territory_ancestry[territory]
\ No newline at end of file
diff --git a/website/doctype/shopping_cart_settings/shopping_cart_settings.txt b/website/doctype/shopping_cart_settings/shopping_cart_settings.txt
index 82e3baed37..1d472b7f1b 100644
--- a/website/doctype/shopping_cart_settings/shopping_cart_settings.txt
+++ b/website/doctype/shopping_cart_settings/shopping_cart_settings.txt
@@ -2,7 +2,7 @@
{
"creation": "2013-06-19 15:57:32",
"docstatus": 0,
- "modified": "2013-06-21 12:59:30",
+ "modified": "2013-07-03 21:01:00",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -53,7 +53,7 @@
},
{
"doctype": "DocField",
- "fieldname": "shopping_cart_price_lists",
+ "fieldname": "price_lists",
"fieldtype": "Table",
"label": "Shopping Cart Price Lists",
"options": "Shopping Cart Price List",
@@ -61,12 +61,20 @@
},
{
"doctype": "DocField",
- "fieldname": "shopping_cart_taxes_and_charges_masters",
+ "fieldname": "sales_taxes_and_charges_masters",
"fieldtype": "Table",
"label": "Shopping Cart Taxes and Charges Masters",
"options": "Shopping Cart Taxes and Charges Master",
"reqd": 0
},
+ {
+ "doctype": "DocField",
+ "fieldname": "shipping_rules",
+ "fieldtype": "Table",
+ "label": "Shopping Cart Shipping Rules",
+ "options": "Shopping Cart Shipping Rule",
+ "reqd": 0
+ },
{
"doctype": "DocField",
"fieldname": "company",
diff --git a/website/doctype/shopping_cart_settings/test_shopping_cart_settings.py b/website/doctype/shopping_cart_settings/test_shopping_cart_settings.py
new file mode 100644
index 0000000000..b4ae6fc5f7
--- /dev/null
+++ b/website/doctype/shopping_cart_settings/test_shopping_cart_settings.py
@@ -0,0 +1,78 @@
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import webnotes
+import unittest
+from website.doctype.shopping_cart_settings.shopping_cart_settings import ShoppingCartSetupError
+
+class TestShoppingCartSettings(unittest.TestCase):
+ def setUp(self):
+ webnotes.conn.sql("""delete from `tabSingles` where doctype="Shipping Cart Settings" """)
+ webnotes.conn.sql("""delete from `tabShopping Cart Price List`""")
+ webnotes.conn.sql("""delete from `tabShopping Cart Taxes and Charges Master`""")
+ webnotes.conn.sql("""delete from `tabShopping Cart Shipping Rule`""")
+
+ def get_cart_settings(self):
+ return webnotes.bean({"doctype": "Shopping Cart Settings",
+ "company": "_Test Company"})
+
+ def test_price_list_territory_overlap(self):
+ cart_settings = self.get_cart_settings()
+
+ def _add_price_list(price_list):
+ cart_settings.doclist.append({
+ "doctype": "Shopping Cart Price List",
+ "parentfield": "price_lists",
+ "price_list": price_list
+ })
+
+ for price_list in ("_Test Price List Rest of the World", "_Test Price List India",
+ "_Test Price List"):
+ _add_price_list(price_list)
+
+ controller = cart_settings.make_controller()
+ controller.validate_overlapping_territories("price_lists", "price_list")
+
+ _add_price_list("_Test Price List 2")
+
+ controller = cart_settings.make_controller()
+ self.assertRaises(ShoppingCartSetupError, controller.validate_overlapping_territories,
+ "price_lists", "price_list")
+
+ return cart_settings
+
+ def test_taxes_territory_overlap(self):
+ cart_settings = self.get_cart_settings()
+
+ def _add_tax_master(tax_master):
+ cart_settings.doclist.append({
+ "doctype": "Shopping Cart Taxes and Charges Master",
+ "parentfield": "sales_taxes_and_charges_masters",
+ "sales_taxes_and_charges_master": tax_master
+ })
+
+ for tax_master in ("_Test Sales Taxes and Charges Master", "_Test India Tax Master"):
+ _add_tax_master(tax_master)
+
+ controller = cart_settings.make_controller()
+ controller.validate_overlapping_territories("sales_taxes_and_charges_masters",
+ "sales_taxes_and_charges_master")
+
+ _add_tax_master("_Test Sales Taxes and Charges Master 2")
+
+ controller = cart_settings.make_controller()
+ self.assertRaises(ShoppingCartSetupError, controller.validate_overlapping_territories,
+ "sales_taxes_and_charges_masters", "sales_taxes_and_charges_master")
+
+ def test_exchange_rate_exists(self):
+ webnotes.conn.sql("""delete from `tabCurrency Exchange`""")
+
+ cart_settings = self.test_price_list_territory_overlap()
+ controller = cart_settings.make_controller()
+ self.assertRaises(ShoppingCartSetupError, controller.validate_exchange_rates_exist)
+
+ from setup.doctype.currency_exchange.test_currency_exchange import test_records as \
+ currency_exchange_records
+ webnotes.bean(currency_exchange_records[0]).insert()
+ controller.validate_exchange_rates_exist()
+
diff --git a/website/doctype/shopping_cart_shipping_rule/__init__.py b/website/doctype/shopping_cart_shipping_rule/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/website/doctype/shopping_cart_shipping_rule/shopping_cart_shipping_rule.py b/website/doctype/shopping_cart_shipping_rule/shopping_cart_shipping_rule.py
new file mode 100644
index 0000000000..928aa9ff9f
--- /dev/null
+++ b/website/doctype/shopping_cart_shipping_rule/shopping_cart_shipping_rule.py
@@ -0,0 +1,8 @@
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import webnotes
+
+class DocType:
+ def __init__(self, d, dl):
+ self.doc, self.doclist = d, dl
\ No newline at end of file
diff --git a/website/doctype/shopping_cart_shipping_rule/shopping_cart_shipping_rule.txt b/website/doctype/shopping_cart_shipping_rule/shopping_cart_shipping_rule.txt
new file mode 100644
index 0000000000..caeba3d96a
--- /dev/null
+++ b/website/doctype/shopping_cart_shipping_rule/shopping_cart_shipping_rule.txt
@@ -0,0 +1,35 @@
+[
+ {
+ "creation": "2013-07-03 13:15:34",
+ "docstatus": 0,
+ "modified": "2013-07-03 13:19:02",
+ "modified_by": "Administrator",
+ "owner": "Administrator"
+ },
+ {
+ "doctype": "DocType",
+ "istable": 1,
+ "module": "Website",
+ "name": "__common__"
+ },
+ {
+ "doctype": "DocField",
+ "fieldname": "shipping_rule",
+ "fieldtype": "Link",
+ "label": "Shipping Rule",
+ "name": "__common__",
+ "options": "Shipping Rule",
+ "parent": "Shopping Cart Shipping Rule",
+ "parentfield": "fields",
+ "parenttype": "DocType",
+ "permlevel": 0,
+ "reqd": 1
+ },
+ {
+ "doctype": "DocType",
+ "name": "Shopping Cart Shipping Rule"
+ },
+ {
+ "doctype": "DocField"
+ }
+]
\ No newline at end of file
diff --git a/website/helpers/cart.py b/website/helpers/cart.py
index bc99e7bd4b..98edc07663 100644
--- a/website/helpers/cart.py
+++ b/website/helpers/cart.py
@@ -4,7 +4,7 @@
from __future__ import unicode_literals
import webnotes
import webnotes.defaults
-from webnotes.utils import cint, get_fullname, fmt_money
+from webnotes.utils import flt, get_fullname, fmt_money
class WebsitePriceListMissingError(webnotes.ValidationError): pass
@@ -18,14 +18,15 @@ def get_cart_quotation(doclist=None):
return {
"doclist": decorate_quotation_doclist(doclist),
"addresses": [{"name": address.name, "display": address.display}
- for address in get_address_docs(party)]
+ for address in get_address_docs(party)],
+ "shipping_rules": get_applicable_shipping_rules(party)
}
@webnotes.whitelist()
def update_cart(item_code, qty, with_doclist=0):
quotation = _get_cart_quotation()
- qty = cint(qty)
+ qty = flt(qty)
if qty == 0:
quotation.set_doclist(quotation.doclist.get({"item_code": ["!=", item_code]}))
else:
@@ -40,8 +41,7 @@ def update_cart(item_code, qty, with_doclist=0):
else:
quotation_items[0].qty = qty
- quotation.ignore_permissions = True
- quotation.save()
+ apply_cart_settings(quotation=quotation)
if with_doclist:
return get_cart_quotation(quotation.doclist)
@@ -69,6 +69,8 @@ def update_cart_address(address_fieldname, address_name):
quotation.ignore_permissions = True
quotation.save()
+
+ apply_cart_settings(quotation=quotation)
return get_cart_quotation(quotation.doclist)
@@ -131,14 +133,23 @@ def get_lead_or_customer():
"doctype": "Lead",
"email_id": webnotes.session.user,
"lead_name": get_fullname(webnotes.session.user),
- "territory": webnotes.conn.get_value("Shopping Cart Settings", None, "territory") or \
- "All Territories",
+ "territory": guess_territory(),
"status": "Open" # TODO: set something better???
})
lead_bean.ignore_permissions = True
lead_bean.insert()
return lead_bean.doc
+
+def guess_territory():
+ territory = None
+ geoip_country = webnotes.session.get("session_country")
+ if geoip_country:
+ territory = webnotes.conn.get_value("Territory", geoip_country)
+
+ return territory or \
+ webnotes.conn.get_value("Shopping Cart Settings", None, "territory") or \
+ "All Territories"
def decorate_quotation_doclist(doclist):
for d in doclist:
@@ -147,7 +158,13 @@ def decorate_quotation_doclist(doclist):
["website_image", "web_short_description", "page_name"], as_dict=True))
d.formatted_rate = fmt_money(d.export_rate, currency=doclist[0].currency)
d.formatted_amount = fmt_money(d.export_amount, currency=doclist[0].currency)
+ elif d.charge_type:
+ d.formatted_tax_amount = fmt_money(d.tax_amount / doclist[0].conversion_rate,
+ currency=doclist[0].currency)
+ doclist[0].formatted_grand_total_export = fmt_money(doclist[0].grand_total_export,
+ currency=doclist[0].currency)
+
return [d.fields for d in doclist]
def _get_cart_quotation(party=None):
@@ -168,42 +185,122 @@ def _get_cart_quotation(party=None):
"order_type": "Shopping Cart",
"status": "Draft",
"__islocal": 1,
- "price_list_name": get_price_list(party),
(party.doctype.lower()): party.name
})
+ qbean.run_method("onload_post_render")
+ apply_cart_settings(party, qbean)
return qbean
-def get_price_list(party):
- if not party.default_price_list:
- party.default_price_list = get_price_list_using_geoip()
- party.save()
+def apply_cart_settings(party=None, quotation=None):
+ if not party:
+ party = get_lead_or_customer()
+ if not quotation:
+ quotation = _get_cart_quotation(party)
+
+ cart_settings = webnotes.get_obj("Shopping Cart Settings")
+
+ billing_territory = get_address_territory(quotation.doc.customer_address) or \
+ party.territory
+
+ set_price_list_and_rate(quotation, cart_settings, billing_territory)
+
+ quotation.run_method("calculate_taxes_and_totals")
+
+ set_taxes(quotation, cart_settings, billing_territory)
+
+ _apply_shipping_rule(party, quotation, cart_settings)
+
+ quotation.ignore_permissions = True
+ quotation.save()
+
+def set_price_list_and_rate(quotation, cart_settings, billing_territory):
+ """set price list based on billing territory"""
+ quotation.doc.price_list_name = cart_settings.get_price_list(billing_territory)
+
+ # reset values
+ quotation.doc.price_list_currency = quotation.doc.currency = \
+ quotation.doc.plc_conversion_rate = quotation.doc.conversion_rate = None
+ for item in quotation.doclist.get({"parentfield": "quotation_details"}):
+ item.ref_rate = item.adj_rate = item.export_rate = item.export_amount = None
+
+ # refetch values
+ quotation.run_method("set_price_list_and_item_details")
+
+def set_taxes(quotation, cart_settings, billing_territory):
+ """set taxes based on billing territory"""
+ quotation.doc.charge = cart_settings.get_tax_master(billing_territory)
+
+ # clear table
+ quotation.doclist = quotation.doc.clear_table(quotation.doclist, "other_charges")
+
+ # append taxes
+ controller = quotation.make_controller()
+ controller.append_taxes_from_master("other_charges", "charge")
+ quotation.set_doclist(controller.doclist)
+
+@webnotes.whitelist()
+def apply_shipping_rule(shipping_rule):
+ quotation = _get_cart_quotation()
+
+ quotation.doc.shipping_rule = shipping_rule
+
+ apply_cart_settings(quotation=quotation)
+
+ quotation.save()
+
+ return get_cart_quotation(quotation.doclist)
+
+def _apply_shipping_rule(party=None, quotation=None, cart_settings=None):
+ shipping_rules = get_shipping_rules(party, quotation, cart_settings)
+
+ if not shipping_rules:
+ return
- return party.default_price_list
-
-def get_price_list_using_geoip():
- country = webnotes.session.get("session_country")
- price_list_name = None
-
- if country:
- price_list_name = webnotes.conn.sql("""select parent
- from `tabPrice List Country` plc
- where country=%s and exists (select name from `tabPrice List` pl
- where use_for_website=1 and ifnull(valid_for_all_countries, 0)=0 and
- pl.name = plc.parent)""", country)
+ elif quotation.doc.shipping_rule not in shipping_rules:
+ quotation.doc.shipping_rule = shipping_rules[0]
- if price_list_name:
- price_list_name = price_list_name[0][0]
- else:
- price_list_name = webnotes.conn.get_value("Price List",
- {"use_for_website": 1, "valid_for_all_countries": 1})
-
- if not price_list_name:
- raise WebsitePriceListMissingError, "No website Price List specified"
+ quotation.run_method("apply_shipping_rule")
+ quotation.run_method("calculate_taxes_and_totals")
- return price_list_name
-
+def get_applicable_shipping_rules(party=None, quotation=None):
+ shipping_rules = get_shipping_rules(party, quotation)
+
+ if shipping_rules:
+ rule_label_map = webnotes.conn.get_values("Shipping Rule", shipping_rules, "label")
+ # we need this in sorted order as per the position of the rule in the settings page
+ return [[rule, rule_label_map.get(rule)] for rule in shipping_rules]
+
+def get_shipping_rules(party=None, quotation=None, cart_settings=None):
+ if not party:
+ party = get_lead_or_customer()
+ if not quotation:
+ quotation = _get_cart_quotation()
+ if not cart_settings:
+ cart_settings = webnotes.get_obj("Shopping Cart Settings")
+
+ # set shipping rule based on shipping territory
+ shipping_territory = get_address_territory(quotation.doc.shipping_address_name) or \
+ party.territory
+
+ shipping_rules = cart_settings.get_shipping_rules(shipping_territory)
+
+ return shipping_rules
+
+def get_address_territory(address_name):
+ """Tries to match city, state and country of address to existing territory"""
+ territory = None
+ if address_name:
+ address_fields = webnotes.conn.get_value("Address", address_name,
+ ["city", "state", "country"])
+ for value in address_fields:
+ territory = webnotes.conn.get_value("Territory", value)
+ if territory:
+ break
+
+ return territory
+
@webnotes.whitelist()
def checkout():
quotation = _get_cart_quotation()
diff --git a/website/templates/js/cart.js b/website/templates/js/cart.js
index db6c5714ec..f1002fc599 100644
--- a/website/templates/js/cart.js
+++ b/website/templates/js/cart.js
@@ -38,7 +38,6 @@ $(document).ready(function() {
} else {
wn.cart.render(r.message);
}
-
}
});
});
@@ -81,9 +80,10 @@ $.extend(wn.cart, {
render: function(out) {
var doclist = out.doclist;
var addresses = out.addresses;
-
+
var $cart_items = $("#cart-items").empty();
var $cart_taxes = $("#cart-taxes").empty();
+ var $cart_totals = $("#cart-totals").empty();
var $cart_billing_address = $("#cart-billing-address").empty();
var $cart_shipping_address = $("#cart-shipping-address").empty();
@@ -94,12 +94,39 @@ $.extend(wn.cart, {
return;
}
+ var shipping_rule_added = false;
+ var taxes_exist = false;
+ var shipping_rule_labels = $.map(out.shipping_rules, function(rule) { return rule[1]; });
$.each(doclist, function(i, doc) {
if(doc.doctype === "Quotation Item") {
wn.cart.render_item_row($cart_items, doc);
+ } else if (doc.doctype === "Sales Taxes and Charges") {
+ if(out.shipping_rules && out.shipping_rules.length &&
+ shipping_rule_labels.indexOf(doc.description)!==-1) {
+ shipping_rule_added = true;
+ wn.cart.render_tax_row($cart_taxes, doc, out.shipping_rules);
+ } else {
+ wn.cart.render_tax_row($cart_taxes, doc);
+ }
+
+ taxes_exist = true;
}
});
+ if(out.shipping_rules && out.shipping_rules.length && !shipping_rule_added) {
+ wn.cart.render_tax_row($cart_taxes, {description: "", formatted_tax_amount: ""},
+ out.shipping_rules);
+ taxes_exist = true;
+ }
+
+ if(taxes_exist)
+ $('
').appendTo($cart_taxes);
+
+ wn.cart.render_tax_row($cart_totals, {
+ description: "Total",
+ formatted_tax_amount: "" + doclist[0].formatted_grand_total_export + ""
+ });
+
if(!(addresses && addresses.length)) {
$cart_shipping_address.html('Hey! Go ahead and add an address
');
} else {
@@ -125,10 +152,10 @@ $.extend(wn.cart, {
\
\
\
-