diff --git a/erpnext/accounts/doctype/coupon_code/__init__.py b/erpnext/accounts/doctype/coupon_code/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/accounts/doctype/coupon_code/coupon_code.js b/erpnext/accounts/doctype/coupon_code/coupon_code.js new file mode 100644 index 0000000000..0bf097f8d5 --- /dev/null +++ b/erpnext/accounts/doctype/coupon_code/coupon_code.js @@ -0,0 +1,35 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Coupon Code', { + coupon_name:function(frm){ + if (frm.doc.__islocal===1) { + frm.trigger("make_coupon_code"); + } + }, + coupon_type:function(frm){ + if (frm.doc.__islocal===1) { + frm.trigger("make_coupon_code"); + } + }, + make_coupon_code: function(frm) { + var coupon_name=frm.doc.coupon_name; + var coupon_code; + if (frm.doc.coupon_type=='Gift Card') { + coupon_code=Math.random().toString(12).substring(2, 12).toUpperCase(); + } + else if(frm.doc.coupon_type=='Promotional'){ + coupon_name=coupon_name.replace(/\s/g,''); + coupon_code=coupon_name.toUpperCase().slice(0,8); + } + frm.doc.coupon_code=coupon_code; + frm.refresh_field('coupon_code'); + }, + refresh: function(frm) { + if (frm.doc.pricing_rule) { + frm.add_custom_button(__("Add/Edit Coupon Conditions"), function(){ + frappe.set_route("Form", "Pricing Rule", frm.doc.pricing_rule); + }); + } + } +}); diff --git a/erpnext/accounts/doctype/coupon_code/coupon_code.json b/erpnext/accounts/doctype/coupon_code/coupon_code.json new file mode 100644 index 0000000000..ba5df7840c --- /dev/null +++ b/erpnext/accounts/doctype/coupon_code/coupon_code.json @@ -0,0 +1,182 @@ +{ + "allow_import": 1, + "autoname": "field:coupon_name", + "creation": "2018-01-22 14:34:39.701832", + "doctype": "DocType", + "document_type": "Other", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "coupon_name", + "coupon_type", + "customer", + "column_break_4", + "coupon_code", + "price_list", + "pricing_rule", + "uses", + "valid_from", + "valid_upto", + "maximum_use", + "used", + "column_break_11", + "description", + "amended_from" + ], + "fields": [ + { + "fieldname": "coupon_name", + "fieldtype": "Data", + "label": "Coupon Name", + "reqd": 1, + "unique": 1 + }, + { + "fieldname": "coupon_type", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Coupon Type", + "options": "Promotional\nGift Card", + "reqd": 1 + }, + { + "depends_on": "eval: doc.coupon_type == \"Gift Card\"", + "fieldname": "customer", + "fieldtype": "Link", + "label": "Customer", + "options": "Customer" + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "description": "To be used to get discount", + "fieldname": "coupon_code", + "fieldtype": "Data", + "label": "Coupon Code", + "no_copy": 1, + "set_only_once": 1, + "unique": 1 + }, + { + "fieldname": "price_list", + "fieldtype": "Link", + "label": "Price List", + "options": "Price List" + }, + { + "fieldname": "pricing_rule", + "fieldtype": "Link", + "label": "Pricing Rule", + "options": "Pricing Rule" + }, + { + "fieldname": "uses", + "fieldtype": "Section Break", + "label": "Uses" + }, + { + "fieldname": "valid_from", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Valid From" + }, + { + "fieldname": "valid_upto", + "fieldtype": "Date", + "label": "Valid Upto" + }, + { + "depends_on": "eval: doc.coupon_type == \"Promotional\"", + "fieldname": "maximum_use", + "fieldtype": "Int", + "label": "Maximum Use" + }, + { + "default": "0", + "fieldname": "used", + "fieldtype": "Int", + "label": "Used", + "no_copy": 1, + "read_only": 1 + }, + { + "fieldname": "column_break_11", + "fieldtype": "Column Break" + }, + { + "fieldname": "description", + "fieldtype": "Text Editor", + "label": "Coupon Description" + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Coupon Code", + "print_hide": 1, + "read_only": 1 + } + ], + "modified": "2019-05-28 21:06:29.395619", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Coupon Code", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts User", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Sales Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Website Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "coupon_name", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/coupon_code/coupon_code.py b/erpnext/accounts/doctype/coupon_code/coupon_code.py new file mode 100644 index 0000000000..7829c9320d --- /dev/null +++ b/erpnext/accounts/doctype/coupon_code/coupon_code.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe import _ +from frappe.model.document import Document +from frappe.utils import (strip) +class CouponCode(Document): + def autoname(self): + self.coupon_name = strip(self.coupon_name) + self.name = self.coupon_name + + if not self.coupon_code: + if self.coupon_type == "Promotional": + self.coupon_code =''.join([i for i in self.coupon_name if not i.isdigit()])[0:8].upper() + elif self.coupon_type == "Gift Card": + self.coupon_code = frappe.generate_hash()[:10].upper() + + def validate(self): + if self.coupon_type == "Gift Card": + self.maximum_use = 1 + if not self.customer: + frappe.throw(_("Please select the customer.")) diff --git a/erpnext/accounts/doctype/coupon_code/test_coupon_code.js b/erpnext/accounts/doctype/coupon_code/test_coupon_code.js new file mode 100644 index 0000000000..460fedc97f --- /dev/null +++ b/erpnext/accounts/doctype/coupon_code/test_coupon_code.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Coupon Code", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Coupon Code + () => frappe.tests.make('Coupon Code', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/accounts/doctype/coupon_code/test_coupon_code.py b/erpnext/accounts/doctype/coupon_code/test_coupon_code.py new file mode 100644 index 0000000000..92744e49de --- /dev/null +++ b/erpnext/accounts/doctype/coupon_code/test_coupon_code.py @@ -0,0 +1,133 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest +from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order +from erpnext.stock.get_item_details import get_item_details +from frappe.test_runner import make_test_objects + +def test_create_test_data(): + frappe.set_user("Administrator") + # create test item + if not frappe.db.exists("Item","_Test Tesla Car"): + item = frappe.get_doc({ + "description": "_Test Tesla Car", + "doctype": "Item", + "has_batch_no": 0, + "has_serial_no": 0, + "inspection_required": 0, + "is_stock_item": 1, + "opening_stock":100, + "is_sub_contracted_item": 0, + "item_code": "_Test Tesla Car", + "item_group": "_Test Item Group", + "item_name": "_Test Tesla Car", + "apply_warehouse_wise_reorder_level": 0, + "warehouse":"_Test Warehouse - _TC", + "gst_hsn_code": "999800", + "valuation_rate": 5000, + "standard_rate":5000, + "item_defaults": [{ + "company": "_Test Company", + "default_warehouse": "_Test Warehouse - _TC", + "default_price_list":"_Test Price List", + "expense_account": "_Test Account Cost for Goods Sold - _TC", + "buying_cost_center": "_Test Cost Center - _TC", + "selling_cost_center": "_Test Cost Center - _TC", + "income_account": "Sales - _TC" + }], + "show_in_website": 1, + "route":"-test-tesla-car", + "website_warehouse": "_Test Warehouse - _TC" + }) + item.insert() + # create test item price + item_price = frappe.get_list('Item Price', filters={'item_code': '_Test Tesla Car', 'price_list': '_Test Price List'}, fields=['name']) + if len(item_price)==0: + item_price = frappe.get_doc({ + "doctype": "Item Price", + "item_code": "_Test Tesla Car", + "price_list": "_Test Price List", + "price_list_rate": 5000 + }) + item_price.insert() + # create test item pricing rule + if not frappe.db.exists("Pricing Rule","_Test Pricing Rule for _Test Item"): + item_pricing_rule = frappe.get_doc({ + "doctype": "Pricing Rule", + "title": "_Test Pricing Rule for _Test Item", + "apply_on": "Item Code", + "items": [{ + "item_code": "_Test Tesla Car" + }], + "warehouse":"_Test Warehouse - _TC", + "coupon_code_based":1, + "selling": 1, + "rate_or_discount": "Discount Percentage", + "discount_percentage": 30, + "company": "_Test Company", + "currency":"INR", + "for_price_list":"_Test Price List" + }) + item_pricing_rule.insert() + # create test item sales partner + if not frappe.db.exists("Sales Partner","_Test Coupon Partner"): + sales_partner = frappe.get_doc({ + "doctype": "Sales Partner", + "partner_name":"_Test Coupon Partner", + "commission_rate":2, + "referral_code": "COPART" + }) + sales_partner.insert() + # create test item coupon code + if not frappe.db.exists("Coupon Code","SAVE30"): + coupon_code = frappe.get_doc({ + "doctype": "Coupon Code", + "coupon_name":"SAVE30", + "coupon_code":"SAVE30", + "price_list": "_Test Price List", + "pricing_rule": "_Test Pricing Rule for _Test Item", + "valid_from": "2014-01-01", + "maximum_use":1, + "used":0 + }) + coupon_code.insert() + + +class TestCouponCode(unittest.TestCase): + def setUp(self): + test_create_test_data() + + def tearDown(self): + frappe.set_user("Administrator") + + def test_1_check_coupon_code_used_before_so(self): + coupon_code = frappe.get_doc("Coupon Code", frappe.db.get_value("Coupon Code", {"coupon_name":"SAVE30"})) + # reset used coupon code count + coupon_code.used=0 + coupon_code.save() + # check no coupon code is used before sales order is made + self.assertEqual(coupon_code.get("used"),0) + + def test_2_sales_order_with_coupon_code(self): + so = make_sales_order(customer="_Test Customer",selling_price_list="_Test Price List",item_code="_Test Tesla Car", rate=5000,qty=1, do_not_submit=True) + so = frappe.get_doc('Sales Order', so.name) + # check item price before coupon code is applied + self.assertEqual(so.items[0].rate, 5000) + so.coupon_code='SAVE30' + so.sales_partner='_Test Coupon Partner' + so.save() + # check item price after coupon code is applied + self.assertEqual(so.items[0].rate, 3500) + so.submit() + + def test_3_check_coupon_code_used_after_so(self): + doc = frappe.get_doc("Coupon Code", frappe.db.get_value("Coupon Code", {"coupon_name":"SAVE30"})) + # check no coupon code is used before sales order is made + self.assertEqual(doc.get("used"),1) + + + diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json index 02102120c8..faefc4d7ab 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json @@ -420,6 +420,40 @@ "translatable": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "", + "fetch_if_empty": 0, + "fieldname": "coupon_code_based", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Coupon Code Based", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_in_quick_entry": 0, diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index bbabac4302..ef26c2e7bf 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -531,4 +531,32 @@ def validate_pricing_rule_for_different_cond(doc): for d in doc.get("items"): validate_pricing_rule_on_items(doc, d, True) - return doc \ No newline at end of file + return doc + +def validate_coupon_code(coupon_name): + from frappe.utils import today,getdate + coupon=frappe.get_doc("Coupon Code",coupon_name) + if coupon.valid_from: + if coupon.valid_from > getdate(today()) : + frappe.throw(_("Sorry,coupon code validity has not started")) + elif coupon.valid_upto: + if coupon.valid_upto < getdate(today()) : + frappe.throw(_("Sorry,coupon code validity has expired")) + elif coupon.used>=coupon.maximum_use: + frappe.throw(_("Sorry,coupon code are exhausted")) + else: + return + +def update_coupon_code_count(coupon_name,transaction_type): + coupon=frappe.get_doc("Coupon Code",coupon_name) + if coupon: + if transaction_type=='used': + if coupon.used0: + coupon.used=coupon.used-1 + coupon.save(ignore_permissions=True) \ No newline at end of file diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index a9b19eddd7..5a2f3bc9b8 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1233,7 +1233,8 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ "is_return": cint(me.frm.doc.is_return), "update_stock": in_list(['Sales Invoice', 'Purchase Invoice'], me.frm.doc.doctype) ? cint(me.frm.doc.update_stock) : 0, "conversion_factor": me.frm.doc.conversion_factor, - "pos_profile": me.frm.doc.doctype == 'Sales Invoice' ? me.frm.doc.pos_profile : '' + "pos_profile": me.frm.doc.doctype == 'Sales Invoice' ? me.frm.doc.pos_profile : '', + "coupon_code": me.frm.doc.coupon_code }; }, @@ -1742,6 +1743,15 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ frappe.model.set_value(me.frm.doctype + " Item", item.name, "warehouse", me.frm.doc.set_warehouse); }); } + }, + coupon_code: function() { + var me = this; + frappe.run_serially([ + () => this.frm.doc.ignore_pricing_rule=1, + () => me.ignore_pricing_rule(), + () => this.frm.doc.ignore_pricing_rule=0, + () => me.apply_pricing_rule() + ]); } }); diff --git a/erpnext/public/js/shopping_cart.js b/erpnext/public/js/shopping_cart.js index 5a0526814f..5705a37375 100644 --- a/erpnext/public/js/shopping_cart.js +++ b/erpnext/public/js/shopping_cart.js @@ -5,6 +5,19 @@ frappe.provide("erpnext.shopping_cart"); var shopping_cart = erpnext.shopping_cart; +var getParams = function (url) { + var params = []; + var parser = document.createElement('a'); + parser.href = url; + var query = parser.search.substring(1); + var vars = query.split('&'); + for (var i = 0; i < vars.length; i++) { + var pair = vars[i].split('='); + params[pair[0]] = decodeURIComponent(pair[1]); + } + return params; +}; + frappe.ready(function() { var full_name = frappe.session && frappe.session.user_fullname; // update user @@ -12,7 +25,28 @@ frappe.ready(function() { $('.navbar li[data-label="User"] a') .html(' ' + full_name); } + // set coupon code and sales partner code + var referral_coupon_code=getParams(window.location.href)['cc']; + var referral_sales_partner=getParams(window.location.href)['sp']; + var d = new Date(); + // expires within 30 minutes + d.setTime(d.getTime() + (0.02 * 24 * 60 * 60 * 1000)); + var expires = "expires="+d.toUTCString(); + if (referral_coupon_code) { + document.cookie = "referral_coupon_code=" + referral_coupon_code + ";" + expires + ";path=/"; + } + if (referral_sales_partner) { + document.cookie = "referral_sales_partner=" + referral_sales_partner + ";" + expires + ";path=/"; + } + referral_coupon_code=frappe.get_cookie("referral_coupon_code"); + referral_sales_partner=frappe.get_cookie("referral_sales_partner"); + if (referral_coupon_code && $(".tot_quotation_discount").val()==undefined ) { + $(".txtcoupon").val(referral_coupon_code); + } + if (referral_sales_partner) { + $(".txtreferral_sales_partner").val(referral_sales_partner); + } // update login shopping_cart.show_shoppingcart_dropdown(); shopping_cart.set_cart_count(); diff --git a/erpnext/selling/doctype/quotation/quotation.json b/erpnext/selling/doctype/quotation/quotation.json index 1b8954578f..365bea91f6 100644 --- a/erpnext/selling/doctype/quotation/quotation.json +++ b/erpnext/selling/doctype/quotation/quotation.json @@ -1904,7 +1904,7 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "Additional Discount", + "label": "Additional Discount and Coupon Code", "length": 0, "no_copy": 0, "permlevel": 0, @@ -1920,6 +1920,74 @@ "translatable": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "coupon_code", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Coupon Code", + "length": 0, + "no_copy": 0, + "options": "Coupon Code", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "referral_sales_partner", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Referral Sales Partner", + "length": 0, + "no_copy": 0, + "options": "Sales Partner", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_in_quick_entry": 0, diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index 46009f45e4..4a56e40400 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -142,6 +142,9 @@ def _make_sales_order(source_name, target_doc=None, ignore_permissions=False): if customer: target.customer = customer.name target.customer_name = customer.customer_name + if source.referral_sales_partner: + target.sales_partner=source.referral_sales_partner + target.commission_rate=frappe.get_value('Sales Partner', source.referral_sales_partner, 'commission_rate') target.ignore_pricing_rule = 1 target.flags.ignore_permissions = ignore_permissions target.run_method("set_missing_values") diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json index 3885b13eae..78e7b4abb8 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.json +++ b/erpnext/selling/doctype/sales_order/sales_order.json @@ -79,6 +79,7 @@ "loyalty_points", "loyalty_amount", "section_break_48", + "coupon_code", "apply_discount_on", "base_discount_amount", "column_break_50", @@ -678,7 +679,13 @@ "collapsible_depends_on": "discount_amount", "fieldname": "section_break_48", "fieldtype": "Section Break", - "label": "Additional Discount" + "label": "Additional Discount and Coupon Code" + }, + { + "fieldname": "coupon_code", + "fieldtype": "Link", + "label": "Coupon Code", + "options": "Coupon Code" }, { "default": "Grand Total", @@ -1185,7 +1192,7 @@ "icon": "fa fa-file-text", "idx": 105, "is_submittable": 1, - "modified": "2019-10-10 08:46:07.540565", + "modified": "2019-10-14 08:46:07.540565", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order", @@ -1262,4 +1269,4 @@ "title_field": "title", "track_changes": 1, "track_seen": 1 -} \ No newline at end of file +} diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 80784ea61f..af78ab250d 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -46,6 +46,10 @@ class SalesOrder(SellingController): self.validate_serial_no_based_delivery() validate_inter_company_party(self.doctype, self.customer, self.company, self.inter_company_order_reference) + if self.coupon_code: + from erpnext.accounts.doctype.pricing_rule.utils import validate_coupon_code + validate_coupon_code(self.coupon_code) + from erpnext.stock.doctype.packed_item.packed_item import make_packing_list make_packing_list(self) @@ -177,6 +181,9 @@ class SalesOrder(SellingController): self.update_blanket_order() update_linked_doc(self.doctype, self.name, self.inter_company_order_reference) + if self.coupon_code: + from erpnext.accounts.doctype.pricing_rule.utils import update_coupon_code_count + update_coupon_code_count(self.coupon_code,'used') def on_cancel(self): super(SalesOrder, self).on_cancel() @@ -195,7 +202,10 @@ class SalesOrder(SellingController): self.update_blanket_order() unlink_inter_company_doc(self.doctype, self.name, self.inter_company_order_reference) - + if self.coupon_code: + from erpnext.accounts.doctype.pricing_rule.utils import update_coupon_code_count + update_coupon_code_count(self.coupon_code,'cancelled') + def update_project(self): if frappe.db.get_single_value('Selling Settings', 'sales_update_frequency') != "Each Transaction": return diff --git a/erpnext/setup/doctype/sales_partner/sales_partner.js b/erpnext/setup/doctype/sales_partner/sales_partner.js index 7164add495..5656d43e85 100644 --- a/erpnext/setup/doctype/sales_partner/sales_partner.js +++ b/erpnext/setup/doctype/sales_partner/sales_partner.js @@ -24,5 +24,11 @@ frappe.ui.form.on('Sales Partner', { } } }; + }, + referral_code:function(frm){ + if (frm.doc.referral_code) { + frm.doc.referral_code=frm.doc.referral_code.toUpperCase(); + frm.refresh_field('referral_code'); + } } }); diff --git a/erpnext/setup/doctype/sales_partner/sales_partner.json b/erpnext/setup/doctype/sales_partner/sales_partner.json index 28cdd3e10c..7e91fc5b8d 100644 --- a/erpnext/setup/doctype/sales_partner/sales_partner.json +++ b/erpnext/setup/doctype/sales_partner/sales_partner.json @@ -510,6 +510,73 @@ "translatable": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "column_break_16", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "", + "description": "To Track inbound purchase", + "fetch_if_empty": 0, + "fieldname": "referral_code", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Referral Code", + "length": 8, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 1 + }, { "allow_bulk_edit": 0, "allow_in_quick_entry": 0, diff --git a/erpnext/shopping_cart/cart.py b/erpnext/shopping_cart/cart.py index 40cb7c8700..f850bc1707 100644 --- a/erpnext/shopping_cart/cart.py +++ b/erpnext/shopping_cart/cart.py @@ -537,3 +537,29 @@ def get_address_territory(address_name): def show_terms(doc): return doc.tc_name + +@frappe.whitelist(allow_guest=True) +def apply_coupon_code(applied_code,applied_referral_sales_partner): + quotation = True + if applied_code: + coupon_list=frappe.get_all('Coupon Code', filters={"docstatus": ("<", "2"), 'coupon_code':applied_code }, fields=['name']) + if coupon_list: + coupon_name=coupon_list[0].name + from erpnext.accounts.doctype.pricing_rule.utils import validate_coupon_code + validate_coupon_code(coupon_name) + quotation = _get_cart_quotation() + quotation.coupon_code=coupon_name + quotation.flags.ignore_permissions = True + quotation.save() + if applied_referral_sales_partner: + sales_partner_list=frappe.get_all('Sales Partner', filters={'docstatus': 0, 'referral_code':applied_referral_sales_partner }, fields=['name']) + if sales_partner_list: + sales_partner_name=sales_partner_list[0].name + quotation.referral_sales_partner=sales_partner_name + quotation.flags.ignore_permissions = True + quotation.save() + else: + frappe.throw(_("Please enter valid coupon code !!")) + else: + frappe.throw(_("Please enter coupon code !!")) + return quotation \ 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 8b9299e42e..330adaee4a 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 @@ -275,6 +275,40 @@ "translatable": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "", + "fetch_if_empty": 0, + "fieldname": "show_apply_coupon_code_in_website", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Show Apply Coupon Code", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_in_quick_entry": 0, diff --git a/erpnext/templates/includes/cart.js b/erpnext/templates/includes/cart.js index 4c35dd04a2..456bc7e413 100644 --- a/erpnext/templates/includes/cart.js +++ b/erpnext/templates/includes/cart.js @@ -20,6 +20,7 @@ $.extend(shopping_cart, { shopping_cart.bind_change_qty(); shopping_cart.bind_change_notes(); shopping_cart.bind_dropdown_cart_buttons(); + shopping_cart.bind_coupon_code(); }, bind_address_select: function() { @@ -193,6 +194,29 @@ $.extend(shopping_cart, { } } }); + }, + + bind_coupon_code: function() { + $(".bt-coupon").on("click", function() { + shopping_cart.apply_coupon_code(this); + }); + }, + + apply_coupon_code: function(btn) { + return frappe.call({ + type: "POST", + method: "erpnext.shopping_cart.cart.apply_coupon_code", + btn: btn, + args : { + applied_code : $('.txtcoupon').val(), + applied_referral_sales_partner: $('.txtreferral_sales_partner').val() + }, + callback: function(r) { + if (r && r.message){ + location.reload(); + } + } + }); } }); diff --git a/erpnext/templates/includes/cart/cart_address.html b/erpnext/templates/includes/cart/cart_address.html index 2c90f8cb87..e857ce1be0 100644 --- a/erpnext/templates/includes/cart/cart_address.html +++ b/erpnext/templates/includes/cart/cart_address.html @@ -4,6 +4,16 @@ {% set select_address = True %} {% endif %} +{% set show_coupon_code = frappe.db.get_single_value('Shopping Cart Settings', 'show_apply_coupon_code_in_website') %} +{% if show_coupon_code == 1%} +
+
+ + + +
+
+{% endif %}
{{ _("Shipping Address") }}
diff --git a/erpnext/templates/includes/order/order_taxes.html b/erpnext/templates/includes/order/order_taxes.html index 1d26700cc4..4a32aa4744 100644 --- a/erpnext/templates/includes/order/order_taxes.html +++ b/erpnext/templates/includes/order/order_taxes.html @@ -22,6 +22,71 @@ {% endif %} {% endfor %} +{% if doc.doctype == 'Quotation' %} +{% if doc.coupon_code %} + + + {{ _("Discount") }} + + + {% set tot_quotation_discount = [] %} + {%- for item in doc.items -%} + {% if tot_quotation_discount.append((((item.price_list_rate * item.qty) + * item.discount_percentage) / 100)) %}{% endif %} + {% endfor %} + {{ frappe.utils.fmt_money((tot_quotation_discount | sum),currency=doc.currency) }} + + +{% endif %} +{% endif %} + +{% if doc.doctype == 'Sales Order' %} +{% if doc.coupon_code %} + + + {{ _("Total Amount") }} + + + + {% set total_amount = [] %} + {%- for item in doc.items -%} + {% if total_amount.append((item.price_list_rate * item.qty)) %}{% endif %} + {% endfor %} + {{ frappe.utils.fmt_money((total_amount | sum),currency=doc.currency) }} + + + + + + {{ _("Applied Coupon Code") }} + + + + {%- for row in frappe.get_all(doctype="Coupon Code", + fields=["coupon_code"], filters={ "name":doc.coupon_code}) -%} + {{ row.coupon_code }} + {% endfor %} + + + + + + {{ _("Discount") }} + + + + {% set tot_SO_discount = [] %} + {%- for item in doc.items -%} + {% if tot_SO_discount.append((((item.price_list_rate * item.qty) + * item.discount_percentage) / 100)) %}{% endif %} + {% endfor %} + {{ frappe.utils.fmt_money((tot_SO_discount | sum),currency=doc.currency) }} + + + +{% endif %} +{% endif %} + {{ _("Grand Total") }}