From baef2624a2615009bcefa9c94eaa083263886688 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 5 Aug 2016 15:41:36 +0530 Subject: [PATCH] Given provision to enter write off and change amount on POS --- .../doctype/pos_profile/pos_profile.json | 28 +++++- erpnext/accounts/doctype/sales_invoice/pos.py | 30 +++--- .../doctype/sales_invoice/sales_invoice.js | 3 +- .../doctype/sales_invoice/sales_invoice.py | 11 ++- .../sales_invoice/test_sales_invoice.py | 19 ++++ erpnext/accounts/page/pos/pos.js | 65 +++--------- erpnext/controllers/taxes_and_totals.py | 5 +- .../public/js/controllers/taxes_and_totals.js | 14 ++- .../public/js/payment/payment_details.html | 2 +- erpnext/public/js/payment/payments.js | 98 +++++++++++++++---- erpnext/public/js/payment/pos_payment.html | 16 ++- erpnext/public/less/erpnext.less | 9 +- 12 files changed, 203 insertions(+), 97 deletions(-) diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.json b/erpnext/accounts/doctype/pos_profile/pos_profile.json index 8882491866..0ab48e283f 100644 --- a/erpnext/accounts/doctype/pos_profile/pos_profile.json +++ b/erpnext/accounts/doctype/pos_profile/pos_profile.json @@ -118,6 +118,32 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "campaign", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Campaign", + "length": 0, + "no_copy": 0, + "options": "Campaign", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, { "allow_on_submit": 0, "bold": 0, @@ -859,7 +885,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2016-08-06 17:05:59.990031", + "modified": "2016-08-09 02:36:07.316464", "modified_by": "Administrator", "module": "Accounts", "name": "POS Profile", diff --git a/erpnext/accounts/doctype/sales_invoice/pos.py b/erpnext/accounts/doctype/sales_invoice/pos.py index 53144cbf2b..5cb9f765dd 100644 --- a/erpnext/accounts/doctype/sales_invoice/pos.py +++ b/erpnext/accounts/doctype/sales_invoice/pos.py @@ -35,7 +35,6 @@ def get_pos_data(): 'customers': get_customers(pos_profile, doc), 'pricing_rules': get_pricing_rules(doc), 'print_template': print_template, - 'write_off_account': pos_profile.get('write_off_account'), 'meta': { 'invoice': frappe.get_meta('Sales Invoice'), 'items': frappe.get_meta('Sales Invoice Item'), @@ -45,7 +44,12 @@ def get_pos_data(): def update_pos_profile_data(doc, pos_profile): company_data = frappe.db.get_value('Company', doc.company, '*', as_dict=1) + doc.campaign = pos_profile.get('campaign') + doc.write_off_account = pos_profile.get('write_off_account') or \ + company_data.write_off_account + doc.change_amount_account = pos_profile.get('change_amount_account') or \ + company_data.default_cash_account doc.taxes_and_charges = pos_profile.get('taxes_and_charges') if doc.taxes_and_charges: update_tax_table(doc) @@ -54,7 +58,8 @@ def update_pos_profile_data(doc, pos_profile): doc.conversion_rate = 1.0 if doc.currency != company_data.default_currency: doc.conversion_rate = get_exchange_rate(doc.currency, company_data.default_currency) - doc.selling_price_list = pos_profile.get('selling_price_list') or frappe.db.get_value('Selling Settings', None, 'selling_price_list') + doc.selling_price_list = pos_profile.get('selling_price_list') or \ + frappe.db.get_value('Selling Settings', None, 'selling_price_list') doc.naming_series = pos_profile.get('naming_series') or 'SINV-' doc.letter_head = pos_profile.get('letter_head') or company_data.default_letter_head doc.ignore_pricing_rule = pos_profile.get('ignore_pricing_rule') or 0 @@ -157,10 +162,11 @@ def get_customers(pos_profile, doc): def get_pricing_rules(doc): pricing_rules = "" if doc.ignore_pricing_rule == 0: - pricing_rules = frappe.db.sql(""" Select * from `tabPricing Rule` where docstatus < 2 and disable = 0 - and selling = 1 and ifnull(company, '') in (%(company)s, '') and - ifnull(for_price_list, '') in (%(price_list)s, '') and %(date)s between - ifnull(valid_from, '2000-01-01') and ifnull(valid_upto, '2500-12-31') order by priority desc, name desc""", + pricing_rules = frappe.db.sql(""" Select * from `tabPricing Rule` where docstatus < 2 + and ifnull(for_price_list, '') in (%(price_list)s, '') and selling = 1 + and ifnull(company, '') in (%(company)s, '') and disable = 0 and %(date)s + between ifnull(valid_from, '2000-01-01') and ifnull(valid_upto, '2500-12-31') + order by priority desc, name desc""", {'company': doc.company, 'price_list': doc.selling_price_list, 'date': nowdate()}, as_dict=1) return pricing_rules @@ -173,9 +179,9 @@ def make_invoice(doc_list): for docs in doc_list: for name, doc in docs.items(): - if not frappe.db.exists('Sales Invoice', {'offline_pos_name': name}): - validate_customer(doc) - validate_item(doc) + if not frappe.db.exists('Sales Invoice', + {'offline_pos_name': name, 'docstatus': ("<", "2")}): + validate_records(doc) si_doc = frappe.new_doc('Sales Invoice') si_doc.offline_pos_name = name si_doc.update(doc) @@ -184,6 +190,10 @@ def make_invoice(doc_list): return name_list +def validate_records(doc): + validate_customer(doc) + validate_item(doc) + def validate_customer(doc): if not frappe.db.exists('Customer', doc.get('customer')): customer_doc = frappe.new_doc('Customer') @@ -195,8 +205,6 @@ def validate_customer(doc): frappe.db.commit() doc['customer'] = customer_doc.name - return doc - def validate_item(doc): for item in doc.get('items'): if not frappe.db.exists('Item', item.get('item_code')): diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 417d3468f2..1191871134 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -275,6 +275,7 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte frappe.msgprint(__("You can not return change amount, limit has been exceeded")) }else{ this.calculate_write_off_amount() + this.frm.refresh_fields(); } } }); @@ -467,7 +468,7 @@ frappe.ui.form.on('Sales Invoice', { ] } } - }, + } }) frappe.ui.form.on('Sales Invoice Timesheet', { diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 994579f5b1..cdf9bd8029 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -237,12 +237,18 @@ class SalesInvoice(SellingController): pos_profile = frappe.get_doc('POS Profile', pos.name) if pos else None update_multi_mode_option(self, pos_profile) + if not self.change_amount_account: + self.change_amount_account = frappe.db.get_value('Company', self.company, 'default_cash_account') + if pos: if not for_validate and not self.customer: self.customer = pos.customer self.mode_of_payment = pos.mode_of_payment # self.set_customer_defaults() + if not self.change_amount_account: + self.change_amount_account = pos.get('change_amount_account') + for fieldname in ('territory', 'naming_series', 'currency', 'taxes_and_charges', 'letter_head', 'tc_name', 'selling_price_list', 'company', 'select_print_heading', 'cash_bank_account', 'write_off_account', 'write_off_cost_center'): @@ -268,7 +274,6 @@ class SalesInvoice(SellingController): # fetch charges if self.taxes_and_charges and not len(self.get("taxes")): self.set_taxes() - self.change_amount_account = pos.change_amount_account return pos @@ -503,7 +508,7 @@ class SalesInvoice(SellingController): gl_entries = merge_similar_entries(gl_entries) self.make_pos_gl_entries(gl_entries) - self.make_gle_for_change(gl_entries) + self.make_gle_for_change_amount(gl_entries) self.make_write_off_gl_entry(gl_entries) @@ -607,7 +612,7 @@ class SalesInvoice(SellingController): }, payment_mode_account_currency) ) - def make_gle_for_change(self, gl_entries): + def make_gle_for_change_amount(self, gl_entries): if cint(self.is_pos) and self.change_amount: if self.change_amount_account: gl_entries.append( diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index dd28059c43..db129de6bc 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -455,6 +455,25 @@ class TestSalesInvoice(unittest.TestCase): self.pos_gl_entry(si, pos, 300) + def test_pos_chnage_amount(self): + set_perpetual_inventory() + self.make_pos_profile() + + self._insert_purchase_receipt() + pos = copy.deepcopy(test_records[1]) + pos["is_pos"] = 1 + pos["update_stock"] = 1 + pos["payments"] = [{'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - _TC', 'amount': 300}, + {'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 340}] + + si = frappe.copy_doc(pos) + si.change_amount = 5.0 + si.insert() + si.submit() + + self.assertEquals(si.grand_total, 630.0) + self.assertEquals(si.write_off_amount, -5) + def test_make_pos_invoice(self): from erpnext.accounts.doctype.sales_invoice.pos import make_invoice diff --git a/erpnext/accounts/page/pos/pos.js b/erpnext/accounts/page/pos/pos.js index 8444dbe91f..fd76d510b5 100644 --- a/erpnext/accounts/page/pos/pos.js +++ b/erpnext/accounts/page/pos/pos.js @@ -183,6 +183,14 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ if(this.frm.doc.customer){ this.party_field.$input.val(this.frm.doc.customer); } + + if(!this.frm.doc.write_off_account){ + this.frm.doc.write_off_account = doc.write_off_account + } + + if(!this.frm.doc.change_amount_account){ + this.frm.doc.change_amount_account = doc.change_amount_account + } }, get_invoice_doc: function(si_docs){ @@ -209,7 +217,6 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ window.meta = r.message.meta; window.print_template = r.message.print_template; me.default_customer = r.message.default_customer || null; - me.write_off_account = r.message.write_off_account; localStorage.setItem('doc', JSON.stringify(r.message.doc)); if(callback){ callback(); @@ -717,49 +724,6 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ }, 1000) }, - write_off_amount: function(){ - var me = this; - var value = 0.0; - - if(this.frm.doc.outstanding_amount > 0){ - dialog = new frappe.ui.Dialog({ - title: 'Write Off Amount', - fields: [ - {fieldtype: "Check", fieldname: "write_off_amount", label: __("Write off Outstanding Amount")}, - {fieldtype: "Link", options:"Account", default:this.write_off_account, fieldname: "write_off_account", - label: __("Write off Account"), get_query: function() { - return { - filters: {'is_group': 0, 'report_type': 'Profit and Loss'} - } - }} - ] - }); - - dialog.show(); - - dialog.fields_dict.write_off_amount.$input.change(function(){ - write_off_amount = dialog.get_values().write_off_amount; - me.frm.doc.write_off_outstanding_amount_automatically = write_off_amount; - me.frm.doc.base_write_off_amount = (write_off_amount==1) ? flt(me.frm.doc.grand_total - me.frm.doc.paid_amount, precision("outstanding_amount")) : 0; - me.frm.doc.write_off_account = (write_off_amount==1) ? dialog.get_values().write_off_account : ''; - me.frm.doc.write_off_amount = flt(me.frm.doc.base_write_off_amount * me.frm.doc.conversion_rate, precision("write_off_amount")) - me.calculate_outstanding_amount(); - me.set_primary_action(); - }) - - dialog.fields_dict.write_off_account.$input.change(function(){ - me.frm.doc.write_off_account = dialog.get_values().write_off_account; - }) - - dialog.set_primary_action(__("Submit"), function(){ - dialog.hide() - me.submit_invoice() - }) - }else{ - this.submit_invoice() - } - }, - submit_invoice: function(){ var me = this; frappe.confirm(__("Do you really want to submit the invoice?"), function () { @@ -970,11 +934,13 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ get_pricing_rule: function(item){ var me = this; return $.grep(this.pricing_rules, function(data){ - if(data.item_code == item.item_code || in_list(['All Item Groups', item.item_group], data.item_group)) { - if(in_list(['Customer', 'Customer Group', 'Territory'], data.applicable_for)){ - return me.validate_condition(data) - }else{ - return true + if(item.qty >= data.min_qty && (item.qty <= (data.max_qty ? data.max_qty : item.qty)) ){ + if(data.item_code == item.item_code || in_list(['All Item Groups', item.item_group], data.item_group)) { + if(in_list(['Customer', 'Customer Group', 'Territory', 'Campaign'], data.applicable_for)){ + return me.validate_condition(data) + }else{ + return true + } } } }) @@ -993,6 +959,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ 'Customer': [data.customer, [this.frm.doc.customer]], 'Customer Group': [data.customer_group, [this.frm.doc.customer_group, 'All Customer Groups']], 'Territory': [data.territory, [this.frm.doc.territory, 'All Territories']], + 'Campaign': [data.campaign, [this.frm.doc.campaign]], } }, diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index afb3befcfc..125f4fedb6 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -470,7 +470,10 @@ class calculate_taxes_and_totals(object): self.doc.precision("base_change_amount")) def calculate_write_off_amount(self): - self.doc.write_off_amount = self.doc.grand_total - self.doc.paid_amount + self.doc.change_amount + if flt(self.doc.change_amount) > 0: + self.doc.write_off_amount = self.doc.grand_total - self.doc.paid_amount + self.doc.change_amount + self.doc.base_write_off_amount = flt(self.doc.write_off_amount * self.doc.conversion_rate, + self.doc.precision("base_write_off_amount")) def calculate_margin(self, item): total_margin = 0.0 diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index ad6f0ed4f9..c3f4b70043 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -617,8 +617,16 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ }, calculate_write_off_amount: function(){ - this.frm.set_value("write_off_amount", - flt(this.frm.doc.grand_total - this.frm.doc.paid_amount + this.frm.doc.change_amount, - precision("write_off_amount"))); + if(this.frm.doc.paid_amount > this.frm.doc.grand_total){ + this.frm.doc.write_off_amount = flt(this.frm.doc.grand_total - this.frm.doc.paid_amount + this.frm.doc.change_amount, + precision("write_off_amount")) + + this.frm.doc.base_write_off_amount = flt(this.frm.doc.write_off_amount * this.frm.doc.conversion_rate, + precision("base_write_off_amount")); + }else{ + this.frm.doc.paid_amount = 0.0 + } + + this.calculate_outstanding_amount(false) } }) diff --git a/erpnext/public/js/payment/payment_details.html b/erpnext/public/js/payment/payment_details.html index 18f9d03885..596f139ea0 100644 --- a/erpnext/public/js/payment/payment_details.html +++ b/erpnext/public/js/payment/payment_details.html @@ -1,5 +1,5 @@
-
{{mode_of_payment}}
+
{{mode_of_payment}}
diff --git a/erpnext/public/js/payment/payments.js b/erpnext/public/js/payment/payments.js index 7437f2a648..06df425262 100644 --- a/erpnext/public/js/payment/payments.js +++ b/erpnext/public/js/payment/payments.js @@ -13,6 +13,14 @@ erpnext.payments = erpnext.stock.StockController.extend({ this.$body = this.dialog.body; this.set_payment_primary_action(); this.make_keyboard(); + this.select_text() + }, + + select_text: function(){ + var me = this; + $(this.$body).find('.form-control').click(function(){ + $(this).select(); + }) }, set_payment_primary_action: function(){ @@ -20,7 +28,7 @@ erpnext.payments = erpnext.stock.StockController.extend({ this.dialog.set_primary_action(__("Submit"), function() { me.dialog.hide() - me.write_off_amount() + me.submit_invoice() }) }, @@ -78,7 +86,7 @@ erpnext.payments = erpnext.stock.StockController.extend({ //When user first time click on row this.payment_val = flt(this.frm.doc.outstanding_amount) this.selected_mode.val(format_number(this.payment_val, 2)); - this.update_paid_amount() + this.update_payment_amount() }else if(flt(this.selected_mode.val()) > 0){ //If user click on existing row which has value this.payment_val = flt(this.selected_mode.val()); @@ -90,17 +98,29 @@ erpnext.payments = erpnext.stock.StockController.extend({ bind_keyboard_event: function(){ var me = this; this.payment_val = ''; - this.bind_payment_mode_keys_event(); - this.bind_keyboard_keys_event(); + this.bind_form_control_event(); + this.bind_numeric_keys_event(); }, - bind_payment_mode_keys_event: function(){ + bind_form_control_event: function(){ var me = this; $(this.$body).find('.pos-payment-row').click(function(){ - if(me.frm.doc.outstanding_amount > 0){ - me.idx = $(this).attr("idx"); - me.set_outstanding_amount() - } + me.idx = $(this).attr("idx"); + me.set_outstanding_amount() + }) + + $(this.$body).find('.form-control').click(function(){ + me.idx = $(this).attr("idx"); + me.set_outstanding_amount(); + me.update_paid_amount(); + }) + + $(this.$body).find('.write_off_amount').change(function(){ + me.write_off_amount(flt($(this).val())); + }) + + $(this.$body).find('.change_amount').change(function(){ + me.change_amount(flt($(this).val())); }) }, @@ -113,13 +133,13 @@ erpnext.payments = erpnext.stock.StockController.extend({ this.selected_mode.attr('disabled', false); }, - bind_keyboard_keys_event: function(){ + bind_numeric_keys_event: function(){ var me = this; $(this.$body).find('.pos-keyboard-key').click(function(){ me.payment_val += $(this).text(); me.selected_mode.val(format_number(me.payment_val, 2)) me.idx = me.selected_mode.attr("idx") - me.update_paid_amount() + me.selected_mode.change() }) $(this.$body).find('.delete-btn').click(function(){ @@ -137,15 +157,11 @@ erpnext.payments = erpnext.stock.StockController.extend({ me.payment_val = flt($(this).val()) || 0.0; me.selected_mode.val(format_number(me.payment_val, 2)) me.idx = me.selected_mode.attr("idx") - me.update_paid_amount() - }) - - this.selected_mode.click(function(){ - me.selected_mode.select(); + me.update_payment_amount() }) }, - clear_amount: function(){ + clear_amount: function() { var me = this; $(this.$body).find('.clr').click(function(e){ e.stopPropagation(); @@ -154,25 +170,65 @@ erpnext.payments = erpnext.stock.StockController.extend({ me.payment_val = 0.0; me.selected_mode.val(0.0); me.highlight_selected_row(); - me.update_paid_amount(); + me.update_payment_amount(); }) }, - update_paid_amount: function(){ + write_off_amount: function(write_off_amount) { var me = this; + + if(this.frm.doc.paid_amount > 0){ + this.frm.doc.write_off_amount = write_off_amount; + this.frm.doc.base_write_off_amount = flt(this.frm.doc.write_off_amount * this.frm.doc.conversion_rate, + precision("base_write_off_amount")); + this.calculate_outstanding_amount(false) + this.show_amounts() + } + }, + + change_amount: function(change_amount) { + var me = this; + + this.frm.doc.change_amount = change_amount; + this.calculate_write_off_amount() + this.show_amounts() + }, + + update_paid_amount: function() { + var me = this; + if(in_list(['change_amount', 'write_off_amount'], this.idx)){ + value = flt(me.selected_mode.val(), 2) + if(me.idx == 'change_amount'){ + me.change_amount(value) + } else{ + if(value == 0) { + value = me.frm.doc.outstanding_amount; + } + me.write_off_amount(value) + } + }else{ + this.update_payment_amount() + } + }, + + update_payment_amount: function(){ + var me = this; + $.each(this.frm.doc.payments, function(index, data){ if(cint(me.idx) == cint(data.idx)){ data.amount = flt(me.selected_mode.val(), 2) } }) + this.calculate_outstanding_amount(false); this.show_amounts(); }, - + show_amounts: function(){ var me = this; + $(this.$body).find(".write_off_amount").val(format_number(this.frm.doc.write_off_amount, 2)); $(this.$body).find('.paid_amount').text(format_currency(this.frm.doc.paid_amount, this.frm.doc.currency)); - $(this.$body).find('.change_amount').text(format_currency(this.frm.doc.change_amount, this.frm.doc.currency)) + $(this.$body).find('.change_amount').val(format_number(this.frm.doc.change_amount, 2)) $(this.$body).find('.outstanding_amount').text(format_currency(this.frm.doc.outstanding_amount, this.frm.doc.currency)) this.update_invoice(); } diff --git a/erpnext/public/js/payment/pos_payment.html b/erpnext/public/js/payment/pos_payment.html index c5d963aa85..e94f3a52ae 100644 --- a/erpnext/public/js/payment/pos_payment.html +++ b/erpnext/public/js/payment/pos_payment.html @@ -1,8 +1,8 @@
+
+

Total Amount: {%= format_currency(grand_total, currency) %}

+
-
-

Total

{%= format_currency(grand_total, currency) %}

-

Paid

@@ -10,13 +10,19 @@

Outstanding

{%= format_currency(outstanding_amount, currency) %}

-

Change

{%= format_currency(change_amount, currency) %}

+

Change +

+
+
+

Write off


-
+
+
+
{% for(var i=0; i<3; i++) { %} diff --git a/erpnext/public/less/erpnext.less b/erpnext/public/less/erpnext.less index 896aee79d9..0400b6c637 100644 --- a/erpnext/public/less/erpnext.less +++ b/erpnext/public/less/erpnext.less @@ -175,13 +175,15 @@ } .pos-payment-row .col-xs-6 { - padding :10px; + padding :15px; } .pos-payment-row { border-bottom:1px solid #d1d8dd; margin: 2px 0px 5px 0px; height: 60px; + margin-top: 0px; + margin-bottom: 0px; } .pos-payment-row:hover, .pos-keyboard-key:hover{ @@ -247,3 +249,8 @@ body[data-route="pos"] .modal-dialog { .pos-invoice-list { padding: 15px 10px; } + +.write_off_amount, .change_amount { + margin: 15px; + width: 130px; +}