Merge branch 'develop' into pr-dn-return
This commit is contained in:
commit
4153a9f8de
@ -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",
|
||||
@ -147,11 +147,6 @@
|
||||
"link_to": "Trial Balance",
|
||||
"type": "Report"
|
||||
},
|
||||
{
|
||||
"label": "Point of Sale",
|
||||
"link_to": "point-of-sale",
|
||||
"type": "Page"
|
||||
},
|
||||
{
|
||||
"label": "Dashboard",
|
||||
"link_to": "Accounts",
|
||||
|
@ -55,14 +55,48 @@ frappe.ui.form.on('POS Closing Entry', {
|
||||
},
|
||||
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 @@ function set_form_data(data, frm) {
|
||||
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_pos_transaction(d, frm) {
|
||||
})
|
||||
}
|
||||
|
||||
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_payments(d, frm) {
|
||||
})
|
||||
}
|
||||
|
||||
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,
|
||||
|
@ -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",
|
||||
|
@ -182,8 +182,9 @@ class TestPOSInvoice(unittest.TestCase):
|
||||
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 @@ class TestPOSInvoice(unittest.TestCase):
|
||||
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()
|
||||
|
@ -24,11 +24,20 @@ class POSInvoiceMergeLog(Document):
|
||||
|
||||
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 @@ class POSInvoiceMergeLog(Document):
|
||||
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
|
||||
|
||||
|
@ -243,7 +243,8 @@ def make_return_doc(doctype, source_name, target_doc=None):
|
||||
'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
|
||||
|
@ -725,4 +725,5 @@ erpnext.patches.v12_0.rename_lost_reason_detail
|
||||
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.update_returned_qty_in_pr_dn
|
||||
erpnext.patches.v13_0.change_default_pos_print_format
|
||||
erpnext.patches.v13_0.update_returned_qty_in_pr_dn
|
||||
|
8
erpnext/patches/v13_0/change_default_pos_print_format.py
Normal file
8
erpnext/patches/v13_0/change_default_pos_print_format.py
Normal file
@ -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'""")
|
@ -673,23 +673,14 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
|
||||
);
|
||||
}
|
||||
|
||||
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();
|
||||
},
|
||||
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
@ -9,7 +9,7 @@ frappe.pages['point-of-sale'].on_page_load = function(wrapper) {
|
||||
title: __('Point of Sale'),
|
||||
single_column: true
|
||||
});
|
||||
// online
|
||||
|
||||
wrapper.pos = new erpnext.PointOfSale.Controller(wrapper);
|
||||
window.cur_pos = wrapper.pos;
|
||||
};
|
@ -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 @@ erpnext.PointOfSale.Controller = class {
|
||||
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 @@ erpnext.PointOfSale.Controller = class {
|
||||
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();
|
||||
});
|
||||
}
|
||||
},
|
||||
|
@ -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 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
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 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
<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 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
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 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
)
|
||||
|
||||
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 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
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 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
`
|
||||
);
|
||||
}
|
||||
|
||||
update_customer_section() {
|
||||
|
||||
update_customer_section() {
|
||||
const { customer, email_id='', mobile_no='', image } = this.customer_info || {};
|
||||
|
||||
if (customer) {
|
||||
@ -403,7 +403,7 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
</div>`
|
||||
);
|
||||
} else {
|
||||
// reset customer selector
|
||||
// reset customer selector
|
||||
this.reset_customer_selector();
|
||||
}
|
||||
|
||||
@ -430,9 +430,9 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
</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 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
|
||||
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 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
)
|
||||
|
||||
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 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
} 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 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
|
||||
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 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
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 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
$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 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
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 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
&& 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 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
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 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
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 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
|
||||
toggle_component(show) {
|
||||
show ? this.$component.removeClass('d-none') : this.$component.addClass('d-none');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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 @@ erpnext.PointOfSale.ItemDetails = class {
|
||||
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 @@ erpnext.PointOfSale.ItemDetails = class {
|
||||
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 @@ erpnext.PointOfSale.ItemDetails = class {
|
||||
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 @@ erpnext.PointOfSale.ItemDetails = class {
|
||||
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 @@ erpnext.PointOfSale.ItemDetails = class {
|
||||
} 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 @@ erpnext.PointOfSale.ItemDetails = class {
|
||||
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 @@ erpnext.PointOfSale.ItemDetails = class {
|
||||
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 @@ erpnext.PointOfSale.ItemDetails = class {
|
||||
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 @@ erpnext.PointOfSale.ItemDetails = class {
|
||||
};
|
||||
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 @@ erpnext.PointOfSale.ItemDetails = class {
|
||||
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 @@ erpnext.PointOfSale.ItemDetails = class {
|
||||
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 @@ erpnext.PointOfSale.ItemDetails = class {
|
||||
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 @@ erpnext.PointOfSale.ItemDetails = class {
|
||||
});
|
||||
}
|
||||
|
||||
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 @@ erpnext.PointOfSale.ItemDetails = class {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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 @@ erpnext.PointOfSale.ItemDetails = class {
|
||||
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 @@ erpnext.PointOfSale.ItemDetails = class {
|
||||
|
||||
toggle_component(show) {
|
||||
show ? this.$component.removeClass('d-none') : this.$component.addClass('d-none');
|
||||
}
|
||||
}
|
||||
}
|
@ -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 @@ erpnext.PointOfSale.ItemSelector = class {
|
||||
},
|
||||
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 @@ erpnext.PointOfSale.ItemSelector = class {
|
||||
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');
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user