diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 030fddbb99..885e7bd160 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -35,6 +35,15 @@ class SalesInvoice(SellingController): 'overflow_type': 'billing' }] + def set_indicator(self): + """Set indicator for portal""" + if self.outstanding_amount > 0: + self.indicator_color = "orange" + self.indicator_title = _("Unpaid") + else: + self.indicator_color = "green" + self.indicator_title = _("Paid") + def validate(self): super(SalesInvoice, self).validate() self.validate_posting_time() @@ -90,7 +99,7 @@ class SalesInvoice(SellingController): # this sequence because outstanding may get -ve self.make_gl_entries() - + if not self.is_return: self.update_billing_status_for_zero_amount_refdoc("Sales Order") self.check_credit_limit() @@ -161,10 +170,10 @@ class SalesInvoice(SellingController): 'extra_cond': """ and exists (select name from `tabSales Invoice` where name=`tabSales Invoice Item`.parent and update_stock=1 and is_return=1)""" } ]) - + def check_credit_limit(self): from erpnext.selling.doctype.customer.customer import check_credit_limit - + validate_against_credit_limit = False for d in self.get("items"): if not (d.sales_order or d.delivery_note): @@ -282,7 +291,7 @@ class SalesInvoice(SellingController): reconcile_against_document(lst) def validate_debit_to_acc(self): - account = frappe.db.get_value("Account", self.debit_to, + account = frappe.db.get_value("Account", self.debit_to, ["account_type", "report_type", "account_currency"], as_dict=True) if account.report_type != "Balance Sheet": @@ -290,7 +299,7 @@ class SalesInvoice(SellingController): if self.customer and account.account_type != "Receivable": frappe.throw(_("Debit To account must be a Receivable account")) - + self.party_account_currency = account.account_currency def validate_fixed_asset_account(self): @@ -437,18 +446,18 @@ class SalesInvoice(SellingController): if cint(self.is_pos) == 1: if flt(self.paid_amount) == 0: if self.cash_bank_account: - frappe.db.set(self, 'paid_amount', - flt(flt(self.grand_total) - flt(self.write_off_amount), self.precision("paid_amount"))) + frappe.db.set(self, 'paid_amount', + flt(flt(self.grand_total) - flt(self.write_off_amount), self.precision("paid_amount"))) else: # show message that the amount is not paid frappe.db.set(self,'paid_amount',0) frappe.msgprint(_("Note: Payment Entry will not be created since 'Cash or Bank Account' was not specified")) else: frappe.db.set(self,'paid_amount',0) - - frappe.db.set(self, 'base_paid_amount', + + frappe.db.set(self, 'base_paid_amount', flt(self.paid_amount*self.conversion_rate, self.precision("base_paid_amount"))) - + def check_prev_docstatus(self): for d in self.get('items'): if d.sales_order and frappe.db.get_value("Sales Order", d.sales_order, "docstatus") != 1: @@ -487,7 +496,7 @@ class SalesInvoice(SellingController): from erpnext.accounts.general_ledger import merge_similar_entries gl_entries = [] - + self.make_customer_gl_entry(gl_entries) self.make_tax_gl_entries(gl_entries) @@ -586,7 +595,7 @@ class SalesInvoice(SellingController): # write off entries, applicable if only pos if self.write_off_account and self.write_off_amount: write_off_account_currency = frappe.db.get_value("Account", self.write_off_account, "account_currency") - + gl_entries.append( self.get_gl_dict({ "account": self.debit_to, diff --git a/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.js b/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.js index d7a4f5ad7f..8828e0c8e6 100644 --- a/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.js +++ b/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.js @@ -5,6 +5,3 @@ cur_frm.cscript.tax_table = "Sales Taxes and Charges"; {% include "public/js/controllers/accounts.js" %} -frappe.ui.form.on("Sales Taxes and Charges Template", "onload", function(frm) { - erpnext.add_applicable_territory(); -}); diff --git a/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.json b/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.json index 9a3a4580f5..d01866e447 100644 --- a/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.json +++ b/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.json @@ -164,29 +164,6 @@ "search_index": 0, "set_only_once": 0, "unique": 0 - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "description": "Specify a list of Territories, for which, this Taxes Master is valid", - "fieldname": "territories", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Valid for Territories", - "no_copy": 0, - "options": "Applicable Territory", - "permlevel": 0, - "print_hide": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 } ], "hide_heading": 0, @@ -198,7 +175,7 @@ "is_submittable": 0, "issingle": 0, "istable": 0, - "modified": "2015-09-11 12:19:46.488710", + "modified": "2015-09-17 07:09:28.797959", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Taxes and Charges Template", @@ -206,7 +183,7 @@ "permissions": [ { "amend": 0, - "apply_user_permissions": 0, + "apply_user_permissions": 1, "cancel": 0, "create": 0, "delete": 0, diff --git a/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.py b/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.py index b36287b691..886400e5f9 100644 --- a/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.py +++ b/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.py @@ -5,7 +5,6 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document from erpnext.controllers.accounts_controller import validate_taxes_and_charges, validate_inclusive_tax -from frappe.utils.nestedset import get_root_of class SalesTaxesandChargesTemplate(Document): def validate(self): @@ -20,10 +19,6 @@ def valdiate_taxes_and_charges_template(doc): where ifnull(is_default,0) = 1 and name != %s and company = %s""".format(doc.doctype), (doc.name, doc.company)) - if doc.meta.get_field("territories"): - if not doc.territories: - doc.append("territories", {"territory": get_root_of("Territory") }) - for tax in doc.get("taxes"): validate_taxes_and_charges(tax) validate_inclusive_tax(tax, doc) diff --git a/erpnext/accounts/doctype/sales_taxes_and_charges_template/test_records.json b/erpnext/accounts/doctype/sales_taxes_and_charges_template/test_records.json index 69dbd8e9a6..2b737b9804 100644 --- a/erpnext/accounts/doctype/sales_taxes_and_charges_template/test_records.json +++ b/erpnext/accounts/doctype/sales_taxes_and_charges_template/test_records.json @@ -20,19 +20,7 @@ "rate": 6.36 } ], - "title": "_Test Sales Taxes and Charges Template", - "territories": [ - { - "doctype": "Applicable Territory", - "parentfield": "territories", - "territory": "All Territories" - }, - { - "doctype": "Applicable Territory", - "parentfield": "territories", - "territory": "_Test Territory Rest Of The World" - } - ] + "title": "_Test Sales Taxes and Charges Template" }, { "company": "_Test Company", @@ -115,14 +103,7 @@ "row_id": 7 } ], - "title": "_Test India Tax Master", - "territories": [ - { - "doctype": "Applicable Territory", - "parentfield": "territories", - "territory": "_Test Territory India" - } - ] + "title": "_Test India Tax Master" }, { "company": "_Test Company", @@ -145,14 +126,7 @@ "rate": 4 } ], - "title": "_Test Sales Taxes and Charges Template - Rest of the World", - "territories": [ - { - "doctype": "Applicable Territory", - "parentfield": "territories", - "territory": "_Test Territory Rest Of The World" - } - ] + "title": "_Test Sales Taxes and Charges Template - Rest of the World" }, { "company": "_Test Company", @@ -175,14 +149,7 @@ "rate": 4 } ], - "title": "_Test Sales Taxes and Charges Template 1", - "territories": [ - { - "doctype": "Applicable Territory", - "parentfield": "territories", - "territory": "_Test Territory Rest Of The World" - } - ] + "title": "_Test Sales Taxes and Charges Template 1" }, { "company": "_Test Company", @@ -205,14 +172,7 @@ "rate": 4 } ], - "title": "_Test Sales Taxes and Charges Template 2", - "territories": [ - { - "doctype": "Applicable Territory", - "parentfield": "territories", - "territory": "_Test Territory Rest Of The World" - } - ] + "title": "_Test Sales Taxes and Charges Template 2" }, { "doctype" : "Sales Taxes and Charges Template", @@ -224,9 +184,6 @@ "cost_center": "Main - _TC", "description": "Test Shopping cart taxes with Tax Rule", "tax_amount": 1000 - }], - "territories":[{ - "territory" : "All Territories" }] }, { @@ -239,9 +196,6 @@ "cost_center": "Main - _TC", "description": "Test Shopping cart taxes with Tax Rule", "tax_amount": 200 - }], - "territories":[{ - "territory" : "All Territories" }] } ] diff --git a/erpnext/accounts/doctype/shipping_rule/shipping_rule.js b/erpnext/accounts/doctype/shipping_rule/shipping_rule.js index 233bea1782..17f2083983 100644 --- a/erpnext/accounts/doctype/shipping_rule/shipping_rule.js +++ b/erpnext/accounts/doctype/shipping_rule/shipping_rule.js @@ -1,8 +1,3 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -$.extend(cur_frm.cscript, { - onload: function() { - erpnext.add_applicable_territory(); - } -}); \ No newline at end of file diff --git a/erpnext/accounts/doctype/shipping_rule/shipping_rule.json b/erpnext/accounts/doctype/shipping_rule/shipping_rule.json index 97828d6f1b..1c5d979dd4 100644 --- a/erpnext/accounts/doctype/shipping_rule/shipping_rule.json +++ b/erpnext/accounts/doctype/shipping_rule/shipping_rule.json @@ -1,388 +1,397 @@ { - "allow_copy": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "field:label", - "creation": "2013-06-25 11:48:03", - "custom": 0, - "description": "Specify conditions to calculate shipping amount", - "docstatus": 0, - "doctype": "DocType", + "allow_copy": 0, + "allow_import": 0, + "allow_rename": 0, + "autoname": "field:label", + "creation": "2013-06-25 11:48:03", + "custom": 0, + "description": "Specify conditions to calculate shipping amount", + "docstatus": 0, + "doctype": "DocType", "fields": [ { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "description": "example: Next Day Shipping", - "fieldname": "label", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Shipping Rule Label", - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "description": "example: Next Day Shipping", + "fieldname": "label", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 1, + "label": "Shipping Rule Label", + "no_copy": 0, + "permlevel": 0, + "print_hide": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, "unique": 0 - }, + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "column_break_2", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "in_filter": 0, - "in_list_view": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "disabled", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Disabled", + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, "unique": 0 - }, + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "default": "Net Total", - "fieldname": "calculate_based_on", - "fieldtype": "Select", - "hidden": 1, - "ignore_user_permissions": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Calculate Based On", - "no_copy": 0, - "options": "Net Total\nNet Weight", - "permlevel": 0, - "print_hide": 0, - "read_only": 1, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "default": "Net Total", + "fieldname": "calculate_based_on", + "fieldtype": "Select", + "hidden": 1, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 1, + "label": "Calculate Based On", + "no_copy": 0, + "options": "Net Total\nNet Weight", + "permlevel": 0, + "print_hide": 0, + "read_only": 1, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, "unique": 0 - }, + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "rule_conditions_section", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Shipping Rule Conditions", - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "depends_on": "eval:!doc.disabled", + "fieldname": "rule_conditions_section", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Shipping Rule Conditions", + "no_copy": 0, + "permlevel": 0, + "print_hide": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, "unique": 0 - }, + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "conditions", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Shipping Rule Conditions", - "no_copy": 0, - "options": "Shipping Rule Condition", - "permlevel": 0, - "print_hide": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "conditions", + "fieldtype": "Table", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Shipping Rule Conditions", + "no_copy": 0, + "options": "Shipping Rule Condition", + "permlevel": 0, + "print_hide": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, "unique": 0 - }, + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "section_break_6", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "in_filter": 0, - "in_list_view": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "depends_on": "eval:!doc.disabled", + "fieldname": "section_break_6", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Valid for Countries", + "no_copy": 0, + "permlevel": 0, + "print_hide": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, "unique": 0 - }, + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "description": "Specify a list of Territories, for which, this Shipping Rule is valid", - "fieldname": "territories", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Valid For Territories", - "no_copy": 0, - "options": "Applicable Territory", - "permlevel": 0, - "print_hide": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "worldwide_shipping", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Worldwide Shipping", + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, "unique": 0 - }, + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "column_break_8", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "in_filter": 0, - "in_list_view": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "depends_on": "eval:!doc.worldwide_shipping", + "fieldname": "countries", + "fieldtype": "Table", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Valid for Countries", + "no_copy": 0, + "options": "Shipping Rule Country", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, "unique": 0 - }, + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "company", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Company", - "no_copy": 0, - "options": "Company", - "permlevel": 0, - "print_hide": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "depends_on": "eval:!doc.disabled", + "fieldname": "section_break_10", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "no_copy": 0, + "permlevel": 0, + "print_hide": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, "unique": 0 - }, + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "section_break_10", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "in_filter": 0, - "in_list_view": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "company", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Company", + "no_copy": 0, + "options": "Company", + "permlevel": 0, + "print_hide": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, "unique": 0 - }, + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "account", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Shipping Account", - "no_copy": 0, - "options": "Account", - "permlevel": 0, - "print_hide": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "column_break_12", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "no_copy": 0, + "permlevel": 0, + "print_hide": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, "unique": 0 - }, + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "column_break_12", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "in_filter": 0, - "in_list_view": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "account", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Shipping Account", + "no_copy": 0, + "options": "Account", + "permlevel": 0, + "print_hide": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, "unique": 0 - }, + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "cost_center", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Cost Center", - "no_copy": 0, - "options": "Cost Center", - "permlevel": 0, - "print_hide": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "cost_center", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Cost Center", + "no_copy": 0, + "options": "Cost Center", + "permlevel": 0, + "print_hide": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, "unique": 0 } - ], - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "icon-truck", - "idx": 1, - "in_create": 0, - "in_dialog": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "modified": "2015-09-07 15:51:26", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Shipping Rule", - "owner": "Administrator", + ], + "hide_heading": 0, + "hide_toolbar": 0, + "icon": "icon-truck", + "idx": 1, + "in_create": 0, + "in_dialog": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "modified": "2015-09-22 08:30:57.226342", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Shipping Rule", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, + "amend": 0, + "apply_user_permissions": 1, + "cancel": 0, + "create": 0, + "delete": 0, + "email": 1, + "export": 0, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts User", + "set_user_permissions": 0, + "share": 0, + "submit": 0, "write": 0 - }, + }, { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Sales User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, + "amend": 0, + "apply_user_permissions": 1, + "cancel": 0, + "create": 0, + "delete": 0, + "email": 1, + "export": 0, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Sales User", + "set_user_permissions": 0, + "share": 0, + "submit": 0, "write": 0 - }, + }, { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 0, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 0, "write": 1 - }, + }, { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Sales Master Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 0, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Sales Master Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 0, "write": 1 } - ], - "read_only": 0, + ], + "read_only": 0, "read_only_onload": 0 -} \ No newline at end of file +} diff --git a/erpnext/accounts/doctype/shipping_rule/shipping_rule.py b/erpnext/accounts/doctype/shipping_rule/shipping_rule.py index dbd121a45e..80e4fb7415 100644 --- a/erpnext/accounts/doctype/shipping_rule/shipping_rule.py +++ b/erpnext/accounts/doctype/shipping_rule/shipping_rule.py @@ -22,6 +22,12 @@ class ShippingRule(Document): self.sort_shipping_rule_conditions() self.validate_overlapping_shipping_rule_conditions() + if self.worldwide_shipping: + self.countries = [] + + elif not len([d.country for d in self.countries if d.country]): + frappe.throw(_("Please specify a country for this Shipping Rule or check Worldwide Shipping")) + def validate_from_to_values(self): zero_to_values = [] diff --git a/erpnext/accounts/doctype/shipping_rule/test_records.json b/erpnext/accounts/doctype/shipping_rule/test_records.json index 96e7770d5a..a271009b2b 100644 --- a/erpnext/accounts/doctype/shipping_rule/test_records.json +++ b/erpnext/accounts/doctype/shipping_rule/test_records.json @@ -1,116 +1,100 @@ [ { - "account": "_Test Account Shipping Charges - _TC", - "calculate_based_on": "Net Total", - "company": "_Test Company", - "cost_center": "_Test Cost Center - _TC", - "doctype": "Shipping Rule", - "label": "_Test Shipping Rule", - "name": "_Test Shipping Rule", + "account": "_Test Account Shipping Charges - _TC", + "calculate_based_on": "Net Total", + "company": "_Test Company", + "cost_center": "_Test Cost Center - _TC", + "doctype": "Shipping Rule", + "label": "_Test Shipping Rule", + "name": "_Test Shipping Rule", "conditions": [ { - "doctype": "Shipping Rule Condition", - "from_value": 0, - "parentfield": "conditions", - "shipping_amount": 50.0, + "doctype": "Shipping Rule Condition", + "from_value": 0, + "parentfield": "conditions", + "shipping_amount": 50.0, "to_value": 100 - }, + }, { - "doctype": "Shipping Rule Condition", - "from_value": 101, - "parentfield": "conditions", - "shipping_amount": 100.0, + "doctype": "Shipping Rule Condition", + "from_value": 101, + "parentfield": "conditions", + "shipping_amount": 100.0, "to_value": 200 - }, + }, { - "doctype": "Shipping Rule Condition", - "from_value": 201, - "parentfield": "conditions", + "doctype": "Shipping Rule Condition", + "from_value": 201, + "parentfield": "conditions", "shipping_amount": 0.0 } - ], - "territories": [ - { - "doctype": "Applicable Territory", - "parentfield": "territories", - "territory": "_Test Territory" - } - ] - }, + ], + "worldwide_shipping": 1 + }, { - "account": "_Test Account Shipping Charges - _TC", - "calculate_based_on": "Net Total", - "company": "_Test Company", - "cost_center": "_Test Cost Center - _TC", - "doctype": "Shipping Rule", - "label": "_Test Shipping Rule - India", - "name": "_Test Shipping Rule - India", + "account": "_Test Account Shipping Charges - _TC", + "calculate_based_on": "Net Total", + "company": "_Test Company", + "cost_center": "_Test Cost Center - _TC", + "doctype": "Shipping Rule", + "label": "_Test Shipping Rule - India", + "name": "_Test Shipping Rule - India", "conditions": [ { - "doctype": "Shipping Rule Condition", - "from_value": 0, - "parentfield": "conditions", - "shipping_amount": 50.0, + "doctype": "Shipping Rule Condition", + "from_value": 0, + "parentfield": "conditions", + "shipping_amount": 50.0, "to_value": 100 - }, + }, { - "doctype": "Shipping Rule Condition", - "from_value": 101, - "parentfield": "conditions", - "shipping_amount": 100.0, + "doctype": "Shipping Rule Condition", + "from_value": 101, + "parentfield": "conditions", + "shipping_amount": 100.0, "to_value": 200 - }, + }, { - "doctype": "Shipping Rule Condition", - "from_value": 201, - "parentfield": "conditions", + "doctype": "Shipping Rule Condition", + "from_value": 201, + "parentfield": "conditions", "shipping_amount": 0.0 } - ], - "territories": [ - { - "doctype": "Applicable Territory", - "parentfield": "territories", - "territory": "_Test Territory India" - } + ], + "countries": [ + {"country": "India"} ] - }, + }, { - "account": "_Test Account Shipping Charges - _TC", - "calculate_based_on": "Net Total", - "company": "_Test Company", - "cost_center": "_Test Cost Center - _TC", - "doctype": "Shipping Rule", - "label": "_Test Shipping Rule - Rest of the World", - "name": "_Test Shipping Rule - Rest of the World", + "account": "_Test Account Shipping Charges - _TC", + "calculate_based_on": "Net Total", + "company": "_Test Company", + "cost_center": "_Test Cost Center - _TC", + "doctype": "Shipping Rule", + "label": "_Test Shipping Rule - Rest of the World", + "name": "_Test Shipping Rule - Rest of the World", "conditions": [ { - "doctype": "Shipping Rule Condition", - "from_value": 0, - "parentfield": "conditions", - "shipping_amount": 500.0, + "doctype": "Shipping Rule Condition", + "from_value": 0, + "parentfield": "conditions", + "shipping_amount": 500.0, "to_value": 1000 - }, + }, { - "doctype": "Shipping Rule Condition", - "from_value": 1001, - "parentfield": "conditions", - "shipping_amount": 1000.0, + "doctype": "Shipping Rule Condition", + "from_value": 1001, + "parentfield": "conditions", + "shipping_amount": 1000.0, "to_value": 2000 - }, + }, { - "doctype": "Shipping Rule Condition", - "from_value": 2001, - "parentfield": "conditions", + "doctype": "Shipping Rule Condition", + "from_value": 2001, + "parentfield": "conditions", "shipping_amount": 1500.0 } - ], - "territories": [ - { - "doctype": "Applicable Territory", - "parentfield": "territories", - "territory": "_Test Territory Rest Of The World" - } - ] + ], + "worldwide_shipping": 1 } -] \ No newline at end of file +] diff --git a/erpnext/setup/doctype/applicable_territory/__init__.py b/erpnext/accounts/doctype/shipping_rule_country/__init__.py similarity index 100% rename from erpnext/setup/doctype/applicable_territory/__init__.py rename to erpnext/accounts/doctype/shipping_rule_country/__init__.py diff --git a/erpnext/shopping_cart/doctype/shopping_cart_price_list/shopping_cart_price_list.json b/erpnext/accounts/doctype/shipping_rule_country/shipping_rule_country.json similarity index 66% rename from erpnext/shopping_cart/doctype/shopping_cart_price_list/shopping_cart_price_list.json rename to erpnext/accounts/doctype/shipping_rule_country/shipping_rule_country.json index e91731e8b3..90fe3d3dd3 100644 --- a/erpnext/shopping_cart/doctype/shopping_cart_price_list/shopping_cart_price_list.json +++ b/erpnext/accounts/doctype/shipping_rule_country/shipping_rule_country.json @@ -2,25 +2,27 @@ "allow_copy": 0, "allow_import": 0, "allow_rename": 0, - "creation": "2013-06-20 16:00:18", + "creation": "2015-09-17 06:43:22.767534", "custom": 0, "docstatus": 0, "doctype": "DocType", + "document_type": "Other", "fields": [ { "allow_on_submit": 0, "bold": 0, "collapsible": 0, - "fieldname": "selling_price_list", + "fieldname": "country", "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 0, "in_filter": 0, "in_list_view": 1, - "label": "Price List", + "label": "Country", "no_copy": 0, - "options": "Price List", + "options": "Country", "permlevel": 0, + "precision": "", "print_hide": 0, "read_only": 0, "report_hide": 0, @@ -32,18 +34,20 @@ ], "hide_heading": 0, "hide_toolbar": 0, - "idx": 1, "in_create": 0, "in_dialog": 0, "is_submittable": 0, "issingle": 0, "istable": 1, - "modified": "2013-12-20 19:30:47", + "modified": "2015-09-17 06:43:22.767534", "modified_by": "Administrator", - "module": "Shopping Cart", - "name": "Shopping Cart Price List", + "module": "Accounts", + "name": "Shipping Rule Country", + "name_case": "", "owner": "Administrator", "permissions": [], "read_only": 0, - "read_only_onload": 0 + "read_only_onload": 0, + "sort_field": "modified", + "sort_order": "DESC" } \ No newline at end of file diff --git a/erpnext/setup/doctype/applicable_territory/applicable_territory.py b/erpnext/accounts/doctype/shipping_rule_country/shipping_rule_country.py similarity index 52% rename from erpnext/setup/doctype/applicable_territory/applicable_territory.py rename to erpnext/accounts/doctype/shipping_rule_country/shipping_rule_country.py index 5e0ab4092a..b9646cfc29 100644 --- a/erpnext/setup/doctype/applicable_territory/applicable_territory.py +++ b/erpnext/accounts/doctype/shipping_rule_country/shipping_rule_country.py @@ -1,12 +1,10 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt - +# -*- coding: utf-8 -*- +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt from __future__ import unicode_literals import frappe - from frappe.model.document import Document -class ApplicableTerritory(Document): - pass \ No newline at end of file +class ShippingRuleCountry(Document): + pass diff --git a/erpnext/accounts/doctype/tax_rule/tax_rule.py b/erpnext/accounts/doctype/tax_rule/tax_rule.py index 0a9bb1114b..bcbd5595d4 100644 --- a/erpnext/accounts/doctype/tax_rule/tax_rule.py +++ b/erpnext/accounts/doctype/tax_rule/tax_rule.py @@ -13,6 +13,9 @@ class IncorrectSupplierType(frappe.ValidationError): pass class ConflictingTaxRule(frappe.ValidationError): pass class TaxRule(Document): + def __setup__(self): + self.flags.ignore_these_exceptions_in_test = [ConflictingTaxRule] + def validate(self): self.validate_tax_template() self.validate_customer_group() @@ -25,7 +28,7 @@ class TaxRule(Document): self.purchase_tax_template = self.supplier = self.supplier_type= None else: self.sales_tax_template= self.customer = self.customer_group= None - + if not (self.sales_tax_template or self.purchase_tax_template): frappe.throw(_("Tax Template is mandatory.")) @@ -34,7 +37,7 @@ class TaxRule(Document): if not frappe.db.get_value("Customer", self.customer, "customer_group") == self.customer_group: frappe.throw(_("Customer {0} does not belong to customer group {1}"). \ format(self.customer, self.customer_group), IncorrectCustomerGroup) - + def validate_supplier_type(self): if self.supplier and self.supplier_type: if not frappe.db.get_value("Supplier", self.supplier, "supplier_type") == self.supplier_type: @@ -60,28 +63,28 @@ class TaxRule(Document): "shipping_country": self.shipping_country, "company": self.company } - + conds="" for d in filters: if conds: conds += " and " conds += """ifnull({0}, '') = '{1}'""".format(d, frappe.db.escape(cstr(filters[d]))) - + if self.from_date and self.to_date: conds += """ and ((from_date > '{from_date}' and from_date < '{to_date}') or (to_date > '{from_date}' and to_date < '{to_date}') or ('{from_date}' > from_date and '{from_date}' < to_date) or ('{from_date}' = from_date and '{to_date}' = to_date))""".format(from_date=self.from_date, to_date=self.to_date) - + elif self.from_date and not self.to_date: conds += """ and to_date > '{from_date}'""".format(from_date = self.from_date) elif self.to_date and not self.from_date: conds += """ and from_date < '{to_date}'""".format(to_date = self.to_date) - + tax_rule = frappe.db.sql("select name, priority \ - from `tabTax Rule` where {0} and name != '{1}'".format(conds, self.name), as_dict=1) - + from `tabTax Rule` where {0} and name != '{1}'".format(conds, self.name), as_dict=1) + if tax_rule: if tax_rule[0].priority == self.priority: frappe.throw(_("Tax Rule Conflicts with {0}".format(tax_rule[0].name)), ConflictingTaxRule) @@ -95,10 +98,10 @@ def get_party_details(party, party_type, args=None): else: billing_filters= {party_type: party, "is_primary_address": 1} shipping_filters= {party_type:party, "is_shipping_address": 1} - + billing_address= frappe.get_all("Address", fields=["city", "state", "country"], filters= billing_filters) shipping_address= frappe.get_all("Address", fields=["city", "state", "country"], filters= shipping_filters) - + if billing_address: out["billing_city"]= billing_address[0].city out["billing_state"]= billing_address[0].state @@ -108,7 +111,7 @@ def get_party_details(party, party_type, args=None): out["shipping_city"]= shipping_address[0].city out["shipping_state"]= shipping_address[0].state out["shipping_country"]= shipping_address[0].country - + return out def get_tax_template(posting_date, args): @@ -117,18 +120,21 @@ def get_tax_template(posting_date, args): conditions = [] for key, value in args.iteritems(): - conditions.append("ifnull({0}, '') in ('', '{1}')".format(key, frappe.db.escape(cstr(value)))) + if key in "use_for_shopping_cart": + conditions.append("use_for_shopping_cart = {0}".format(1 if value else 0)) + else: + conditions.append("ifnull({0}, '') in ('', '{1}')".format(key, frappe.db.escape(cstr(value)))) matching = frappe.db.sql("""select * from `tabTax Rule` where {0}""".format(" and ".join(conditions)), as_dict = True) - + if not matching: return None - + for rule in matching: rule.no_of_keys_matched = 0 for key in args: if rule.get(key): rule.no_of_keys_matched += 1 - + rule = sorted(matching, lambda b, a: cmp(a.no_of_keys_matched, b.no_of_keys_matched) or cmp(a.priority, b.priority))[0] - return rule.sales_tax_template or rule.purchase_tax_template \ No newline at end of file + return rule.sales_tax_template or rule.purchase_tax_template diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index 1a6e85bf77..baa237e07d 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -103,6 +103,19 @@ def set_other_values(out, party, party_type): if party.get("default_" + f): out[f] = party.get("default_" + f) +def get_default_price_list(party): + """Return default price list for party (Document object)""" + if party.default_price_list: + return party.default_price_list + + if party.doctype == "Customer": + price_list = frappe.db.get_value("Customer Group", + party.customer_group, "default_price_list") + if price_list: + return price_list + + return None + def set_price_list(out, party, party_type, given_price_list): # price list price_list = filter(None, get_user_permissions().get("Price List", [])) @@ -110,11 +123,7 @@ def set_price_list(out, party, party_type, given_price_list): price_list = price_list[0] if len(price_list)==1 else None if not price_list: - price_list = party.default_price_list - - if not price_list and party_type=="Customer": - price_list = frappe.db.get_value("Customer Group", - party.customer_group, "default_price_list") + price_list = get_default_price_list(party) if not price_list: price_list = given_price_list @@ -271,7 +280,7 @@ def set_taxes(party, party_type, posting_date, company, customer_group=None, sup billing_address=None, shipping_address=None, use_for_shopping_cart=None): from erpnext.accounts.doctype.tax_rule.tax_rule import get_tax_template, get_party_details args = { - party_type: party, + party_type.lower(): party, "customer_group": customer_group, "supplier_type": supplier_type, "company": company diff --git a/erpnext/controllers/website_list_for_contact.py b/erpnext/controllers/website_list_for_contact.py index 9282be286f..51fb0a5889 100644 --- a/erpnext/controllers/website_list_for_contact.py +++ b/erpnext/controllers/website_list_for_contact.py @@ -21,37 +21,57 @@ def get_list_context(context=None): def get_transaction_list(doctype, txt=None, filters=None, limit_start=0, limit_page_length=20): from frappe.templates.pages.list import get_list user = frappe.session.user + key = None + + if not filters: filters = [] + + filters.append((doctype, "docstatus", "=", 1)) if user != "Guest" and is_website_user(): # find party for this contact customers, suppliers = get_customers_suppliers(doctype, user) + if customers: - return post_process(get_list(doctype, txt, filters=[(doctype, "customer", "in", customers)], - limit_start=limit_start, limit_page_length=limit_page_length, ignore_permissions=True)) - + key, parties = "customer", customers elif suppliers: - return post_process(get_list(doctype, txt, filters=[(doctype, "supplier", "in", suppliers)], - limit_start=limit_start, limit_page_length=limit_page_length, ignore_permissions=True)) + key, parties = "supplier", suppliers + filters.append((doctype, key, "in", parties)) + + if key: + return post_process(doctype, get_list(doctype, txt, + filters=filters, fields = "name", + limit_start=limit_start, limit_page_length=limit_page_length, + ignore_permissions=True, + order_by = "modified desc")) else: return [] - return post_process(get_list(doctype, txt, filters, limit_start, limit_page_length)) + return post_process(doctype, get_list(doctype, txt, filters, limit_start, limit_page_length, + fields="name", order_by = "modified desc")) -def post_process(result): - for r in result: - r.status_percent = 0 - r.status_display = [] +def post_process(doctype, data): + result = [] + for d in data: + doc = frappe.get_doc(doctype, d.name) - if r.get("per_billed"): - r.status_percent += flt(r.per_billed) - r.status_display.append(_("Billed") if r.per_billed==100 else _("{0}% Billed").format(r.per_billed)) + doc.status_percent = 0 + doc.status_display = [] - if r.get("per_delivered"): - r.status_percent += flt(r.per_delivered) - r.status_display.append(_("Delivered") if r.per_delivered==100 else _("{0}% Delivered").format(r.per_delivered)) + if doc.get("per_billed"): + doc.status_percent += flt(doc.per_billed) + doc.status_display.append(_("Billed") if doc.per_billed==100 else _("{0}% Billed").format(doc.per_billed)) - r.status_display = ", ".join(r.status_display) + if doc.get("per_delivered"): + doc.status_percent += flt(doc.per_delivered) + doc.status_display.append(_("Delivered") if doc.per_delivered==100 else _("{0}% Delivered").format(doc.per_delivered)) + + if hasattr(doc, "set_indicator"): + doc.set_indicator() + + doc.status_display = ", ".join(doc.status_display) + doc.items_preview = ", ".join([d.item_name for d in doc.items]) + result.append(doc) return result diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 54f81b89d5..96f70a866f 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -1,4 +1,6 @@ from __future__ import unicode_literals +from frappe import _ + app_name = "erpnext" app_title = "ERPNext" app_publisher = "Frappe Technologies Pvt. Ltd." @@ -62,11 +64,26 @@ website_context = { website_route_rules = [ {"from_route": "/orders", "to_route": "Sales Order"}, - {"from_route": "/orders/", "to_route": "print", "defaults": {"doctype": "Sales Order"}}, + {"from_route": "/orders/", "to_route": "order", + "defaults": { + "doctype": "Sales Order", + "parents": [{"title": _("Orders"), "name": "orders"}] + } + }, {"from_route": "/invoices", "to_route": "Sales Invoice"}, - {"from_route": "/invoices/", "to_route": "print", "defaults": {"doctype": "Sales Invoice"}}, + {"from_route": "/invoices/", "to_route": "order", + "defaults": { + "doctype": "Sales Invoice", + "parents": [{"title": _("Invoices"), "name": "invoices"}] + } + }, {"from_route": "/shipments", "to_route": "Delivery Note"}, - {"from_route": "/shipments/", "to_route": "print", "defaults": {"doctype": "Delivery Note"}} + {"from_route": "/shipments/", "to_route": "order", + "defaults": { + "doctype": "Delivery Notes", + "parents": [{"title": _("Shipments"), "name": "shipments"}] + } + } ] has_website_permission = { diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js index 3bad2f081e..9883ba30f6 100644 --- a/erpnext/manufacturing/doctype/bom/bom.js +++ b/erpnext/manufacturing/doctype/bom/bom.js @@ -147,7 +147,7 @@ cur_frm.fields_dict['project_name'].get_query = function(doc, dt, dn) { cur_frm.fields_dict['items'].grid.get_field('item_code').get_query = function(doc) { return{ query: "erpnext.controllers.queries.item_query", - filters: [["Item", "name", "!=", doc.item]] + filters: [["Item", "name", "!=", cur_frm.doc.item]] } } diff --git a/erpnext/patches.txt b/erpnext/patches.txt index e20aab0b2e..51356e5ed9 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -209,3 +209,5 @@ erpnext.patches.v6_0.fix_planned_qty erpnext.patches.v6_0.multi_currency erpnext.patches.v6_2.remove_newsletter_duplicates erpnext.patches.v6_2.fix_missing_default_taxes_and_lead +erpnext.patches.v5_8.tax_rule +erpnext.patches.v6_3.convert_applicable_territory diff --git a/erpnext/shopping_cart/doctype/shopping_cart_price_list/__init__.py b/erpnext/patches/v6_3/__init__.py similarity index 100% rename from erpnext/shopping_cart/doctype/shopping_cart_price_list/__init__.py rename to erpnext/patches/v6_3/__init__.py diff --git a/erpnext/patches/v6_3/convert_applicable_territory.py b/erpnext/patches/v6_3/convert_applicable_territory.py new file mode 100644 index 0000000000..3054ecc879 --- /dev/null +++ b/erpnext/patches/v6_3/convert_applicable_territory.py @@ -0,0 +1,17 @@ +import frappe + +def execute(): + # for price list + countries = frappe.db.sql_list("select name from tabCountry") + + for doctype in ("Price List", "Shipping Rule"): + for at in frappe.db.sql("""select name, parent, territory from `tabApplicable Territory` where + parenttype = %s """, doctype, as_dict=True): + if at.territory in countries: + parent = frappe.get_doc(doctype, at.parent) + if not parent.countries: + parent.append("countries", {"country": at.territory}) + parent.save() + + + frappe.delete_doc("DocType", "Applicable Territory") diff --git a/erpnext/public/css/website.css b/erpnext/public/css/website.css index 9b4df40bd1..f3bc58d3de 100644 --- a/erpnext/public/css/website.css +++ b/erpnext/public/css/website.css @@ -29,6 +29,7 @@ background-repeat: no-repeat; background-position: center top; border-radius: 0.5em; + border: 1px solid #ebeff2; } .product-image.missing-image { width: 100%; @@ -38,6 +39,7 @@ background-repeat: no-repeat; background-position: center top; border-radius: 0.5em; + border: 1px solid #ebeff2; border: 1px dashed #d1d8dd; position: relative; } diff --git a/erpnext/public/js/shopping_cart.js b/erpnext/public/js/shopping_cart.js index cff831d714..f52c296e3d 100644 --- a/erpnext/public/js/shopping_cart.js +++ b/erpnext/public/js/shopping_cart.js @@ -30,7 +30,7 @@ $.extend(shopping_cart, { args: { item_code: opts.item_code, qty: opts.qty, - with_doc: opts.with_doc || 0 + with_items: opts.with_items || 0 }, btn: opts.btn, callback: function(r) { diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index 68a447b601..7f69202522 100644 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -49,18 +49,6 @@ $.extend(erpnext, { } }, - add_applicable_territory: function() { - if(cur_frm.doc.__islocal && (cur_frm.doc.territories || []).length===0) { - var default_territory = frappe.defaults.get_user_default("territory"); - if(default_territory) { - var territory = frappe.model.add_child(cur_frm.doc, "Applicable Territory", - "territories"); - territory.territory = default_territory; - } - - } - }, - setup_serial_no: function() { var grid_row = cur_frm.open_grid_row(); if(!grid_row.fields_dict.serial_no || @@ -131,8 +119,8 @@ $.extend(erpnext.utils, { } ); } - }, - + }, + copy_value_in_all_row: function(doc, dt, dn, table_fieldname, fieldname) { var d = locals[dt][dn]; if(d[fieldname]){ diff --git a/erpnext/public/less/website.less b/erpnext/public/less/website.less index c0667cceab..96dd0966ab 100644 --- a/erpnext/public/less/website.less +++ b/erpnext/public/less/website.less @@ -1,4 +1,5 @@ @border-color: #d1d8dd; +@light-border-color: #EBEFF2; .web-long-description { font-size: 18px; @@ -35,6 +36,7 @@ background-repeat: no-repeat; background-position: center top; border-radius: 0.5em; + border: 1px solid @light-border-color; } .product-image.missing-image { diff --git a/erpnext/setup/page/setup_wizard/setup_wizard.py b/erpnext/setup/page/setup_wizard/setup_wizard.py index 77ad90a0b7..a1370333ad 100644 --- a/erpnext/setup/page/setup_wizard/setup_wizard.py +++ b/erpnext/setup/page/setup_wizard/setup_wizard.py @@ -174,10 +174,7 @@ def create_price_lists(args): "enabled": 1, "buying": 1 if pl_type == "Buying" else 0, "selling": 1 if pl_type == "Selling" else 0, - "currency": args["currency"], - "territories": [{ - "territory": get_root_of("Territory") - }] + "currency": args["currency"] }).insert() def set_defaults(args): @@ -304,7 +301,7 @@ def get_fy_details(fy_start_date, fy_end_date): return fy def create_taxes(args): - + for i in xrange(1,6): if args.get("tax_" + str(i)): # replace % in case someone also enters the % symbol @@ -324,7 +321,7 @@ def create_taxes(args): raise except RootNotEditable, e: pass - + def make_tax_head(args, i, tax_group, tax_rate): return frappe.get_doc({ "doctype":"Account", diff --git a/erpnext/shopping_cart/__init__.py b/erpnext/shopping_cart/__init__.py index 1858c0d842..db44f94b52 100644 --- a/erpnext/shopping_cart/__init__.py +++ b/erpnext/shopping_cart/__init__.py @@ -1,103 +1,2 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe -from frappe import _ -from frappe.utils import get_fullname, flt -from erpnext.shopping_cart.doctype.shopping_cart_settings.shopping_cart_settings import check_shopping_cart_enabled, get_default_territory - -# TODO -# validate stock of each item in Website Warehouse or have a list of possible warehouses in Shopping Cart Settings -# Below functions are used for test cases - -def get_quotation(user=None): - if not user: - user = frappe.session.user - if user == "Guest": - raise frappe.PermissionError - - check_shopping_cart_enabled() - party = get_party(user) - values = { - "order_type": "Shopping Cart", - party.doctype.lower(): party.name, - "docstatus": 0, - "contact_email": user, - "selling_price_list": "_Test Price List Rest of the World", - "currency": "USD" - } - - try: - quotation = frappe.get_doc("Quotation", values) - - except frappe.DoesNotExistError: - quotation = frappe.new_doc("Quotation") - quotation.update(values) - if party.doctype == "Customer": - quotation.contact_person = frappe.db.get_value("Contact", {"customer": party.name, "email_id": user}) - quotation.insert(ignore_permissions=True) - - return quotation - -def set_item_in_cart(item_code, qty, user=None): - validate_item(item_code) - quotation = get_quotation(user=user) - qty = flt(qty) - quotation_item = quotation.get("items", {"item_code": item_code}) - if qty==0: - if quotation_item: - # remove - quotation.get("items").remove(quotation_item[0]) - else: - # add or update - if quotation_item: - quotation_item[0].qty = qty - else: - quotation.append("items", { - "doctype": "Quotation Item", - "item_code": item_code, - "qty": qty - }) - quotation.save(ignore_permissions=True) - return quotation - -def validate_item(item_code): - item = frappe.db.get_value("Item", item_code, ["item_name", "show_in_website"], as_dict=True) - if not item.show_in_website: - frappe.throw(_("{0} cannot be purchased using Shopping Cart").format(item.item_name)) - -def get_party(user): - def _get_party(user): - customer = frappe.db.get_value("Contact", {"email_id": user}, "customer") - if customer: - return frappe.get_doc("Customer", customer) - - lead = frappe.db.get_value("Lead", {"email_id": user}) - if lead: - return frappe.get_doc("Lead", lead) - - # create a lead - lead = frappe.new_doc("Lead") - lead.update({ - "email_id": user, - "lead_name": get_fullname(user), - "territory": guess_territory() - }) - lead.insert(ignore_permissions=True) - - return lead - - if not getattr(frappe.local, "shopping_cart_party", None): - frappe.local.shopping_cart_party = {} - - if not frappe.local.shopping_cart_party.get(user): - frappe.local.shopping_cart_party[user] = _get_party(user) - - return frappe.local.shopping_cart_party[user] - -def guess_territory(): - territory = None - if frappe.session.get("session_country"): - territory = frappe.db.get_value("Territory", frappe.session.get("session_country")) - return territory or get_default_territory() diff --git a/erpnext/shopping_cart/cart.py b/erpnext/shopping_cart/cart.py index 30f4aba493..c4f3db0395 100644 --- a/erpnext/shopping_cart/cart.py +++ b/erpnext/shopping_cart/cart.py @@ -5,7 +5,7 @@ from __future__ import unicode_literals import frappe from frappe import throw, _ import frappe.defaults -from frappe.utils import cint, flt, get_fullname, fmt_money, cstr +from frappe.utils import cint, flt, get_fullname, cstr from erpnext.utilities.doctype.address.address import get_address_display from erpnext.shopping_cart.doctype.shopping_cart_settings.shopping_cart_settings import get_shopping_cart_settings from frappe.utils.nestedset import get_root_of @@ -17,11 +17,13 @@ def set_cart_count(quotation=None): if not quotation: quotation = _get_cart_quotation() cart_count = cstr(len(quotation.get("items"))) - frappe.local.cookie_manager.set_cookie("cart_count", cart_count) + + if hasattr(frappe.local, "cookie_manager"): + frappe.local.cookie_manager.set_cookie("cart_count", cart_count) @frappe.whitelist() def get_cart_quotation(doc=None): - party = get_lead_or_customer() + party = get_customer() if not doc: quotation = _get_cart_quotation(party) @@ -58,21 +60,19 @@ def place_order(): sales_order.flags.ignore_permissions = True sales_order.insert() sales_order.submit() - frappe.local.cookie_manager.delete_cookie("cart_count") + + if hasattr(frappe.local, "cookie_manager"): + frappe.local.cookie_manager.delete_cookie("cart_count") return sales_order.name @frappe.whitelist() -def update_cart(item_code, qty, with_doc): +def update_cart(item_code, qty, with_items=False): quotation = _get_cart_quotation() qty = flt(qty) if qty == 0: quotation.set("items", quotation.get("items", {"item_code": ["!=", item_code]})) - if not quotation.get("items") and \ - not quotation.get("__islocal"): - quotation.__delete = True - else: quotation_items = quotation.get("items", {"item_code": item_code}) if not quotation_items: @@ -86,17 +86,19 @@ def update_cart(item_code, qty, with_doc): apply_cart_settings(quotation=quotation) - if hasattr(quotation, "__delete"): - frappe.delete_doc("Quotation", quotation.name, ignore_permissions=True) - quotation = _get_cart_quotation() - else: - quotation.flags.ignore_permissions = True - quotation.save() + quotation.flags.ignore_permissions = True + quotation.save() set_cart_count(quotation) - if with_doc: - return get_cart_quotation(quotation) + if with_items: + context = get_cart_quotation(quotation) + return { + "items": frappe.render_template("templates/includes/cart/cart_items.html", + context), + "taxes": frappe.render_template("templates/includes/order/order_taxes.html", + context), + } else: return quotation.name @@ -122,7 +124,11 @@ def update_cart_address(address_fieldname, address_name): quotation.flags.ignore_permissions = True quotation.save() - return get_cart_quotation(quotation) + context = get_cart_quotation(quotation) + return { + "taxes": frappe.render_template("templates/includes/order/order_taxes.html", + context), + } def guess_territory(): territory = None @@ -134,32 +140,23 @@ def guess_territory(): frappe.db.get_value("Shopping Cart Settings", None, "territory") or \ get_root_of("Territory") -def decorate_quotation_doc(quotation_doc): - doc = frappe._dict(quotation_doc.as_dict()) +def decorate_quotation_doc(doc): for d in doc.get("items", []): - d.update(frappe.db.get_value("Item", d["item_code"], + d.update(frappe.db.get_value("Item", d.item_code, ["website_image", "description", "page_name"], as_dict=True)) - d["formatted_rate"] = fmt_money(d.get("rate"), currency=doc.currency) - d["formatted_amount"] = fmt_money(d.get("amount"), currency=doc.currency) - - for d in doc.get("taxes", []): - d["formatted_tax_amount"] = fmt_money(flt(d.get("tax_amount_after_discount_amount")), - currency=doc.currency) - - doc.formatted_grand_total_export = fmt_money(doc.grand_total, - currency=doc.currency) return doc def _get_cart_quotation(party=None): if not party: - party = get_lead_or_customer() + party = get_customer() - quotation = frappe.db.get_value("Quotation", - {party.doctype.lower(): party.name, "order_type": "Shopping Cart", "docstatus": 0}) + quotation = frappe.get_all("Quotation", fields=["name"], filters= + {party.doctype.lower(): party.name, "order_type": "Shopping Cart", "docstatus": 0}, + order_by="modified desc", limit_page_length=1) if quotation: - qdoc = frappe.get_doc("Quotation", quotation) + qdoc = frappe.get_doc("Quotation", quotation[0].name) else: qdoc = frappe.get_doc({ "doctype": "Quotation", @@ -173,9 +170,9 @@ def _get_cart_quotation(party=None): (party.doctype.lower()): party.name }) - if party.doctype == "Customer": - qdoc.contact_person = frappe.db.get_value("Contact", {"email_id": frappe.session.user, - "customer": party.name}) + qdoc.contact_person = frappe.db.get_value("Contact", {"email_id": frappe.session.user, + "customer": party.name}) + qdoc.contact_email = frappe.session.user qdoc.flags.ignore_permissions = True qdoc.run_method("set_missing_values") @@ -184,27 +181,21 @@ def _get_cart_quotation(party=None): return qdoc def update_party(fullname, company_name=None, mobile_no=None, phone=None): - party = get_lead_or_customer() + party = get_customer() - if party.doctype == "Lead": - party.company_name = company_name - party.lead_name = fullname - party.mobile_no = mobile_no - party.phone = phone - else: - party.customer_name = company_name or fullname - party.customer_type == "Company" if company_name else "Individual" + party.customer_name = company_name or fullname + party.customer_type == "Company" if company_name else "Individual" - contact_name = frappe.db.get_value("Contact", {"email_id": frappe.session.user, - "customer": party.name}) - contact = frappe.get_doc("Contact", contact_name) - contact.first_name = fullname - contact.last_name = None - contact.customer_name = party.customer_name - contact.mobile_no = mobile_no - contact.phone = phone - contact.flags.ignore_permissions = True - contact.save() + contact_name = frappe.db.get_value("Contact", {"email_id": frappe.session.user, + "customer": party.name}) + contact = frappe.get_doc("Contact", contact_name) + contact.first_name = fullname + contact.last_name = None + contact.customer_name = party.customer_name + contact.mobile_no = mobile_no + contact.phone = phone + contact.flags.ignore_permissions = True + contact.save() party_doc = frappe.get_doc(party.as_dict()) party_doc.flags.ignore_permissions = True @@ -219,26 +210,24 @@ def update_party(fullname, company_name=None, mobile_no=None, phone=None): def apply_cart_settings(party=None, quotation=None): if not party: - party = get_lead_or_customer() + party = get_customer() if not quotation: quotation = _get_cart_quotation(party) cart_settings = frappe.get_doc("Shopping Cart Settings") - billing_territory = get_address_territory(quotation.customer_address) or \ - party.territory or get_root_of("Territory") - set_price_list_and_rate(quotation, cart_settings, billing_territory) + set_price_list_and_rate(quotation, cart_settings) quotation.run_method("calculate_taxes_and_totals") - set_taxes(quotation, cart_settings, billing_territory) + set_taxes(quotation, cart_settings) _apply_shipping_rule(party, quotation, cart_settings) -def set_price_list_and_rate(quotation, cart_settings, billing_territory): +def set_price_list_and_rate(quotation, cart_settings): """set price list based on billing territory""" - _set_price_list(quotation, cart_settings, billing_territory) + _set_price_list(quotation, cart_settings) # reset values quotation.price_list_currency = quotation.currency = \ @@ -249,27 +238,33 @@ def set_price_list_and_rate(quotation, cart_settings, billing_territory): # refetch values quotation.run_method("set_price_list_and_item_details") - # set it in cookies for using in product page - frappe.local.cookie_manager.set_cookie("selling_price_list", quotation.selling_price_list) + if hasattr(frappe.local, "cookie_manager"): + # set it in cookies for using in product page + frappe.local.cookie_manager.set_cookie("selling_price_list", quotation.selling_price_list) + +def _set_price_list(quotation, cart_settings): + """Set price list based on customer or shopping cart default""" + if quotation.selling_price_list: + return -def _set_price_list(quotation, cart_settings, billing_territory): # check if customer price list exists selling_price_list = None if quotation.customer: - selling_price_list = frappe.db.get_value("Customer", quotation.customer, "default_price_list") + from erpnext.accounts.party import get_default_price_list + selling_price_list = get_default_price_list(frappe.get_doc("Customer", quotation.customer)) # else check for territory based price list if not selling_price_list: - selling_price_list = cart_settings.get_price_list(billing_territory) + selling_price_list = cart_settings.price_list quotation.selling_price_list = selling_price_list -def set_taxes(quotation, cart_settings, billing_territory): +def set_taxes(quotation, cart_settings): """set taxes based on billing territory""" from erpnext.accounts.party import set_taxes - + customer_group = frappe.db.get_value("Customer", quotation.customer, "customer_group") - + quotation.taxes_and_charges = set_taxes(quotation.customer, "Customer", \ quotation.transaction_date, quotation.company, customer_group, None, \ quotation.customer_address, quotation.shipping_address_name, 1) @@ -280,32 +275,38 @@ def set_taxes(quotation, cart_settings, billing_territory): # # append taxes quotation.append_taxes_from_master() -def get_lead_or_customer(): - customer = frappe.db.get_value("Contact", {"email_id": frappe.session.user}, "customer") +def get_customer(user=None): + if not user: + user = frappe.session.user + + customer = frappe.db.get_value("Contact", {"email_id": user}, "customer") if customer: return frappe.get_doc("Customer", customer) - lead = frappe.db.get_value("Lead", {"email_id": frappe.session.user}) - if lead: - return frappe.get_doc("Lead", lead) else: - lead_doc = frappe.get_doc({ - "doctype": "Lead", - "email_id": frappe.session.user, - "lead_name": get_fullname(frappe.session.user), - "territory": guess_territory(), - "status": "Open" # TODO: set something better??? + customer = frappe.new_doc("Customer") + fullname = get_fullname(user) + customer.update({ + "customer_name": fullname, + "customer_type": "Individual", + "customer_group": get_shopping_cart_settings().default_customer_group, + "territory": get_root_of("Territory") }) + customer.insert(ignore_permissions=True) - if frappe.session.user not in ("Guest", "Administrator"): - lead_doc.flags.ignore_permissions = True - lead_doc.insert() + contact = frappe.new_doc("Contact") + contact.update({ + "customer": customer.name, + "first_name": fullname, + "email_id": user + }) + contact.insert(ignore_permissions=True) - return lead_doc + return customer def get_address_docs(doctype=None, txt=None, filters=None, limit_start=0, limit_page_length=20, party=None): if not party: - party = get_lead_or_customer() + party = get_customer() address_docs = frappe.db.sql("""select * from `tabAddress` where `{0}`=%s order by name limit {1}, {2}""".format(party.doctype.lower(), @@ -314,7 +315,6 @@ def get_address_docs(doctype=None, txt=None, filters=None, limit_start=0, limit_ for address in address_docs: address.display = get_address_display(address) - address.display = (address.display).replace("\n", "
\n") return address_docs @@ -332,38 +332,39 @@ def apply_shipping_rule(shipping_rule): return get_cart_quotation(quotation) def _apply_shipping_rule(party=None, quotation=None, cart_settings=None): - shipping_rules = get_shipping_rules(party, quotation, cart_settings) + if not quotation.shipping_rule: + shipping_rules = get_shipping_rules(quotation, cart_settings) - if not shipping_rules: - return + if not shipping_rules: + return - elif quotation.shipping_rule not in shipping_rules: - quotation.shipping_rule = shipping_rules[0] + elif quotation.shipping_rule not in shipping_rules: + quotation.shipping_rule = shipping_rules[0] - quotation.run_method("apply_shipping_rule") - quotation.run_method("calculate_taxes_and_totals") + if quotation.shipping_rule: + quotation.run_method("apply_shipping_rule") + quotation.run_method("calculate_taxes_and_totals") def get_applicable_shipping_rules(party=None, quotation=None): - shipping_rules = get_shipping_rules(party, quotation) + shipping_rules = get_shipping_rules(quotation) if shipping_rules: rule_label_map = frappe.db.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() +def get_shipping_rules(quotation=None, cart_settings=None): if not quotation: quotation = _get_cart_quotation() - if not cart_settings: - cart_settings = frappe.get_doc("Shopping Cart Settings") - # set shipping rule based on shipping territory - shipping_territory = get_address_territory(quotation.shipping_address_name) or \ - party.territory - - shipping_rules = cart_settings.get_shipping_rules(shipping_territory) + shipping_rules = [] + if quotation.shipping_address_name: + country = frappe.db.get_value("Address", quotation.shipping_address_name, "country") + if country: + shipping_rules = frappe.db.sql_list("""select distinct sr.name + from `tabShipping Rule Country` src, `tabShipping Rule` sr + where src.country = %s and + sr.disabled != 1 and sr.name = src.parent""", country) return shipping_rules diff --git a/erpnext/shopping_cart/doctype/shopping_cart_price_list/shopping_cart_price_list.py b/erpnext/shopping_cart/doctype/shopping_cart_price_list/shopping_cart_price_list.py deleted file mode 100644 index 53c38d01a3..0000000000 --- a/erpnext/shopping_cart/doctype/shopping_cart_price_list/shopping_cart_price_list.py +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -# For license information, please see license.txt - -from __future__ import unicode_literals -import frappe - -from frappe.model.document import Document - -class ShoppingCartPriceList(Document): - pass \ No newline at end of file diff --git a/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.json b/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.json index 881ff4e377..fbc9ba0220 100644 --- a/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.json +++ b/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.json @@ -75,17 +75,17 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, - "description": "Add / Edit", - "fieldname": "default_territory", + "fieldname": "price_list", "fieldtype": "Link", "hidden": 0, - "ignore_user_permissions": 1, + "ignore_user_permissions": 0, "in_filter": 0, - "in_list_view": 1, - "label": "Default Territory", + "in_list_view": 0, + "label": "Price List", "no_copy": 0, - "options": "Territory", + "options": "Price List", "permlevel": 0, + "precision": "", "print_hide": 0, "read_only": 0, "report_hide": 0, @@ -118,7 +118,7 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, - "description": "Add / Edit", + "description": "", "fieldname": "default_customer_group", "fieldtype": "Link", "hidden": 0, @@ -157,91 +157,6 @@ "search_index": 0, "set_only_once": 0, "unique": 0 - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "section_break_6", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "in_filter": 0, - "in_list_view": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "price_lists", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Price Lists", - "no_copy": 0, - "options": "Shopping Cart Price List", - "permlevel": 0, - "print_hide": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "column_break_10", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "in_filter": 0, - "in_list_view": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "shipping_rules", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Shipping Rules", - "no_copy": 0, - "options": "Shopping Cart Shipping Rule", - "permlevel": 0, - "print_hide": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 } ], "hide_heading": 0, @@ -253,7 +168,7 @@ "is_submittable": 0, "issingle": 1, "istable": 0, - "modified": "2015-09-11 19:03:54.750937", + "modified": "2015-09-17 07:56:09.176098", "modified_by": "Administrator", "module": "Shopping Cart", "name": "Shopping Cart Settings", diff --git a/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.py b/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.py index da7d27af10..d8d00efa96 100644 --- a/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.py +++ b/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.py @@ -8,8 +8,6 @@ import frappe from frappe import _, msgprint from frappe.utils import comma_and from frappe.model.document import Document -from frappe.utils.nestedset import get_ancestors_of, get_root_of -from erpnext.utilities.doctype.address.address import get_territory_from_address class ShoppingCartSetupError(frappe.ValidationError): pass @@ -19,57 +17,7 @@ class ShoppingCartSettings(Document): def validate(self): if self.enabled: - self.validate_price_lists() self.validate_exchange_rates_exist() - self.validate_tax_rule() - - 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: - frappe.throw(_("{0} {1} has a common territory {2}").format(_(doctype), comma_and(names), territory), ShoppingCartSetupError) - - return territory_name_map - - def validate_price_lists(self): - self.validate_overlapping_territories("price_lists", "selling_price_list") - - # validate that a Shopping Cart Price List exists for the default territory as a catch all! - price_list_for_default_territory = self.get_name_from_territory(self.default_territory, "price_lists", - "selling_price_list") - - if not price_list_for_default_territory: - msgprint(_("Please specify a Price List which is valid for Territory") + - ": " + self.default_territory, raise_exception=ShoppingCartSetupError) - - def get_territory_name_map(self, parentfield, fieldname): - territory_name_map = {} - - # entries in table - names = [doc.get(fieldname) for doc in self.get(parentfield)] - if names: - # for condition in territory check - parenttype = frappe.get_meta(self.meta.get_options(parentfield)).get_options(fieldname) - # 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 = frappe.db.sql("""select `territory`, `parent` - from `tabApplicable 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)""" @@ -79,7 +27,7 @@ class ShoppingCartSettings(Document): raise_exception=ShoppingCartSetupError) price_list_currency_map = frappe.db.get_values("Price List", - [d.selling_price_list for d in self.get("price_lists")], + [self.price_list], "currency") # check if all price lists have a currency @@ -102,32 +50,10 @@ class ShoppingCartSettings(Document): msgprint(_("Missing Currency Exchange Rates for {0}").format(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) - 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", "selling_price_list") - if not (price_list and price_list[0]): - price_list = self.get_name_from_territory(self.default_territory or get_root_of("Territory"), - "price_lists", "selling_price_list") - - return price_list and price_list[0] or None - def validate_tax_rule(self): if not frappe.db.get_value("Tax Rule", {"use_for_shopping_cart" : 1}, "name"): frappe.throw(frappe._("Set Tax Rule for shopping cart"), ShoppingCartSetupError) - + 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") @@ -136,15 +62,6 @@ class ShoppingCartSettings(Document): 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): - 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] - def validate_cart_settings(doc, method): frappe.get_doc("Shopping Cart Settings", "Shopping Cart Settings").run_method("validate") @@ -157,10 +74,7 @@ def get_shopping_cart_settings(): def is_cart_enabled(): return get_shopping_cart_settings().enabled -def get_default_territory(): - return get_shopping_cart_settings().default_territory or get_root_of("Territory") - def check_shopping_cart_enabled(): if not get_shopping_cart_settings().enabled: frappe.throw(_("You need to enable Shopping Cart"), ShoppingCartSetupError) - + diff --git a/erpnext/shopping_cart/doctype/shopping_cart_settings/test_shopping_cart_settings.py b/erpnext/shopping_cart/doctype/shopping_cart_settings/test_shopping_cart_settings.py index b18cece44a..10bbcfe85a 100644 --- a/erpnext/shopping_cart/doctype/shopping_cart_settings/test_shopping_cart_settings.py +++ b/erpnext/shopping_cart/doctype/shopping_cart_settings/test_shopping_cart_settings.py @@ -11,55 +11,29 @@ from erpnext.shopping_cart.doctype.shopping_cart_settings.shopping_cart_settings class TestShoppingCartSettings(unittest.TestCase): def setUp(self): frappe.db.sql("""delete from `tabSingles` where doctype="Shipping Cart Settings" """) - frappe.db.sql("""delete from `tabShopping Cart Price List`""") - frappe.db.sql("""delete from `tabShopping Cart Shipping Rule`""") - + def get_cart_settings(self): return frappe.get_doc({"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.append("price_lists", { - "doctype": "Shopping Cart Price List", - "selling_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 - controller.validate_overlapping_territories("price_lists", "selling_price_list") - - _add_price_list("_Test Price List 2") - - controller = cart_settings - self.assertRaises(ShoppingCartSetupError, controller.validate_overlapping_territories, - "price_lists", "selling_price_list") - - return cart_settings - + def test_exchange_rate_exists(self): frappe.db.sql("""delete from `tabCurrency Exchange`""") - - cart_settings = self.test_price_list_territory_overlap() - controller = cart_settings - self.assertRaises(ShoppingCartSetupError, controller.validate_exchange_rates_exist) - + + cart_settings = self.get_cart_settings() + cart_settings.price_list = "_Test Price List Rest of the World" + self.assertRaises(ShoppingCartSetupError, cart_settings.validate_exchange_rates_exist) + from erpnext.setup.doctype.currency_exchange.test_currency_exchange import test_records as \ currency_exchange_records frappe.get_doc(currency_exchange_records[0]).insert() - controller.validate_exchange_rates_exist() - + cart_settings.validate_exchange_rates_exist() + def test_tax_rule_validation(self): frappe.db.sql("update `tabTax Rule` set use_for_shopping_cart = 0") frappe.db.commit() - + cart_settings = self.get_cart_settings() - cart_settings.enabled = 1 + cart_settings.enabled = 1 if not frappe.db.get_value("Tax Rule", {"use_for_shopping_cart": 1}, "name"): self.assertRaises(ShoppingCartSetupError, cart_settings.validate_tax_rule) - + diff --git a/erpnext/shopping_cart/test_shopping_cart.py b/erpnext/shopping_cart/test_shopping_cart.py index bd0b138d45..41f7442b7c 100644 --- a/erpnext/shopping_cart/test_shopping_cart.py +++ b/erpnext/shopping_cart/test_shopping_cart.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import unittest import frappe -from erpnext.shopping_cart import get_quotation, set_item_in_cart, get_party +from erpnext.shopping_cart.cart import _get_cart_quotation, update_cart, get_customer class TestShoppingCart(unittest.TestCase): """ @@ -23,23 +23,11 @@ class TestShoppingCart(unittest.TestCase): self.login_as_new_user() # test if lead is created and quotation with new lead is fetched - quotation = get_quotation() - self.assertEquals(quotation.quotation_to, "Lead") - self.assertEquals(frappe.db.get_value("Lead", quotation.lead, "email_id"), + quotation = _get_cart_quotation() + self.assertEquals(quotation.quotation_to, "Customer") + self.assertEquals(frappe.db.get_value("Contact", {"customer": quotation.customer}, "email_id"), "test_cart_user@example.com") - self.assertEquals(quotation.customer, None) - self.assertEquals(quotation.contact_email, frappe.session.user) - - return quotation - - def test_get_cart_lead(self): - self.login_as_lead() - - # test if quotation with lead is fetched - quotation = get_quotation() - self.assertEquals(quotation.quotation_to, "Lead") - self.assertEquals(quotation.lead, frappe.db.get_value("Lead", {"email_id": "test_cart_lead@example.com"})) - self.assertEquals(quotation.customer, None) + self.assertEquals(quotation.lead, None) self.assertEquals(quotation.contact_email, frappe.session.user) return quotation @@ -48,7 +36,7 @@ class TestShoppingCart(unittest.TestCase): self.login_as_customer() # test if quotation with customer is fetched - quotation = get_quotation() + quotation = _get_cart_quotation() self.assertEquals(quotation.quotation_to, "Customer") self.assertEquals(quotation.customer, "_Test Customer") self.assertEquals(quotation.lead, None) @@ -57,21 +45,24 @@ class TestShoppingCart(unittest.TestCase): return quotation def test_add_to_cart(self): - self.login_as_lead() + self.login_as_customer() # remove from cart self.remove_all_items_from_cart() - + # add first item - set_item_in_cart("_Test Item", 1) - quotation = self.test_get_cart_lead() + update_cart("_Test Item", 1) + + quotation = self.test_get_cart_customer() + self.assertEquals(quotation.get("items")[0].item_code, "_Test Item") self.assertEquals(quotation.get("items")[0].qty, 1) self.assertEquals(quotation.get("items")[0].amount, 10) + # add second item - set_item_in_cart("_Test Item 2", 1) - quotation = self.test_get_cart_lead() + update_cart("_Test Item 2", 1) + quotation = self.test_get_cart_customer() self.assertEquals(quotation.get("items")[1].item_code, "_Test Item 2") self.assertEquals(quotation.get("items")[1].qty, 1) self.assertEquals(quotation.get("items")[1].amount, 20) @@ -83,8 +74,8 @@ class TestShoppingCart(unittest.TestCase): self.test_add_to_cart() # update first item - set_item_in_cart("_Test Item", 5) - quotation = self.test_get_cart_lead() + update_cart("_Test Item", 5) + quotation = self.test_get_cart_customer() self.assertEquals(quotation.get("items")[0].item_code, "_Test Item") self.assertEquals(quotation.get("items")[0].qty, 5) self.assertEquals(quotation.get("items")[0].amount, 50) @@ -96,8 +87,9 @@ class TestShoppingCart(unittest.TestCase): self.test_add_to_cart() # remove first item - set_item_in_cart("_Test Item", 0) - quotation = self.test_get_cart_lead() + update_cart("_Test Item", 0) + quotation = self.test_get_cart_customer() + self.assertEquals(quotation.get("items")[0].item_code, "_Test Item 2") self.assertEquals(quotation.get("items")[0].qty, 1) self.assertEquals(quotation.get("items")[0].amount, 20) @@ -105,16 +97,16 @@ class TestShoppingCart(unittest.TestCase): self.assertEquals(len(quotation.get("items")), 1) # remove second item - set_item_in_cart("_Test Item 2", 0) - quotation = self.test_get_cart_lead() - self.assertEquals(quotation.net_total, 0) + update_cart("_Test Item 2", 0) + quotation = self.test_get_cart_customer() + self.assertEquals(len(quotation.get("items")), 0) - - - def test_taxe_rule(self): + self.assertEquals(quotation.net_total, 0) + + def test_tax_rule(self): self.login_as_customer() quotation = self.create_quotation() - + from erpnext.accounts.party import set_taxes tax_rule_master = set_taxes(quotation.customer, "Customer", \ @@ -123,17 +115,17 @@ class TestShoppingCart(unittest.TestCase): self.assertEquals(quotation.taxes_and_charges, tax_rule_master) self.assertEquals(quotation.total_taxes_and_charges, 1000.0) - + self.remove_test_quotation(quotation) - + def create_quotation(self): quotation = frappe.new_doc("Quotation") - + values = { "doctype": "Quotation", "quotation_to": "Customer", "order_type": "Shopping Cart", - "customer": get_party(frappe.session.user).name, + "customer": get_customer(frappe.session.user).name, "docstatus": 0, "contact_email": frappe.session.user, "selling_price_list": "_Test Price List Rest of the World", @@ -146,13 +138,13 @@ class TestShoppingCart(unittest.TestCase): "taxes": frappe.get_doc("Sales Taxes and Charges Template", "_Test Tax 1").taxes, "company": "_Test Company" } - + quotation.update(values) - + quotation.insert(ignore_permissions=True) - + return quotation - + def remove_test_quotation(self, quotation): frappe.set_user("Administrator") quotation.delete() @@ -161,26 +153,29 @@ class TestShoppingCart(unittest.TestCase): def enable_shopping_cart(self): settings = frappe.get_doc("Shopping Cart Settings", "Shopping Cart Settings") - if settings.get("price_lists"): - settings.enabled = 1 - else: - settings.update({ - "enabled": 1, - "company": "_Test Company", - "default_territory": "_Test Territory Rest Of The World", - "default_customer_group": "_Test Customer Group", - "quotation_series": "_T-Quotation-" - }) - settings.set("price_lists", [ - # price lists - {"doctype": "Shopping Cart Price List", "parentfield": "price_lists", - "selling_price_list": "_Test Price List India"}, - {"doctype": "Shopping Cart Price List", "parentfield": "price_lists", - "selling_price_list": "_Test Price List Rest of the World"} - ]) - settings.set("shipping_rules", {"doctype": "Shopping Cart Shipping Rule", "parentfield": "shipping_rules", - "shipping_rule": "_Test Shipping Rule - India"}) - + settings.update({ + "enabled": 1, + "company": "_Test Company", + "default_customer_group": "_Test Customer Group", + "quotation_series": "_T-Quotation-", + "price_list": "_Test Price List India" + }) + + # insert item price + if not frappe.db.get_value("Item Price", {"price_list": "_Test Price List India", + "item_code": "_Test Item"}): + frappe.get_doc({ + "doctype": "Item Price", + "price_list": "_Test Price List India", + "item_code": "_Test Item", + "price_list_rate": 10 + }).insert() + frappe.get_doc({ + "doctype": "Item Price", + "price_list": "_Test Price List India", + "item_code": "_Test Item 2", + "price_list_rate": 20 + }).insert() settings.save() frappe.local.shopping_cart_settings = None @@ -194,56 +189,13 @@ class TestShoppingCart(unittest.TestCase): def login_as_new_user(self): frappe.set_user("test_cart_user@example.com") - def login_as_lead(self): - self.create_lead() - frappe.set_user("test_cart_lead@example.com") - def login_as_customer(self): frappe.set_user("test_contact_customer@example.com") - def create_lead(self): - if frappe.db.get_value("Lead", {"email_id": "test_cart_lead@example.com"}): - return - - lead = frappe.get_doc({ - "doctype": "Lead", - "email_id": "test_cart_lead@example.com", - "lead_name": "_Test Website Lead", - "status": "Open", - "territory": "_Test Territory Rest Of The World", - "company": "_Test Company" - }) - lead.insert(ignore_permissions=True) - - frappe.get_doc({ - "doctype": "Address", - "address_line1": "_Test Address Line 1", - "address_title": "_Test Cart Lead Address", - "address_type": "Office", - "city": "_Test City", - "country": "United States", - "lead": lead.name, - "lead_name": "_Test Website Lead", - "is_primary_address": 1, - "phone": "+91 0000000000" - }).insert(ignore_permissions=True) - - frappe.get_doc({ - "doctype": "Address", - "address_line1": "_Test Address Line 1", - "address_title": "_Test Cart Lead Address", - "address_type": "Personal", - "city": "_Test City", - "country": "India", - "lead": lead.name, - "lead_name": "_Test Website Lead", - "phone": "+91 0000000000" - }).insert(ignore_permissions=True) - def remove_all_items_from_cart(self): - quotation = get_quotation() + quotation = _get_cart_quotation() quotation.set("items", []) quotation.save(ignore_permissions=True) - + test_dependencies = ["Sales Taxes and Charges Template", "Price List", "Item Price", "Shipping Rule", "Currency Exchange", "Customer Group", "Lead", "Customer", "Contact", "Address", "Item", "Tax Rule"] diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index 6eadb83779..310f4cca22 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -19,6 +19,7 @@ class Item(WebsiteGenerator): condition_field = "show_in_website", template = "templates/generators/item.html", parent_website_route_field = "item_group", + no_cache = 1 ) def onload(self): diff --git a/erpnext/stock/doctype/price_list/price_list.js b/erpnext/stock/doctype/price_list/price_list.js index 54a77732a9..f109633fdd 100644 --- a/erpnext/stock/doctype/price_list/price_list.js +++ b/erpnext/stock/doctype/price_list/price_list.js @@ -2,10 +2,6 @@ // License: GNU General Public License v3. See license.txt $.extend(cur_frm.cscript, { - onload: function() { - erpnext.add_applicable_territory(); - }, - refresh: function() { cur_frm.add_custom_button(__("Add / Edit Prices"), function() { frappe.route_options = { @@ -14,4 +10,4 @@ $.extend(cur_frm.cscript, { frappe.set_route("Report", "Item Price"); }, "icon-money"); } -}); \ No newline at end of file +}); diff --git a/erpnext/stock/doctype/price_list/price_list.json b/erpnext/stock/doctype/price_list/price_list.json index fcd204c18e..0d2baa461f 100644 --- a/erpnext/stock/doctype/price_list/price_list.json +++ b/erpnext/stock/doctype/price_list/price_list.json @@ -163,21 +163,21 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, - "description": "Specify a list of Territories, for which, this Price List is valid", - "fieldname": "territories", + "fieldname": "countries", "fieldtype": "Table", "hidden": 0, "ignore_user_permissions": 0, "in_filter": 0, "in_list_view": 0, - "label": "Valid for Territories", + "label": "Applicable for Countries", "no_copy": 0, - "options": "Applicable Territory", + "options": "Price List Country", "permlevel": 0, + "precision": "", "print_hide": 0, "read_only": 0, "report_hide": 0, - "reqd": 1, + "reqd": 0, "search_index": 0, "set_only_once": 0, "unique": 0 @@ -193,7 +193,7 @@ "issingle": 0, "istable": 0, "max_attachments": 1, - "modified": "2015-09-14 02:55:58.919822", + "modified": "2015-09-17 06:50:31.465221", "modified_by": "Administrator", "module": "Stock", "name": "Price List", @@ -201,7 +201,7 @@ "permissions": [ { "amend": 0, - "apply_user_permissions": 0, + "apply_user_permissions": 1, "cancel": 0, "create": 0, "delete": 0, @@ -241,7 +241,7 @@ }, { "amend": 0, - "apply_user_permissions": 0, + "apply_user_permissions": 1, "cancel": 0, "create": 0, "delete": 0, @@ -281,7 +281,7 @@ }, { "amend": 0, - "apply_user_permissions": 0, + "apply_user_permissions": 1, "cancel": 0, "create": 0, "delete": 0, diff --git a/erpnext/stock/doctype/price_list/price_list.py b/erpnext/stock/doctype/price_list/price_list.py index 6b03bb6b0e..8773b9c33f 100644 --- a/erpnext/stock/doctype/price_list/price_list.py +++ b/erpnext/stock/doctype/price_list/price_list.py @@ -13,19 +13,6 @@ class PriceList(Document): if not cint(self.buying) and not cint(self.selling): throw(_("Price List must be applicable for Buying or Selling")) - try: - # at least one territory - self.validate_table_has_rows("territories") - except frappe.EmptyTableError: - # if no territory, set default territory - if frappe.defaults.get_user_default("territory"): - self.append("territories", { - "doctype": "Applicable Territory", - "territory": frappe.defaults.get_user_default("territory") - }) - else: - raise - def on_update(self): self.set_default_if_missing() self.update_item_price() diff --git a/erpnext/stock/doctype/price_list/test_records.json b/erpnext/stock/doctype/price_list/test_records.json index d91f8872db..ca8a620d23 100644 --- a/erpnext/stock/doctype/price_list/test_records.json +++ b/erpnext/stock/doctype/price_list/test_records.json @@ -1,67 +1,34 @@ [ { - "buying": 1, - "currency": "INR", - "doctype": "Price List", - "enabled": 1, - "price_list_name": "_Test Price List", - "selling": 1, - "territories": [ - { - "doctype": "Applicable Territory", - "parentfield": "territories", - "territory": "All Territories" - } - ] - }, + "buying": 1, + "currency": "INR", + "doctype": "Price List", + "enabled": 1, + "price_list_name": "_Test Price List", + "selling": 1 + }, { - "buying": 1, - "currency": "INR", - "doctype": "Price List", - "enabled": 1, - "price_list_name": "_Test Price List 2", - "selling": 1, - "territories": [ - { - "doctype": "Applicable Territory", - "parentfield": "territories", - "territory": "_Test Territory Rest Of The World" - } - ] - }, + "buying": 1, + "currency": "INR", + "doctype": "Price List", + "enabled": 1, + "price_list_name": "_Test Price List 2", + "selling": 1 + }, { - "buying": 1, - "currency": "INR", - "doctype": "Price List", - "enabled": 1, - "price_list_name": "_Test Price List India", - "selling": 1, - "territories": [ - { - "doctype": "Applicable Territory", - "parentfield": "territories", - "territory": "_Test Territory India" - } - ] - }, + "buying": 1, + "currency": "INR", + "doctype": "Price List", + "enabled": 1, + "price_list_name": "_Test Price List India", + "selling": 1 + }, { - "buying": 1, - "currency": "USD", - "doctype": "Price List", - "enabled": 1, - "price_list_name": "_Test Price List Rest of the World", - "selling": 1, - "territories": [ - { - "doctype": "Applicable Territory", - "parentfield": "territories", - "territory": "_Test Territory Rest Of The World" - }, - { - "doctype": "Applicable Territory", - "parentfield": "territories", - "territory": "_Test Territory United States" - } - ] + "buying": 1, + "currency": "USD", + "doctype": "Price List", + "enabled": 1, + "price_list_name": "_Test Price List Rest of the World", + "selling": 1 } -] \ No newline at end of file +] diff --git a/erpnext/stock/doctype/price_list_country/__init__.py b/erpnext/stock/doctype/price_list_country/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/setup/doctype/applicable_territory/applicable_territory.json b/erpnext/stock/doctype/price_list_country/price_list_country.json similarity index 67% rename from erpnext/setup/doctype/applicable_territory/applicable_territory.json rename to erpnext/stock/doctype/price_list_country/price_list_country.json index 6fc549faa6..be02c202cc 100644 --- a/erpnext/setup/doctype/applicable_territory/applicable_territory.json +++ b/erpnext/stock/doctype/price_list_country/price_list_country.json @@ -2,26 +2,27 @@ "allow_copy": 0, "allow_import": 0, "allow_rename": 0, - "creation": "2013-06-20 12:48:38", + "creation": "2015-09-17 06:49:51.810318", "custom": 0, "docstatus": 0, "doctype": "DocType", + "document_type": "", "fields": [ { "allow_on_submit": 0, "bold": 0, "collapsible": 0, - "description": "", - "fieldname": "territory", + "fieldname": "country", "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 0, "in_filter": 0, "in_list_view": 1, - "label": "Territory", + "label": "Country", "no_copy": 0, - "options": "Territory", + "options": "Country", "permlevel": 0, + "precision": "", "print_hide": 0, "read_only": 0, "report_hide": 0, @@ -33,18 +34,20 @@ ], "hide_heading": 0, "hide_toolbar": 0, - "idx": 1, "in_create": 0, "in_dialog": 0, "is_submittable": 0, "issingle": 0, "istable": 1, - "modified": "2015-01-01 14:29:58.724652", + "modified": "2015-09-17 06:49:51.810318", "modified_by": "Administrator", - "module": "Setup", - "name": "Applicable Territory", + "module": "Stock", + "name": "Price List Country", + "name_case": "", "owner": "Administrator", "permissions": [], "read_only": 0, - "read_only_onload": 0 + "read_only_onload": 0, + "sort_field": "modified", + "sort_order": "DESC" } \ No newline at end of file diff --git a/erpnext/stock/doctype/price_list_country/price_list_country.py b/erpnext/stock/doctype/price_list_country/price_list_country.py new file mode 100644 index 0000000000..db1a0607e6 --- /dev/null +++ b/erpnext/stock/doctype/price_list_country/price_list_country.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.model.document import Document + +class PriceListCountry(Document): + pass diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 5f85ef91a8..68274d1b42 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -49,7 +49,7 @@ class StockEntry(StockController): self.validate_finished_goods() self.validate_with_material_request() self.validate_batch() - + self.set_actual_qty() self.calculate_rate_and_amount() @@ -212,10 +212,10 @@ class StockEntry(StockController): if fg_qty_already_entered >= qty: frappe.throw(_("Stock Entries already created for Production Order ") + self.production_order + ":" + ", ".join(other_ste), DuplicateEntryForProductionOrderError) - + def set_actual_qty(self): allow_negative_stock = cint(frappe.db.get_value("Stock Settings", None, "allow_negative_stock")) - + for d in self.get('items'): previous_sle = get_previous_sle({ "item_code": d.item_code, @@ -232,7 +232,7 @@ class StockEntry(StockController): frappe.throw(_("""Row {0}: Qty not avalable in warehouse {1} on {2} {3}. Available Qty: {4}, Transfer Qty: {5}""").format(d.idx, d.s_warehouse, self.posting_date, self.posting_time, d.actual_qty, d.transfer_qty), NegativeStockError) - + def get_stock_and_rate(self): self.set_actual_qty() self.calculate_rate_and_amount() @@ -244,7 +244,7 @@ class StockEntry(StockController): self.validate_valuation_rate() self.set_total_incoming_outgoing_value() self.set_total_amount() - + def set_basic_rate(self, force=False): """get stock and incoming rate on posting date""" raw_material_cost = 0.0 @@ -269,9 +269,9 @@ class StockEntry(StockController): d.basic_amount = flt(flt(d.transfer_qty) * flt(d.basic_rate), d.precision("basic_amount")) if not d.t_warehouse: raw_material_cost += flt(d.basic_amount) - + self.set_basic_rate_for_finished_goods(raw_material_cost) - + def set_basic_rate_for_finished_goods(self, raw_material_cost): if self.purpose in ["Manufacture", "Repack"]: number_of_fg_items = len([t.t_warehouse for t in self.get("items") if t.t_warehouse]) @@ -279,11 +279,11 @@ class StockEntry(StockController): if d.bom_no or (d.t_warehouse and number_of_fg_items == 1): d.basic_rate = flt(raw_material_cost / flt(d.transfer_qty), d.precision("basic_rate")) d.basic_amount = flt(raw_material_cost, d.precision("basic_amount")) - + def distribute_additional_costs(self): if self.purpose == "Material Issue": self.additional_costs = [] - + self.total_additional_costs = sum([flt(t.amount) for t in self.get("additional_costs")]) total_basic_amount = sum([flt(t.basic_amount) for t in self.get("items") if t.t_warehouse]) @@ -292,11 +292,11 @@ class StockEntry(StockController): d.additional_cost = (flt(d.basic_amount) / total_basic_amount) * self.total_additional_costs else: d.additional_cost = 0 - + def update_valuation_rate(self): for d in self.get("items"): d.amount = flt(d.basic_amount + flt(d.additional_cost), d.precision("amount")) - d.valuation_rate = flt(flt(d.basic_rate) + flt(d.additional_cost) / flt(d.transfer_qty), + d.valuation_rate = flt(flt(d.basic_rate) + flt(d.additional_cost) / flt(d.transfer_qty), d.precision("valuation_rate")) def validate_valuation_rate(self): @@ -373,7 +373,7 @@ class StockEntry(StockController): def update_stock_ledger(self): sl_entries = [] - for d in self.get('items'): + for d in self.get('items'): if cstr(d.s_warehouse) and self.docstatus == 1: sl_entries.append(self.get_sl_entries(d, { "warehouse": cstr(d.s_warehouse), @@ -399,15 +399,15 @@ class StockEntry(StockController): })) self.make_sl_entries(sl_entries, self.amended_from and 'Yes' or 'No') - + def get_gl_entries(self, warehouse_account): expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation") - + gl_entries = super(StockEntry, self).get_gl_entries(warehouse_account) - + for d in self.get("items"): additional_cost = flt(d.additional_cost, d.precision("additional_cost")) - if additional_cost: + if additional_cost: gl_entries.append(self.get_gl_dict({ "account": expenses_included_in_valuation, "against": d.expense_account, @@ -415,7 +415,7 @@ class StockEntry(StockController): "remarks": self.get("remarks") or _("Accounting Entry for Stock"), "credit": additional_cost })) - + gl_entries.append(self.get_gl_dict({ "account": d.expense_account, "against": expenses_included_in_valuation, @@ -521,7 +521,7 @@ class StockEntry(StockController): def get_items(self): self.set('items', []) self.validate_production_order() - + if not self.posting_date or not self.posting_time: frappe.throw(_("Posting date and posting time is mandatory")) @@ -548,7 +548,7 @@ class StockEntry(StockController): for item in item_dict.values(): item["to_warehouse"] = self.pro_doc.wip_warehouse self.add_to_stock_entry_detail(item_dict) - + elif self.production_order and self.purpose == "Manufacture" and \ frappe.db.get_single_value("Manufacturing Settings", "backflush_raw_materials_based_on")== "Material Transferred for Manufacture": self.get_transfered_raw_materials() @@ -578,10 +578,14 @@ class StockEntry(StockController): to_warehouse = self.pro_doc.fg_warehouse else: item_code = frappe.db.get_value("BOM", self.bom_no, "item") - to_warehouse = "" + to_warehouse = self.to_warehouse item = frappe.db.get_value("Item", item_code, ["item_name", - "description", "stock_uom", "expense_account", "buying_cost_center", "name"], as_dict=1) + "description", "stock_uom", "expense_account", "buying_cost_center", "name", "default_warehouse"], as_dict=1) + + if not self.production_order and not to_warehouse: + # in case of BOM + to_warehouse = item.default_warehouse self.add_to_stock_entry_detail({ item.name: { @@ -600,57 +604,57 @@ class StockEntry(StockController): from erpnext.manufacturing.doctype.bom.bom import get_bom_items_as_dict # item dict = { item_code: {qty, description, stock_uom} } - item_dict = get_bom_items_as_dict(self.bom_no, self.company, qty=qty, + item_dict = get_bom_items_as_dict(self.bom_no, self.company, qty=qty, fetch_exploded = self.use_multi_level_bom) for item in item_dict.values(): item.from_warehouse = self.from_warehouse or item.default_warehouse return item_dict - + def get_transfered_raw_materials(self): transferred_materials = frappe.db.sql(""" - select - item_name, item_code, sum(qty) as qty, sed.t_warehouse as warehouse, - description, stock_uom, expense_account, cost_center - from `tabStock Entry` se,`tabStock Entry Detail` sed - where - se.name = sed.parent and se.docstatus=1 and se.purpose='Material Transfer for Manufacture' - and se.production_order= %s and ifnull(sed.t_warehouse, '') != '' + select + item_name, item_code, sum(qty) as qty, sed.t_warehouse as warehouse, + description, stock_uom, expense_account, cost_center + from `tabStock Entry` se,`tabStock Entry Detail` sed + where + se.name = sed.parent and se.docstatus=1 and se.purpose='Material Transfer for Manufacture' + and se.production_order= %s and ifnull(sed.t_warehouse, '') != '' group by sed.item_code, sed.t_warehouse """, self.production_order, as_dict=1) - + materials_already_backflushed = frappe.db.sql(""" - select - item_code, sed.s_warehouse as warehouse, sum(qty) as qty - from - `tabStock Entry` se, `tabStock Entry Detail` sed - where - se.name = sed.parent and se.docstatus=1 and se.purpose='Manufacture' - and se.production_order= %s and ifnull(sed.s_warehouse, '') != '' + select + item_code, sed.s_warehouse as warehouse, sum(qty) as qty + from + `tabStock Entry` se, `tabStock Entry Detail` sed + where + se.name = sed.parent and se.docstatus=1 and se.purpose='Manufacture' + and se.production_order= %s and ifnull(sed.s_warehouse, '') != '' group by sed.item_code, sed.s_warehouse """, self.production_order, as_dict=1) - + backflushed_materials= {} for d in materials_already_backflushed: backflushed_materials.setdefault(d.item_code,[]).append({d.warehouse: d.qty}) - + po_qty = frappe.db.sql("""select qty, produced_qty, material_transferred_for_manufacturing from `tabProduction Order` where name=%s""", self.production_order, as_dict=1)[0] manufacturing_qty = flt(po_qty.qty) produced_qty = flt(po_qty.produced_qty) trans_qty = flt(po_qty.material_transferred_for_manufacturing) - + for item in transferred_materials: qty= item.qty - + if manufacturing_qty > (produced_qty + flt(self.fg_completed_qty)): qty = (qty/trans_qty) * flt(self.fg_completed_qty) - + elif backflushed_materials.get(item.item_code): for d in backflushed_materials.get(item.item_code): if d.get(item.warehouse): qty-= d.get(item.warehouse) - + if qty > 0: self.add_to_stock_entry_detail({ item.item_code: { @@ -729,7 +733,7 @@ class StockEntry(StockController): se_child.s_warehouse = self.from_warehouse if se_child.t_warehouse==None: se_child.t_warehouse = self.to_warehouse - + # in stock uom se_child.transfer_qty = flt(item_dict[d]["qty"]) se_child.conversion_factor = 1.00 @@ -761,7 +765,7 @@ class StockEntry(StockController): def get_production_order_details(production_order): production_order = frappe.get_doc("Production Order", production_order) pending_qty_to_produce = flt(production_order.qty) - flt(production_order.produced_qty) - + return { "from_bom": 1, "bom_no": production_order.bom_no, @@ -771,7 +775,7 @@ def get_production_order_details(production_order): "fg_completed_qty": pending_qty_to_produce, "additional_costs": get_additional_costs(production_order, fg_qty=pending_qty_to_produce) } - + def get_additional_costs(production_order=None, bom_no=None, fg_qty=None): additional_costs = [] operating_cost_per_unit = get_operating_cost_per_unit(production_order, bom_no) @@ -780,33 +784,33 @@ def get_additional_costs(production_order=None, bom_no=None, fg_qty=None): "description": "Operating Cost as per Production Order / BOM", "amount": operating_cost_per_unit * flt(fg_qty) }) - + if production_order and production_order.additional_operating_cost: additional_operating_cost_per_unit = \ flt(production_order.additional_operating_cost) / flt(production_order.qty) - + additional_costs.append({ "description": "Additional Operating Cost", "amount": additional_operating_cost_per_unit * flt(fg_qty) }) - + return additional_costs - + def get_operating_cost_per_unit(production_order=None, bom_no=None): operating_cost_per_unit = 0 if production_order: if not bom_no: bom_no = production_order.bom_no - + for d in production_order.get("operations"): if flt(d.completed_qty): operating_cost_per_unit += flt(d.actual_operating_cost) / flt(d.completed_qty) else: operating_cost_per_unit += flt(d.planned_operating_cost) / flt(production_order.qty) - + # Get operating cost from BOM if not found in production_order. if not operating_cost_per_unit and bom_no: bom = frappe.db.get_value("BOM", bom_no, ["operating_cost", "quantity"], as_dict=1) operating_cost_per_unit = flt(bom.operating_cost) / flt(bom.quantity) - - return operating_cost_per_unit \ No newline at end of file + + return operating_cost_per_unit diff --git a/erpnext/templates/generators/item.html b/erpnext/templates/generators/item.html index 535edcdb73..af4a795c9f 100644 --- a/erpnext/templates/generators/item.html +++ b/erpnext/templates/generators/item.html @@ -27,22 +27,31 @@ {{ _("Item Code") }}: {{ name }}


- +

+
+
+
+ +
+ +
diff --git a/erpnext/templates/includes/address_row.html b/erpnext/templates/includes/address_row.html index f7eaa76ae9..fd287addf4 100644 --- a/erpnext/templates/includes/address_row.html +++ b/erpnext/templates/includes/address_row.html @@ -1,19 +1,8 @@
- - diff --git a/erpnext/templates/includes/cart.css b/erpnext/templates/includes/cart.css new file mode 100644 index 0000000000..7a18530286 --- /dev/null +++ b/erpnext/templates/includes/cart.css @@ -0,0 +1,25 @@ +.cart-content { + min-height: 400px; + margin-top: 60px; +} + +.cart-header, .cart-footer { + margin-bottom: 60px; +} + +.cart-item-header { + padding-bottom: 10px; + margin-bottom: 10px; + border-bottom: 1px solid #d1d8dd; +} + +.tax-grand-total-row { + font-size: 14px; + margin-top: 30px; + font-weight: bold; +} + +.cart-addresses { + margin-top: 80px; + margin-bottom: 60px; +} diff --git a/erpnext/templates/includes/cart.js b/erpnext/templates/includes/cart.js index d5956f9912..cc321579bf 100644 --- a/erpnext/templates/includes/cart.js +++ b/erpnext/templates/includes/cart.js @@ -13,126 +13,64 @@ $.extend(shopping_cart, { }, bind_events: function() { - // bind update button - $(document).on("click", ".item-update-cart button", function() { - var item_code = $(this).attr("data-item-code"); - shopping_cart.update_cart({ - item_code: item_code, - qty: $('input[data-item-code="'+item_code+'"]').val(), - with_doc: 1, - btn: this, - callback: function(r) { - if(!r.exc) { - shopping_cart.render(r.message); - var $button = $('button[data-item-code="'+item_code+'"]').addClass("btn-success"); - setTimeout(function() { $button.removeClass("btn-success"); }, 1000); + shopping_cart.bind_address_select(); + shopping_cart.bind_place_order(); + shopping_cart.bind_change_qty(); + + }, + + bind_address_select: function() { + $(".cart-addresses").find('input[data-address-name]').on("click", function() { + if($(this).prop("checked")) { + var me = this; + + return frappe.call({ + type: "POST", + method: "erpnext.shopping_cart.cart.update_cart_address", + args: { + address_fieldname: $(this).attr("data-fieldname"), + address_name: $(this).attr("data-address-name") + }, + callback: function(r) { + if(!r.exc) { + $(".cart-tax-items").html(r.message.taxes); + } } - }, - }); + }); + } else { + return false; + } }); - $("#cart-add-shipping-address").on("click", function() { - window.location.href = "addresses"; - }); - - $("#cart-add-billing-address").on("click", function() { - window.location.href = "address"; - }); + }, + bind_place_order: function() { $(".btn-place-order").on("click", function() { shopping_cart.place_order(this); }); }, - render: function(out) { - var doc = out.doc; - 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(); - - var no_items = $.map(doc.items || [], - function(d) { return d.item_code || null;}).length===0; - if(no_items) { - shopping_cart.show_error("Cart Empty", frappe._("Go ahead and add something to your cart.")); - $("#cart-addresses").toggle(false); - return; - } - - var shipping_rule_added = false; - var taxes_exist = false; - var shipping_rule_labels = $.map(out.shipping_rules || [], function(rule) { return rule[1]; }); - - $.each(doc.items || [], function(i, d) { - shopping_cart.render_item_row($cart_items, d); + bind_change_qty: function() { + // bind update button + $(".cart-items").on("change", ".cart-qty", function() { + var item_code = $(this).attr("data-item-code"); + frappe.freeze(); + shopping_cart.update_cart({ + item_code: item_code, + qty: $(this).val(), + with_items: 1, + btn: this, + callback: function(r) { + frappe.unfreeze(); + if(!r.exc) { + $(".cart-items").html(r.message.items); + $(".cart-tax-items").html(r.message.taxes); + } + $(".tax-grand-total").temp_highlight(); + }, + }); }); - $.each(doc.taxes || [], function(i, d) { - if(out.shipping_rules && out.shipping_rules.length && - shipping_rule_labels.indexOf(d.description)!==-1) { - shipping_rule_added = true; - shopping_cart.render_tax_row($cart_taxes, d, out.shipping_rules); - } else { - shopping_cart.render_tax_row($cart_taxes, d); - } - - taxes_exist = true; - }); - - if(out.shipping_rules && out.shipping_rules.length && !shipping_rule_added) { - shopping_cart.render_tax_row($cart_taxes, {description: "", formatted_tax_amount: ""}, - out.shipping_rules); - taxes_exist = true; - } - - if(taxes_exist) - $('
').appendTo($cart_taxes); - - shopping_cart.render_tax_row($cart_totals, { - description: "Total", - formatted_tax_amount: "" + doc.formatted_grand_total_export + "" - }); - - if(!(addresses && addresses.length)) { - $cart_shipping_address.html('
'+frappe._("Hey! Go ahead and add an address")+'
'); - } else { - shopping_cart.render_address($cart_shipping_address, addresses, doc.shipping_address_name); - shopping_cart.render_address($cart_billing_address, addresses, doc.customer_address); - } - }, - - render_item_row: function($cart_items, doc) { - doc.image_html = doc.website_image ? - '
': ""; - - if(doc.description === doc.item_name) doc.description = ""; - - $(repl('
\ -
\ -
\ -
%(image_html)s
\ -
\ -

%(item_name)s

\ -

%(description)s

\ -
\ -
\ -
\ -
\ -
\ - \ -
\ - \ -
\ -
\ -

at %(formatted_rate)s

\ - = %(formatted_amount)s\ -
\ -

', doc)).appendTo($cart_items); }, render_tax_row: function($cart_taxes, doc, shipping_rules) { @@ -182,72 +120,6 @@ $.extend(shopping_cart, { }); }, - render_address: function($address_wrapper, addresses, address_name) { - $.each(addresses, function(i, address) { - $(repl('
\ -
\ -
\ -
%(name)s
\ -
\ -
\ -
\ -
\ -
%(display)s
\ -
\ -
', address)) - .css({"margin": "10px auto"}) - .appendTo($address_wrapper); - }); - - $address_wrapper.find(".panel-heading") - .find(".address-title") - .css({"cursor": "pointer"}) - .on("click", function() { - $address_wrapper.find('.panel-collapse[data-address-name="' - +$(this).attr("data-address-name")+'"]').collapse("toggle"); - }); - - $address_wrapper.find('input[type="checkbox"]').on("click", function() { - if($(this).prop("checked")) { - var me = this; - $address_wrapper.find('input[type="checkbox"]').each(function(i, chk) { - if($(chk).attr("data-address-name")!=$(me).attr("data-address-name")) { - $(chk).prop("checked", false); - } - }); - - return frappe.call({ - type: "POST", - method: "erpnext.shopping_cart.cart.update_cart_address", - args: { - address_fieldname: $address_wrapper.attr("data-fieldname"), - address_name: $(this).attr("data-address-name") - }, - callback: function(r) { - if(!r.exc) { - shopping_cart.render(r.message); - } - } - }); - } else { - return false; - } - }); - - $address_wrapper.find('input[type="checkbox"][data-address-name="'+ address_name +'"]') - .prop("checked", true); - - $address_wrapper.find(".panel-collapse").collapse({ - parent: $address_wrapper, - toggle: false - }); - - $address_wrapper.find('.panel-collapse[data-address-name="'+ address_name +'"]') - .collapse("show"); - }, - place_order: function(btn) { return frappe.call({ type: "POST", @@ -274,24 +146,4 @@ $.extend(shopping_cart, { $(document).ready(function() { shopping_cart.bind_events(); - return frappe.call({ - type: "POST", - method: "erpnext.shopping_cart.cart.get_cart_quotation", - callback: function(r) { - $("#cart-container").removeClass("hide"); - $(".progress").remove(); - if(r.exc) { - if(r.exc.indexOf("WebsitePriceListMissingError")!==-1) { - shopping_cart.show_error("Configuration Error", frappe._("Price List not configured.")); - } else if(r["403"]) { - shopping_cart.show_error("Not Allowed", frappe._("You need to be logged in to view your cart.")); - } else { - shopping_cart.show_error("Error", frappe._("Something went wrong.")); - } - } else { - shopping_cart.set_cart_count(); - shopping_cart.render(r.message); - } - } - }); }); diff --git a/erpnext/templates/includes/cart/cart_address.html b/erpnext/templates/includes/cart/cart_address.html new file mode 100644 index 0000000000..44964da47c --- /dev/null +++ b/erpnext/templates/includes/cart/cart_address.html @@ -0,0 +1,24 @@ +{% from "erpnext/templates/includes/cart/cart_macros.html" + import show_address %} +
+
+

{{ _("Shipping Address") }}

+
+ {% for address in addresses %} + {{ show_address(address, doc, "shipping_address_name") }} + {% endfor %} +
+ + {{ _("Manage Addresses") }} +
+
+

Billing Address

+
+ {% for address in addresses %} + {{ show_address(address, doc, "customer_address") }} + {% endfor %} +
+
+
diff --git a/erpnext/templates/includes/cart/cart_items.html b/erpnext/templates/includes/cart/cart_items.html new file mode 100644 index 0000000000..f7efa78709 --- /dev/null +++ b/erpnext/templates/includes/cart/cart_items.html @@ -0,0 +1,23 @@ +{% from "erpnext/templates/includes/order/order_macros.html" import item_name_and_description %} + +{% for d in doc.items %} +
+
+
+ {{ item_name_and_description(d) }} +
+
+ + +

+ {{ _("Rate") + ': ' + d.get_formatted("rate") }} +

+
+
+ {{ d.get_formatted("amount") }} +
+
+
+{% endfor %} diff --git a/erpnext/templates/includes/cart/cart_macros.html b/erpnext/templates/includes/cart/cart_macros.html new file mode 100644 index 0000000000..250b487920 --- /dev/null +++ b/erpnext/templates/includes/cart/cart_macros.html @@ -0,0 +1,21 @@ +{% macro show_address(address, doc, fieldname) %} +{% set selected=address.name==doc.get(fieldname) %} +
+
+
+
+ {{ address.name }}
+
+
+
+
+
+
{{ address.display }}
+
+
+{% endmacro %} diff --git a/erpnext/templates/includes/issue_row.html b/erpnext/templates/includes/issue_row.html index 16a8f7b7b7..2935a24ea4 100644 --- a/erpnext/templates/includes/issue_row.html +++ b/erpnext/templates/includes/issue_row.html @@ -1,21 +1,15 @@
+
-
- - {{ doc.subject }} - +
+ + {{ doc.name }} + + {{ doc.subject }}
-
- - {{ doc.status }} -
- -
- {{ frappe.format_date(doc.creation) }} +
+ {{ frappe.format_date(doc.modified) }}
+
diff --git a/erpnext/templates/includes/macros.html b/erpnext/templates/includes/macros.html index 6748a5ebd2..81a10c2779 100644 --- a/erpnext/templates/includes/macros.html +++ b/erpnext/templates/includes/macros.html @@ -6,11 +6,12 @@ {% endmacro %} {% macro product_image(website_image, css_class="") %} -
- {% if website_image -%} - - {%- else -%} - - {%- endif %} -
+
+ {% if website_image -%} + + {%- else -%} + + {%- endif %} +
{% endmacro %} + diff --git a/erpnext/templates/includes/order/order.css b/erpnext/templates/includes/order/order.css new file mode 100644 index 0000000000..54c8d0ffe2 --- /dev/null +++ b/erpnext/templates/includes/order/order.css @@ -0,0 +1,25 @@ +.order-container { + margin: 50px 0px; +} + +.order-items { + margin: 20px 0px; +} + +.order-item-table { + margin: 0px -15px; +} + +.order-item-header { + border-bottom: 1px solid #d1d8dd; +} + +.order-image-col { + padding-right: 0px; +} + +.order-image { + max-width: 55px; + max-height: 55px; + margin-top: -5px; +} diff --git a/erpnext/templates/includes/order/order_macros.html b/erpnext/templates/includes/order/order_macros.html new file mode 100644 index 0000000000..af974aad2f --- /dev/null +++ b/erpnext/templates/includes/order/order_macros.html @@ -0,0 +1,15 @@ +{% from "erpnext/templates/includes/macros.html" import product_image_square %} + +{% macro item_name_and_description(d) %} +
+
+
+ {{ product_image_square(d.image) }} +
+
+
+ {{ d.item_code }} +

{{ d.description }}

+
+
+{% endmacro %} diff --git a/erpnext/templates/includes/order/order_taxes.html b/erpnext/templates/includes/order/order_taxes.html new file mode 100644 index 0000000000..8c8e88621b --- /dev/null +++ b/erpnext/templates/includes/order/order_taxes.html @@ -0,0 +1,22 @@ +{% if doc.taxes %} +
+
{{ _("Net Total") }}
+
+ {{ doc.get_formatted("net_total") }}
+
+{% endif %} +{% for d in doc.taxes %} +
+
{{ d.description }}
+
+ {{ d.get_formatted("base_tax_amount") }}
+
+{% endfor %} +
+
{{ _("Grand Total") }}
+
+ + {{ doc.get_formatted("grand_total") }} + +
+
diff --git a/erpnext/templates/includes/product_page.js b/erpnext/templates/includes/product_page.js index 4f4f11c9f3..98331a4f20 100644 --- a/erpnext/templates/includes/product_page.js +++ b/erpnext/templates/includes/product_page.js @@ -1,10 +1,10 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -$(document).ready(function() { +frappe.ready(function() { var item_code = $('[itemscope] [itemprop="productID"]').text().trim(); var qty = 0; - + frappe.call({ type: "POST", method: "erpnext.shopping_cart.product.get_product_info", @@ -12,10 +12,12 @@ $(document).ready(function() { item_code: "{{ name }}" }, callback: function(r) { + console.log(r.message); + $(".item-cart").toggleClass("hide", !!!r.message.price); if(r.message && r.message.price) { $(".item-price") .html(r.message.price.formatted_price + " per " + r.message.uom); - + if(r.message.stock==0) { $(".item-stock").html("
Not in stock
"); } @@ -23,18 +25,17 @@ $(document).ready(function() { $(".item-stock").html("
\ Available (in stock)
"); } - - $(".item-price-info").toggle(true); - + if(r.message.qty) { qty = r.message.qty; - toggle_update_cart(qty); - $("#item-update-cart input").val(qty); + toggle_update_cart(r.message.qty); + } else { + toggle_update_cart(0); } } } }) - + $("#item-add-to-cart button").on("click", function() { shopping_cart.update_cart({ item_code: item_code, @@ -45,10 +46,10 @@ $(document).ready(function() { qty = 1; } }, - btn: this, + btn: this, }); }); - + $("#item-update-cart button").on("click", function() { shopping_cart.update_cart({ item_code: item_code, @@ -63,7 +64,7 @@ $(document).ready(function() { }, }); }); - + if(localStorage && localStorage.getItem("pending_add_to_cart") && full_name) { localStorage.removeItem("pending_add_to_cart"); $("#item-add-to-cart button").trigger("click"); @@ -75,4 +76,4 @@ var toggle_update_cart = function(qty) { $("#item-update-cart") .toggle(qty ? true : false) .find("input").val(qty); -} \ No newline at end of file +} diff --git a/erpnext/templates/includes/transaction_row.html b/erpnext/templates/includes/transaction_row.html index a677fa5ad3..9b9fd52350 100644 --- a/erpnext/templates/includes/transaction_row.html +++ b/erpnext/templates/includes/transaction_row.html @@ -1,29 +1,22 @@ -{% set doc = frappe.get_doc(doc) %}
-
+
-
{{ doc.customer or doc.supplier }}
+
+
{{ doc.name }}
+
{{ doc.items_preview }}
+
- {%- if doc.status_percent > 0 -%} - {%- if doc.status_percent % 100 == 0 -%} - {{ doc.status_display }} - {%- else -%} - {{ doc.status_display }} - {%- endif -%} - {%- elif doc.status -%} - {{ doc.status }} - {%- endif -%} + + {{ doc.indicator_title or doc.status or "Submitted" }} +
{{ doc.get_formatted("grand_total") }}
-
- {{ doc.name }} -
{{ frappe.utils.pretty_date(doc.creation) }}
diff --git a/erpnext/templates/pages/cart.html b/erpnext/templates/pages/cart.html index e4e4a6a6c1..a97e65876c 100644 --- a/erpnext/templates/pages/cart.html +++ b/erpnext/templates/pages/cart.html @@ -3,53 +3,63 @@ {% block header %}

{{ _("My Cart") }}

{% endblock %} {% block script %}{% include "templates/includes/cart.js" %}{% endblock %} +{% block style %}{% include "templates/includes/cart.css" %}{% endblock %} + + +{% block header_actions %} +{% if doc.items %} + +{% endif %} +{% endblock %} {% block content %} + +{% from "templates/includes/macros.html" import item_name_and_description %} +
-
{{ _("Loading") }}...
-
-

-
- -
-
-
-
-

{{ _("Item Details") }}

-
-
-

{{ _("Qty, Amount") }}

-

-
-
-
-
-
-
-
-
-
-
-

{{ _("Shipping Address") }}

-
- -
-
-

Billing Address

-
- -
-
-
-
-

-
+
+ +
+
+
+ Items +
+
+ Qty +
+
+ Amount +
+
+ {% if doc.items %} +
+ {% include "templates/includes/cart/cart_items.html" %} +
+ {% else %} +

{{ _("Cart is Empty") }}

+ {% endif %} +
+ {% if doc.items %} + +
+
+
+ {% include "templates/includes/order/order_taxes.html" %} +
+
+
+
+
+ {% include "templates/includes/cart/cart_address.html" %} +
+ + {% endif %} +
diff --git a/erpnext/templates/pages/cart.py b/erpnext/templates/pages/cart.py index fe50f0fe47..c57d826130 100644 --- a/erpnext/templates/pages/cart.py +++ b/erpnext/templates/pages/cart.py @@ -2,7 +2,11 @@ # License: GNU General Public License v3. See license.txt from __future__ import unicode_literals -from __future__ import unicode_literals - no_cache = 1 -no_sitemap = 1 \ No newline at end of file +no_sitemap = 1 + +import frappe +from erpnext.shopping_cart.cart import get_cart_quotation + +def get_context(context): + context.update(get_cart_quotation()) diff --git a/erpnext/templates/pages/order.html b/erpnext/templates/pages/order.html new file mode 100644 index 0000000000..73763921f8 --- /dev/null +++ b/erpnext/templates/pages/order.html @@ -0,0 +1,72 @@ +{% block header %} +

{{ doc.name }}

+ +{% endblock %} + +{% block style %}{% include "templates/includes/order/order.css" %}{% endblock %} + +{% block content %} + +{% from "erpnext/templates/includes/order/order_macros.html" import item_name_and_description %} + +
+
+ + {{ doc.indicator_title or doc.status or "Submitted" }} + +
+
+ {{ doc.get_formatted("transaction_date") }} +
+
+ +{% if doc._header %} +{{ doc._header }} +{% endif %} + +
+ + +
+
+
+ {{ _("Item") }} +
+
+ {{ _("Quantity") }} +
+
+ {{ _("Amount") }} +
+
+ {% for d in doc.items %} +
+
+ {{ item_name_and_description(d) }} +
+
+ {{ d.qty }} + {% if d.delivered_qty != None %} +

{{ + _("Delivered: {0}").format(d.delivered_qty) }}

+ {% endif %} +
+
+ {{ d.get_formatted("amount") }} +

{{ + _("Rate: {0}").format(d.get_formatted("rate")) }}

+
+
+ {% endfor %} +
+ + +
+
+
+ {% include "erpnext/templates/includes/order/order_taxes.html" %} +
+
+
+ +{% endblock %} diff --git a/erpnext/templates/pages/order.py b/erpnext/templates/pages/order.py new file mode 100644 index 0000000000..36444d1f30 --- /dev/null +++ b/erpnext/templates/pages/order.py @@ -0,0 +1,18 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe + +from frappe import _ + +def get_context(context): + context.no_cache = 1 + context.doc = frappe.get_doc(frappe.form_dict.doctype, frappe.form_dict.name) + if hasattr(context.doc, "set_indicator"): + context.doc.set_indicator() + + context.parents = frappe.form_dict.parents + + if not context.doc.has_permission("read"): + frappe.throw(_("Not Permitted"), frappe.PermissionError) diff --git a/erpnext/templates/pages/user.py b/erpnext/templates/pages/user.py index c944289661..5ab5545d87 100644 --- a/erpnext/templates/pages/user.py +++ b/erpnext/templates/pages/user.py @@ -5,36 +5,31 @@ from __future__ import unicode_literals import frappe from frappe import _ from frappe.utils import cstr -from erpnext.shopping_cart.cart import get_lead_or_customer +from erpnext.shopping_cart.cart import get_customer no_cache = 1 no_sitemap = 1 def get_context(context): - party = get_lead_or_customer() - if party.doctype == "Lead": - mobile_no = party.mobile_no - phone = party.phone - else: - mobile_no, phone = frappe.db.get_value("Contact", {"email_id": frappe.session.user, - "customer": party.name}, ["mobile_no", "phone"]) - + party = get_customer() + mobile_no, phone = frappe.db.get_value("Contact", {"email_id": frappe.session.user, + "customer": party.name}, ["mobile_no", "phone"]) + return { "company_name": cstr(party.customer_name if party.doctype == "Customer" else party.company_name), "mobile_no": cstr(mobile_no), "phone": cstr(phone) } - + @frappe.whitelist() def update_user(fullname, password=None, company_name=None, mobile_no=None, phone=None): from erpnext.shopping_cart.cart import update_party update_party(fullname, company_name, mobile_no, phone) - + if not fullname: return _("Name is required") - + frappe.db.set_value("User", frappe.session.user, "first_name", fullname) frappe.local.cookie_manager.set_cookie("full_name", fullname) - + return _("Updated") - \ No newline at end of file diff --git a/erpnext/utilities/doctype/address/address.py b/erpnext/utilities/doctype/address/address.py index b3f67175e5..8ae7c4da6f 100644 --- a/erpnext/utilities/doctype/address/address.py +++ b/erpnext/utilities/doctype/address/address.py @@ -70,6 +70,9 @@ class Address(Document): (is_address_type, fieldname, "%s", "%s"), (self.get(fieldname), self.name)) break + def get_display(self): + return get_address_display(self.as_dict()) + @frappe.whitelist() def get_address_display(address_dict): if not address_dict: