Merge branch 'develop' into youtube-analytics
diff --git a/erpnext/accounts/desk_page/accounting/accounting.json b/erpnext/accounts/desk_page/accounting/accounting.json
index 2c52314..3f23ba9 100644
--- a/erpnext/accounts/desk_page/accounting/accounting.json
+++ b/erpnext/accounts/desk_page/accounting/accounting.json
@@ -43,7 +43,7 @@
{
"hidden": 0,
"label": "Bank Statement",
- "links": "[\n {\n \"label\": \"Bank\",\n \"name\": \"Bank\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Account\",\n \"name\": \"Bank Account\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Reconciliation\",\n \"name\": \"bank-reconciliation\",\n \"type\": \"page\"\n },\n {\n \"label\": \"Bank Clearance\",\n \"name\": \"Bank Clearance\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Statement Transaction Entry\",\n \"name\": \"Bank Statement Transaction Entry\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Statement Settings\",\n \"name\": \"Bank Statement Settings\",\n \"type\": \"doctype\"\n }\n]"
+ "links": "[\n {\n \"label\": \"Bank\",\n \"name\": \"Bank\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Account\",\n \"name\": \"Bank Account\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Statement Transaction Entry\",\n \"name\": \"Bank Statement Transaction Entry\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Statement Settings\",\n \"name\": \"Bank Statement Settings\",\n \"type\": \"doctype\"\n }\n]"
},
{
"hidden": 0,
@@ -98,7 +98,7 @@
"idx": 0,
"is_standard": 1,
"label": "Accounting",
- "modified": "2020-09-03 10:37:07.865801",
+ "modified": "2020-09-09 11:45:33.766400",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounting",
@@ -148,11 +148,6 @@
"type": "Report"
},
{
- "label": "Point of Sale",
- "link_to": "point-of-sale",
- "type": "Page"
- },
- {
"label": "Dashboard",
"link_to": "Accounts",
"type": "Dashboard"
diff --git a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js
index 8dcd2e4..9336fc3 100644
--- a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js
+++ b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js
@@ -55,14 +55,48 @@
},
callback: (r) => {
let pos_docs = r.message;
- set_form_data(pos_docs, frm)
- refresh_fields(frm)
- set_html_data(frm)
+ set_form_data(pos_docs, frm);
+ refresh_fields(frm);
+ set_html_data(frm);
}
})
}
});
+cur_frm.cscript.before_pos_transactions_remove = function(doc, cdt, cdn) {
+ const removed_row = locals[cdt][cdn];
+
+ if (!removed_row.pos_invoice) return;
+
+ frappe.db.get_doc("POS Invoice", removed_row.pos_invoice).then(doc => {
+ cur_frm.doc.grand_total -= flt(doc.grand_total);
+ cur_frm.doc.net_total -= flt(doc.net_total);
+ cur_frm.doc.total_quantity -= flt(doc.total_qty);
+ refresh_payments(doc, cur_frm, 1);
+ refresh_taxes(doc, cur_frm, 1);
+ refresh_fields(cur_frm);
+ set_html_data(cur_frm);
+ });
+}
+
+frappe.ui.form.on('POS Invoice Reference', {
+ pos_invoice(frm, cdt, cdn) {
+ const added_row = locals[cdt][cdn];
+
+ if (!added_row.pos_invoice) return;
+
+ frappe.db.get_doc("POS Invoice", added_row.pos_invoice).then(doc => {
+ frm.doc.grand_total += flt(doc.grand_total);
+ frm.doc.net_total += flt(doc.net_total);
+ frm.doc.total_quantity += flt(doc.total_qty);
+ refresh_payments(doc, frm);
+ refresh_taxes(doc, frm);
+ refresh_fields(frm);
+ set_html_data(frm);
+ });
+ }
+})
+
frappe.ui.form.on('POS Closing Entry Detail', {
closing_amount: (frm, cdt, cdn) => {
const row = locals[cdt][cdn];
@@ -76,8 +110,8 @@
frm.doc.grand_total += flt(d.grand_total);
frm.doc.net_total += flt(d.net_total);
frm.doc.total_quantity += flt(d.total_qty);
- add_to_payments(d, frm);
- add_to_taxes(d, frm);
+ refresh_payments(d, frm);
+ refresh_taxes(d, frm);
});
}
@@ -90,11 +124,12 @@
})
}
-function add_to_payments(d, frm) {
+function refresh_payments(d, frm, remove) {
d.payments.forEach(p => {
const payment = frm.doc.payment_reconciliation.find(pay => pay.mode_of_payment === p.mode_of_payment);
if (payment) {
- payment.expected_amount += flt(p.amount);
+ if (!remove) payment.expected_amount += flt(p.amount);
+ else payment.expected_amount -= flt(p.amount);
} else {
frm.add_child("payment_reconciliation", {
mode_of_payment: p.mode_of_payment,
@@ -105,11 +140,12 @@
})
}
-function add_to_taxes(d, frm) {
+function refresh_taxes(d, frm, remove) {
d.taxes.forEach(t => {
const tax = frm.doc.taxes.find(tx => tx.account_head === t.account_head && tx.rate === t.rate);
if (tax) {
- tax.amount += flt(t.tax_amount);
+ if (!remove) tax.amount += flt(t.tax_amount);
+ else tax.amount -= flt(t.tax_amount);
} else {
frm.add_child("taxes", {
account_head: t.account_head,
diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json
index 2a2e3df..4780688 100644
--- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json
+++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json
@@ -279,7 +279,8 @@
"fieldtype": "Check",
"label": "Is Return (Credit Note)",
"no_copy": 1,
- "print_hide": 1
+ "print_hide": 1,
+ "set_only_once": 1
},
{
"fieldname": "column_break1",
@@ -1578,9 +1579,10 @@
}
],
"icon": "fa fa-file-text",
+ "index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
- "modified": "2020-05-29 15:08:39.337385",
+ "modified": "2020-09-07 12:43:09.138720",
"modified_by": "Administrator",
"module": "Accounts",
"name": "POS Invoice",
diff --git a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py
index 9c62a87..514a2ac 100644
--- a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py
+++ b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py
@@ -182,8 +182,9 @@
def test_pos_returns_with_repayment(self):
pos = create_pos_invoice(qty = 10, do_not_save=True)
+ pos.set('payments', [])
pos.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - _TC', 'amount': 500})
- pos.append("payments", {'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 500})
+ pos.append("payments", {'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 500, 'default': 1})
pos.insert()
pos.submit()
@@ -200,8 +201,9 @@
income_account = "Sales - _TC", expense_account = "Cost of Goods Sold - _TC", rate=105,
cost_center = "Main - _TC", do_not_save=True)
+ pos.set('payments', [])
pos.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - _TC', 'amount': 50})
- pos.append("payments", {'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 60})
+ pos.append("payments", {'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 60, 'default': 1})
pos.insert()
pos.submit()
diff --git a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py
index 00dbad5..11b9d25 100644
--- a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py
+++ b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py
@@ -24,11 +24,20 @@
def validate_pos_invoice_status(self):
for d in self.pos_invoices:
- status, docstatus = frappe.db.get_value('POS Invoice', d.pos_invoice, ['status', 'docstatus'])
+ status, docstatus, is_return, return_against = frappe.db.get_value(
+ 'POS Invoice', d.pos_invoice, ['status', 'docstatus', 'is_return', 'return_against'])
+
if docstatus != 1:
frappe.throw(_("Row #{}: POS Invoice {} is not submitted yet").format(d.idx, d.pos_invoice))
- if status in ['Consolidated']:
+ if status == "Consolidated":
frappe.throw(_("Row #{}: POS Invoice {} has been {}").format(d.idx, d.pos_invoice, status))
+ if is_return and return_against not in [d.pos_invoice for d in self.pos_invoices] and status != "Consolidated":
+ # if return entry is not getting merged in the current pos closing and if it is not consolidated
+ frappe.throw(
+ _("Row #{}: Return Invoice {} cannot be made against unconsolidated invoice. \
+ You can add original invoice {} manually to proceed.")
+ .format(d.idx, frappe.bold(d.pos_invoice), frappe.bold(return_against))
+ )
def on_submit(self):
pos_invoice_docs = [frappe.get_doc("POS Invoice", d.pos_invoice) for d in self.pos_invoices]
@@ -36,12 +45,12 @@
returns = [d for d in pos_invoice_docs if d.get('is_return') == 1]
sales = [d for d in pos_invoice_docs if d.get('is_return') == 0]
- sales_invoice = self.process_merging_into_sales_invoice(sales)
+ sales_invoice, credit_note = "", ""
+ if sales:
+ sales_invoice = self.process_merging_into_sales_invoice(sales)
- if len(returns):
+ if returns:
credit_note = self.process_merging_into_credit_note(returns)
- else:
- credit_note = ""
self.save() # save consolidated_sales_invoice & consolidated_credit_note ref in merge log
diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py
index a03dee1..afc5f81 100644
--- a/erpnext/controllers/sales_and_purchase_return.py
+++ b/erpnext/controllers/sales_and_purchase_return.py
@@ -242,7 +242,8 @@
'type': data.type,
'amount': -1 * paid_amount,
'base_amount': -1 * base_paid_amount,
- 'account': data.account
+ 'account': data.account,
+ 'default': data.default
})
if doc.is_pos:
doc.paid_amount = -1 * source.paid_amount
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 20d035f..4935fe0 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -725,4 +725,5 @@
erpnext.patches.v13_0.drop_razorpay_payload_column
erpnext.patches.v13_0.update_start_end_date_for_old_shift_assignment
erpnext.patches.v13_0.setting_custom_roles_for_some_regional_reports
+erpnext.patches.v13_0.change_default_pos_print_format
erpnext.patches.v13_0.set_youtube_video_id
diff --git a/erpnext/patches/v13_0/change_default_pos_print_format.py b/erpnext/patches/v13_0/change_default_pos_print_format.py
new file mode 100644
index 0000000..605a29e
--- /dev/null
+++ b/erpnext/patches/v13_0/change_default_pos_print_format.py
@@ -0,0 +1,8 @@
+from __future__ import unicode_literals
+import frappe
+
+def execute():
+ frappe.db.sql(
+ """UPDATE `tabPOS Profile` profile
+ SET profile.`print_format` = 'POS Invoice'
+ WHERE profile.`print_format` = 'Point of Sale'""")
\ No newline at end of file
diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js
index 6951539..6d58fd2 100644
--- a/erpnext/public/js/controllers/taxes_and_totals.js
+++ b/erpnext/public/js/controllers/taxes_and_totals.js
@@ -673,23 +673,14 @@
);
}
- frappe.db.get_value('Sales Invoice Payment', {'parent': this.frm.doc.pos_profile, 'default': 1},
- ['mode_of_payment', 'account', 'type'], (value) => {
- if (this.frm.is_dirty()) {
- frappe.model.clear_table(this.frm.doc, 'payments');
- if (value) {
- let row = frappe.model.add_child(this.frm.doc, 'Sales Invoice Payment', 'payments');
- row.mode_of_payment = value.mode_of_payment;
- row.type = value.type;
- row.account = value.account;
- row.default = 1;
- row.amount = total_amount_to_pay;
- } else {
- this.frm.set_value('is_pos', 1);
- }
- this.frm.refresh_fields();
- }
- }, 'Sales Invoice');
+ this.frm.doc.payments.find(pay => {
+ if (pay.default) {
+ pay.amount = total_amount_to_pay;
+ } else {
+ pay.amount = 0.0
+ }
+ });
+ this.frm.refresh_fields();
this.calculate_paid_amount();
},
diff --git a/erpnext/selling/desk_page/retail/retail.json b/erpnext/selling/desk_page/retail/retail.json
index 581e14c..c4ddf26 100644
--- a/erpnext/selling/desk_page/retail/retail.json
+++ b/erpnext/selling/desk_page/retail/retail.json
@@ -2,8 +2,18 @@
"cards": [
{
"hidden": 0,
- "label": "Retail Operations",
- "links": "[\n {\n \"description\": \"Setup default values for POS Invoices\",\n \"label\": \"Point of Sale Profile\",\n \"name\": \"POS Profile\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"POS Profile\"\n ],\n \"description\": \"Point of Sale\",\n \"label\": \"Point of Sale\",\n \"name\": \"point-of-sale\",\n \"onboard\": 1,\n \"type\": \"page\"\n },\n {\n \"description\": \"Setup mode of POS (Online / Offline)\",\n \"label\": \"POS Settings\",\n \"name\": \"POS Settings\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Cashier Closing\",\n \"label\": \"Cashier Closing\",\n \"name\": \"Cashier Closing\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"To make Customer based incentive schemes.\",\n \"label\": \"Loyalty Program\",\n \"name\": \"Loyalty Program\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"To view logs of Loyalty Points assigned to a Customer.\",\n \"label\": \"Loyalty Point Entry\",\n \"name\": \"Loyalty Point Entry\",\n \"type\": \"doctype\"\n }\n]"
+ "label": "Settings & Configurations",
+ "links": "[\n {\n \"description\": \"Setup default values for POS Invoices\",\n \"label\": \"Point-of-Sale Profile\",\n \"name\": \"POS Profile\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"POS Settings\",\n \"name\": \"POS Settings\",\n \"type\": \"doctype\"\n }\n]"
+ },
+ {
+ "hidden": 0,
+ "label": "Loyalty Program",
+ "links": "[\n {\n \"description\": \"To make Customer based incentive schemes.\",\n \"label\": \"Loyalty Program\",\n \"name\": \"Loyalty Program\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"To view logs of Loyalty Points assigned to a Customer.\",\n \"label\": \"Loyalty Point Entry\",\n \"name\": \"Loyalty Point Entry\",\n \"type\": \"doctype\"\n }\n]"
+ },
+ {
+ "hidden": 0,
+ "label": "Opening & Closing",
+ "links": "[\n {\n \"label\": \"POS Opening Entry\",\n \"name\": \"POS Opening Entry\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"POS Closing Entry\",\n \"name\": \"POS Closing Entry\",\n \"type\": \"doctype\"\n }\n]"
}
],
"category": "Domains",
@@ -18,7 +28,7 @@
"idx": 0,
"is_standard": 1,
"label": "Retail",
- "modified": "2020-08-20 18:00:07.515691",
+ "modified": "2020-09-09 11:46:28.297435",
"modified_by": "Administrator",
"module": "Selling",
"name": "Retail",
@@ -28,25 +38,10 @@
"restrict_to_domain": "Retail",
"shortcuts": [
{
- "color": "#9deca2",
"doc_view": "",
- "format": "{} Active",
- "label": "Point of Sale Profile",
- "link_to": "POS Profile",
- "stats_filter": "{\n \"disabled\": 0\n}",
- "type": "DocType"
- },
- {
- "doc_view": "",
- "label": "Point of Sale",
+ "label": "Point Of Sale",
"link_to": "point-of-sale",
"type": "Page"
- },
- {
- "doc_view": "",
- "label": "POS Settings",
- "link_to": "POS Settings",
- "type": "DocType"
}
]
}
\ No newline at end of file
diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.js b/erpnext/selling/page/point_of_sale/point_of_sale.js
index 2ce0b27..8d4ac78 100644
--- a/erpnext/selling/page/point_of_sale/point_of_sale.js
+++ b/erpnext/selling/page/point_of_sale/point_of_sale.js
@@ -9,7 +9,7 @@
title: __('Point of Sale'),
single_column: true
});
- // online
+
wrapper.pos = new erpnext.PointOfSale.Controller(wrapper);
window.cur_pos = wrapper.pos;
};
\ No newline at end of file
diff --git a/erpnext/selling/page/point_of_sale/pos_controller.js b/erpnext/selling/page/point_of_sale/pos_controller.js
index ae5471b..5018254 100644
--- a/erpnext/selling/page/point_of_sale/pos_controller.js
+++ b/erpnext/selling/page/point_of_sale/pos_controller.js
@@ -8,7 +8,7 @@
{% include "erpnext/selling/page/point_of_sale/pos_past_order_summary.js" %}
erpnext.PointOfSale.Controller = class {
- constructor(wrapper) {
+ constructor(wrapper) {
this.wrapper = $(wrapper).find('.layout-main-section');
this.page = wrapper.page;
@@ -36,7 +36,7 @@
const table_fields = [
{ fieldname: "mode_of_payment", fieldtype: "Link", in_list_view: 1, label: "Mode of Payment", options: "Mode of Payment", reqd: 1 },
{ fieldname: "opening_amount", fieldtype: "Currency", default: 0, in_list_view: 1, label: "Opening Amount",
- options: "company:company_currency", reqd: 1 }
+ options: "company:company_currency" }
];
const dialog = new frappe.ui.Dialog({
@@ -51,29 +51,16 @@
options: 'POS Profile', fieldname: 'pos_profile', reqd: 1,
onchange: () => {
const pos_profile = dialog.fields_dict.pos_profile.get_value();
- const company = dialog.fields_dict.company.get_value();
- const user = frappe.session.user
- if (!pos_profile || !company || !user) return;
+ if (!pos_profile) return;
- // auto fetch last closing entry's balance details
- frappe.db.get_list("POS Closing Entry", {
- filters: { company, pos_profile, user },
- limit: 1,
- order_by: 'period_end_date desc'
- }).then((res) => {
- if (!res.length) return;
- const pos_closing_entry = res[0];
- frappe.db.get_doc("POS Closing Entry", pos_closing_entry.name).then(({ payment_reconciliation }) => {
- dialog.fields_dict.balance_details.df.data = [];
- payment_reconciliation.forEach(pay => {
- const { mode_of_payment } = pay;
- dialog.fields_dict.balance_details.df.data.push({
- mode_of_payment: mode_of_payment
- });
- });
- dialog.fields_dict.balance_details.grid.refresh();
+ frappe.db.get_doc("POS Profile", pos_profile).then(doc => {
+ dialog.fields_dict.balance_details.df.data = [];
+ doc.payments.forEach(pay => {
+ const { mode_of_payment } = pay;
+ dialog.fields_dict.balance_details.df.data.push({ mode_of_payment });
});
+ dialog.fields_dict.balance_details.grid.refresh();
});
}
},
diff --git a/erpnext/selling/page/point_of_sale/pos_item_cart.js b/erpnext/selling/page/point_of_sale/pos_item_cart.js
index eadeb8f..724b60b 100644
--- a/erpnext/selling/page/point_of_sale/pos_item_cart.js
+++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js
@@ -1,36 +1,36 @@
erpnext.PointOfSale.ItemCart = class {
- constructor({ wrapper, events }) {
+ constructor({ wrapper, events }) {
this.wrapper = wrapper;
this.events = events;
- this.customer_info = undefined;
-
- this.init_component();
- }
-
- init_component() {
- this.prepare_dom();
- this.init_child_components();
+ this.customer_info = undefined;
+
+ this.init_component();
+ }
+
+ init_component() {
+ this.prepare_dom();
+ this.init_child_components();
this.bind_events();
this.attach_shortcuts();
- }
+ }
- prepare_dom() {
+ prepare_dom() {
this.wrapper.append(
- `<section class="col-span-4 flex flex-col shadow rounded item-cart bg-white mx-h-70 h-100"></section>`
- )
+ `<section class="col-span-4 flex flex-col shadow rounded item-cart bg-white mx-h-70 h-100"></section>`
+ )
- this.$component = this.wrapper.find('.item-cart');
- }
+ this.$component = this.wrapper.find('.item-cart');
+ }
- init_child_components() {
- this.init_customer_selector();
- this.init_cart_components();
- }
+ init_child_components() {
+ this.init_customer_selector();
+ this.init_cart_components();
+ }
- init_customer_selector() {
+ init_customer_selector() {
this.$component.append(
- `<div class="customer-section rounded flex flex-col m-8 mb-0"></div>`
- )
+ `<div class="customer-section rounded flex flex-col m-8 mb-0"></div>`
+ )
this.$customer_section = this.$component.find('.customer-section');
}
@@ -41,9 +41,9 @@
this.make_customer_selector();
this.customer_field.set_focus();
}
-
- init_cart_components() {
- this.$component.append(
+
+ init_cart_components() {
+ this.$component.append(
`<div class="cart-container flex flex-col items-center rounded flex-1 relative">
<div class="absolute flex flex-col p-8 pt-0 w-full h-full">
<div class="flex text-grey cart-header pt-2 pb-2 p-4 mt-2 mb-2 w-full f-shrink-0">
@@ -55,23 +55,23 @@
<div class="cart-totals-section flex flex-col w-full mt-4 f-shrink-0"></div>
<div class="numpad-section flex flex-col mt-4 d-none w-full p-8 pt-0 pb-0 f-shrink-0"></div>
</div>
- </div>`
- );
+ </div>`
+ );
this.$cart_container = this.$component.find('.cart-container');
this.make_cart_totals_section();
this.make_cart_items_section();
- this.make_cart_numpad();
- }
+ this.make_cart_numpad();
+ }
- make_cart_items_section() {
- this.$cart_header = this.$component.find('.cart-header');
- this.$cart_items_wrapper = this.$component.find('.cart-items-section');
+ make_cart_items_section() {
+ this.$cart_header = this.$component.find('.cart-header');
+ this.$cart_items_wrapper = this.$component.find('.cart-items-section');
this.make_no_items_placeholder();
- }
-
- make_no_items_placeholder() {
+ }
+
+ make_no_items_placeholder() {
this.$cart_header.addClass('d-none');
this.$cart_items_wrapper.html(
`<div class="no-item-wrapper flex items-center h-18">
@@ -81,8 +81,8 @@
this.$cart_items_wrapper.addClass('mt-4 border-grey border-dashed');
}
- make_cart_totals_section() {
- this.$totals_section = this.$component.find('.cart-totals-section');
+ make_cart_totals_section() {
+ this.$totals_section = this.$component.find('.cart-totals-section');
this.$totals_section.append(
`<div class="add-discount flex items-center pt-4 pb-4 pr-4 pl-4 text-grey pointer no-select d-none">
@@ -116,9 +116,9 @@
)
this.$add_discount_elem = this.$component.find(".add-discount");
- }
-
- make_cart_numpad() {
+ }
+
+ make_cart_numpad() {
this.$numpad_section = this.$component.find('.numpad-section');
this.number_pad = new erpnext.PointOfSale.NumberPad({
@@ -155,9 +155,9 @@
Checkout
</div>`
)
- }
-
- bind_events() {
+ }
+
+ bind_events() {
const me = this;
this.$customer_section.on('click', '.add-remove-customer', function (e) {
const customer_info_is_visible = me.$cart_container.hasClass('d-none');
@@ -381,8 +381,8 @@
`
);
}
-
- update_customer_section() {
+
+ update_customer_section() {
const { customer, email_id='', mobile_no='', image } = this.customer_info || {};
if (customer) {
@@ -403,7 +403,7 @@
</div>`
);
} else {
- // reset customer selector
+ // reset customer selector
this.reset_customer_selector();
}
@@ -430,9 +430,9 @@
</div>`
}
}
- }
-
- update_totals_section(frm) {
+ }
+
+ update_totals_section(frm) {
if (!frm) frm = this.events.get_frm();
this.render_net_total(frm.doc.base_net_total);
@@ -440,9 +440,9 @@
const taxes = frm.doc.taxes.map(t => { return { description: t.description, rate: t.rate }})
this.render_taxes(frm.doc.base_total_taxes_and_charges, taxes);
- }
-
- render_net_total(value) {
+ }
+
+ render_net_total(value) {
const currency = this.events.get_frm().doc.currency;
this.$totals_section.find('.net-total').html(
`<div class="flex flex-col">
@@ -454,9 +454,9 @@
)
this.$numpad_section.find('.numpad-net-total').html(`Net Total: <span class="text-bold">${format_currency(value, currency)}</span>`)
- }
-
- render_grand_total(value) {
+ }
+
+ render_grand_total(value) {
const currency = this.events.get_frm().doc.currency;
this.$totals_section.find('.grand-total').html(
`<div class="flex flex-col">
@@ -495,20 +495,20 @@
} else {
this.$totals_section.find('.taxes').html('')
}
- }
+ }
- get_cart_item({ item_code, batch_no, uom }) {
+ get_cart_item({ item_code, batch_no, uom }) {
const batch_attr = `[data-batch-no="${escape(batch_no)}"]`;
const item_code_attr = `[data-item-code="${escape(item_code)}"]`;
const uom_attr = `[data-uom=${escape(uom)}]`;
- const item_selector = batch_no ?
- `.cart-item-wrapper${batch_attr}${uom_attr}` : `.cart-item-wrapper${item_code_attr}${uom_attr}`;
-
- return this.$cart_items_wrapper.find(item_selector);
- }
-
- update_item_html(item, remove_item) {
+ const item_selector = batch_no ?
+ `.cart-item-wrapper${batch_attr}${uom_attr}` : `.cart-item-wrapper${item_code_attr}${uom_attr}`;
+
+ return this.$cart_items_wrapper.find(item_selector);
+ }
+
+ update_item_html(item, remove_item) {
const $item = this.get_cart_item(item);
if (remove_item) {
@@ -524,33 +524,33 @@
const no_of_cart_items = this.$cart_items_wrapper.children().length;
no_of_cart_items > 0 && this.highlight_checkout_btn(no_of_cart_items > 0);
-
+
this.update_empty_cart_section(no_of_cart_items);
}
-
- render_cart_item(item_data, $item_to_update) {
+
+ render_cart_item(item_data, $item_to_update) {
const currency = this.events.get_frm().doc.currency;
const me = this;
- if (!$item_to_update.length) {
- this.$cart_items_wrapper.append(
- `<div class="cart-item-wrapper flex items-center h-18 pr-4 pl-4 rounded border-grey pointer no-select"
+ if (!$item_to_update.length) {
+ this.$cart_items_wrapper.append(
+ `<div class="cart-item-wrapper flex items-center h-18 pr-4 pl-4 rounded border-grey pointer no-select"
data-item-code="${escape(item_data.item_code)}" data-uom="${escape(item_data.uom)}"
data-batch-no="${escape(item_data.batch_no || '')}">
- </div>`
- )
- $item_to_update = this.get_cart_item(item_data);
- }
+ </div>`
+ )
+ $item_to_update = this.get_cart_item(item_data);
+ }
$item_to_update.html(
`<div class="flex flex-col flex-1 f-shrink-1 overflow-hidden whitespace-nowrap">
- <div class="text-md text-dark-grey text-bold">
- ${item_data.item_name}
- </div>
- ${get_description_html()}
- </div>
- ${get_rate_discount_html()}
- </div>`
+ <div class="text-md text-dark-grey text-bold">
+ ${item_data.item_name}
+ </div>
+ ${get_description_html()}
+ </div>
+ ${get_rate_discount_html()}
+ </div>`
)
set_dynamic_rate_header_width();
@@ -572,7 +572,7 @@
me.$cart_header.find(".rate-list-header").css("width", max_width);
me.$cart_items_wrapper.find(".rate-col").css("width", max_width);
}
-
+
function get_rate_discount_html() {
if (item_data.rate && item_data.amount && item_data.rate !== item_data.amount) {
return `
@@ -625,7 +625,7 @@
$item_to_update.attr(`data-${selector}`, value);
}
- toggle_checkout_btn(show_checkout) {
+ toggle_checkout_btn(show_checkout) {
if (show_checkout) {
this.$totals_section.find('.checkout-btn').removeClass('d-none');
this.$totals_section.find('.edit-cart-btn').addClass('d-none');
@@ -635,7 +635,7 @@
}
}
- highlight_checkout_btn(toggle) {
+ highlight_checkout_btn(toggle) {
const has_primary_class = this.$totals_section.find('.checkout-btn').hasClass('bg-primary');
if (toggle && !has_primary_class) {
this.$totals_section.find('.checkout-btn').addClass('bg-primary text-white text-lg');
@@ -643,8 +643,8 @@
this.$totals_section.find('.checkout-btn').removeClass('bg-primary text-white text-lg');
}
}
-
- update_empty_cart_section(no_of_cart_items) {
+
+ update_empty_cart_section(no_of_cart_items) {
const $no_item_element = this.$cart_items_wrapper.find('.no-item-wrapper');
// if cart has items and no item is present
@@ -652,27 +652,27 @@
&& this.$cart_items_wrapper.removeClass('mt-4 border-grey border-dashed') && this.$cart_header.removeClass('d-none');
no_of_cart_items === 0 && !$no_item_element.length && this.make_no_items_placeholder();
- }
-
- on_numpad_event($btn) {
+ }
+
+ on_numpad_event($btn) {
const current_action = $btn.attr('data-button-value');
const action_is_field_edit = ['qty', 'discount_percentage', 'rate'].includes(current_action);
this.highlight_numpad_btn($btn, current_action);
- const action_is_pressed_twice = this.prev_action === current_action;
- const first_click_event = !this.prev_action;
- const field_to_edit_changed = this.prev_action && this.prev_action != current_action;
+ const action_is_pressed_twice = this.prev_action === current_action;
+ const first_click_event = !this.prev_action;
+ const field_to_edit_changed = this.prev_action && this.prev_action != current_action;
if (action_is_field_edit) {
if (first_click_event || field_to_edit_changed) {
- this.prev_action = current_action;
+ this.prev_action = current_action;
} else if (action_is_pressed_twice) {
this.prev_action = undefined;
}
- this.numpad_value = '';
-
+ this.numpad_value = '';
+
} else if (current_action === 'checkout') {
this.prev_action = undefined;
this.toggle_item_highlight();
@@ -688,7 +688,7 @@
this.numpad_value = this.numpad_value || 0;
}
- const first_click_event_is_not_field_edit = !action_is_field_edit && first_click_event;
+ const first_click_event_is_not_field_edit = !action_is_field_edit && first_click_event;
if (first_click_event_is_not_field_edit) {
frappe.show_alert({
@@ -708,34 +708,34 @@
this.numpad_value = current_action;
}
- this.events.numpad_event(this.numpad_value, this.prev_action);
- }
-
- highlight_numpad_btn($btn, curr_action) {
- const curr_action_is_highlighted = $btn.hasClass('shadow-inner');
- const curr_action_is_action = ['qty', 'discount_percentage', 'rate', 'done'].includes(curr_action);
+ this.events.numpad_event(this.numpad_value, this.prev_action);
+ }
+
+ highlight_numpad_btn($btn, curr_action) {
+ const curr_action_is_highlighted = $btn.hasClass('shadow-inner');
+ const curr_action_is_action = ['qty', 'discount_percentage', 'rate', 'done'].includes(curr_action);
- if (!curr_action_is_highlighted) {
- $btn.addClass('shadow-inner bg-selected');
- }
- if (this.prev_action === curr_action && curr_action_is_highlighted) {
- // if Qty is pressed twice
- $btn.removeClass('shadow-inner bg-selected');
- }
- if (this.prev_action && this.prev_action !== curr_action && curr_action_is_action) {
- // Order: Qty -> Rate then remove Qty highlight
- const prev_btn = $(`[data-button-value='${this.prev_action}']`);
- prev_btn.removeClass('shadow-inner bg-selected');
- }
- if (!curr_action_is_action || curr_action === 'done') {
- // if numbers are clicked
- setTimeout(() => {
- $btn.removeClass('shadow-inner bg-selected');
- }, 100);
- }
- }
+ if (!curr_action_is_highlighted) {
+ $btn.addClass('shadow-inner bg-selected');
+ }
+ if (this.prev_action === curr_action && curr_action_is_highlighted) {
+ // if Qty is pressed twice
+ $btn.removeClass('shadow-inner bg-selected');
+ }
+ if (this.prev_action && this.prev_action !== curr_action && curr_action_is_action) {
+ // Order: Qty -> Rate then remove Qty highlight
+ const prev_btn = $(`[data-button-value='${this.prev_action}']`);
+ prev_btn.removeClass('shadow-inner bg-selected');
+ }
+ if (!curr_action_is_action || curr_action === 'done') {
+ // if numbers are clicked
+ setTimeout(() => {
+ $btn.removeClass('shadow-inner bg-selected');
+ }, 100);
+ }
+ }
- toggle_numpad(show) {
+ toggle_numpad(show) {
if (show) {
this.$totals_section.addClass('d-none');
this.$numpad_section.removeClass('d-none');
@@ -946,6 +946,6 @@
toggle_component(show) {
show ? this.$component.removeClass('d-none') : this.$component.addClass('d-none');
- }
-
+ }
+
}
diff --git a/erpnext/selling/page/point_of_sale/pos_item_details.js b/erpnext/selling/page/point_of_sale/pos_item_details.js
index 86a1be9..3a5f89b 100644
--- a/erpnext/selling/page/point_of_sale/pos_item_details.js
+++ b/erpnext/selling/page/point_of_sale/pos_item_details.js
@@ -1,28 +1,28 @@
erpnext.PointOfSale.ItemDetails = class {
- constructor({ wrapper, events }) {
+ constructor({ wrapper, events }) {
this.wrapper = wrapper;
- this.events = events;
- this.current_item = {};
+ this.events = events;
+ this.current_item = {};
- this.init_component();
- }
+ this.init_component();
+ }
- init_component() {
- this.prepare_dom();
- this.init_child_components();
+ init_component() {
+ this.prepare_dom();
+ this.init_child_components();
this.bind_events();
this.attach_shortcuts();
- }
+ }
- prepare_dom() {
- this.wrapper.append(
- `<section class="col-span-4 flex shadow rounded item-details bg-white mx-h-70 h-100 d-none"></section>`
- )
+ prepare_dom() {
+ this.wrapper.append(
+ `<section class="col-span-4 flex shadow rounded item-details bg-white mx-h-70 h-100 d-none"></section>`
+ )
- this.$component = this.wrapper.find('.item-details');
- }
+ this.$component = this.wrapper.find('.item-details');
+ }
- init_child_components() {
+ init_child_components() {
this.$component.html(
`<div class="details-container flex flex-col p-8 rounded w-full">
<div class="flex justify-between mb-2">
@@ -49,28 +49,28 @@
this.$item_image = this.$component.find('.item-image');
this.$form_container = this.$component.find('.form-container');
this.$dicount_section = this.$component.find('.discount-section');
- }
+ }
- toggle_item_details_section(item) {
+ toggle_item_details_section(item) {
const { item_code, batch_no, uom } = this.current_item;
const item_code_is_same = item && item_code === item.item_code;
const batch_is_same = item && batch_no == item.batch_no;
const uom_is_same = item && uom === item.uom;
- this.item_has_changed = !item ? false : item_code_is_same && batch_is_same && uom_is_same ? false : true;
+ this.item_has_changed = !item ? false : item_code_is_same && batch_is_same && uom_is_same ? false : true;
- this.events.toggle_item_selector(this.item_has_changed);
+ this.events.toggle_item_selector(this.item_has_changed);
this.toggle_component(this.item_has_changed);
-
+
if (this.item_has_changed) {
- this.doctype = item.doctype;
+ this.doctype = item.doctype;
this.item_meta = frappe.get_meta(this.doctype);
this.name = item.name;
this.item_row = item;
- this.currency = this.events.get_frm().doc.currency;
-
- this.current_item = { item_code: item.item_code, batch_no: item.batch_no, uom: item.uom };
-
+ this.currency = this.events.get_frm().doc.currency;
+
+ this.current_item = { item_code: item.item_code, batch_no: item.batch_no, uom: item.uom };
+
this.render_dom(item);
this.render_discount_dom(item);
this.render_form(item);
@@ -102,9 +102,9 @@
this.events.remove_item_from_cart();
}
}
-
- render_dom(item) {
- let { item_code ,item_name, description, image, price_list_rate } = item;
+
+ render_dom(item) {
+ let { item_code ,item_name, description, image, price_list_rate } = item;
function get_description_html() {
if (description) {
@@ -112,8 +112,8 @@
return description;
}
return ``;
- }
-
+ }
+
this.$item_name.html(item_name);
this.$item_description.html(get_description_html());
this.$item_price.html(format_currency(price_list_rate, this.currency));
@@ -125,9 +125,9 @@
this.$item_image.html(frappe.get_abbr(item_code));
}
- }
-
- render_discount_dom(item) {
+ }
+
+ render_discount_dom(item) {
if (item.discount_percentage) {
this.$dicount_section.html(
`<div class="text-grey line-through mr-4 text-md mb-2">
@@ -141,9 +141,9 @@
} else {
this.$dicount_section.html(``)
}
- }
+ }
- render_form(item) {
+ render_form(item) {
const fields_to_display = this.get_form_fields(item);
this.$form_container.html('');
@@ -157,7 +157,7 @@
const field_meta = this.item_meta.fields.find(df => df.fieldname === fieldname);
fieldname === 'discount_percentage' ? (field_meta.label = __('Discount (%)')) : '';
const me = this;
-
+
this[`${fieldname}_control`] = frappe.ui.form.make_control({
df: {
...field_meta,
@@ -174,16 +174,16 @@
this.make_auto_serial_selection_btn(item);
this.bind_custom_control_change_event();
- }
+ }
- get_form_fields(item) {
+ get_form_fields(item) {
const fields = ['qty', 'uom', 'rate', 'price_list_rate', 'discount_percentage', 'warehouse', 'actual_qty'];
if (item.has_serial_no) fields.push('serial_no');
if (item.has_batch_no) fields.push('batch_no');
return fields;
}
- make_auto_serial_selection_btn(item) {
+ make_auto_serial_selection_btn(item) {
if (item.has_serial_no) {
this.$form_container.append(
`<div class="grid-filler no-select"></div>`
@@ -203,8 +203,8 @@
this.$form_container.find('.serial_no-control').parent().addClass('row-span-2');
}
}
-
- bind_custom_control_change_event() {
+
+ bind_custom_control_change_event() {
const me = this;
if (this.rate_control) {
this.rate_control.df.onchange = function() {
@@ -276,8 +276,8 @@
};
this.batch_no_control.df.onchange = function() {
me.events.set_value_in_current_cart_item('batch-no', this.value);
- me.events.form_updated(me.doctype, me.name, 'batch_no', this.value);
- me.current_item.batch_no = this.value;
+ me.events.form_updated(me.doctype, me.name, 'batch_no', this.value);
+ me.current_item.batch_no = this.value;
}
this.batch_no_control.refresh();
}
@@ -289,9 +289,9 @@
me.current_item.uom = this.value;
}
}
- }
-
- async auto_update_batch_no() {
+ }
+
+ async auto_update_batch_no() {
if (this.serial_no_control && this.batch_no_control) {
const selected_serial_nos = this.serial_no_control.get_value().split(`\n`).filter(s => s);
if (!selected_serial_nos.length) return;
@@ -310,9 +310,9 @@
const batch_no = Object.keys(batch_serial_map)[0];
const batch_serial_nos = batch_serial_map[batch_no].join(`\n`);
// eg. 10 selected serial no. -> 5 belongs to first batch other 5 belongs to second batch
- const serial_nos_belongs_to_other_batch = selected_serial_nos.length !== batch_serial_map[batch_no].length;
-
- const current_batch_no = this.batch_no_control.get_value();
+ const serial_nos_belongs_to_other_batch = selected_serial_nos.length !== batch_serial_map[batch_no].length;
+
+ const current_batch_no = this.batch_no_control.get_value();
current_batch_no != batch_no && await this.batch_no_control.set_value(batch_no);
if (serial_nos_belongs_to_other_batch) {
@@ -326,8 +326,8 @@
this.events.clone_new_batch_item_in_frm(batch_serial_map, this.current_item);
}
}
-
- bind_events() {
+
+ bind_events() {
this.bind_auto_serial_fetch_event();
this.bind_fields_to_numpad_fields();
@@ -345,7 +345,7 @@
});
}
- bind_fields_to_numpad_fields() {
+ bind_fields_to_numpad_fields() {
const me = this;
this.$form_container.on('click', '.input-with-feedback', function() {
const fieldname = $(this).attr('data-fieldname');
@@ -355,8 +355,8 @@
}
});
}
-
- bind_auto_serial_fetch_event() {
+
+ bind_auto_serial_fetch_event() {
this.$form_container.on('click', '.auto-fetch-btn', () => {
this.batch_no_control.set_value('');
let qty = this.qty_control.get_value();
@@ -382,7 +382,7 @@
frappe.msgprint(`Fetched only ${records_length} available serial numbers.`);
this.qty_control.set_value(records_length);
}
- numbers = auto_fetched_serial_numbers.join(`\n`);
+ numbers = auto_fetched_serial_numbers.join(`\n`);
this.serial_no_control.set_value(numbers);
});
})
@@ -390,5 +390,5 @@
toggle_component(show) {
show ? this.$component.removeClass('d-none') : this.$component.addClass('d-none');
- }
+ }
}
\ No newline at end of file
diff --git a/erpnext/selling/page/point_of_sale/pos_item_selector.js b/erpnext/selling/page/point_of_sale/pos_item_selector.js
index ee0c06d..c87b845 100644
--- a/erpnext/selling/page/point_of_sale/pos_item_selector.js
+++ b/erpnext/selling/page/point_of_sale/pos_item_selector.js
@@ -1,115 +1,115 @@
erpnext.PointOfSale.ItemSelector = class {
- constructor({ frm, wrapper, events, pos_profile }) {
+ constructor({ frm, wrapper, events, pos_profile }) {
this.wrapper = wrapper;
this.events = events;
- this.pos_profile = pos_profile;
-
- this.inti_component();
- }
-
- inti_component() {
- this.prepare_dom();
- this.make_search_bar();
- this.load_items_data();
- this.bind_events();
- this.attach_shortcuts();
- }
+ this.pos_profile = pos_profile;
+
+ this.inti_component();
+ }
+
+ inti_component() {
+ this.prepare_dom();
+ this.make_search_bar();
+ this.load_items_data();
+ this.bind_events();
+ this.attach_shortcuts();
+ }
- prepare_dom() {
+ prepare_dom() {
this.wrapper.append(
- `<section class="col-span-6 flex shadow rounded items-selector bg-white mx-h-70 h-100">
- <div class="flex flex-col rounded w-full scroll-y">
- <div class="filter-section flex p-8 pb-2 bg-white sticky z-100">
- <div class="search-field flex f-grow-3 mr-8 items-center text-grey"></div>
- <div class="item-group-field flex f-grow-1 items-center text-grey text-bold"></div>
- </div>
- <div class="flex flex-1 flex-col p-8 pt-2">
- <div class="text-grey mb-6">ALL ITEMS</div>
- <div class="items-container grid grid-cols-4 gap-8">
- </div>
- </div>
- </div>
- </section>`
- );
-
- this.$component = this.wrapper.find('.items-selector');
- }
+ `<section class="col-span-6 flex shadow rounded items-selector bg-white mx-h-70 h-100">
+ <div class="flex flex-col rounded w-full scroll-y">
+ <div class="filter-section flex p-8 pb-2 bg-white sticky z-100">
+ <div class="search-field flex f-grow-3 mr-8 items-center text-grey"></div>
+ <div class="item-group-field flex f-grow-1 items-center text-grey text-bold"></div>
+ </div>
+ <div class="flex flex-1 flex-col p-8 pt-2">
+ <div class="text-grey mb-6">ALL ITEMS</div>
+ <div class="items-container grid grid-cols-4 gap-8">
+ </div>
+ </div>
+ </div>
+ </section>`
+ );
+
+ this.$component = this.wrapper.find('.items-selector');
+ }
- async load_items_data() {
- if (!this.item_group) {
- const res = await frappe.db.get_value("Item Group", {lft: 1, is_group: 1}, "name");
- this.parent_item_group = res.message.name;
- };
- if (!this.price_list) {
- const res = await frappe.db.get_value("POS Profile", this.pos_profile, "selling_price_list");
- this.price_list = res.message.selling_price_list;
- }
+ async load_items_data() {
+ if (!this.item_group) {
+ const res = await frappe.db.get_value("Item Group", {lft: 1, is_group: 1}, "name");
+ this.parent_item_group = res.message.name;
+ };
+ if (!this.price_list) {
+ const res = await frappe.db.get_value("POS Profile", this.pos_profile, "selling_price_list");
+ this.price_list = res.message.selling_price_list;
+ }
- this.get_items({}).then(({message}) => {
- this.render_item_list(message.items);
- });
- }
+ this.get_items({}).then(({message}) => {
+ this.render_item_list(message.items);
+ });
+ }
- get_items({start = 0, page_length = 40, search_value=''}) {
- const price_list = this.events.get_frm().doc?.selling_price_list || this.price_list;
- let { item_group, pos_profile } = this;
+ get_items({start = 0, page_length = 40, search_value=''}) {
+ const price_list = this.events.get_frm().doc?.selling_price_list || this.price_list;
+ let { item_group, pos_profile } = this;
- !item_group && (item_group = this.parent_item_group);
-
+ !item_group && (item_group = this.parent_item_group);
+
return frappe.call({
method: "erpnext.selling.page.point_of_sale.point_of_sale.get_items",
freeze: true,
- args: { start, page_length, price_list, item_group, search_value, pos_profile },
- });
+ args: { start, page_length, price_list, item_group, search_value, pos_profile },
+ });
}
render_item_list(items) {
- this.$items_container = this.$component.find('.items-container');
- this.$items_container.html('');
+ this.$items_container = this.$component.find('.items-container');
+ this.$items_container.html('');
- items.forEach(item => {
- const item_html = this.get_item_html(item);
- this.$items_container.append(item_html);
- })
- }
+ items.forEach(item => {
+ const item_html = this.get_item_html(item);
+ this.$items_container.append(item_html);
+ })
+ }
- get_item_html(item) {
- const { item_image, serial_no, batch_no, barcode, actual_qty, stock_uom } = item;
- const indicator_color = actual_qty > 10 ? "green" : actual_qty !== 0 ? "orange" : "red";
+ get_item_html(item) {
+ const { item_image, serial_no, batch_no, barcode, actual_qty, stock_uom } = item;
+ const indicator_color = actual_qty > 10 ? "green" : actual_qty !== 0 ? "orange" : "red";
- function get_item_image_html() {
- if (item_image) {
- return `<div class="flex items-center justify-center h-32 border-b-grey text-6xl text-grey-100">
- <img class="h-full" src="${item_image}" alt="${item_image}" style="object-fit: cover;">
- </div>`
- } else {
- return `<div class="flex items-center justify-center h-32 bg-light-grey text-6xl text-grey-100">
- ${frappe.get_abbr(item.item_name)}
- </div>`
- }
- }
+ function get_item_image_html() {
+ if (item_image) {
+ return `<div class="flex items-center justify-center h-32 border-b-grey text-6xl text-grey-100">
+ <img class="h-full" src="${item_image}" alt="${item_image}" style="object-fit: cover;">
+ </div>`
+ } else {
+ return `<div class="flex items-center justify-center h-32 bg-light-grey text-6xl text-grey-100">
+ ${frappe.get_abbr(item.item_name)}
+ </div>`
+ }
+ }
return (
- `<div class="item-wrapper rounded shadow pointer no-select" data-item-code="${escape(item.item_code)}"
- data-serial-no="${escape(serial_no)}" data-batch-no="${escape(batch_no)}" data-uom="${escape(stock_uom)}"
- title="Avaiable Qty: ${actual_qty}">
- ${get_item_image_html()}
- <div class="flex items-center pr-4 pl-4 h-10 justify-between">
- <div class="flex items-center f-shrink-1 text-dark-grey overflow-hidden whitespace-nowrap">
- <span class="indicator ${indicator_color}"></span>
- ${frappe.ellipsis(item.item_name, 18)}
- </div>
- <div class="f-shrink-0 text-dark-grey text-bold ml-4">${format_currency(item.price_list_rate, item.currency, 0) || 0}</div>
- </div>
- </div>`
- )
- }
+ `<div class="item-wrapper rounded shadow pointer no-select" data-item-code="${escape(item.item_code)}"
+ data-serial-no="${escape(serial_no)}" data-batch-no="${escape(batch_no)}" data-uom="${escape(stock_uom)}"
+ title="Avaiable Qty: ${actual_qty}">
+ ${get_item_image_html()}
+ <div class="flex items-center pr-4 pl-4 h-10 justify-between">
+ <div class="flex items-center f-shrink-1 text-dark-grey overflow-hidden whitespace-nowrap">
+ <span class="indicator ${indicator_color}"></span>
+ ${frappe.ellipsis(item.item_name, 18)}
+ </div>
+ <div class="f-shrink-0 text-dark-grey text-bold ml-4">${format_currency(item.price_list_rate, item.currency, 0) || 0}</div>
+ </div>
+ </div>`
+ )
+ }
- make_search_bar() {
- const me = this;
- this.$component.find('.search-field').html('');
- this.$component.find('.item-group-field').html('');
+ make_search_bar() {
+ const me = this;
+ this.$component.find('.search-field').html('');
+ this.$component.find('.item-group-field').html('');
this.search_field = frappe.ui.form.make_control({
df: {
@@ -119,104 +119,104 @@
},
parent: this.$component.find('.search-field'),
render_input: true,
- });
+ });
this.item_group_field = frappe.ui.form.make_control({
df: {
label: __('Item Group'),
fieldtype: 'Link',
options: 'Item Group',
- placeholder: __('Select item group'),
- onchange: function() {
- me.item_group = this.value;
- !me.item_group && (me.item_group = me.parent_item_group);
- me.filter_items();
- },
- get_query: function () {
- return {
- query: 'erpnext.selling.page.point_of_sale.point_of_sale.item_group_query',
- filters: {
- pos_profile: me.events.get_frm().doc?.pos_profile
- }
- }
- },
+ placeholder: __('Select item group'),
+ onchange: function() {
+ me.item_group = this.value;
+ !me.item_group && (me.item_group = me.parent_item_group);
+ me.filter_items();
+ },
+ get_query: function () {
+ return {
+ query: 'erpnext.selling.page.point_of_sale.point_of_sale.item_group_query',
+ filters: {
+ pos_profile: me.events.get_frm().doc?.pos_profile
+ }
+ }
+ },
},
- parent: this.$component.find('.item-group-field'),
+ parent: this.$component.find('.item-group-field'),
render_input: true,
- });
- this.search_field.toggle_label(false);
+ });
+ this.search_field.toggle_label(false);
this.item_group_field.toggle_label(false);
}
- bind_events() {
- const me = this;
- onScan.attachTo(document, {
- onScan: (sScancode) => {
- if (this.search_field && this.$component.is(':visible')) {
- this.search_field.set_focus();
- $(this.search_field.$input[0]).val(sScancode).trigger("input");
- this.barcode_scanned = true;
- }
- }
- });
+ bind_events() {
+ const me = this;
+ onScan.attachTo(document, {
+ onScan: (sScancode) => {
+ if (this.search_field && this.$component.is(':visible')) {
+ this.search_field.set_focus();
+ $(this.search_field.$input[0]).val(sScancode).trigger("input");
+ this.barcode_scanned = true;
+ }
+ }
+ });
this.$component.on('click', '.item-wrapper', function() {
const $item = $(this);
const item_code = unescape($item.attr('data-item-code'));
- let batch_no = unescape($item.attr('data-batch-no'));
- let serial_no = unescape($item.attr('data-serial-no'));
- let uom = unescape($item.attr('data-uom'));
-
- // escape(undefined) returns "undefined" then unescape returns "undefined"
- batch_no = batch_no === "undefined" ? undefined : batch_no;
- serial_no = serial_no === "undefined" ? undefined : serial_no;
- uom = uom === "undefined" ? undefined : uom;
+ let batch_no = unescape($item.attr('data-batch-no'));
+ let serial_no = unescape($item.attr('data-serial-no'));
+ let uom = unescape($item.attr('data-uom'));
+
+ // escape(undefined) returns "undefined" then unescape returns "undefined"
+ batch_no = batch_no === "undefined" ? undefined : batch_no;
+ serial_no = serial_no === "undefined" ? undefined : serial_no;
+ uom = uom === "undefined" ? undefined : uom;
- me.events.item_selected({ field: 'qty', value: "+1", item: { item_code, batch_no, serial_no, uom }});
- })
+ me.events.item_selected({ field: 'qty', value: "+1", item: { item_code, batch_no, serial_no, uom }});
+ })
- this.search_field.$input.on('input', (e) => {
+ this.search_field.$input.on('input', (e) => {
clearTimeout(this.last_search);
this.last_search = setTimeout(() => {
const search_term = e.target.value;
this.filter_items({ search_term });
}, 300);
- });
- }
+ });
+ }
- attach_shortcuts() {
- frappe.ui.keys.on("ctrl+i", () => {
- const selector_is_visible = this.$component.is(':visible');
- if (!selector_is_visible) return;
- this.search_field.set_focus();
- });
- frappe.ui.keys.on("ctrl+g", () => {
- const selector_is_visible = this.$component.is(':visible');
- if (!selector_is_visible) return;
- this.item_group_field.set_focus();
- });
- // for selecting the last filtered item on search
- frappe.ui.keys.on("enter", () => {
- const selector_is_visible = this.$component.is(':visible');
- if (!selector_is_visible || this.search_field.get_value() === "") return;
+ attach_shortcuts() {
+ frappe.ui.keys.on("ctrl+i", () => {
+ const selector_is_visible = this.$component.is(':visible');
+ if (!selector_is_visible) return;
+ this.search_field.set_focus();
+ });
+ frappe.ui.keys.on("ctrl+g", () => {
+ const selector_is_visible = this.$component.is(':visible');
+ if (!selector_is_visible) return;
+ this.item_group_field.set_focus();
+ });
+ // for selecting the last filtered item on search
+ frappe.ui.keys.on("enter", () => {
+ const selector_is_visible = this.$component.is(':visible');
+ if (!selector_is_visible || this.search_field.get_value() === "") return;
- if (this.items.length == 1) {
- this.$items_container.find(".item-wrapper").click();
- frappe.utils.play_sound("submit");
- $(this.search_field.$input[0]).val("").trigger("input");
- } else if (this.items.length == 0 && this.barcode_scanned) {
- // only show alert of barcode is scanned and enter is pressed
- frappe.show_alert({
- message: __("No items found. Scan barcode again."),
- indicator: 'orange'
- });
- frappe.utils.play_sound("error");
- this.barcode_scanned = false;
- $(this.search_field.$input[0]).val("").trigger("input");
- }
- });
- }
-
- filter_items({ search_term='' }={}) {
+ if (this.items.length == 1) {
+ this.$items_container.find(".item-wrapper").click();
+ frappe.utils.play_sound("submit");
+ $(this.search_field.$input[0]).val("").trigger("input");
+ } else if (this.items.length == 0 && this.barcode_scanned) {
+ // only show alert of barcode is scanned and enter is pressed
+ frappe.show_alert({
+ message: __("No items found. Scan barcode again."),
+ indicator: 'orange'
+ });
+ frappe.utils.play_sound("error");
+ this.barcode_scanned = false;
+ $(this.search_field.$input[0]).val("").trigger("input");
+ }
+ });
+ }
+
+ filter_items({ search_term='' }={}) {
if (search_term) {
search_term = search_term.toLowerCase();
@@ -227,39 +227,39 @@
this.items = items;
this.render_item_list(items);
return;
- }
+ }
}
this.get_items({ search_value: search_term })
- .then(({ message }) => {
- const { items, serial_no, batch_no, barcode } = message;
+ .then(({ message }) => {
+ const { items, serial_no, batch_no, barcode } = message;
if (search_term && !barcode) {
this.search_index[search_term] = items;
}
this.items = items;
- this.render_item_list(items);
- });
+ this.render_item_list(items);
+ });
}
-
- resize_selector(minimize) {
- minimize ?
- this.$component.find('.search-field').removeClass('mr-8') :
- this.$component.find('.search-field').addClass('mr-8');
+
+ resize_selector(minimize) {
+ minimize ?
+ this.$component.find('.search-field').removeClass('mr-8') :
+ this.$component.find('.search-field').addClass('mr-8');
- minimize ?
- this.$component.find('.filter-section').addClass('flex-col') :
- this.$component.find('.filter-section').removeClass('flex-col');
+ minimize ?
+ this.$component.find('.filter-section').addClass('flex-col') :
+ this.$component.find('.filter-section').removeClass('flex-col');
- minimize ?
- this.$component.removeClass('col-span-6').addClass('col-span-2') :
- this.$component.removeClass('col-span-2').addClass('col-span-6')
+ minimize ?
+ this.$component.removeClass('col-span-6').addClass('col-span-2') :
+ this.$component.removeClass('col-span-2').addClass('col-span-6')
- minimize ?
- this.$items_container.removeClass('grid-cols-4').addClass('grid-cols-1') :
- this.$items_container.removeClass('grid-cols-1').addClass('grid-cols-4')
- }
+ minimize ?
+ this.$items_container.removeClass('grid-cols-4').addClass('grid-cols-1') :
+ this.$items_container.removeClass('grid-cols-1').addClass('grid-cols-4')
+ }
- toggle_component(show) {
+ toggle_component(show) {
show ? this.$component.removeClass('d-none') : this.$component.addClass('d-none');
- }
+ }
}
\ No newline at end of file
diff --git a/erpnext/selling/page/point_of_sale/pos_number_pad.js b/erpnext/selling/page/point_of_sale/pos_number_pad.js
index 2ffc2c0..4b8e841 100644
--- a/erpnext/selling/page/point_of_sale/pos_number_pad.js
+++ b/erpnext/selling/page/point_of_sale/pos_number_pad.js
@@ -1,49 +1,48 @@
erpnext.PointOfSale.NumberPad = class {
- constructor({ wrapper, events, cols, keys, css_classes, fieldnames_map }) {
- this.wrapper = wrapper;
- this.events = events;
- this.cols = cols;
- this.keys = keys;
- this.css_classes = css_classes || [];
- this.fieldnames = fieldnames_map || {};
+ constructor({ wrapper, events, cols, keys, css_classes, fieldnames_map }) {
+ this.wrapper = wrapper;
+ this.events = events;
+ this.cols = cols;
+ this.keys = keys;
+ this.css_classes = css_classes || [];
+ this.fieldnames = fieldnames_map || {};
- this.init_component();
- }
+ this.init_component();
+ }
- init_component() {
- this.prepare_dom();
- this.bind_events();
- }
+ init_component() {
+ this.prepare_dom();
+ this.bind_events();
+ }
- prepare_dom() {
- const { cols, keys, css_classes, fieldnames } = this;
+ prepare_dom() {
+ const { cols, keys, css_classes, fieldnames } = this;
- function get_keys() {
- return keys.reduce((a, row, i) => {
- return a + row.reduce((a2, number, j) => {
- const class_to_append = css_classes && css_classes[i] ? css_classes[i][j] : '';
- const fieldname = fieldnames && fieldnames[number] ?
- fieldnames[number] :
- typeof number === 'string' ? frappe.scrub(number) : number;
-
- return a2 + `<div class="numpad-btn pointer no-select rounded ${class_to_append}
- flex items-center justify-center h-16 text-md border-grey border" data-button-value="${fieldname}">${number}</div>`
- }, '')
- }, '');
- }
+ function get_keys() {
+ return keys.reduce((a, row, i) => {
+ return a + row.reduce((a2, number, j) => {
+ const class_to_append = css_classes && css_classes[i] ? css_classes[i][j] : '';
+ const fieldname = fieldnames && fieldnames[number] ?
+ fieldnames[number] : typeof number === 'string' ? frappe.scrub(number) : number;
- this.wrapper.html(
- `<div class="grid grid-cols-${cols} gap-4">
- ${get_keys()}
- </div>`
- )
- }
+ return a2 + `<div class="numpad-btn pointer no-select rounded ${class_to_append}
+ flex items-center justify-center h-16 text-md border-grey border" data-button-value="${fieldname}">${number}</div>`
+ }, '')
+ }, '');
+ }
- bind_events() {
- const me = this;
- this.wrapper.on('click', '.numpad-btn', function() {
- const $btn = $(this);
- me.events.numpad_event($btn);
- })
- }
+ this.wrapper.html(
+ `<div class="grid grid-cols-${cols} gap-4">
+ ${get_keys()}
+ </div>`
+ )
+ }
+
+ bind_events() {
+ const me = this;
+ this.wrapper.on('click', '.numpad-btn', function() {
+ const $btn = $(this);
+ me.events.numpad_event($btn);
+ });
+ }
}
\ No newline at end of file