diff --git a/.gitignore b/.gitignore index 823a8764..0e569cbb 100755 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,7 @@ propms/docs/current # Cloned by the frappe-semgrep-rules pre-commit hook frappe-semgrep-rules/ + +# Node dependencies for the client-side test harness +node_modules/ +coverage/ diff --git a/propms/__init__.py b/propms/__init__.py index 1609d49b..5e5e9109 100755 --- a/propms/__init__.py +++ b/propms/__init__.py @@ -1 +1 @@ -__version__ = "15.3.0" +__version__ = "16.0.0" diff --git a/propms/hooks.py b/propms/hooks.py index 0d923bcc..f868721c 100755 --- a/propms/hooks.py +++ b/propms/hooks.py @@ -21,7 +21,6 @@ # include js in page # page_js = {"page" : "public/js/file.js"} page_js = { - "pos": "property_management_solution/point_of_sale.js", "point-of-sale": "property_management_solution/point_of_sale.js", } diff --git a/propms/issue_hook.py b/propms/issue_hook.py index 3773a21b..d701246a 100644 --- a/propms/issue_hook.py +++ b/propms/issue_hook.py @@ -34,7 +34,7 @@ def make_transaction(doc, for_self_consumption=False): lease = get_latest_active_lease(doc.property_name) def make_stock_entry(items_list=None, pos=None): - if not len(items_list) > 0: + if not items_list: return # Create a stock entry for purpose material issue @@ -53,13 +53,14 @@ def make_stock_entry(items_list=None, pos=None): if stock_entry_doc: stock_entry_doc.insert(ignore_permissions=True) stock_entry_url = frappe.utils.get_url_to_form(stock_entry_doc.doctype, stock_entry_doc.name) - se_msgprint = f"Stock Entry Created {stock_entry_doc.name}" frappe.flags.ignore_account_permission = True if submit_maintenance_stock_entry == 1 and not pos: stock_entry_doc.submit() if pos: frappe.throw(_("POS Stock Entry cannot be created for Self Consumption items")) - frappe.msgprint(_(se_msgprint)) + frappe.msgprint( + _("Stock Entry Created {0}").format(f"{stock_entry_doc.name}") + ) for item_row in doc.materials_billed: if ( item_row.item @@ -77,12 +78,16 @@ def make_stock_entry(items_list=None, pos=None): frappe.db.commit() def make_sales_invoice(items_list=None, pos=None, self_customer=None): - if not len(items_list) > 0 or not doc.customer: + if not items_list or not doc.customer: return default_tax_template = frappe.db.get_value("Company", company, "default_maintenance_tax_template") if not default_tax_template: url = frappe.utils.get_url_to_form("Company", company) - frappe.throw(_(f"Please Setup Default Maintenance Tax Template in {company}")) + frappe.throw( + _("Please Setup Default Maintenance Tax Template in {0}").format( + f"{company}" + ) + ) if self_customer: invoice_customer = self_consumption_customer else: @@ -125,14 +130,15 @@ def make_sales_invoice(items_list=None, pos=None, self_customer=None): invoice_doc.save() if invoice_doc: invoice_url = frappe.utils.get_url_to_form(invoice_doc.doctype, invoice_doc.name) - si_msgprint = f"Sales invoice Created {invoice_doc.name}" + invoice_link = f"{invoice_doc.name}" frappe.flags.ignore_account_permission = True if submit_maintenance_invoice == 1 and not pos: invoice_doc.submit() if pos: make_sales_pos_payment(invoice_doc, user_pos_profile.name) - si_msgprint = "POS " + si_msgprint - frappe.msgprint(_(si_msgprint)) + frappe.msgprint(_("POS Sales invoice Created {0}").format(invoice_link)) + else: + frappe.msgprint(_("Sales invoice Created {0}").format(invoice_link)) for item_row in doc.materials_billed: if ( item_row.item diff --git a/propms/pos.js b/propms/pos.js deleted file mode 100755 index 1b6857e0..00000000 --- a/propms/pos.js +++ /dev/null @@ -1,2116 +0,0 @@ -frappe.provide("erpnext.pos"); -{% include "erpnext/public/js/controllers/taxes_and_totals.js" %} - -frappe.pages['pos'].on_page_load = function (wrapper) { - var page = frappe.ui.make_app_page({ - parent: wrapper, - title: __('Point of Sale'), - single_column: true - }); - - frappe.db.get_value('POS Settings', {name: 'POS Settings'}, 'is_online', (r) => { - if (r && r.use_pos_in_offline_mode && cint(r.use_pos_in_offline_mode)) { - // offline - wrapper.pos = new erpnext.pos.PointOfSale(wrapper); - cur_pos = wrapper.pos; - } else { - // online - frappe.flags.is_online = true - frappe.set_route('point-of-sale'); - } - }); -} - -frappe.pages['pos'].refresh = function (wrapper) { - window.onbeforeunload = function () { - return wrapper.pos.beforeunload() - } - - if (frappe.flags.is_online) { - frappe.set_route('point-of-sale'); - } -} - -erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ - init: function (wrapper) { - this.page_len = 20; - this.freeze = false; - this.page = wrapper.page; - this.wrapper = $(wrapper).find('.page-content'); - this.set_indicator(); - this.onload(); - this.make_menu_list(); - this.bind_events(); - this.bind_items_event(); - this.si_docs = this.get_doc_from_localstorage(); - }, - - beforeunload: function (e) { - if (this.connection_status == false && frappe.get_route()[0] == "pos") { - e = e || window.event; - - // For IE and Firefox prior to version 4 - if (e) { - e.returnValue = __("You are in offline mode. You will not be able to reload until you have network."); - return - } - - // For Safari - return __("You are in offline mode. You will not be able to reload until you have network."); - } - }, - - check_internet_connection: function () { - var me = this; - //Check Internet connection after every 30 seconds - setInterval(function () { - me.set_indicator(); - }, 5000) - }, - - set_indicator: function () { - var me = this; - // navigator.onLine - this.connection_status = false; - this.page.set_indicator(__("Offline"), "grey") - frappe.call({ - method: "frappe.handler.ping", - callback: function (r) { - if (r.message) { - me.connection_status = true; - me.page.set_indicator(__("Online"), "green") - } - } - }) - }, - - onload: function () { - var me = this; - this.get_data_from_server(function () { - me.make_control(); - me.create_new(); - me.make(); - }); - }, - - make_menu_list: function () { - var me = this; - this.page.clear_menu(); - - // for mobile - this.page.add_menu_item(__("Pay"), function () { - me.validate(); - me.update_paid_amount_status(true); - me.create_invoice(); - me.make_payment(); - }).addClass('visible-xs'); - - this.page.add_menu_item(__("New Sales Invoice"), function () { - me.save_previous_entry(); - me.create_new(); - }) - - this.page.add_menu_item(__("Sync Master Data"), function () { - me.get_data_from_server(function () { - me.load_data(false); - me.make_item_list(); - me.set_missing_values(); - }) - }); - - this.page.add_menu_item(__("Sync Offline Invoices"), function () { - me.freeze_screen = true; - me.sync_sales_invoice() - }); - - this.page.add_menu_item(__("Cashier Closing"), function () { - frappe.set_route('List', 'Cashier Closing'); - }); - - this.page.add_menu_item(__("POS Profile"), function () { - frappe.set_route('List', 'POS Profile'); - }); - }, - - email_prompt: function() { - var me = this; - var fields = [{label:__("To"), fieldtype:"Data", reqd: 0, fieldname:"recipients",length:524288}, - {fieldtype: "Section Break", collapsible: 1, label: "CC & Email Template"}, - {fieldtype: "Section Break"}, - {label:__("Subject"), fieldtype:"Data", reqd: 1, - fieldname:"subject",length:524288}, - {fieldtype: "Section Break"}, - {label:__("Message"), fieldtype:"Text Editor", reqd: 1, - fieldname:"content"}, - {fieldtype: "Section Break"}, - {fieldtype: "Column Break"}]; - - this.email_dialog = new frappe.ui.Dialog({ - title: "Email", - fields: fields, - primary_action_label: __("Send"), - primary_action: function() { - me.send_action(); - } - }); - - this.email_dialog.show() - }, - - send_action: function() { - this.email_queue = this.get_email_queue() - this.email_queue[this.frm.doc.offline_pos_name] = JSON.stringify(this.email_dialog.get_values()) - this.update_email_queue() - this.email_dialog.hide() - }, - - update_email_queue: function () { - try { - localStorage.setItem('email_queue', JSON.stringify(this.email_queue)); - } catch (e) { - frappe.throw(__("LocalStorage is full, did not save")) - } - }, - - get_email_queue: function () { - try { - return JSON.parse(localStorage.getItem('email_queue')) || {}; - } catch (e) { - return {} - } - }, - - get_customers_details: function () { - try { - return JSON.parse(localStorage.getItem('customer_details')) || {}; - } catch (e) { - return {} - } - }, - - edit_record: function () { - var me = this; - - doc_data = this.get_invoice_doc(this.si_docs); - if (doc_data) { - this.frm.doc = doc_data[0][this.frm.doc.offline_pos_name]; - this.set_missing_values(); - this.refresh(false); - this.toggle_input_field(); - this.list_dialog && this.list_dialog.hide(); - } - }, - - delete_records: function () { - var me = this; - this.validate_list() - this.remove_doc_from_localstorage() - this.update_localstorage(); - this.toggle_delete_button(); - }, - - validate_list: function() { - var me = this; - this.si_docs = this.get_submitted_invoice() - $.each(this.removed_items, function(index, pos_name){ - $.each(me.si_docs, function(key, data){ - if(me.si_docs[key][pos_name] && me.si_docs[key][pos_name].offline_pos_name == pos_name ){ - frappe.throw(__("Submitted orders can not be deleted")) - } - }) - }) - }, - - toggle_delete_button: function () { - var me = this; - if(this.pos_profile_data["allow_delete"]) { - if (this.removed_items && this.removed_items.length > 0) { - $(this.page.wrapper).find('.btn-danger').show(); - } else { - $(this.page.wrapper).find('.btn-danger').hide(); - } - } - }, - - get_doctype_status: function (doc) { - if (doc.docstatus == 0) { - return { status: "Draft", indicator: "red" } - } else if (doc.outstanding_amount == 0) { - return { status: "Paid", indicator: "green" } - } else { - return { status: "Submitted", indicator: "blue" } - } - }, - - set_missing_values: function () { - var me = this; - doc = JSON.parse(localStorage.getItem('doc')) - if (this.frm.doc.payments.length == 0) { - this.frm.doc.payments = doc.payments; - this.calculate_outstanding_amount(); - } - - this.set_customer_value_in_party_field(); - - if (!this.frm.doc.write_off_account) { - this.frm.doc.write_off_account = doc.write_off_account - } - - if (!this.frm.doc.account_for_change_amount) { - this.frm.doc.account_for_change_amount = doc.account_for_change_amount - } - }, - - set_customer_value_in_party_field: function() { - if (this.frm.doc.customer) { - this.party_field.$input.val(this.frm.doc.customer); - } - }, - - get_invoice_doc: function (si_docs) { - var me = this; - this.si_docs = this.get_doc_from_localstorage(); - - return $.grep(this.si_docs, function (data) { - for (key in data) { - return key == me.frm.doc.offline_pos_name; - } - }) - }, - - get_data_from_server: function (callback) { - var me = this; - frappe.call({ - method: "erpnext.accounts.doctype.sales_invoice.pos.get_pos_data", - freeze: true, - freeze_message: __("Master data syncing, it might take some time"), - callback: function (r) { - localStorage.setItem('doc', JSON.stringify(r.message.doc)); - me.init_master_data(r) - me.set_interval_for_si_sync(); - me.check_internet_connection(); - if (callback) { - callback(); - } - }, - error: () => { - setTimeout(() => frappe.set_route('List', 'POS Profile'), 2000); - } - }) - }, - - init_master_data: function (r) { - var me = this; - this.doc = JSON.parse(localStorage.getItem('doc')); - this.meta = r.message.meta; - this.item_data = r.message.items; - this.cost_center = r.message.cost_center; - this.item_groups = r.message.item_groups; - this.customers = r.message.customers; - this.serial_no_data = r.message.serial_no_data; - this.batch_no_data = r.message.batch_no_data; - this.barcode_data = r.message.barcode_data; - this.tax_data = r.message.tax_data; - this.contacts = r.message.contacts; - this.address = r.message.address || {}; - this.price_list_data = r.message.price_list_data; - this.customer_wise_price_list = r.message.customer_wise_price_list - this.bin_data = r.message.bin_data; - this.pricing_rules = r.message.pricing_rules; - this.print_template = r.message.print_template; - this.pos_profile_data = r.message.pos_profile; - this.default_customer = r.message.default_customer || null; - this.print_settings = locals[":Print Settings"]["Print Settings"]; - this.letter_head = (this.pos_profile_data.length > 0) ? frappe.boot.letter_heads[this.pos_profile_data[letter_head]] : {}; - }, - - save_previous_entry: function () { - if (this.frm.doc.docstatus < 1 && this.frm.doc.items.length > 0) { - this.create_invoice(); - } - }, - - create_new: function () { - var me = this; - this.frm = {} - this.load_data(true); - this.setup(); - this.set_default_customer() - }, - - load_data: function (load_doc) { - var me = this; - - this.items = this.item_data; - this.actual_qty_dict = {}; - - if (load_doc) { - this.frm.doc = JSON.parse(localStorage.getItem('doc')); - this.frm.doc.offline_pos_name = null; - } - - $.each(this.meta, function (i, data) { - frappe.meta.sync(data) - locals["DocType"][data.name] = data; - }) - - this.print_template_data = frappe.render_template("print_template", { - content: this.print_template, - title: "POS", - base_url: frappe.urllib.get_base_url(), - print_css: frappe.boot.print_css, - print_settings: this.print_settings, - header: this.letter_head.header, - footer: this.letter_head.footer, - landscape: false, - columns: [] - }) - }, - - setup: function () { - this.set_primary_action(); - this.party_field.$input.attr('disabled', false); - if(this.selected_row) { - this.selected_row.hide() - } - }, - - set_default_customer: function() { - if (this.default_customer && !this.frm.doc.customer) { - this.party_field.$input.val(this.default_customer); - this.frm.doc.customer = this.default_customer; - this.numeric_keypad.show(); - this.toggle_list_customer(false) - this.toggle_item_cart(true) - } - }, - - set_transaction_defaults: function (party) { - var me = this; - this.party = party; - this.price_list = (party == "Customer" ? - this.frm.doc.selling_price_list : this.frm.doc.buying_price_list); - this.price_list_field = (party == "Customer" ? "selling_price_list" : "buying_price_list"); - this.sales_or_purchase = (party == "Customer" ? "Sales" : "Purchase"); - }, - - make: function () { - this.make_item_list(); - this.make_discount_field() - }, - - make_control: function() { - this.frm = {} - this.frm.doc = this.doc - this.set_transaction_defaults("Customer"); - this.frm.doc["allow_user_to_edit_rate"] = this.pos_profile_data["allow_user_to_edit_rate"] ? true : false; - this.frm.doc["allow_user_to_edit_discount"] = this.pos_profile_data["allow_user_to_edit_discount"] ? true : false; - this.wrapper.html(frappe.render_template("pos", this.frm.doc)); - this.make_search(); - this.make_customer(); - this.make_list_customers(); - this.bind_numeric_keypad(); - }, - - make_search: function () { - var me = this; - this.search_item = frappe.ui.form.make_control({ - df: { - "fieldtype": "Data", - "label": __("Item"), - "fieldname": "pos_item", - "placeholder": __("Search Item") - }, - parent: this.wrapper.find(".search-item"), - only_input: true, - }); - - this.search_item.make_input(); - - - this.cost_center = frappe.ui.form.make_control({ - df: { - "fieldtype": "Data", - "label": __("Item"), - "fieldname": "pos_item", - "placeholder": __("Search Item") - }, - parent: this.wrapper.find(".search-item"), - only_input: true, - }); - this.cost_center.make_input(); - - this.search_item.$input.on("keypress", function (event) { - - clearTimeout(me.last_search_timeout); - me.last_search_timeout = setTimeout(() => { - if((me.search_item.$input.val() != "") || (event.which == 13)) { - me.items = me.get_items(); - me.make_item_list(); - } - }, 400); - }); - - this.search_cost_center = this.wrapper.find('.search-cost-center'); - - var dropdown_html = this.cost_center.map(function(cost_center) { - return "
  • "+cost_center+"
  • "; - }).join(""); - - this.search_item_group = this.wrapper.find('.search-item-group'); - sorted_item_groups = this.get_sorted_item_groups() - var dropdown_html = sorted_item_groups.map(function(item_group) { - return "
  • "+item_group+"
  • "; - }).join(""); - - this.search_item_group.find('.dropdown-menu').html(dropdown_html); - - this.search_item_group.on('click', '.dropdown-menu a', function() { - me.selected_item_group = $(this).attr('data-value'); - me.search_item_group.find('.dropdown-text').text(me.selected_item_group); - - me.page_len = 20; - me.items = me.get_items(); - me.make_item_list(); - }) - - me.toggle_more_btn(); - - this.wrapper.on("click", ".btn-more", function() { - me.page_len += 20; - me.items = me.get_items(); - me.make_item_list(); - me.toggle_more_btn(); - }); - - this.page.wrapper.on("click", ".edit-customer-btn", function() { - me.update_customer() - }) - }, - - get_sorted_item_groups: function() { - list = {} - $.each(this.item_groups, function(i, data) { - list[i] = data[0] - }) - - return Object.keys(list).sort(function(a,b){return list[a]-list[b]}) - }, - - toggle_more_btn: function() { - if(!this.items || this.items.length <= this.page_len) { - this.wrapper.find(".btn-more").hide(); - } else { - this.wrapper.find(".btn-more").show(); - } - }, - - toggle_totals_area: function(show) { - - if(show === undefined) { - show = this.is_totals_area_collapsed; - } - - var totals_area = this.wrapper.find('.totals-area'); - totals_area.find('.net-total-area, .tax-area, .discount-amount-area') - .toggle(show); - - if(show) { - totals_area.find('.collapse-btn i') - .removeClass('octicon-chevron-down') - .addClass('octicon-chevron-up'); - } else { - totals_area.find('.collapse-btn i') - .removeClass('octicon-chevron-up') - .addClass('octicon-chevron-down'); - } - - this.is_totals_area_collapsed = !show; - }, - - make_list_customers: function () { - var me = this; - this.list_customers_btn = this.page.wrapper.find('.list-customers-btn'); - this.add_customer_btn = this.wrapper.find('.add-customer-btn'); - this.pos_bill = this.wrapper.find('.pos-bill-wrapper').hide(); - this.list_customers = this.wrapper.find('.list-customers'); - this.numeric_keypad = this.wrapper.find('.numeric_keypad'); - this.list_customers_btn.addClass("view_customer") - - me.render_list_customers(); - me.toggle_totals_area(false); - - this.page.wrapper.on('click', '.list-customers-btn', function() { - $(this).toggleClass("view_customer"); - if($(this).hasClass("view_customer")) { - me.render_list_customers(); - me.list_customers.show(); - me.pos_bill.hide(); - me.numeric_keypad.hide(); - me.toggle_delete_button() - } else { - if(me.frm.doc.docstatus == 0) { - me.party_field.$input.attr('disabled', false); - } - me.pos_bill.show(); - me.toggle_totals_area(false); - me.toggle_delete_button() - me.list_customers.hide(); - me.numeric_keypad.show(); - } - }); - this.add_customer_btn.on('click', function() { - me.save_previous_entry(); - me.create_new(); - me.refresh(); - me.set_focus(); - }); - this.pos_bill.on('click', '.collapse-btn', function() { - me.toggle_totals_area(); - }); - }, - - bind_numeric_keypad: function() { - var me = this; - $(this.numeric_keypad).find('.pos-operation').on('click', function(){ - me.numeric_val = ''; - }) - - $(this.numeric_keypad).find('.numeric-keypad').on('click', function(){ - me.numeric_id = $(this).attr("id") || me.numeric_id; - me.val = $(this).attr("val") - if(me.numeric_id) { - me.selected_field = $(me.wrapper).find('.selected-item').find('.' + me.numeric_id) - } - - if(me.val && me.numeric_id) { - me.numeric_val += me.val; - me.selected_field.val(flt(me.numeric_val)) - me.selected_field.trigger("change") - // me.render_selected_item() - } - - if(me.numeric_id && $(this).hasClass('pos-operation')) { - me.numeric_keypad.find('button.pos-operation').removeClass('active'); - $(this).addClass('active'); - - me.selected_row.find('.pos-list-row').removeClass('active'); - me.selected_field.closest('.pos-list-row').addClass('active'); - } - }) - - $(this.numeric_keypad).find('.numeric-del').click(function(){ - if(me.numeric_id) { - me.selected_field = $(me.wrapper).find('.selected-item').find('.' + me.numeric_id) - me.numeric_val = cstr(flt(me.selected_field.val())).slice(0, -1); - me.selected_field.val(me.numeric_val); - me.selected_field.trigger("change") - } else { - //Remove an item from the cart, if focus is at selected item - me.remove_selected_item() - } - }) - - $(this.numeric_keypad).find('.pos-pay').click(function(){ - me.validate(); - me.update_paid_amount_status(true); - me.create_invoice(); - me.make_payment(); - }) - }, - - remove_selected_item: function() { - this.remove_item = [] - idx = $(this.wrapper).find(".pos-selected-item-action").attr("data-idx") - this.remove_item.push(idx) - this.remove_zero_qty_items_from_cart() - this.update_paid_amount_status(false) - }, - - render_list_customers: function () { - var me = this; - - this.removed_items = []; - // this.list_customers.empty(); - this.si_docs = this.get_doc_from_localstorage(); - if (!this.si_docs.length) { - this.list_customers.find('.list-customers-table').html(""); - return; - } - - var html = ""; - if(this.si_docs.length) { - this.si_docs.forEach(function (data, i) { - for (var key in data) { - html += frappe.render_template("pos_invoice_list", { - sr: i + 1, - name: key, - customer: data[key].customer, - paid_amount: format_currency(data[key].paid_amount, me.frm.doc.currency), - grand_total: format_currency(data[key].grand_total, me.frm.doc.currency), - data: me.get_doctype_status(data[key]) - }); - } - }); - } - this.list_customers.find('.list-customers-table').html(html); - - this.list_customers.on('click', '.customer-row', function () { - me.list_customers.hide(); - me.numeric_keypad.show(); - me.list_customers_btn.toggleClass("view_customer"); - me.pos_bill.show(); - me.list_customers_btn.show(); - me.frm.doc.offline_pos_name = $(this).parents().attr('invoice-name') - me.edit_record(); - }) - - //actions - $(this.wrapper).find('.list-select-all').click(function () { - me.list_customers.find('.list-delete').prop("checked", $(this).is(":checked")) - me.removed_items = []; - if ($(this).is(":checked")) { - $.each(me.si_docs, function (index, data) { - for (key in data) { - me.removed_items.push(key) - } - }); - } - - me.toggle_delete_button(); - }); - - $(this.wrapper).find('.list-delete').click(function () { - me.frm.doc.offline_pos_name = $(this).parent().parent().attr('invoice-name'); - if ($(this).is(":checked")) { - me.removed_items.push(me.frm.doc.offline_pos_name); - } else { - me.removed_items.pop(me.frm.doc.offline_pos_name) - } - - me.toggle_delete_button(); - }); - }, - - bind_delete_event: function() { - var me = this; - - $(this.page.wrapper).on('click', '.btn-danger', function(){ - frappe.confirm(__("Delete permanently?"), function () { - me.delete_records(); - me.list_customers.find('.list-customers-table').html(""); - me.render_list_customers(); - }) - }) - }, - - set_focus: function () { - if (this.default_customer || this.frm.doc.customer) { - this.set_customer_value_in_party_field(); - this.search_item.$input.focus(); - } else { - this.party_field.$input.focus(); - } - }, - - make_customer: function () { - var me = this; - - if(!this.party_field) { - if(this.page.wrapper.find('.pos-bill-toolbar').length === 0) { - $(frappe.render_template('customer_toolbar', { - allow_delete: this.pos_profile_data["allow_delete"] - })).insertAfter(this.page.$title_area.hide()); - } - - this.party_field = frappe.ui.form.make_control({ - df: { - "fieldtype": "Data", - "options": this.party, - "label": this.party, - "fieldname": this.party.toLowerCase(), - "placeholder": __("Select or add new customer") - }, - parent: this.page.wrapper.find(".party-area"), - only_input: true, - }); - - this.party_field.make_input(); - setTimeout(this.set_focus.bind(this), 500); - me.toggle_delete_button(); - } - - this.party_field.awesomeplete = - new Awesomplete(this.party_field.$input.get(0), { - minChars: 0, - maxItems: 99, - autoFirst: true, - list: [], - filter: function (item, input) { - if (item.value.includes('is_action')) { - return true; - } - - input = input.toLowerCase(); - item = this.get_item(item.value); - result = item ? item.searchtext.includes(input) : ''; - if(!result) { - me.prepare_customer_mapper(input); - } else { - return result; - } - }, - item: function (item, input) { - var d = this.get_item(item.value); - var html = "" + __(d.label || d.value) + ""; - if(d.customer_name) { - html += '
    ' + __(d.customer_name) + ''; - } - - return $('
  • ') - .data('item.autocomplete', d) - .html('

    ' + html + '

    ') - .get(0); - } - }); - - this.prepare_customer_mapper() - this.autocomplete_customers(); - - this.party_field.$input - .on('input', function (e) { - if(me.customers_mapper.length <= 1) { - me.prepare_customer_mapper(e.target.value); - } - me.party_field.awesomeplete.list = me.customers_mapper; - }) - .on('awesomplete-select', function (e) { - var customer = me.party_field.awesomeplete - .get_item(e.originalEvent.text.value); - if (!customer) return; - // create customer link - if (customer.action) { - customer.action.apply(me); - return; - } - me.toggle_list_customer(false); - me.toggle_edit_button(true); - me.update_customer_data(customer); - me.refresh(); - me.set_focus(); - me.list_customers_btn.removeClass("view_customer"); - }) - .on('focus', function (e) { - $(e.target).val('').trigger('input'); - me.toggle_edit_button(false); - - if(me.frm.doc.items.length) { - me.toggle_list_customer(false) - me.toggle_item_cart(true) - } else { - me.toggle_list_customer(true) - me.toggle_item_cart(false) - } - }) - .on("awesomplete-selectcomplete", function (e) { - var item = me.party_field.awesomeplete - .get_item(e.originalEvent.text.value); - // clear text input if item is action - if (item.action) { - $(this).val(""); - } - me.make_item_list(item.customer_name); - }); - }, - - prepare_customer_mapper: function(key) { - var me = this; - var customer_data = ''; - - if (key) { - key = key.toLowerCase().trim(); - var re = new RegExp('%', 'g'); - var reg = new RegExp(key.replace(re, '\\w*\\s*[a-zA-Z0-9]*')); - - customer_data = $.grep(this.customers, function(data) { - contact = me.contacts[data.name]; - if(reg.test(data.name.toLowerCase()) - || reg.test(data.customer_name.toLowerCase()) - || (contact && reg.test(contact["mobile_no"])) - || (contact && reg.test(contact["phone"])) - || (data.customer_group && reg.test(data.customer_group.toLowerCase()))){ - return data; - } - }) - } else { - customer_data = this.customers; - } - - this.customers_mapper = []; - - customer_data.forEach(function (c, index) { - if(index < 30) { - contact = me.contacts[c.name]; - if(contact && !c['phone']) { - c["phone"] = contact["phone"]; - c["email_id"] = contact["email_id"]; - c["mobile_no"] = contact["mobile_no"]; - } - - me.customers_mapper.push({ - label: c.name, - value: c.name, - customer_name: c.customer_name, - customer_group: c.customer_group, - territory: c.territory, - phone: contact ? contact["phone"] : '', - mobile_no: contact ? contact["mobile_no"] : '', - email_id: contact ? contact["email_id"] : '', - searchtext: ['customer_name', 'customer_group', 'name', 'value', - 'label', 'email_id', 'phone', 'mobile_no'] - .map(key => c[key]).join(' ') - .toLowerCase() - }); - } else { - return; - } - }); - - this.customers_mapper.push({ - label: "" - + " " - + __("Create a new Customer") - + "", - value: 'is_action', - action: me.add_customer - }); - }, - - autocomplete_customers: function() { - this.party_field.awesomeplete.list = this.customers_mapper; - }, - - toggle_edit_button: function(flag) { - this.page.wrapper.find('.edit-customer-btn').toggle(flag); - }, - - toggle_list_customer: function(flag) { - this.list_customers.toggle(flag); - }, - - toggle_item_cart: function(flag) { - this.wrapper.find('.pos-bill-wrapper').toggle(flag); - }, - - add_customer: function() { - this.frm.doc.customer = ""; - this.update_customer(true); - this.numeric_keypad.show(); - }, - - update_customer: function (new_customer) { - var me = this; - - this.customer_doc = new frappe.ui.Dialog({ - 'title': 'Customer', - fields: [ - { - "label": __("Full Name"), - "fieldname": "full_name", - "fieldtype": "Data", - "reqd": 1 - }, - { - "fieldtype": "Section Break" - }, - { - "label": __("Email Id"), - "fieldname": "email_id", - "fieldtype": "Data" - }, - { - "fieldtype": "Column Break" - }, - { - "label": __("Contact Number"), - "fieldname": "phone", - "fieldtype": "Data" - }, - { - "fieldtype": "Section Break" - }, - { - "label": __("Address Name"), - "read_only": 1, - "fieldname": "name", - "fieldtype": "Data" - }, - { - "label": __("Address Line 1"), - "fieldname": "address_line1", - "fieldtype": "Data" - }, - { - "label": __("Address Line 2"), - "fieldname": "address_line2", - "fieldtype": "Data" - }, - { - "fieldtype": "Column Break" - }, - { - "label": __("City"), - "fieldname": "city", - "fieldtype": "Data" - }, - { - "label": __("State"), - "fieldname": "state", - "fieldtype": "Data" - }, - { - "label": __("ZIP Code"), - "fieldname": "pincode", - "fieldtype": "Data" - }, - { - "label": __("Customer POS Id"), - "fieldname": "customer_pos_id", - "fieldtype": "Data", - "hidden": 1 - } - ] - }) - this.customer_doc.show() - this.render_address_data() - - this.customer_doc.set_primary_action(__("Save"), function () { - me.make_offline_customer(new_customer); - me.pos_bill.show(); - me.list_customers.hide(); - }); - }, - - render_address_data: function() { - var me = this; - this.address_data = this.address[this.frm.doc.customer] || {}; - if(!this.address_data.email_id || !this.address_data.phone) { - this.address_data = this.contacts[this.frm.doc.customer]; - } - - this.customer_doc.set_values(this.address_data) - if(!this.customer_doc.fields_dict.full_name.$input.val()) { - this.customer_doc.set_value("full_name", this.frm.doc.customer) - } - - if(!this.customer_doc.fields_dict.customer_pos_id.value) { - this.customer_doc.set_value("customer_pos_id", $.now()) - } - }, - - get_address_from_localstorage: function() { - this.address_details = this.get_customers_details() - return this.address_details[this.frm.doc.customer] - }, - - make_offline_customer: function(new_customer) { - this.frm.doc.customer = this.frm.doc.customer || this.customer_doc.get_values().full_name; - this.frm.doc.customer_pos_id = this.customer_doc.fields_dict.customer_pos_id.value; - this.customer_details = this.get_customers_details(); - this.customer_details[this.frm.doc.customer] = this.get_prompt_details(); - this.party_field.$input.val(this.frm.doc.customer); - this.update_address_and_customer_list(new_customer) - this.autocomplete_customers(); - this.update_customer_in_localstorage() - this.update_customer_in_localstorage() - this.customer_doc.hide() - }, - - update_address_and_customer_list: function(new_customer) { - var me = this; - if(new_customer) { - this.customers_mapper.push({ - label: this.frm.doc.customer, - value: this.frm.doc.customer, - customer_group: "", - territory: "" - }); - } - - this.address[this.frm.doc.customer] = JSON.parse(this.get_prompt_details()) - }, - - get_prompt_details: function() { - this.prompt_details = this.customer_doc.get_values(); - this.prompt_details['country'] = this.pos_profile_data.country; - this.prompt_details['territory'] = this.pos_profile_data["territory"]; - this.prompt_details['customer_group'] = this.pos_profile_data["customer_group"]; - this.prompt_details['customer_pos_id'] = this.customer_doc.fields_dict.customer_pos_id.value; - return JSON.stringify(this.prompt_details) - }, - - update_customer_data: function (doc) { - var me = this; - this.frm.doc.customer = doc.label || doc.name; - this.frm.doc.customer_name = doc.customer_name; - this.frm.doc.customer_group = doc.customer_group; - this.frm.doc.territory = doc.territory; - this.pos_bill.show(); - this.numeric_keypad.show(); - }, - - make_item_list: function (customer) { - var me = this; - if (!this.price_list) { - frappe.msgprint(__("Price List not found or disabled")); - return; - } - - me.item_timeout = null; - - var $wrap = me.wrapper.find(".item-list"); - me.wrapper.find(".item-list").empty(); - - if (this.items.length > 0) { - $.each(this.items, function(index, obj) { - let customer_price_list = me.customer_wise_price_list[customer]; - let item_price - if (customer && customer_price_list && customer_price_list[obj.name]) { - item_price = format_currency(customer_price_list[obj.name], me.frm.doc.currency); - } else { - item_price = format_currency(me.price_list_data[obj.name], me.frm.doc.currency); - } - if(index < me.page_len) { - $(frappe.render_template("pos_item", { - item_code: obj.name, - item_price: item_price, - item_name: obj.name === obj.item_name ? "" : obj.item_name, - item_image: obj.image, - item_stock: __('Stock Qty') + ": " + me.get_actual_qty(obj), - item_uom: obj.stock_uom, - color: frappe.get_palette(obj.item_name), - abbr: frappe.get_abbr(obj.item_name) - })).tooltip().appendTo($wrap); - } - }); - - $wrap.append(` -
    -
    - -
    Load more items
    -
    -
    - `); - - me.toggle_more_btn(); - } else { - $("

    " - +__("Not items found")+"

    ").appendTo($wrap) - } - - if (this.items.length == 1 - && this.search_item.$input.val()) { - this.search_item.$input.val(""); - this.add_to_cart(); - } - }, - - get_items: function (item_code) { - // To search item as per the key enter - - var me = this; - this.item_serial_no = {}; - this.item_batch_no = {}; - - if (item_code) { - return $.grep(this.item_data, function (item) { - if (item.item_code == item_code) { - return true - } - }) - } - - this.items_list = this.apply_category(); - - key = this.search_item.$input.val().toLowerCase().replace(/[&\/\\#,+()\[\]$~.'":*?<>{}]/g, '\\$&'); - var re = new RegExp('%', 'g'); - var reg = new RegExp(key.replace(re, '[\\w*\\s*[a-zA-Z0-9]*]*')) - search_status = true - - if (key) { - return $.grep(this.items_list, function (item) { - if (search_status) { - if (in_list(me.batch_no_data[item.item_code], me.search_item.$input.val())) { - search_status = false; - return me.item_batch_no[item.item_code] = me.search_item.$input.val() - } else if (me.serial_no_data[item.item_code] - && in_list(Object.keys(me.serial_no_data[item.item_code]), me.search_item.$input.val())) { - search_status = false; - me.item_serial_no[item.item_code] = [me.search_item.$input.val(), me.serial_no_data[item.item_code][me.search_item.$input.val()]] - return true - } else if (in_list(me.barcode_data[item.item_code], me.search_item.$input.val())) { - search_status = false; - return true; - } else if (reg.test(item.item_code.toLowerCase()) || (item.description && reg.test(item.description.toLowerCase())) || - reg.test(item.item_name.toLowerCase()) || reg.test(item.item_group.toLowerCase())) { - return true - } - } - }) - } else { - return this.items_list; - } - }, - - apply_category: function() { - var me = this; - category = this.selected_item_group || "All Item Groups"; - if(category == 'All Item Groups') { - return this.item_data - } else { - return this.item_data.filter(function(element, index, array){ - return element.item_group == category; - }); - } - }, - - bind_items_event: function() { - var me = this; - $(this.wrapper).on('click', '.pos-bill-item', function() { - $(me.wrapper).find('.pos-bill-item').removeClass('active'); - $(this).addClass('active'); - me.numeric_val = ""; - me.numeric_id = "" - me.item_code = $(this).attr("data-item-code"); - me.render_selected_item() - me.bind_qty_event() - me.update_rate() - $(me.wrapper).find(".selected-item").scrollTop(1000); - }) - }, - - bind_qty_event: function () { - var me = this; - - $(this.wrapper).on("change", ".pos-item-qty", function () { - var item_code = $(this).parents(".pos-selected-item-action").attr("data-item-code"); - var qty = $(this).val(); - me.update_qty(item_code, qty); - me.update_value(); - }) - - $(this.wrapper).on("focusout", ".pos-item-qty", function () { - var item_code = $(this).parents(".pos-selected-item-action").attr("data-item-code"); - var qty = $(this).val(); - me.update_qty(item_code, qty, true); - me.update_value(); - }) - - $(this.wrapper).find("[data-action='increase-qty']").on("click", function () { - var item_code = $(this).parents(".pos-bill-item").attr("data-item-code"); - var qty = flt($(this).parents(".pos-bill-item").find('.pos-item-qty').val()) + 1; - me.update_qty(item_code, qty); - }) - - $(this.wrapper).find("[data-action='decrease-qty']").on("click", function () { - var item_code = $(this).parents(".pos-bill-item").attr("data-item-code"); - var qty = flt($(this).parents(".pos-bill-item").find('.pos-item-qty').val()) - 1; - me.update_qty(item_code, qty); - }) - - $(this.wrapper).on("change", ".pos-item-disc", function () { - var item_code = $(this).parents(".pos-selected-item-action").attr("data-item-code"); - var discount = $(this).val(); - if(discount > 100){ - discount = $(this).val(''); - frappe.show_alert({ - indicator: 'red', - message: __('Discount amount cannot be greater than 100%') - }); - me.update_discount(item_code, discount); - }else{ - me.update_discount(item_code, discount); - me.update_value(); - } - }) - }, - - bind_events: function() { - var me = this; - // if form is local then allow this function - // $(me.wrapper).find(".pos-item-wrapper").on("click", function () { - $(this.wrapper).on("click", ".pos-item-wrapper", function () { - me.item_code = ''; - me.customer_validate(); - if($(me.pos_bill).is(":hidden")) return; - - if (me.frm.doc.docstatus == 0) { - me.items = me.get_items($(this).attr("data-item-code")) - me.add_to_cart(); - me.clear_selected_row(); - } - }); - - me.bind_delete_event() - }, - - update_qty: function (item_code, qty, remove_zero_qty_items) { - var me = this; - this.items = this.get_items(item_code); - this.validate_serial_no() - this.set_item_details(item_code, "qty", qty, remove_zero_qty_items); - }, - - update_discount: function(item_code, discount) { - var me = this; - this.items = this.get_items(item_code); - this.set_item_details(item_code, "discount_percentage", discount); - }, - - update_rate: function () { - var me = this; - $(this.wrapper).on("change", ".pos-item-price", function () { - var item_code = $(this).parents(".pos-selected-item-action").attr("data-item-code"); - me.set_item_details(item_code, "rate", $(this).val()); - me.update_value() - }) - }, - - update_value: function() { - var me = this; - var fields = {qty: ".pos-item-qty", "discount_percentage": ".pos-item-disc", - "rate": ".pos-item-price", "amount": ".pos-amount"} - this.child_doc = this.get_child_item(this.item_code); - - if(me.child_doc.length) { - $.each(fields, function(key, field) { - $(me.selected_row).find(field).val(me.child_doc[0][key]) - }) - } else { - this.clear_selected_row(); - } - }, - - clear_selected_row: function() { - $(this.wrapper).find('.selected-item').empty(); - }, - - render_selected_item: function() { - this.child_doc = this.get_child_item(this.item_code); - $(this.wrapper).find('.selected-item').empty(); - if(this.child_doc.length) { - this.child_doc[0]["allow_user_to_edit_rate"] = this.pos_profile_data["allow_user_to_edit_rate"] ? true : false, - this.child_doc[0]["allow_user_to_edit_discount"] = this.pos_profile_data["allow_user_to_edit_discount"] ? true : false; - this.selected_row = $(frappe.render_template("pos_selected_item", this.child_doc[0])) - $(this.wrapper).find('.selected-item').html(this.selected_row) - } - - $(this.selected_row).find('.form-control').click(function(){ - $(this).select(); - }) - }, - - get_child_item: function(item_code) { - var me = this; - return $.map(me.frm.doc.items, function(doc){ - if(doc.item_code == item_code) { - return doc - } - }) - }, - - set_item_details: function (item_code, field, value, remove_zero_qty_items) { - var me = this; - if (value < 0) { - frappe.throw(__("Enter value must be positive")); - } - - this.remove_item = [] - $.each(this.frm.doc["items"] || [], function (i, d) { - if (d.item_code == item_code) { - if (d.serial_no && field == 'qty') { - me.validate_serial_no_qty(d, item_code, field, value) - } - - d[field] = flt(value); - d.amount = flt(d.rate) * flt(d.qty); - if (d.qty == 0 && remove_zero_qty_items) { - me.remove_item.push(d.idx) - } - - if(field=="discount_percentage" && value == 0) { - d.rate = d.price_list_rate; - } - } - }); - - if (field == 'qty') { - this.remove_zero_qty_items_from_cart(); - } - - this.update_paid_amount_status(false) - }, - - remove_zero_qty_items_from_cart: function () { - var me = this; - var idx = 0; - this.items = [] - $.each(this.frm.doc["items"] || [], function (i, d) { - if (!in_list(me.remove_item, d.idx)) { - d.idx = idx; - me.items.push(d); - idx++; - } - }); - - this.frm.doc["items"] = this.items; - }, - - make_discount_field: function () { - var me = this; - - this.wrapper.find('input.discount-percentage').on("change", function () { - me.frm.doc.additional_discount_percentage = flt($(this).val(), precision("additional_discount_percentage")); - - if(me.frm.doc.additional_discount_percentage && me.frm.doc.discount_amount) { - // Reset discount amount - me.frm.doc.discount_amount = 0; - } - - var total = me.frm.doc.grand_total - - if (me.frm.doc.apply_discount_on == 'Net Total') { - total = me.frm.doc.net_total - } - - me.frm.doc.discount_amount = flt(total * flt(me.frm.doc.additional_discount_percentage) / 100, precision("discount_amount")); - me.refresh(); - me.wrapper.find('input.discount-amount').val(me.frm.doc.discount_amount) - }); - - this.wrapper.find('input.discount-amount').on("change", function () { - me.frm.doc.discount_amount = flt($(this).val(), precision("discount_amount")); - me.frm.doc.additional_discount_percentage = 0.0; - me.refresh(); - me.wrapper.find('input.discount-percentage').val(0); - }); - }, - - customer_validate: function () { - var me = this; - if (!this.frm.doc.customer || this.party_field.get_value() == "") { - frappe.throw(__("Please select customer")) - } - }, - - add_to_cart: function () { - var me = this; - var caught = false; - var no_of_items = me.wrapper.find(".pos-bill-item").length; - - this.customer_validate(); - this.mandatory_batch_no(); - this.validate_serial_no(); - this.validate_warehouse(); - - if (no_of_items != 0) { - $.each(this.frm.doc["items"] || [], function (i, d) { - if (d.item_code == me.items[0].item_code) { - caught = true; - d.qty += 1; - d.amount = flt(d.rate) * flt(d.qty); - if (me.item_serial_no[d.item_code]) { - d.serial_no += '\n' + me.item_serial_no[d.item_code][0] - d.warehouse = me.item_serial_no[d.item_code][1] - } - - if (me.item_batch_no.length) { - d.batch_no = me.item_batch_no[d.item_code] - } - } - }); - } - - // if item not found then add new item - if (!caught) - this.add_new_item_to_grid(); - - this.update_paid_amount_status(false) - this.wrapper.find(".item-cart-items").scrollTop(1000); - }, - - add_new_item_to_grid: function () { - var me = this; - this.child = frappe.model.add_child(this.frm.doc, this.frm.doc.doctype + " Item", "items"); - this.child.item_code = this.items[0].item_code; - this.child.item_name = this.items[0].item_name; - this.child.stock_uom = this.items[0].stock_uom; - this.child.uom = this.items[0].sales_uom || this.items[0].stock_uom; - this.child.conversion_factor = this.items[0].conversion_factor || 1; - this.child.brand = this.items[0].brand; - this.child.description = this.items[0].description || this.items[0].item_name; - this.child.discount_percentage = 0.0; - this.child.qty = 1; - this.child.item_group = this.items[0].item_group; - this.child.cost_center = this.pos_profile_data['cost_center'] || this.items[0].cost_center; - this.child.income_account = this.pos_profile_data['income_account'] || this.items[0].income_account; - this.child.warehouse = (this.item_serial_no[this.child.item_code] - ? this.item_serial_no[this.child.item_code][1] : (this.pos_profile_data['warehouse'] || this.items[0].default_warehouse)); - - customer = this.frm.doc.customer; - let rate; - - customer_price_list = this.customer_wise_price_list[customer] - if (customer_price_list && customer_price_list[this.child.item_code]){ - rate = flt(this.customer_wise_price_list[customer][this.child.item_code] * this.child.conversion_factor, 9) / flt(this.frm.doc.conversion_rate, 9); - } - else{ - rate = flt(this.price_list_data[this.child.item_code] * this.child.conversion_factor, 9) / flt(this.frm.doc.conversion_rate, 9); - } - - this.child.price_list_rate = rate; - this.child.rate = rate; - this.child.actual_qty = me.get_actual_qty(this.items[0]); - this.child.amount = flt(this.child.qty) * flt(this.child.rate); - this.child.batch_no = this.item_batch_no[this.child.item_code]; - this.child.serial_no = (this.item_serial_no[this.child.item_code] - ? this.item_serial_no[this.child.item_code][0] : ''); - this.child.item_tax_rate = JSON.stringify(this.tax_data[this.child.item_code]); - }, - - update_paid_amount_status: function (update_paid_amount) { - if (this.frm.doc.offline_pos_name) { - update_paid_amount = update_paid_amount ? false : true; - } - - this.refresh(update_paid_amount); - }, - - refresh: function (update_paid_amount) { - var me = this; - this.refresh_fields(update_paid_amount); - this.set_primary_action(); - }, - - refresh_fields: function (update_paid_amount) { - this.apply_pricing_rule(); - this.discount_amount_applied = false; - this._calculate_taxes_and_totals(); - this.calculate_discount_amount(); - this.show_items_in_item_cart(); - this.set_taxes(); - this.calculate_outstanding_amount(update_paid_amount); - this.set_totals(); - this.update_total_qty(); - }, - - get_company_currency: function () { - return erpnext.get_currency(this.frm.doc.company); - }, - - show_items_in_item_cart: function () { - var me = this; - var $items = this.wrapper.find(".items").empty(); - var $no_items_message = this.wrapper.find(".no-items-message"); - $no_items_message.toggle(this.frm.doc.items.length === 0); - - var $totals_area = this.wrapper.find('.totals-area'); - $totals_area.toggle(this.frm.doc.items.length > 0); - - $.each(this.frm.doc.items || [], function (i, d) { - $(frappe.render_template("pos_bill_item_new", { - item_code: d.item_code, - item_name: (d.item_name === d.item_code || !d.item_name) ? "" : ("
    " + d.item_name), - qty: d.qty, - discount_percentage: d.discount_percentage || 0.0, - actual_qty: me.actual_qty_dict[d.item_code] || 0.0, - projected_qty: d.projected_qty, - rate: format_currency(d.rate, me.frm.doc.currency), - amount: format_currency(d.amount, me.frm.doc.currency), - selected_class: (me.item_code == d.item_code) ? "active" : "" - })).appendTo($items); - }); - - this.wrapper.find("input.pos-item-qty").on("focus", function () { - $(this).select(); - }); - - this.wrapper.find("input.pos-item-disc").on("focus", function () { - $(this).select(); - }); - - this.wrapper.find("input.pos-item-price").on("focus", function () { - $(this).select(); - }); - }, - - set_taxes: function () { - var me = this; - me.frm.doc.total_taxes_and_charges = 0.0 - - var taxes = this.frm.doc.taxes || []; - $(this.wrapper) - .find(".tax-area").toggleClass("hide", (taxes && taxes.length) ? false : true) - .find(".tax-table").empty(); - - $.each(taxes, function (i, d) { - if (d.tax_amount && cint(d.included_in_print_rate) == 0) { - $(frappe.render_template("pos_tax_row", { - description: d.description, - tax_amount: format_currency(flt(d.tax_amount_after_discount_amount), - me.frm.doc.currency) - })).appendTo(me.wrapper.find(".tax-table")); - } - }); - }, - - set_totals: function () { - var me = this; - this.wrapper.find(".net-total").text(format_currency(me.frm.doc.total, me.frm.doc.currency)); - this.wrapper.find(".grand-total").text(format_currency(me.frm.doc.grand_total, me.frm.doc.currency)); - this.wrapper.find('input.discount-percentage').val(this.frm.doc.additional_discount_percentage); - this.wrapper.find('input.discount-amount').val(this.frm.doc.discount_amount); - }, - - update_total_qty: function() { - var me = this; - var qty_total = 0; - $.each(this.frm.doc["items"] || [], function (i, d) { - if (d.item_code) { - qty_total += d.qty; - } - }); - this.frm.doc.qty_total = qty_total; - this.wrapper.find('.qty-total').text(this.frm.doc.qty_total); - }, - - set_primary_action: function () { - var me = this; - this.page.set_primary_action(__("New Cart"), function () { - me.make_new_cart() - me.make_menu_list() - }, "fa fa-plus") - - if (this.frm.doc.docstatus == 1 || this.pos_profile_data["allow_print_before_pay"]) { - this.page.set_secondary_action(__("Print"), function () { - me.create_invoice(); - var html = frappe.render(me.print_template_data, me.frm.doc) - me.print_document(html) - }) - } - - if (this.frm.doc.docstatus == 1) { - this.page.add_menu_item(__("Email"), function () { - me.email_prompt() - }) - } - }, - - make_new_cart: function (){ - this.item_code = ''; - this.page.clear_secondary_action(); - this.save_previous_entry(); - this.create_new(); - this.refresh(); - this.toggle_input_field(); - this.render_list_customers(); - this.set_focus(); - }, - - print_dialog: function () { - var me = this; - - this.msgprint = frappe.msgprint( - `${__('Print')} - ${__('New')}`); - - this.msgprint.msg_area.find('.print_doc').on('click', function() { - var html = frappe.render(me.print_template_data, me.frm.doc); - me.print_document(html); - }) - - this.msgprint.msg_area.find('.new_doc').on('click', function() { - me.msgprint.hide(); - me.make_new_cart(); - }) - - }, - - print_document: function (html) { - var w = window.open(); - w.document.write(html); - w.document.close(); - setTimeout(function () { - w.print(); - w.close(); - }, 1000) - }, - - submit_invoice: function () { - var me = this; - this.change_status(); - this.update_serial_no() - if (this.frm.doc.docstatus == 1) { - this.print_dialog() - } - }, - - update_serial_no: function() { - var me = this; - - //Remove the sold serial no from the cache - $.each(this.frm.doc.items, function(index, data) { - var sn = data.serial_no.split('\n') - if(sn.length) { - var serial_no_list = me.serial_no_data[data.item_code] - if(serial_no_list) { - $.each(sn, function(i, serial_no) { - if(in_list(Object.keys(serial_no_list), serial_no)) { - delete serial_no_list[serial_no] - } - }) - me.serial_no_data[data.item_code] = serial_no_list; - } - } - }) - }, - - change_status: function () { - if (this.frm.doc.docstatus == 0) { - this.frm.doc.docstatus = 1; - this.update_invoice(); - this.toggle_input_field(); - } - }, - - toggle_input_field: function () { - var pointer_events = 'inherit' - var disabled = this.frm.doc.docstatus == 1 ? true: false; - $(this.wrapper).find('input').attr("disabled", disabled); - $(this.wrapper).find('select').attr("disabled", disabled); - $(this.wrapper).find('input').attr("disabled", disabled); - $(this.wrapper).find('select').attr("disabled", disabled); - $(this.wrapper).find('button').attr("disabled", disabled); - this.party_field.$input.attr('disabled', disabled); - - if (this.frm.doc.docstatus == 1) { - pointer_events = 'none'; - } - - $(this.wrapper).find('.pos-bill').css('pointer-events', pointer_events); - $(this.wrapper).find('.pos-items-section').css('pointer-events', pointer_events); - this.set_primary_action(); - }, - - create_invoice: function () { - var me = this; - var invoice_data = {}; - this.si_docs = this.get_doc_from_localstorage(); - - if (this.frm.doc.offline_pos_name) { - this.update_invoice() - //to retrieve and set the default payment - invoice_data[this.frm.doc.offline_pos_name] = this.frm.doc; - invoice_data[this.frm.doc.offline_pos_name].payments[0].amount = this.frm.doc.net_total - invoice_data[this.frm.doc.offline_pos_name].payments[0].base_amount = this.frm.doc.net_total - - this.frm.doc.paid_amount = this.frm.doc.net_total - this.frm.doc.outstanding_amount = 0 - } else { - this.frm.doc.offline_pos_name = $.now(); - this.frm.doc.posting_date = frappe.datetime.get_today(); - this.frm.doc.posting_time = frappe.datetime.now_time(); - this.frm.doc.pos_total_qty = this.frm.doc.qty_total; - this.frm.doc.pos_profile = this.pos_profile_data['name']; - invoice_data[this.frm.doc.offline_pos_name] = this.frm.doc; - this.si_docs.push(invoice_data); - this.update_localstorage(); - this.set_primary_action(); - } - return invoice_data; - }, - - update_invoice: function () { - var me = this; - this.si_docs = this.get_doc_from_localstorage(); - $.each(this.si_docs, function (index, data) { - for (var key in data) { - if (key == me.frm.doc.offline_pos_name) { - me.si_docs[index][key] = me.frm.doc; - me.update_localstorage(); - } - } - }); - }, - - update_localstorage: function () { - try { - localStorage.setItem('sales_invoice_doc', JSON.stringify(this.si_docs)); - } catch (e) { - frappe.throw(__("LocalStorage is full , did not save")) - } - }, - - get_doc_from_localstorage: function () { - try { - return JSON.parse(localStorage.getItem('sales_invoice_doc')) || []; - } catch (e) { - return [] - } - }, - - set_interval_for_si_sync: function () { - var me = this; - setInterval(function () { - me.freeze_screen = false; - me.sync_sales_invoice() - }, 180000) - }, - - sync_sales_invoice: function () { - var me = this; - this.si_docs = this.get_submitted_invoice() || []; - this.email_queue_list = this.get_email_queue() || {}; - this.customers_list = this.get_customers_details() || {}; - if(this.customer_doc) { - this.freeze = this.customer_doc.display - } - - freeze_screen = this.freeze_screen || false; - - if ((this.si_docs.length || this.email_queue_list || this.customers_list) && !this.freeze) { - this.freeze = true; - - frappe.call({ - method: "erpnext.accounts.doctype.sales_invoice.pos.make_invoice", - freeze: freeze_screen, - args: { - doc_list: me.si_docs, - email_queue_list: me.email_queue_list, - customers_list: me.customers_list - }, - callback: function (r) { - if (r.message) { - me.freeze = false; - me.customers = r.message.synced_customers_list; - me.address = r.message.synced_address; - me.contacts = r.message.synced_contacts; - me.removed_items = r.message.invoice; - me.removed_email = r.message.email_queue; - me.removed_customers = r.message.customers; - me.remove_doc_from_localstorage(); - me.remove_email_queue_from_localstorage(); - me.remove_customer_from_localstorage(); - me.prepare_customer_mapper(); - me.autocomplete_customers(); - me.render_list_customers(); - } - } - }) - } - }, - - get_submitted_invoice: function () { - var invoices = []; - var index = 1; - var docs = this.get_doc_from_localstorage(); - if (docs) { - invoices = $.map(docs, function (data) { - for (var key in data) { - if (data[key].docstatus == 1 && index < 50) { - index++ - data[key].docstatus = 0; - return data - } - } - }); - } - - return invoices - }, - - remove_doc_from_localstorage: function () { - var me = this; - this.si_docs = this.get_doc_from_localstorage(); - this.new_si_docs = []; - if (this.removed_items) { - $.each(this.si_docs, function (index, data) { - for (var key in data) { - if (!in_list(me.removed_items, key)) { - me.new_si_docs.push(data); - } - } - }) - this.removed_items = []; - this.si_docs = this.new_si_docs; - this.update_localstorage(); - } - }, - - remove_email_queue_from_localstorage: function() { - var me = this; - this.email_queue = this.get_email_queue() - if (this.removed_email) { - $.each(this.email_queue_list, function (index, data) { - if (in_list(me.removed_email, index)) { - delete me.email_queue[index] - } - }) - this.update_email_queue(); - } - }, - - remove_customer_from_localstorage: function() { - var me = this; - this.customer_details = this.get_customers_details() - if (this.removed_customers) { - $.each(this.customers_list, function (index, data) { - if (in_list(me.removed_customers, index)) { - delete me.customer_details[index] - } - }) - this.update_customer_in_localstorage(); - } - }, - - validate: function () { - var me = this; - this.customer_validate(); - this.validate_zero_qty_items(); - this.item_validate(); - this.validate_mode_of_payments(); - }, - - validate_zero_qty_items: function() { - this.remove_item = []; - - this.frm.doc.items.forEach(d => { - if (d.qty == 0) { - this.remove_item.push(d.idx); - } - }); - - if(this.remove_item) { - this.remove_zero_qty_items_from_cart(); - } - }, - - item_validate: function () { - if (this.frm.doc.items.length == 0) { - frappe.throw(__("Select items to save the invoice")) - } - }, - - validate_mode_of_payments: function () { - if (this.frm.doc.payments.length === 0) { - frappe.throw(__("Payment Mode is not configured. Please check, whether account has been set on Mode of Payments or on POS Profile.")) - } - }, - - validate_serial_no: function () { - var me = this; - var item_code = '' - var serial_no = ''; - for (var key in this.item_serial_no) { - item_code = key; - serial_no = me.item_serial_no[key][0]; - } - - if (this.items[0].has_serial_no && serial_no == "") { - this.refresh(); - frappe.throw(__(repl("Error: Serial no is mandatory for item %(item)s", { - 'item': this.items[0].item_code - }))) - } - - if (item_code && serial_no) { - $.each(this.frm.doc.items, function (index, data) { - if (data.item_code == item_code) { - if (in_list(data.serial_no.split('\n'), serial_no)) { - frappe.throw(__(repl("Serial no %(serial_no)s is already taken", { - 'serial_no': serial_no - }))) - } - } - }) - } - }, - - validate_serial_no_qty: function (args, item_code, field, value) { - var me = this; - if (args.item_code == item_code && args.serial_no - && field == 'qty' && cint(value) != value) { - args.qty = 0.0; - this.refresh(); - frappe.throw(__("Serial no item cannot be a fraction")) - } - - if (args.item_code == item_code && args.serial_no && args.serial_no.split('\n').length != cint(value)) { - args.qty = 0.0; - args.serial_no = '' - this.refresh(); - frappe.throw(__(repl("Total nos of serial no is not equal to quantity for item %(item)s.", { - 'item': item_code - }))) - } - }, - - mandatory_batch_no: function () { - var me = this; - if (this.items[0].has_batch_no && !this.item_batch_no[this.items[0].item_code]) { - frappe.prompt([{ - 'fieldname': 'batch', - 'fieldtype': 'Select', - 'label': __('Batch No'), - 'reqd': 1, - 'options': this.batch_no_data[this.items[0].item_code] - }], - function(values){ - me.item_batch_no[me.items[0].item_code] = values.batch; - }, - __('Select Batch No')) - } - }, - - apply_pricing_rule: function () { - var me = this; - $.each(this.frm.doc["items"], function (n, item) { - var pricing_rule = me.get_pricing_rule(item) - me.validate_pricing_rule(pricing_rule) - if (pricing_rule.length) { - item.pricing_rule = pricing_rule[0].name; - item.margin_type = pricing_rule[0].margin_type; - item.price_list_rate = pricing_rule[0].price || item.price_list_rate; - item.margin_rate_or_amount = pricing_rule[0].margin_rate_or_amount; - item.discount_percentage = pricing_rule[0].discount_percentage || 0.0; - me.apply_pricing_rule_on_item(item) - } else if (item.pricing_rule) { - item.price_list_rate = me.price_list_data[item.item_code] - item.margin_rate_or_amount = 0.0; - item.discount_percentage = 0.0; - item.pricing_rule = null; - me.apply_pricing_rule_on_item(item) - } - - if(item.discount_percentage > 0) { - me.apply_pricing_rule_on_item(item) - } - }) - }, - - get_pricing_rule: function (item) { - var me = this; - return $.grep(this.pricing_rules, function (data) { - if (item.qty >= data.min_qty && (item.qty <= (data.max_qty ? data.max_qty : item.qty))) { - if (me.validate_item_condition(data, item)) { - if (in_list(['Customer', 'Customer Group', 'Territory', 'Campaign'], data.applicable_for)) { - return me.validate_condition(data) - } else { - return true - } - } - } - }) - }, - - validate_item_condition: function (data, item) { - var apply_on = frappe.model.scrub(data.apply_on); - - return (data.apply_on == 'Item Group') - ? this.validate_item_group(data.item_group, item.item_group) : (data[apply_on] == item[apply_on]); - }, - - validate_item_group: function (pr_item_group, cart_item_group) { - //pr_item_group = pricing rule's item group - //cart_item_group = cart item's item group - //this.item_groups has information about item group's lft and rgt - //for example: {'Foods': [12, 19]} - - pr_item_group = this.item_groups[pr_item_group] - cart_item_group = this.item_groups[cart_item_group] - - return (cart_item_group[0] >= pr_item_group[0] && - cart_item_group[1] <= pr_item_group[1]) - }, - - validate_condition: function (data) { - //This method check condition based on applicable for - var condition = this.get_mapper_for_pricing_rule(data)[data.applicable_for] - if (in_list(condition[1], condition[0])) { - return true - } - }, - - get_mapper_for_pricing_rule: function (data) { - return { - '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]], - } - }, - - validate_pricing_rule: function (pricing_rule) { - //This method validate duplicate pricing rule - var pricing_rule_name = ''; - var priority = 0; - var pricing_rule_list = []; - var priority_list = [] - - if (pricing_rule.length > 1) { - - $.each(pricing_rule, function (index, data) { - pricing_rule_name += data.name + ',' - priority_list.push(data.priority) - if (priority <= data.priority) { - priority = data.priority - pricing_rule_list.push(data) - } - }) - - var count = 0 - $.each(priority_list, function (index, value) { - if (value == priority) { - count++ - } - }) - - if (priority == 0 || count > 1) { - frappe.throw(__(repl("Multiple Price Rules exists with same criteria, please resolve conflict by assigning priority. Price Rules: %(pricing_rule)s", { - 'pricing_rule': pricing_rule_name - }))) - } - - return pricing_rule_list - } - }, - - validate_warehouse: function () { - if (this.items[0].is_stock_item && !this.items[0].default_warehouse && !this.pos_profile_data['warehouse']) { - frappe.throw(__("Default warehouse is required for selected item")) - } - }, - - get_actual_qty: function (item) { - this.actual_qty = 0.0; - - var warehouse = this.pos_profile_data['warehouse'] || item.default_warehouse; - if (warehouse && this.bin_data[item.item_code]) { - this.actual_qty = this.bin_data[item.item_code][warehouse] || 0; - this.actual_qty_dict[item.item_code] = this.actual_qty - } - - return this.actual_qty - }, - - update_customer_in_localstorage: function() { - var me = this; - try { - localStorage.setItem('customer_details', JSON.stringify(this.customer_details)); - } catch (e) { - frappe.throw(__("LocalStorage is full , did not save")) - } - } -}) diff --git a/propms/property_management_solution/doctype/custom_error_log/custom_error_log.json b/propms/property_management_solution/doctype/custom_error_log/custom_error_log.json index d1fbfa16..898b5e1b 100755 --- a/propms/property_management_solution/doctype/custom_error_log/custom_error_log.json +++ b/propms/property_management_solution/doctype/custom_error_log/custom_error_log.json @@ -13,7 +13,7 @@ "doctype": "DocType", "document_type": "System", "editable_grid": 0, - "engine": "MyISAM", + "engine": "InnoDB", "fields": [ { "allow_bulk_edit": 0, @@ -124,7 +124,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-12-18 21:18:01.379834", + "modified": "2026-08-27 12:00:00.000000", "modified_by": "Administrator", "module": "Property Management Solution", "name": "Custom Error Log", diff --git a/propms/property_management_solution/doctype/lease/lease.js b/propms/property_management_solution/doctype/lease/lease.js index 3fc78dd6..3244e769 100755 --- a/propms/property_management_solution/doctype/lease/lease.js +++ b/propms/property_management_solution/doctype/lease/lease.js @@ -165,7 +165,7 @@ frappe.ui.form.on('Lease', { frm.set_intro( __("This lease is a renewal of {0}, initiated by {1} on {2}.", [ - `${frm.doc.renewed_from}`, + `${frm.doc.renewed_from}`, `${d.renewal_initiated_by || __("Unknown")}`, `${created_on}`, ]), @@ -186,7 +186,7 @@ frappe.ui.form.on('Lease', { frm.set_intro( __("This lease has been renewed: {0} by {1} on {2}.", [ - `${d.name}`, + `${d.name}`, `${d.renewal_initiated_by || ""}`, `${creation_time}`, ]), diff --git a/propms/property_management_solution/doctype/lease/lease.py b/propms/property_management_solution/doctype/lease/lease.py index 3d5ae9a4..d30a777d 100755 --- a/propms/property_management_solution/doctype/lease/lease.py +++ b/propms/property_management_solution/doctype/lease/lease.py @@ -93,8 +93,8 @@ def validate(self): frappe.db.set_value("Property", prop, "status", "Off Lease in 3 Months") frappe.msgprint( _( - f'Property "{prop}" has now been set Off Lease in 3 Months for Lease "{self.name}"' - ) + 'Property "{0}" has now been set Off Lease in 3 Months for Lease "{1}"' + ).format(prop, self.name) ) elif self.lease_status != "Draft" and ( get_datetime(self.start_date) @@ -104,16 +104,16 @@ def validate(self): frappe.db.set_value("Property", prop, "status", "On Lease") frappe.msgprint( _( - f'Property "{prop}" has now been set On Lease from Active for Lease "{self.name}"' - ) + 'Property "{0}" has now been set On Lease from Active for Lease "{1}"' + ).format(prop, self.name) ) else: if self.lease_status != "Draft": frappe.db.set_value("Property", prop, "status", "On Lease") frappe.msgprint( _( - f'Property "{prop}" has now been set On Lease from Active for Lease "{self.name}"' - ) + 'Property "{0}" has now been set On Lease from Active for Lease "{1}"' + ).format(prop, self.name) ) except frappe.ValidationError: raise @@ -225,7 +225,8 @@ def update_lease_statuses(): ), ) - frappe.db.commit() + # Scheduler entry point, not a document hook: the batch has to be durable. + frappe.db.commit() # nosemgrep def get_status_for_lease(lease, today_date=None): @@ -512,10 +513,10 @@ def make_lease_invoice_schedule(leasedoc): idx += 1 invoice_date = add_days(invoice_period_end, 1) - frappe.msgprint("Completed making of invoice schedule.") + frappe.msgprint(_("Completed making of invoice schedule.")) except Exception as e: - frappe.msgprint("Exception error! Check app error log.") + frappe.msgprint(_("Exception error! Check app error log.")) app_error_log(frappe.session.user, str(e)) @@ -580,7 +581,7 @@ def initiate_lease_renewal(source_lease_name): # Post a message/comment to the old lease with initiator and link details comment_text = _( - "Lease renewal draft {0} has been initiated by {1} on {2}." + "Lease renewal draft {0} has been initiated by {1} on {2}." ).format(new_lease.name, frappe.session.user, frappe.utils.formatdate(today())) source_doc.add_comment(text=comment_text) diff --git a/propms/property_management_solution/doctype/property/property.py b/propms/property_management_solution/doctype/property/property.py index 2abc6932..0dba93e6 100755 --- a/propms/property_management_solution/doctype/property/property.py +++ b/propms/property_management_solution/doctype/property/property.py @@ -71,13 +71,19 @@ def add_node(): @frappe.whitelist() def get_children(doctype, parent=None, company=None, is_root=False): + """Tree view children. v16 rejects raw SQL expressions in filters.""" if is_root: parent = "" - fields = ["name as value", "is_group as expandable"] - filters = [ - ["ifnull(`parent_property`, '')", "=", parent], - ["company", "in", (company, None, "")], - ] - - return frappe.get_list(doctype, fields=fields, filters=filters, order_by="name") + filters = [["company", "in", (company, None, "")]] + if parent: + filters.append(["parent_property", "=", parent]) + else: + filters.append(["parent_property", "is", "not set"]) + + return frappe.get_list( + doctype, + fields=["name as value", "is_group as expandable"], + filters=filters, + order_by="name", + ) diff --git a/propms/property_management_solution/point_of_sale.js b/propms/property_management_solution/point_of_sale.js index 03d24bf0..1bb86e79 100755 --- a/propms/property_management_solution/point_of_sale.js +++ b/propms/property_management_solution/point_of_sale.js @@ -1,94 +1,80 @@ -/* Customized version 2019-12-30 */ -/* Added cost_center field */ -// POS page js. -try { - erpnext.pos.PointOfSale = erpnext.pos.PointOfSale.extend({ - }); -} catch (e) { //online POS - class NewPOSCart extends POSCart { - constructor(wrapper) { - super(wrapper); - } - make() { - this.make_dom(); - console.log("AakvaERP POS making cost center field") - this.make_cost_center_field(); - this.make_customer_field(); - this.make_loyalty_points(); - this.make_numpad(); - } - make_cost_center_field() { - // console.log("AakvaERP POS making cost center field") - this.cost_center_field = frappe.ui.form.make_control({ - df: { - fieldtype: 'Link', - label: 'Cost Center', - fieldname: 'cost_center', - options: 'Cost Center', - reqd: 0, - get_query: function () { - return { - filters: { - "is_group": 0 - } - } - }, - onchange: () => { - frappe.call({ - method: "propms.pos.get_pos_data", - freeze: true, - args: { - 'cost_center': this.cost_center_field.get_value() - } - }).then(r => { - var pos_customer = "Cash Customer" - if (r.message) { - if (r.message.hasOwnProperty('customer')) { - pos_customer = r.message.customer - } - } - this.customer_field.set_value(pos_customer); - this.frm.set_value('customer', pos_customer); - //for(var i = 0; i< r.message.lease_item.length; i++ ){ - // this.events.on_field_change(r.message.lease_item[i].lease_item); - //} - }); - }, - }, - parent: this.wrapper.find('.customer-field'), - render_input: true - }); - } +// Copyright (c) 2019, Aakvatech and contributors +// For license information, please see license.txt + +// Adds a Cost Center selector to the Point of Sale cart. Picking a cost centre +// looks up the property's current lease customer and stamps the cost centre on +// the invoice and on every item before checkout. +frappe.provide("propms.point_of_sale"); + +propms.point_of_sale.patch_pos = function () { + const ItemCart = erpnext.PointOfSale && erpnext.PointOfSale.ItemCart; + const Controller = erpnext.PointOfSale && erpnext.PointOfSale.Controller; + + if (!ItemCart || !Controller || ItemCart.prototype.propms_cost_center_patched) { + return; + } + ItemCart.prototype.propms_cost_center_patched = true; + + const make_customer_selector = ItemCart.prototype.make_customer_selector; + + ItemCart.prototype.make_customer_selector = function () { + make_customer_selector.call(this); + this.propms_make_cost_center_field(); }; - POSCart = NewPOSCart; - class PointOfSale extends erpnext.pos.PointOfSale { - constructor(wrapper) { - super(wrapper); - } + ItemCart.prototype.propms_make_cost_center_field = function () { + this.$customer_section.append(`
    `); + + this.cost_center_field = frappe.ui.form.make_control({ + df: { + fieldtype: "Link", + label: __("Cost Center"), + fieldname: "propms_cost_center", + options: "Cost Center", + placeholder: __("Select cost center"), + get_query: () => ({ filters: { is_group: 0 } }), + onchange: () => this.propms_apply_cost_center(this.cost_center_field.get_value()), + }, + parent: this.$customer_section.find(".propms-cost-center-field"), + render_input: true, + }); + this.cost_center_field.toggle_label(false); + }; - submit_sales_invoice() { - for (var i = 0; i < this.frm.doc.items.length; i++) { - console.log("AakvaERP POS ", document.querySelector('input[data-fieldname="cost_center"]').value) - this.frm.doc.items[i].cost_center = document.querySelector('input[data-fieldname="cost_center"]').value; - } - this.frm.doc.cost_center = document.querySelector('input[data-fieldname="cost_center"]').value; - this.frm.savesubmit() - .then((r) => { - if (r && r.doc) { - this.frm.doc.docstatus = r.doc.docstatus; - frappe.show_alert({ - indicator: 'green', - message: __(`Sales invoice ${r.doc.name} created succesfully`) - }); + ItemCart.prototype.propms_apply_cost_center = function (cost_center) { + const frm = this.events.get_frm(); + frm.doc.cost_center = cost_center || ""; + (frm.doc.items || []).forEach((item) => { + item.cost_center = cost_center || ""; + }); - this.toggle_editing(); - this.set_form_action(); - this.set_primary_action_in_modal(); - } - }); + if (!cost_center) { + return; } + frappe.call({ + method: "propms.pos.get_pos_data", + freeze: true, + args: { cost_center: cost_center }, + }).then((r) => { + const customer = (r.message && r.message.customer) || "Cash Customer"; + this.customer_field && this.customer_field.set_value(customer); + frm.set_value("customer", customer); + }); }; - erpnext.pos.PointOfSale = PointOfSale; -} + + const save_and_checkout = Controller.prototype.save_and_checkout; + + Controller.prototype.save_and_checkout = async function () { + const cost_center = this.cart && this.cart.cost_center_field && this.cart.cost_center_field.get_value(); + if (cost_center) { + this.frm.doc.cost_center = cost_center; + (this.frm.doc.items || []).forEach((item) => { + item.cost_center = cost_center; + }); + } + return save_and_checkout.call(this); + }; +}; + +frappe.require("point-of-sale.bundle.js", () => propms.point_of_sale.patch_pos()); diff --git a/propms/property_management_solution/report/self_consumption_in_maintenance_job_card/self_consumption_in_maintenance_job_card.json b/propms/property_management_solution/report/self_consumption_in_maintenance_job_card/self_consumption_in_maintenance_job_card.json index 2b678616..25cf98a4 100644 --- a/propms/property_management_solution/report/self_consumption_in_maintenance_job_card/self_consumption_in_maintenance_job_card.json +++ b/propms/property_management_solution/report/self_consumption_in_maintenance_job_card/self_consumption_in_maintenance_job_card.json @@ -7,13 +7,13 @@ "doctype": "Report", "idx": 0, "is_standard": "Yes", - "modified": "2020-04-18 23:59:53.516924", + "modified": "2026-08-27 12:00:00.000000", "modified_by": "Administrator", "module": "Property Management Solution", "name": "Self Consumption in Maintenance Job Card", "owner": "Administrator", "prepared_report": 0, - "query": "SELECT jc.name as \"Maintenance Job Card:Link/Issue:200\",\n jc.property_name as \"Property:Link/Property:200\",\n jc.customer as \"Customer:Link/Customer:200\",\n jc.opening_date as \"Opening Date:Date:120\",\n jc.resolution_date as \"Resolution Date:Date:120\",\n jc.modified_by as \"Modified By:Link/User:200\",\n jc.status as \"Status:Data:120\",\n jcmb.item as \"Item:Link/Item:200\",\n jcmb.quantity as \"Quantity:Float:200\",\n jcmb.uom as \"UOM:Link/UOM:200\",\n jcmb.rate as \"Rate:Currency:120\",\n jcmb.amount as \"Amount:Currency:200\",\n \"\" as \"Sales Invoice:Link/Sales Invoice:200\"\n FROM `tabIssue` jc\n INNER JOIN `tabIssue Materials Detail` jcmb on jcmb.parent = jc.name\n WHERE jc.docstatus = 0\n AND DATE(jc.opening_date) BETWEEN %(start_date)s AND %(end_date)s\nUNION ALL\nSELECT jc.name as \"Maintenance Job Card:Link/Issue:200\",\n jc.property_name as \"Property:Link/Property:200\",\n jc.customer as \"Customer:Link/Customer:200\",\n jc.opening_date as \"Opening Date:Date:120\",\n jc.resolution_date as \"Resolution Date:Date:120\",\n jc.modified_by as \"Modified By:Link/User:200\",\n jc.status as \"Status:Data:120\",\n jcb.item as \"Item:Link/Item:200\",\n jcb.quantity as \"Quantity:Float:200\",\n jcb.uom as \"UOM:Link/UOM:200\",\n jcb.rate as \"Rate:Currency:120\",\n jcb.amount as \"Amount:Currency:200\",\n jcb.sales_invoice as \"Sales Invoice:Link/Sales Invoice:200\"\n FROM `tabIssue` jc\n INNER JOIN `tabIssue Materials Billed` jcb on jcb.parent = jc.name\n WHERE jc.docstatus = 1\n AND DATE(jc.opening_date) BETWEEN %(start_date)s AND %(end_date)s\nORDER BY \"Opening Date\", \"Maintenance Job Card\", \"Sales Invoice\", \"Item\"\n", + "query": "SELECT jc.name as \"Maintenance Job Card:Link/Issue:200\",\n jc.property_name as \"Property:Link/Property:200\",\n jc.customer as \"Customer:Link/Customer:200\",\n jc.opening_date as \"Opening Date:Date:120\",\n jc.sla_resolution_date as \"Resolution Date:Date:120\",\n jc.modified_by as \"Modified By:Link/User:200\",\n jc.status as \"Status:Data:120\",\n jcmb.item as \"Item:Link/Item:200\",\n jcmb.quantity as \"Quantity:Float:200\",\n jcmb.uom as \"UOM:Link/UOM:200\",\n jcmb.rate as \"Rate:Currency:120\",\n jcmb.amount as \"Amount:Currency:200\",\n \"\" as \"Sales Invoice:Link/Sales Invoice:200\"\n FROM `tabIssue` jc\n INNER JOIN `tabIssue Materials Detail` jcmb on jcmb.parent = jc.name\n WHERE jc.docstatus = 0\n AND DATE(jc.opening_date) BETWEEN %(start_date)s AND %(end_date)s\nUNION ALL\nSELECT jc.name as \"Maintenance Job Card:Link/Issue:200\",\n jc.property_name as \"Property:Link/Property:200\",\n jc.customer as \"Customer:Link/Customer:200\",\n jc.opening_date as \"Opening Date:Date:120\",\n jc.sla_resolution_date as \"Resolution Date:Date:120\",\n jc.modified_by as \"Modified By:Link/User:200\",\n jc.status as \"Status:Data:120\",\n jcb.item as \"Item:Link/Item:200\",\n jcb.quantity as \"Quantity:Float:200\",\n jcb.uom as \"UOM:Link/UOM:200\",\n jcb.rate as \"Rate:Currency:120\",\n jcb.amount as \"Amount:Currency:200\",\n jcb.sales_invoice as \"Sales Invoice:Link/Sales Invoice:200\"\n FROM `tabIssue` jc\n INNER JOIN `tabIssue Materials Billed` jcb on jcb.parent = jc.name\n WHERE jc.docstatus = 1\n AND DATE(jc.opening_date) BETWEEN %(start_date)s AND %(end_date)s\nORDER BY \"Opening Date\", \"Maintenance Job Card\", \"Sales Invoice\", \"Item\"\n", "ref_doctype": "Issue", "report_name": "Self Consumption in Maintenance Job Card", "report_type": "Query Report", @@ -31,4 +31,4 @@ "role": "Maintenance Job in-charge" } ] -} \ No newline at end of file +} diff --git a/pyproject.toml b/pyproject.toml index 06898420..64ee0d3d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,8 +19,8 @@ requires = ["flit_core >=3.4,<4"] build-backend = "flit_core.buildapi" [tool.bench.frappe-dependencies] -frappe = ">=16.0.0,<17.0.0" -erpnext = ">=16.0.0,<17.0.0" +frappe = ">=15.0.0,<17.0.0" +erpnext = ">=15.0.0,<17.0.0" [tool.ruff] line-length = 110 @@ -38,6 +38,3 @@ ignore = ["E101", "E402", "E501", "E741", "W191"] # These dependencies are only installed when developer mode is enabled [tool.bench.dev-dependencies] # package_name = "~=1.1.0" - -[tool.setuptools.dynamic] -version = {attr = "propms.__version__"}