Merge branch 'rebrand-ui' of https://github.com/frappe/erpnext into rebrand-ui
This commit is contained in:
commit
94ebbc550e
@ -64,11 +64,11 @@ class OpeningInvoiceCreationTool(Document):
|
||||
prepare_invoice_summary(doctype, invoices)
|
||||
|
||||
return invoices_summary, max_count
|
||||
|
||||
|
||||
def validate_company(self):
|
||||
if not self.company:
|
||||
frappe.throw(_("Please select the Company"))
|
||||
|
||||
|
||||
def set_missing_values(self, row):
|
||||
row.qty = row.qty or 1.0
|
||||
row.temporary_opening_account = row.temporary_opening_account or get_temporary_opening_account(self.company)
|
||||
@ -209,7 +209,7 @@ def start_import(invoices):
|
||||
frappe.db.commit()
|
||||
if errors:
|
||||
frappe.msgprint(_("You had {} errors while creating opening invoices. Check {} for more details")
|
||||
.format(errors, "<a href='#List/Error Log' class='variant-click'>Error Log</a>"), indicator="red", title=_("Error Occured"))
|
||||
.format(errors, "<a href='/app/List/Error Log' class='variant-click'>Error Log</a>"), indicator="red", title=_("Error Occured"))
|
||||
return names
|
||||
|
||||
def publish(index, total, doctype):
|
||||
|
@ -471,7 +471,7 @@ class Asset(AccountsController):
|
||||
|
||||
asset_bought_with_invoice = (purchase_document == self.purchase_invoice)
|
||||
fixed_asset_account = self.get_fixed_asset_account()
|
||||
|
||||
|
||||
cwip_enabled = is_cwip_accounting_enabled(self.asset_category)
|
||||
cwip_account = self.get_cwip_account(cwip_enabled=cwip_enabled)
|
||||
|
||||
@ -503,10 +503,10 @@ class Asset(AccountsController):
|
||||
purchase_document = self.purchase_invoice if asset_bought_with_invoice else self.purchase_receipt
|
||||
|
||||
return purchase_document
|
||||
|
||||
|
||||
def get_fixed_asset_account(self):
|
||||
return get_asset_category_account('fixed_asset_account', None, self.name, None, self.asset_category, self.company)
|
||||
|
||||
|
||||
def get_cwip_account(self, cwip_enabled=False):
|
||||
cwip_account = None
|
||||
try:
|
||||
@ -659,7 +659,7 @@ def transfer_asset(args):
|
||||
|
||||
frappe.db.commit()
|
||||
|
||||
frappe.msgprint(_("Asset Movement record {0} created").format("<a href='#Form/Asset Movement/{0}'>{0}</a>").format(movement_entry.name))
|
||||
frappe.msgprint(_("Asset Movement record {0} created").format("<a href='/app/Form/Asset Movement/{0}'>{0}</a>").format(movement_entry.name))
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_item_details(item_code, asset_category):
|
||||
|
@ -71,7 +71,7 @@ class SupplierQuotation(BuyingController):
|
||||
doc_sup = doc_sup[0] if doc_sup else None
|
||||
if not doc_sup:
|
||||
frappe.throw(_("Supplier {0} not found in {1}").format(self.supplier,
|
||||
"<a href='desk#Form/Request for Quotation/{0}'> Request for Quotation {0} </a>".format(doc.name)))
|
||||
"<a href='desk/app/Form/Request for Quotation/{0}'> Request for Quotation {0} </a>".format(doc.name)))
|
||||
|
||||
quote_status = _('Received')
|
||||
for item in doc.items:
|
||||
|
@ -173,7 +173,7 @@ def get_data():
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Course Schedule",
|
||||
"route": "#List/Course Schedule/Calendar"
|
||||
"route": "/app/List/Course Schedule/Calendar"
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
|
@ -16,13 +16,13 @@ def get_data():
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Task",
|
||||
"route": "#List/Task",
|
||||
"route": "/app/List/Task",
|
||||
"description": _("Project activity / task."),
|
||||
"onboard": 1,
|
||||
},
|
||||
{
|
||||
"type": "report",
|
||||
"route": "#List/Task/Gantt",
|
||||
"route": "/app/List/Task/Gantt",
|
||||
"doctype": "Task",
|
||||
"name": "Gantt Chart",
|
||||
"description": _("Gantt chart of all tasks."),
|
||||
@ -97,5 +97,5 @@ def get_data():
|
||||
},
|
||||
]
|
||||
},
|
||||
|
||||
|
||||
]
|
||||
|
@ -128,7 +128,7 @@ frappe.ui.form.on('Assessment Result Tool', {
|
||||
result_table.find(`span[data-student=${assessment_result.student}].total-score-grade`).html(assessment_result.grade);
|
||||
let link_span = result_table.find(`span[data-student=${assessment_result.student}].total-result-link`);
|
||||
$(link_span).css("display", "block");
|
||||
$(link_span).find("a").attr("href", "#Form/Assessment Result/"+assessment_result.name);
|
||||
$(link_span).find("a").attr("href", "/desk/Form/Assessment Result/"+assessment_result.name);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ frappe.ui.form.on('Course Scheduling Tool', {
|
||||
<thead><tr><th>${__("Course")}</th><th>${__("Date")}</th></tr></thead>
|
||||
<tbody>
|
||||
${course_schedules.map(
|
||||
c => `<tr><td><a href="#Form/Course Schedule/${c.name}">${c.name}</a></td>
|
||||
c => `<tr><td><a href="/desk/Form/Course Schedule/${c.name}">${c.name}</a></td>
|
||||
<td>${c.schedule_date}</td></tr>`
|
||||
).join('')}
|
||||
</tbody>
|
||||
|
@ -87,7 +87,7 @@ class ProgramEnrollment(Document):
|
||||
fees.submit()
|
||||
fee_list.append(fees.name)
|
||||
if fee_list:
|
||||
fee_list = ["""<a href="#Form/Fees/%s" target="_blank">%s</a>""" % \
|
||||
fee_list = ["""<a href="/app/Form/Fees/%s" target="_blank">%s</a>""" % \
|
||||
(fee, fee) for fee in fee_list]
|
||||
msgprint(_("Fee Records Created - {0}").format(comma_and(fee_list)))
|
||||
|
||||
|
@ -23,10 +23,10 @@ frappe.ui.form.on("Tally Migration", {
|
||||
frappe.msgprint({
|
||||
message: __("An error has occurred during {0}. Check {1} for more details",
|
||||
[
|
||||
repl("<a href='#Form/Tally Migration/%(tally_document)s' class='variant-click'>%(tally_document)s</a>", {
|
||||
repl("<a href='/desk/Form/Tally Migration/%(tally_document)s' class='variant-click'>%(tally_document)s</a>", {
|
||||
tally_document: frm.docname
|
||||
}),
|
||||
"<a href='#List/Error Log' class='variant-click'>Error Log</a>"
|
||||
"<a href='/desk/List/Error Log' class='variant-click'>Error Log</a>"
|
||||
]
|
||||
),
|
||||
title: __("Tally Migration Error"),
|
||||
|
@ -86,7 +86,7 @@ frappe.ui.form.on('Clinical Procedure', {
|
||||
if (r.message) {
|
||||
frappe.show_alert({
|
||||
message: __('Stock Entry {0} created',
|
||||
['<a class="bold" href="#Form/Stock Entry/'+ r.message + '">' + r.message + '</a>']),
|
||||
['<a class="bold" href="/desk/Form/Stock Entry/'+ r.message + '">' + r.message + '</a>']),
|
||||
indicator: 'green'
|
||||
});
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ class InpatientRecord(Document):
|
||||
|
||||
if ip_record:
|
||||
msg = _(("Already {0} Patient {1} with Inpatient Record ").format(ip_record[0].status, self.patient) \
|
||||
+ """ <b><a href="#Form/Inpatient Record/{0}">{0}</a></b>""".format(ip_record[0].name))
|
||||
+ """ <b><a href="/app/Form/Inpatient Record/{0}">{0}</a></b>""".format(ip_record[0].name))
|
||||
frappe.throw(msg)
|
||||
|
||||
def admit(self, service_unit, check_in, expected_discharge=None):
|
||||
|
@ -63,7 +63,7 @@ class PatientAppointment(Document):
|
||||
|
||||
if overlaps:
|
||||
overlapping_details = _('Appointment overlaps with ')
|
||||
overlapping_details += "<b><a href='#Form/Patient Appointment/{0}'>{0}</a></b><br>".format(overlaps[0][0])
|
||||
overlapping_details += "<b><a href='/app/Form/Patient Appointment/{0}'>{0}</a></b><br>".format(overlaps[0][0])
|
||||
overlapping_details += _('{0} has appointment scheduled with {1} at {2} having {3} minute(s) duration.').format(
|
||||
overlaps[0][1], overlaps[0][2], overlaps[0][3], overlaps[0][4])
|
||||
frappe.throw(overlapping_details, title=_('Appointments Overlapping'))
|
||||
@ -75,7 +75,7 @@ class PatientAppointment(Document):
|
||||
if frappe.db.get_single_value('Healthcare Settings', 'automate_appointment_invoicing'):
|
||||
if not frappe.db.get_value('Patient', self.patient, 'customer'):
|
||||
msg = _("Please set a Customer linked to the Patient")
|
||||
msg += " <b><a href='#Form/Patient/{0}'>{0}</a></b>".format(self.patient)
|
||||
msg += " <b><a href='/app/Form/Patient/{0}'>{0}</a></b>".format(self.patient)
|
||||
frappe.throw(msg, title=_('Customer Not Found'))
|
||||
|
||||
def update_prescription_details(self):
|
||||
|
@ -32,7 +32,7 @@ def get_healthcare_services_to_invoice(patient, company):
|
||||
def validate_customer_created(patient):
|
||||
if not frappe.db.get_value('Patient', patient.name, 'customer'):
|
||||
msg = _("Please set a Customer linked to the Patient")
|
||||
msg += " <b><a href='#Form/Patient/{0}'>{0}</a></b>".format(patient.name)
|
||||
msg += " <b><a href='/app/Form/Patient/{0}'>{0}</a></b>".format(patient.name)
|
||||
frappe.throw(msg, title=_('Customer Not Found'))
|
||||
|
||||
|
||||
@ -169,7 +169,7 @@ def get_clinical_procedures_to_invoice(patient, company):
|
||||
service_item = get_healthcare_service_item('clinical_procedure_consumable_item')
|
||||
if not service_item:
|
||||
msg = _('Please Configure Clinical Procedure Consumable Item in ')
|
||||
msg += '''<b><a href='#Form/Healthcare Settings'>Healthcare Settings</a></b>'''
|
||||
msg += '''<b><a href='/app/Form/Healthcare Settings'>Healthcare Settings</a></b>'''
|
||||
frappe.throw(msg, title=_('Missing Configuration'))
|
||||
|
||||
clinical_procedures_to_invoice.append({
|
||||
@ -324,7 +324,7 @@ def throw_config_service_item(is_inpatient):
|
||||
service_item_label = _('Inpatient Visit Charge Item')
|
||||
|
||||
msg = _(('Please Configure {0} in ').format(service_item_label) \
|
||||
+ '''<b><a href='#Form/Healthcare Settings'>Healthcare Settings</a></b>''')
|
||||
+ '''<b><a href='/app/Form/Healthcare Settings'>Healthcare Settings</a></b>''')
|
||||
frappe.throw(msg, title=_('Missing Configuration'))
|
||||
|
||||
|
||||
@ -334,7 +334,7 @@ def throw_config_practitioner_charge(is_inpatient, practitioner):
|
||||
charge_name = _('Inpatient Visit Charge')
|
||||
|
||||
msg = _(('Please Configure {0} for Healthcare Practitioner').format(charge_name) \
|
||||
+ ''' <b><a href='#Form/Healthcare Practitioner/{0}'>{0}</a></b>'''.format(practitioner))
|
||||
+ ''' <b><a href='/app/Form/Healthcare Practitioner/{0}'>{0}</a></b>'''.format(practitioner))
|
||||
frappe.throw(msg, title=_('Missing Configuration'))
|
||||
|
||||
|
||||
@ -654,6 +654,6 @@ def render_doc_as_html(doctype, docname, exclude_fields = []):
|
||||
><div class='col-md-12 col-sm-12'>" \
|
||||
+ section_html + html +'</div></div>'
|
||||
if doc_html:
|
||||
doc_html = "<div class='small'><div class='col-md-12 text-right'><a class='btn btn-default btn-xs' href='#Form/%s/%s'></a></div>" %(doctype, docname) + doc_html + '</div>'
|
||||
doc_html = "<div class='small'><div class='col-md-12 text-right'><a class='btn btn-default btn-xs' href='/app/Form/%s/%s'></a></div>" %(doctype, docname) + doc_html + '</div>'
|
||||
|
||||
return {'html': doc_html}
|
||||
|
@ -50,7 +50,7 @@ class EmployeeTransfer(Document):
|
||||
employee = frappe.get_doc("Employee", self.employee)
|
||||
if self.create_new_employee_id:
|
||||
if self.new_employee_id:
|
||||
frappe.throw(_("Please delete the Employee <a href='#Form/Employee/{0}'>{0}</a>\
|
||||
frappe.throw(_("Please delete the Employee <a href='/app/Form/Employee/{0}'>{0}</a>\
|
||||
to cancel this document").format(self.new_employee_id))
|
||||
#mark the employee as active
|
||||
employee.status = "Active"
|
||||
|
@ -82,7 +82,7 @@ class LeaveAllocation(Document):
|
||||
frappe.msgprint(_("{0} already allocated for Employee {1} for period {2} to {3}")
|
||||
.format(self.leave_type, self.employee, formatdate(self.from_date), formatdate(self.to_date)))
|
||||
|
||||
frappe.throw(_('Reference') + ': <a href="#Form/Leave Allocation/{0}">{0}</a>'
|
||||
frappe.throw(_('Reference') + ': <a href="/app/Form/Leave Allocation/{0}">{0}</a>'
|
||||
.format(leave_allocation[0][0]), OverlapError)
|
||||
|
||||
def validate_back_dated_allocation(self):
|
||||
|
@ -246,7 +246,7 @@ class LeaveApplication(Document):
|
||||
def throw_overlap_error(self, d):
|
||||
msg = _("Employee {0} has already applied for {1} between {2} and {3} : ").format(self.employee,
|
||||
d['leave_type'], formatdate(d['from_date']), formatdate(d['to_date'])) \
|
||||
+ """ <b><a href="#Form/Leave Application/{0}">{0}</a></b>""".format(d["name"])
|
||||
+ """ <b><a href="/app/Form/Leave Application/{0}">{0}</a></b>""".format(d["name"])
|
||||
frappe.throw(msg, OverlapError)
|
||||
|
||||
def get_total_leaves_on_half_day(self):
|
||||
|
@ -87,5 +87,5 @@ class ShiftRequest(Document):
|
||||
def throw_overlap_error(self, d):
|
||||
msg = _("Employee {0} has already applied for {1} between {2} and {3} : ").format(self.employee,
|
||||
d['shift_type'], formatdate(d['from_date']), formatdate(d['to_date'])) \
|
||||
+ """ <b><a href="#Form/Shift Request/{0}">{0}</a></b>""".format(d["name"])
|
||||
+ """ <b><a href="/app/Form/Shift Request/{0}">{0}</a></b>""".format(d["name"])
|
||||
frappe.throw(msg, OverlapError)
|
@ -211,7 +211,7 @@ def get_doc_condition(doctype):
|
||||
def throw_overlap_error(doc, exists_for, overlap_doc, from_date, to_date):
|
||||
msg = _("A {0} exists between {1} and {2} (").format(doc.doctype,
|
||||
formatdate(from_date), formatdate(to_date)) \
|
||||
+ """ <b><a href="#Form/{0}/{1}">{1}</a></b>""".format(doc.doctype, overlap_doc) \
|
||||
+ """ <b><a href="/app/Form/{0}/{1}">{1}</a></b>""".format(doc.doctype, overlap_doc) \
|
||||
+ _(") for {0}").format(exists_for)
|
||||
frappe.throw(msg)
|
||||
|
||||
|
@ -134,7 +134,7 @@ frappe.ui.form.on("BOM", {
|
||||
frm.set_intro(__('This is a Template BOM and will be used to make the work order for {0} of the item {1}',
|
||||
[
|
||||
`<a class="variants-intro">variants</a>`,
|
||||
`<a href="#Form/Item/${frm.doc.item}">${frm.doc.item}</a>`,
|
||||
`<a href="/desk/Form/Item/${frm.doc.item}">${frm.doc.item}</a>`,
|
||||
]), true);
|
||||
|
||||
frm.$wrapper.find(".variants-intro").on("click", () => {
|
||||
|
@ -12,11 +12,11 @@
|
||||
<hr style="margin: 15px -15px;">
|
||||
<p>
|
||||
{% if data.value %}
|
||||
<a style="margin-right: 7px; margin-bottom: 7px" class="btn btn-default btn-xs" href="#Form/BOM/{{ data.value }}">
|
||||
<a style="margin-right: 7px; margin-bottom: 7px" class="btn btn-default btn-xs" href="/app/Form/BOM/{{ data.value }}">
|
||||
{{ __("Open BOM {0}", [data.value.bold()]) }}</a>
|
||||
{% endif %}
|
||||
{% if data.item_code %}
|
||||
<a class="btn btn-default btn-xs" href="#Form/Item/{{ data.item_code }}">
|
||||
<a class="btn btn-default btn-xs" href="/app/Form/Item/{{ data.item_code }}">
|
||||
{{ __("Open Item {0}", [data.item_code.bold()]) }}</a>
|
||||
{% endif %}
|
||||
</p>
|
||||
|
@ -319,7 +319,7 @@ class ProductionPlan(Document):
|
||||
frappe.flags.mute_messages = False
|
||||
|
||||
if wo_list:
|
||||
wo_list = ["""<a href="#Form/Work Order/%s" target="_blank">%s</a>""" % \
|
||||
wo_list = ["""<a href="/app/Form/Work Order/%s" target="_blank">%s</a>""" % \
|
||||
(p, p) for p in wo_list]
|
||||
msgprint(_("{0} created").format(comma_and(wo_list)))
|
||||
else :
|
||||
@ -423,7 +423,7 @@ class ProductionPlan(Document):
|
||||
frappe.flags.mute_messages = False
|
||||
|
||||
if material_request_list:
|
||||
material_request_list = ["""<a href="#Form/Material Request/{0}">{1}</a>""".format(m.name, m.name) \
|
||||
material_request_list = ["""<a href="/app/Form/Material Request/{0}">{1}</a>""".format(m.name, m.name) \
|
||||
for m in material_request_list]
|
||||
msgprint(_("{0} created").format(comma_and(material_request_list)))
|
||||
else :
|
||||
|
@ -27,9 +27,9 @@ frappe.query_reports["BOM Stock Report"] = {
|
||||
value = default_formatter(value, row, column, data);
|
||||
if (column.id == "Item"){
|
||||
if (data["Enough Parts to Build"] > 0){
|
||||
value = `<a style='color:green' href="#Form/Item/${data['Item']}" data-doctype="Item">${data['Item']}</a>`
|
||||
value = `<a style='color:green' href="/desk/Form/Item/${data['Item']}" data-doctype="Item">${data['Item']}</a>`
|
||||
} else {
|
||||
value = `<a style='color:red' href="#Form/Item/${data['Item']}" data-doctype="Item">${data['Item']}</a>`
|
||||
value = `<a style='color:red' href="/desk/Form/Item/${data['Item']}" data-doctype="Item">${data['Item']}</a>`
|
||||
}
|
||||
}
|
||||
return value
|
||||
|
@ -41,7 +41,7 @@ class PayrollPeriod(Document):
|
||||
if overlap_doc:
|
||||
msg = _("A {0} exists between {1} and {2} (").format(self.doctype,
|
||||
formatdate(self.start_date), formatdate(self.end_date)) \
|
||||
+ """ <b><a href="#Form/{0}/{1}">{1}</a></b>""".format(self.doctype, overlap_doc[0].name) \
|
||||
+ """ <b><a href="/app/Form/{0}/{1}">{1}</a></b>""".format(self.doctype, overlap_doc[0].name) \
|
||||
+ _(") for {0}").format(self.company)
|
||||
frappe.throw(msg)
|
||||
|
||||
|
@ -26,7 +26,7 @@ frappe.listview_settings['Task'] = {
|
||||
},
|
||||
gantt_custom_popup_html: function(ganttobj, task) {
|
||||
var html = `<h5><a style="text-decoration:underline"\
|
||||
href="#Form/Task/${ganttobj.id}""> ${ganttobj.name} </a></h5>`;
|
||||
href="/desk/Form/Task/${ganttobj.id}""> ${ganttobj.name} </a></h5>`;
|
||||
|
||||
if(task.project) html += `<p>Project: ${task.project}</p>`;
|
||||
html += `<p>Progress: ${ganttobj.progress}</p>`;
|
||||
|
@ -2,7 +2,8 @@
|
||||
"css/erpnext.css": [
|
||||
"public/less/erpnext.less",
|
||||
"public/less/hub.less",
|
||||
"public/less/call_popup.less"
|
||||
"public/less/call_popup.less",
|
||||
"public/scss/point-of-sale.scss"
|
||||
],
|
||||
"css/marketplace.css": [
|
||||
"public/less/hub.less"
|
||||
@ -27,16 +28,6 @@
|
||||
"public/js/payment/payments.js",
|
||||
"public/js/controllers/taxes_and_totals.js",
|
||||
"public/js/controllers/transaction.js",
|
||||
"public/js/pos/pos.html",
|
||||
"public/js/pos/pos_bill_item.html",
|
||||
"public/js/pos/pos_bill_item_new.html",
|
||||
"public/js/pos/pos_selected_item.html",
|
||||
"public/js/pos/pos_item.html",
|
||||
"public/js/pos/pos_tax_row.html",
|
||||
"public/js/pos/customer_toolbar.html",
|
||||
"public/js/pos/pos_invoice_list.html",
|
||||
"public/js/payment/pos_payment.html",
|
||||
"public/js/payment/payment_details.html",
|
||||
"public/js/templates/item_selector.html",
|
||||
"public/js/templates/employees_to_mark_attendance.html",
|
||||
"public/js/utils/item_selector.js",
|
||||
@ -55,5 +46,15 @@
|
||||
"stock/dashboard/item_dashboard.html",
|
||||
"stock/dashboard/item_dashboard_list.html",
|
||||
"stock/dashboard/item_dashboard.js"
|
||||
],
|
||||
"js/point-of-sale.min.js": [
|
||||
"selling/page/point_of_sale/pos_item_selector.js",
|
||||
"selling/page/point_of_sale/pos_item_cart.js",
|
||||
"selling/page/point_of_sale/pos_item_details.js",
|
||||
"selling/page/point_of_sale/pos_number_pad.js",
|
||||
"selling/page/point_of_sale/pos_payment.js",
|
||||
"selling/page/point_of_sale/pos_past_order_list.js",
|
||||
"selling/page/point_of_sale/pos_past_order_summary.js",
|
||||
"selling/page/point_of_sale/pos_controller.js"
|
||||
]
|
||||
}
|
||||
|
@ -1,217 +0,0 @@
|
||||
[data-route="point-of-sale"] .layout-main-section { border: none; font-size: 12px; }
|
||||
[data-route="point-of-sale"] .layout-main-section-wrapper { margin-bottom: 0; }
|
||||
[data-route="point-of-sale"] .pos-items-wrapper { max-height: calc(100vh - 210px); }
|
||||
:root { --border-color: #d1d8dd; --text-color: #8d99a6; --primary: #5e64ff; }
|
||||
[data-route="point-of-sale"] .flex { display: flex; }
|
||||
[data-route="point-of-sale"] .grid { display: grid; }
|
||||
[data-route="point-of-sale"] .absolute { position: absolute; }
|
||||
[data-route="point-of-sale"] .relative { position: relative; }
|
||||
[data-route="point-of-sale"] .abs-center { top: 50%; left: 50%; transform: translate(-50%, -50%); }
|
||||
[data-route="point-of-sale"] .inline { display: inline; }
|
||||
[data-route="point-of-sale"] .float-right { float: right; }
|
||||
[data-route="point-of-sale"] .grid-cols-1 { grid-template-columns: repeat(1, minmax(0, 1fr)); }
|
||||
[data-route="point-of-sale"] .grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||
[data-route="point-of-sale"] .grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
|
||||
[data-route="point-of-sale"] .grid-cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
|
||||
[data-route="point-of-sale"] .grid-cols-5 { grid-template-columns: repeat(5, minmax(0, 1fr)); }
|
||||
[data-route="point-of-sale"] .grid-cols-10 { grid-template-columns: repeat(10, minmax(0, 1fr)); }
|
||||
[data-route="point-of-sale"] .gap-2 { grid-gap: 0.5rem; gap: 0.5rem; }
|
||||
[data-route="point-of-sale"] .gap-4 { grid-gap: 1rem; gap: 1rem; }
|
||||
[data-route="point-of-sale"] .gap-6 { grid-gap: 1.25rem; gap: 1.25rem; }
|
||||
[data-route="point-of-sale"] .gap-8 { grid-gap: 1.5rem; gap: 1.5rem; }
|
||||
[data-route="point-of-sale"] .row-gap-2 { grid-row-gap: 0.5rem; row-gap: 0.5rem; }
|
||||
[data-route="point-of-sale"] .col-gap-4 { grid-column-gap: 1rem; column-gap: 1rem; }
|
||||
[data-route="point-of-sale"] .col-span-2 { grid-column: span 2 / span 2; }
|
||||
[data-route="point-of-sale"] .col-span-3 { grid-column: span 3 / span 3; }
|
||||
[data-route="point-of-sale"] .col-span-4 { grid-column: span 4 / span 4; }
|
||||
[data-route="point-of-sale"] .col-span-6 { grid-column: span 6 / span 6; }
|
||||
[data-route="point-of-sale"] .col-span-10 { grid-column: span 10 / span 10; }
|
||||
[data-route="point-of-sale"] .row-span-2 { grid-row: span 2 / span 2; }
|
||||
[data-route="point-of-sale"] .grid-auto-row { grid-auto-rows: 5.5rem; }
|
||||
[data-route="point-of-sale"] .d-none { display: none; }
|
||||
[data-route="point-of-sale"] .flex-wrap { flex-wrap: wrap; }
|
||||
[data-route="point-of-sale"] .flex-row { flex-direction: row; }
|
||||
[data-route="point-of-sale"] .flex-col { flex-direction: column; }
|
||||
[data-route="point-of-sale"] .flex-row-rev { flex-direction: row-reverse; }
|
||||
[data-route="point-of-sale"] .flex-col-rev { flex-direction: column-reverse; }
|
||||
[data-route="point-of-sale"] .flex-1 { flex: 1 1 0%; }
|
||||
[data-route="point-of-sale"] .items-center { align-items: center; }
|
||||
[data-route="point-of-sale"] .items-end { align-items: flex-end; }
|
||||
[data-route="point-of-sale"] .f-grow-1 { flex-grow: 1; }
|
||||
[data-route="point-of-sale"] .f-grow-2 { flex-grow: 2; }
|
||||
[data-route="point-of-sale"] .f-grow-3 { flex-grow: 3; }
|
||||
[data-route="point-of-sale"] .f-grow-4 { flex-grow: 4; }
|
||||
[data-route="point-of-sale"] .f-shrink-0 { flex-shrink: 0; }
|
||||
[data-route="point-of-sale"] .f-shrink-1 { flex-shrink: 1; }
|
||||
[data-route="point-of-sale"] .f-shrink-2 { flex-shrink: 2; }
|
||||
[data-route="point-of-sale"] .f-shrink-3 { flex-shrink: 3; }
|
||||
[data-route="point-of-sale"] .shadow { box-shadow: 0 0px 3px 0 rgba(0, 0, 0, 0.2), 0 1px 2px 0 rgba(0, 0, 0, 0.06); }
|
||||
[data-route="point-of-sale"] .shadow-sm { box-shadow: 0 0.5px 3px 0 rgba(0, 0, 0, 0.125); }
|
||||
[data-route="point-of-sale"] .shadow-inner { box-shadow: inset 0 2px 4px 0 rgba(0, 0, 0, 0.1); }
|
||||
[data-route="point-of-sale"] .rounded { border-radius: 0.3rem; }
|
||||
[data-route="point-of-sale"] .rounded-b { border-bottom-left-radius: 0.3rem; border-bottom-right-radius: 0.3rem; }
|
||||
[data-route="point-of-sale"] .p-8 { padding: 2rem; }
|
||||
[data-route="point-of-sale"] .p-16 { padding: 4rem; }
|
||||
[data-route="point-of-sale"] .p-32 { padding: 8rem; }
|
||||
[data-route="point-of-sale"] .p-6 { padding: 1.5rem; }
|
||||
[data-route="point-of-sale"] .p-4 { padding: 1rem; }
|
||||
[data-route="point-of-sale"] .p-3 { padding: 0.75rem; }
|
||||
[data-route="point-of-sale"] .p-2 { padding: 0.5rem; }
|
||||
[data-route="point-of-sale"] .m-8 { margin: 2rem; }
|
||||
[data-route="point-of-sale"] .p-1 { padding: 0.25rem; }
|
||||
[data-route="point-of-sale"] .pr-0 { padding-right: 0rem; }
|
||||
[data-route="point-of-sale"] .pl-0 { padding-left: 0rem; }
|
||||
[data-route="point-of-sale"] .pt-0 { padding-top: 0rem; }
|
||||
[data-route="point-of-sale"] .pb-0 { padding-bottom: 0rem; }
|
||||
[data-route="point-of-sale"] .mr-0 { margin-right: 0rem; }
|
||||
[data-route="point-of-sale"] .ml-0 { margin-left: 0rem; }
|
||||
[data-route="point-of-sale"] .mt-0 { margin-top: 0rem; }
|
||||
[data-route="point-of-sale"] .mb-0 { margin-bottom: 0rem; }
|
||||
[data-route="point-of-sale"] .pr-2 { padding-right: 0.5rem; }
|
||||
[data-route="point-of-sale"] .pl-2 { padding-left: 0.5rem; }
|
||||
[data-route="point-of-sale"] .pt-2 { padding-top: 0.5rem; }
|
||||
[data-route="point-of-sale"] .pb-2 { padding-bottom: 0.5rem; }
|
||||
[data-route="point-of-sale"] .pr-3 { padding-right: 0.75rem; }
|
||||
[data-route="point-of-sale"] .pl-3 { padding-left: 0.75rem; }
|
||||
[data-route="point-of-sale"] .pt-3 { padding-top: 0.75rem; }
|
||||
[data-route="point-of-sale"] .pb-3 { padding-bottom: 0.75rem; }
|
||||
[data-route="point-of-sale"] .pr-4 { padding-right: 1rem; }
|
||||
[data-route="point-of-sale"] .pl-4 { padding-left: 1rem; }
|
||||
[data-route="point-of-sale"] .pt-4 { padding-top: 1rem; }
|
||||
[data-route="point-of-sale"] .pb-4 { padding-bottom: 1rem; }
|
||||
[data-route="point-of-sale"] .mr-4 { margin-right: 1rem; }
|
||||
[data-route="point-of-sale"] .ml-4 { margin-left: 1rem; }
|
||||
[data-route="point-of-sale"] .mt-4 { margin-top: 1rem; }
|
||||
[data-route="point-of-sale"] .mb-4 { margin-bottom: 1rem; }
|
||||
[data-route="point-of-sale"] .mr-2 { margin-right: 0.5rem; }
|
||||
[data-route="point-of-sale"] .ml-2 { margin-left: 0.5rem; }
|
||||
[data-route="point-of-sale"] .mt-2 { margin-top: 0.5rem; }
|
||||
[data-route="point-of-sale"] .mb-2 { margin-bottom: 0.5rem; }
|
||||
[data-route="point-of-sale"] .mr-1 { margin-right: 0.25rem; }
|
||||
[data-route="point-of-sale"] .ml-1 { margin-left: 0.25rem; }
|
||||
[data-route="point-of-sale"] .mt-1 { margin-top: 0.25rem; }
|
||||
[data-route="point-of-sale"] .mb-1 { margin-bottom: 0.25rem; }
|
||||
[data-route="point-of-sale"] .mr-auto { margin-right: auto; }
|
||||
[data-route="point-of-sale"] .ml-auto { margin-left: auto; }
|
||||
[data-route="point-of-sale"] .mt-auto { margin-top: auto; }
|
||||
[data-route="point-of-sale"] .mb-auto { margin-bottom: auto; }
|
||||
[data-route="point-of-sale"] .pr-6 { padding-right: 1.5rem; }
|
||||
[data-route="point-of-sale"] .pl-6 { padding-left: 1.5rem; }
|
||||
[data-route="point-of-sale"] .pt-6 { padding-top: 1.5rem; }
|
||||
[data-route="point-of-sale"] .pb-6 { padding-bottom: 1.5rem; }
|
||||
[data-route="point-of-sale"] .mr-6 { margin-right: 1.5rem; }
|
||||
[data-route="point-of-sale"] .ml-6 { margin-left: 1.5rem; }
|
||||
[data-route="point-of-sale"] .mt-6 { margin-top: 1.5rem; }
|
||||
[data-route="point-of-sale"] .mb-6 { margin-bottom: 1.5rem; }
|
||||
[data-route="point-of-sale"] .mr-8 { margin-right: 2rem; }
|
||||
[data-route="point-of-sale"] .ml-8 { margin-left: 2rem; }
|
||||
[data-route="point-of-sale"] .mt-8 { margin-top: 2rem; }
|
||||
[data-route="point-of-sale"] .mb-8 { margin-bottom: 2rem; }
|
||||
[data-route="point-of-sale"] .pr-8 { padding-right: 2rem; }
|
||||
[data-route="point-of-sale"] .pl-8 { padding-left: 2rem; }
|
||||
[data-route="point-of-sale"] .pt-8 { padding-top: 2rem; }
|
||||
[data-route="point-of-sale"] .pb-8 { padding-bottom: 2rem; }
|
||||
[data-route="point-of-sale"] .pr-16 { padding-right: 4rem; }
|
||||
[data-route="point-of-sale"] .pl-16 { padding-left: 4rem; }
|
||||
[data-route="point-of-sale"] .pt-16 { padding-top: 4rem; }
|
||||
[data-route="point-of-sale"] .pb-16 { padding-bottom: 4rem; }
|
||||
[data-route="point-of-sale"] .w-full { width: 100%; }
|
||||
[data-route="point-of-sale"] .h-full { height: 100%; }
|
||||
[data-route="point-of-sale"] .w-quarter { width: 25%; }
|
||||
[data-route="point-of-sale"] .w-half { width: 50%; }
|
||||
[data-route="point-of-sale"] .w-66 { width: 66.66%; }
|
||||
[data-route="point-of-sale"] .w-33 { width: 33.33%; }
|
||||
[data-route="point-of-sale"] .w-60 { width: 60%; }
|
||||
[data-route="point-of-sale"] .w-40 { width: 40%; }
|
||||
[data-route="point-of-sale"] .w-fit { width: fit-content; }
|
||||
[data-route="point-of-sale"] .w-6 { width: 2rem; }
|
||||
[data-route="point-of-sale"] .h-6 { min-height: 2rem; height: 2rem; }
|
||||
[data-route="point-of-sale"] .w-8 { width: 2.5rem; }
|
||||
[data-route="point-of-sale"] .h-8 { min-height: 2.5rem; height: 2.5rem; }
|
||||
[data-route="point-of-sale"] .w-10 { width: 3rem; }
|
||||
[data-route="point-of-sale"] .h-10 { min-height:3rem; height: 3rem; }
|
||||
[data-route="point-of-sale"] .h-12 { min-height: 3.3rem; height: 3.3rem; }
|
||||
[data-route="point-of-sale"] .w-12 { width: 3.3rem; }
|
||||
[data-route="point-of-sale"] .h-14 { min-height: 4.2rem; height: 4.2rem; }
|
||||
[data-route="point-of-sale"] .h-16 { min-height: 4.6rem; height: 4.6rem; }
|
||||
[data-route="point-of-sale"] .h-18 { min-height: 5rem; height: 5rem; }
|
||||
[data-route="point-of-sale"] .w-18 { width: 5.4rem; }
|
||||
[data-route="point-of-sale"] .w-24 { width: 7.2rem; }
|
||||
[data-route="point-of-sale"] .w-26 { width: 8.4rem; }
|
||||
[data-route="point-of-sale"] .h-24 { min-height: 7.2rem; height: 7.2rem; }
|
||||
[data-route="point-of-sale"] .h-32 { min-height: 9.6rem; height: 9.6rem; }
|
||||
[data-route="point-of-sale"] .w-46 { width: 15rem; }
|
||||
[data-route="point-of-sale"] .h-46 { min-height:15rem; height: 15rem; }
|
||||
[data-route="point-of-sale"] .h-100 { height: 100vh; }
|
||||
[data-route="point-of-sale"] .mx-h-70 { max-height: 67rem; }
|
||||
[data-route="point-of-sale"] .border-grey-300 { border-color: #e2e8f0; }
|
||||
[data-route="point-of-sale"] .border-grey { border: 1px solid #d1d8dd; }
|
||||
[data-route="point-of-sale"] .border-white { border: 1px solid #fff; }
|
||||
[data-route="point-of-sale"] .border-b-grey { border-bottom: 1px solid #d1d8dd; }
|
||||
[data-route="point-of-sale"] .border-t-grey { border-top: 1px solid #d1d8dd; }
|
||||
[data-route="point-of-sale"] .border-r-grey { border-right: 1px solid #d1d8dd; }
|
||||
[data-route="point-of-sale"] .text-dark-grey { color: #5f5f5f; }
|
||||
[data-route="point-of-sale"] .text-grey { color: #8d99a6; }
|
||||
[data-route="point-of-sale"] .text-grey-100 { color: #d1d8dd; }
|
||||
[data-route="point-of-sale"] .text-grey-200 { color: #a0aec0; }
|
||||
[data-route="point-of-sale"] .bg-green-200 { background-color: #c6f6d5; }
|
||||
[data-route="point-of-sale"] .text-bold { font-weight: bold; }
|
||||
[data-route="point-of-sale"] .italic { font-style: italic; }
|
||||
[data-route="point-of-sale"] .font-weight-450 { font-weight: 450; }
|
||||
[data-route="point-of-sale"] .justify-around { justify-content: space-around; }
|
||||
[data-route="point-of-sale"] .justify-between { justify-content: space-between; }
|
||||
[data-route="point-of-sale"] .justify-center { justify-content: center; }
|
||||
[data-route="point-of-sale"] .justify-end { justify-content: flex-end; }
|
||||
[data-route="point-of-sale"] .bg-white { background-color: white; }
|
||||
[data-route="point-of-sale"] .bg-light-grey { background-color: #f0f4f7; }
|
||||
[data-route="point-of-sale"] .bg-grey-100 { background-color: #f7fafc; }
|
||||
[data-route="point-of-sale"] .bg-grey-200 { background-color: #edf2f7; }
|
||||
[data-route="point-of-sale"] .bg-grey { background-color: #f4f5f6; }
|
||||
[data-route="point-of-sale"] .text-center { text-align: center; }
|
||||
[data-route="point-of-sale"] .text-right { text-align: right; }
|
||||
[data-route="point-of-sale"] .text-sm { font-size: 1rem; }
|
||||
[data-route="point-of-sale"] .text-md-0 { font-size: 1.25rem; }
|
||||
[data-route="point-of-sale"] .text-md { font-size: 1.4rem; }
|
||||
[data-route="point-of-sale"] .text-lg { font-size: 1.6rem; }
|
||||
[data-route="point-of-sale"] .text-xl { font-size: 2.2rem; }
|
||||
[data-route="point-of-sale"] .text-2xl { font-size: 2.8rem; }
|
||||
[data-route="point-of-sale"] .text-2-5xl { font-size: 3rem; }
|
||||
[data-route="point-of-sale"] .text-3xl { font-size: 3.8rem; }
|
||||
[data-route="point-of-sale"] .text-6xl { font-size: 4.8rem; }
|
||||
[data-route="point-of-sale"] .line-through { text-decoration: line-through; }
|
||||
[data-route="point-of-sale"] .text-primary { color: #5e64ff; }
|
||||
[data-route="point-of-sale"] .text-white { color: #fff; }
|
||||
[data-route="point-of-sale"] .text-green-500 { color: #48bb78; }
|
||||
[data-route="point-of-sale"] .bg-primary { background-color: #5e64ff; }
|
||||
[data-route="point-of-sale"] .border-primary { border-color: #5e64ff; }
|
||||
[data-route="point-of-sale"] .text-danger { color: #e53e3e; }
|
||||
[data-route="point-of-sale"] .scroll-x { overflow-x: scroll;overflow-y: hidden; }
|
||||
[data-route="point-of-sale"] .scroll-y { overflow-y: scroll;overflow-x: hidden; }
|
||||
[data-route="point-of-sale"] .overflow-hidden { overflow: hidden; }
|
||||
[data-route="point-of-sale"] .whitespace-nowrap { white-space: nowrap; }
|
||||
[data-route="point-of-sale"] .sticky { position: sticky; top: -1px; }
|
||||
[data-route="point-of-sale"] .bg-white { background-color: #fff; }
|
||||
[data-route="point-of-sale"] .bg-selected { background-color: #fffdf4; }
|
||||
[data-route="point-of-sale"] .border-dashed { border-width:1px; border-style: dashed; }
|
||||
[data-route="point-of-sale"] .z-100 { z-index: 100; }
|
||||
|
||||
[data-route="point-of-sale"] .frappe-control { margin: 0 !important; width: 100%; }
|
||||
[data-route="point-of-sale"] .form-control { font-size: 12px; }
|
||||
[data-route="point-of-sale"] .form-group { margin: 0 !important; }
|
||||
[data-route="point-of-sale"] .pointer { cursor: pointer; }
|
||||
[data-route="point-of-sale"] .no-select { user-select: none; }
|
||||
[data-route="point-of-sale"] .item-wrapper:hover { transform: scale(1.02, 1.02); }
|
||||
[data-route="point-of-sale"] .hover-underline:hover { text-decoration: underline; }
|
||||
[data-route="point-of-sale"] .item-wrapper { transition: scale 0.2s ease-in-out; }
|
||||
[data-route="point-of-sale"] .cart-items-section .cart-item-wrapper:not(:first-child) { border-top: none; }
|
||||
[data-route="point-of-sale"] .customer-transactions .invoice-wrapper:not(:first-child) { border-top: none; }
|
||||
|
||||
[data-route="point-of-sale"] .payment-summary-wrapper:last-child { border-bottom: none; }
|
||||
[data-route="point-of-sale"] .item-summary-wrapper:last-child { border-bottom: none; }
|
||||
[data-route="point-of-sale"] .total-summary-wrapper:last-child { border-bottom: none; }
|
||||
[data-route="point-of-sale"] .invoices-container .invoice-wrapper:last-child { border-bottom: none; }
|
||||
[data-route="point-of-sale"] .new-btn { background-color: #5e64ff; color: white; border: none;}
|
||||
[data-route="point-of-sale"] .summary-btns:last-child { margin-right: 0px; }
|
||||
[data-route="point-of-sale"] ::-webkit-scrollbar { width: 1px }
|
||||
|
||||
[data-route="point-of-sale"] .indicator.grey::before { background-color: #8d99a6; }
|
@ -85,7 +85,7 @@ class CallPopup {
|
||||
<br>
|
||||
<a
|
||||
class="text-small text-muted"
|
||||
href="#Form/Call Log/${this.call_log.name}">
|
||||
href="/desk/Form/Call Log/${this.call_log.name}">
|
||||
${__('View call log')}
|
||||
</a>
|
||||
`,
|
||||
@ -167,7 +167,7 @@ class CallPopup {
|
||||
const issue_field = this.dialog.get_field("last_issue");
|
||||
issue_field.set_value(issue.subject);
|
||||
issue_field.$wrapper.append(`
|
||||
<a class="text-medium" href="#List/Issue?customer=${issue.customer}">
|
||||
<a class="text-medium" href="/desk/List/Issue?customer=${issue.customer}">
|
||||
${__('View all issues from {0}', [issue.customer])}
|
||||
</a>
|
||||
`);
|
||||
|
@ -84,7 +84,7 @@ frappe.ui.form.on("Communication", {
|
||||
frm.reload_doc();
|
||||
frappe.show_alert({
|
||||
message: __("Opportunity {0} created",
|
||||
['<a href="#Form/Opportunity/'+r.message+'">' + r.message + '</a>']),
|
||||
['<a href="/desk/Form/Opportunity/'+r.message+'">' + r.message + '</a>']),
|
||||
indicator: 'green'
|
||||
});
|
||||
}
|
||||
|
@ -19,7 +19,7 @@
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for s in students %}
|
||||
<tr
|
||||
<tr
|
||||
{% if(s.assessment_details && s.docstatus && s.docstatus == 1) { %} class="text-muted" {% } %}
|
||||
data-student="{{s.student}}">
|
||||
|
||||
@ -29,7 +29,7 @@
|
||||
<td>
|
||||
<span data-student="{{s.student}}" data-criteria="{{c.assessment_criteria}}" class="student-result-grade badge" >
|
||||
{% if(s.assessment_details) { %}
|
||||
{{s.assessment_details[c.assessment_criteria][1]}}
|
||||
{{s.assessment_details[c.assessment_criteria][1]}}
|
||||
{% } %}
|
||||
</span>
|
||||
<input type="number" class="student-result-data" style="width:70%; float:right;"
|
||||
@ -61,7 +61,7 @@
|
||||
{% } %}
|
||||
</span>
|
||||
<span data-student="{{s.student}}" class="total-result-link" style="width: 10%; display:{% if(!s.assessment_details) { %}None{% } %}; float:right;">
|
||||
<a class="btn-open no-decoration" title="Open Link" href="#Form/Assessment Result/{% if(s.assessment_details) { %}{{s.name}}{% } %}">
|
||||
<a class="btn-open no-decoration" title="Open Link" href="/app/Form/Assessment Result/{% if(s.assessment_details) { %}{{s.name}}{% } %}">
|
||||
<i class="octicon octicon-arrow-right"></i>
|
||||
</a>
|
||||
</span>
|
||||
|
@ -1,11 +0,0 @@
|
||||
<div class="row pos-payment-row" type="{{type}}" idx={{idx}}>
|
||||
<div class="col-xs-6" style="padding:20px">{{mode_of_payment}}</div>
|
||||
<div class="col-xs-6">
|
||||
<div class="input-group">
|
||||
<input disabled class="form-control text-right amount" idx="{{idx}}" type="text" value="{%= format_currency(amount, currency) %}">
|
||||
<span class="input-group-btn">
|
||||
<button type="button" class="btn btn-default clr" idx="{{idx}}" style="border:1px solid #d1d8dd">C</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -1,42 +0,0 @@
|
||||
<div class="pos_payment row">
|
||||
<div class="row" style="padding: 0px 30px;">
|
||||
<h3>{{ __("Total Amount") }}: <span class="label label-default" style="font-size:20px;padding:5px">{%= format_currency(grand_total, currency) %}</span></h3>
|
||||
</div>
|
||||
<div class="row amount-row">
|
||||
<div class="col-xs-6 col-sm-3 text-center">
|
||||
<p class="amount-label"> {{ __("Paid") }} <h3 class="paid_amount">{%= format_currency(paid_amount, currency) %}</h3></p>
|
||||
</div>
|
||||
<div class="col-xs-6 col-sm-3 text-center">
|
||||
<p class="amount-label"> {{ __("Outstanding") }} <h3 class="outstanding_amount">{%= format_currency(outstanding_amount, currency) %} </h3></p>
|
||||
</div>
|
||||
<div class="col-xs-6 col-sm-3 text-center">
|
||||
<p class="amount-label"> {{ __("Change") }} <input class="form-control text-right change_amount bold" type="text" idx="change_amount" value="{{format_number(change_amount, null, 2)}}">
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-xs-6 col-sm-3 text-center">
|
||||
<p class="amount-label"> {{ __("Write off") }} <input class="form-control text-right write_off_amount bold" type="text" idx="write_off_amount" value="{{format_number(write_off_amount, null, 2)}}">
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="row">
|
||||
<div class="col-sm-6 ">
|
||||
<div class ="row multimode-payments" style = "margin-right:10px">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6 payment-toolbar">
|
||||
{% for(var i=0; i<3; i++) { %}
|
||||
<div class="row">
|
||||
{% for(var j=i*3; j<(i+1)*3; j++) { %}
|
||||
<button type="button" class="btn btn-default pos-keyboard-key">{{j+1}}</button>
|
||||
{% } %}
|
||||
</div>
|
||||
{% } %}
|
||||
<div class="row">
|
||||
<button type="button" class="btn btn-default delete-btn">{{ __("Del") }}</button>
|
||||
<button type="button" class="btn btn-default pos-keyboard-key">0</button>
|
||||
<button type="button" class="btn btn-default pos-keyboard-key">.</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -1,330 +0,0 @@
|
||||
/* eslint-disable */
|
||||
/*! Clusterize.js - v0.17.6 - 2017-03-05
|
||||
* http://NeXTs.github.com/Clusterize.js/
|
||||
* Copyright (c) 2015 Denis Lukov; Licensed GPLv3 */
|
||||
|
||||
;(function(name, definition) {
|
||||
if (typeof module != 'undefined') module.exports = definition();
|
||||
else if (typeof define == 'function' && typeof define.amd == 'object') define(definition);
|
||||
else this[name] = definition();
|
||||
}('Clusterize', function() {
|
||||
"use strict"
|
||||
|
||||
// detect ie9 and lower
|
||||
// https://gist.github.com/padolsey/527683#comment-786682
|
||||
var ie = (function(){
|
||||
for( var v = 3,
|
||||
el = document.createElement('b'),
|
||||
all = el.all || [];
|
||||
el.innerHTML = '<!--[if gt IE ' + (++v) + ']><i><![endif]-->',
|
||||
all[0];
|
||||
){}
|
||||
return v > 4 ? v : document.documentMode;
|
||||
}()),
|
||||
is_mac = navigator.platform.toLowerCase().indexOf('mac') + 1;
|
||||
var Clusterize = function(data) {
|
||||
if( ! (this instanceof Clusterize))
|
||||
return new Clusterize(data);
|
||||
var self = this;
|
||||
|
||||
var defaults = {
|
||||
rows_in_block: 50,
|
||||
blocks_in_cluster: 4,
|
||||
tag: null,
|
||||
show_no_data_row: true,
|
||||
no_data_class: 'clusterize-no-data',
|
||||
no_data_text: 'No data',
|
||||
keep_parity: true,
|
||||
callbacks: {}
|
||||
}
|
||||
|
||||
// public parameters
|
||||
self.options = {};
|
||||
var options = ['rows_in_block', 'blocks_in_cluster', 'show_no_data_row', 'no_data_class', 'no_data_text', 'keep_parity', 'tag', 'callbacks'];
|
||||
for(var i = 0, option; option = options[i]; i++) {
|
||||
self.options[option] = typeof data[option] != 'undefined' && data[option] != null
|
||||
? data[option]
|
||||
: defaults[option];
|
||||
}
|
||||
|
||||
var elems = ['scroll', 'content'];
|
||||
for(var i = 0, elem; elem = elems[i]; i++) {
|
||||
self[elem + '_elem'] = data[elem + 'Id']
|
||||
? document.getElementById(data[elem + 'Id'])
|
||||
: data[elem + 'Elem'];
|
||||
if( ! self[elem + '_elem'])
|
||||
throw new Error("Error! Could not find " + elem + " element");
|
||||
}
|
||||
|
||||
// tabindex forces the browser to keep focus on the scrolling list, fixes #11
|
||||
if( ! self.content_elem.hasAttribute('tabindex'))
|
||||
self.content_elem.setAttribute('tabindex', 0);
|
||||
|
||||
// private parameters
|
||||
var rows = isArray(data.rows)
|
||||
? data.rows
|
||||
: self.fetchMarkup(),
|
||||
cache = {},
|
||||
scroll_top = self.scroll_elem.scrollTop;
|
||||
|
||||
// append initial data
|
||||
self.insertToDOM(rows, cache);
|
||||
|
||||
// restore the scroll position
|
||||
self.scroll_elem.scrollTop = scroll_top;
|
||||
|
||||
// adding scroll handler
|
||||
var last_cluster = false,
|
||||
scroll_debounce = 0,
|
||||
pointer_events_set = false,
|
||||
scrollEv = function() {
|
||||
// fixes scrolling issue on Mac #3
|
||||
if (is_mac) {
|
||||
if( ! pointer_events_set) self.content_elem.style.pointerEvents = 'none';
|
||||
pointer_events_set = true;
|
||||
clearTimeout(scroll_debounce);
|
||||
scroll_debounce = setTimeout(function () {
|
||||
self.content_elem.style.pointerEvents = 'auto';
|
||||
pointer_events_set = false;
|
||||
}, 50);
|
||||
}
|
||||
if (last_cluster != (last_cluster = self.getClusterNum()))
|
||||
self.insertToDOM(rows, cache);
|
||||
if (self.options.callbacks.scrollingProgress)
|
||||
self.options.callbacks.scrollingProgress(self.getScrollProgress());
|
||||
},
|
||||
resize_debounce = 0,
|
||||
resizeEv = function() {
|
||||
clearTimeout(resize_debounce);
|
||||
resize_debounce = setTimeout(self.refresh, 100);
|
||||
}
|
||||
on('scroll', self.scroll_elem, scrollEv);
|
||||
on('resize', window, resizeEv);
|
||||
|
||||
// public methods
|
||||
self.destroy = function(clean) {
|
||||
off('scroll', self.scroll_elem, scrollEv);
|
||||
off('resize', window, resizeEv);
|
||||
self.html((clean ? self.generateEmptyRow() : rows).join(''));
|
||||
}
|
||||
self.refresh = function(force) {
|
||||
if(self.getRowsHeight(rows) || force) self.update(rows);
|
||||
}
|
||||
self.update = function(new_rows) {
|
||||
rows = isArray(new_rows)
|
||||
? new_rows
|
||||
: [];
|
||||
var scroll_top = self.scroll_elem.scrollTop;
|
||||
// fixes #39
|
||||
if(rows.length * self.options.item_height < scroll_top) {
|
||||
self.scroll_elem.scrollTop = 0;
|
||||
last_cluster = 0;
|
||||
}
|
||||
self.insertToDOM(rows, cache);
|
||||
self.scroll_elem.scrollTop = scroll_top;
|
||||
}
|
||||
self.clear = function() {
|
||||
self.update([]);
|
||||
}
|
||||
self.getRowsAmount = function() {
|
||||
return rows.length;
|
||||
}
|
||||
self.getScrollProgress = function() {
|
||||
return this.options.scroll_top / (rows.length * this.options.item_height) * 100 || 0;
|
||||
}
|
||||
|
||||
var add = function(where, _new_rows) {
|
||||
var new_rows = isArray(_new_rows)
|
||||
? _new_rows
|
||||
: [];
|
||||
if( ! new_rows.length) return;
|
||||
rows = where == 'append'
|
||||
? rows.concat(new_rows)
|
||||
: new_rows.concat(rows);
|
||||
self.insertToDOM(rows, cache);
|
||||
}
|
||||
self.append = function(rows) {
|
||||
add('append', rows);
|
||||
}
|
||||
self.prepend = function(rows) {
|
||||
add('prepend', rows);
|
||||
}
|
||||
}
|
||||
|
||||
Clusterize.prototype = {
|
||||
constructor: Clusterize,
|
||||
// fetch existing markup
|
||||
fetchMarkup: function() {
|
||||
var rows = [], rows_nodes = this.getChildNodes(this.content_elem);
|
||||
while (rows_nodes.length) {
|
||||
rows.push(rows_nodes.shift().outerHTML);
|
||||
}
|
||||
return rows;
|
||||
},
|
||||
// get tag name, content tag name, tag height, calc cluster height
|
||||
exploreEnvironment: function(rows, cache) {
|
||||
var opts = this.options;
|
||||
opts.content_tag = this.content_elem.tagName.toLowerCase();
|
||||
if( ! rows.length) return;
|
||||
if(ie && ie <= 9 && ! opts.tag) opts.tag = rows[0].match(/<([^>\s/]*)/)[1].toLowerCase();
|
||||
if(this.content_elem.children.length <= 1) cache.data = this.html(rows[0] + rows[0] + rows[0]);
|
||||
if( ! opts.tag) opts.tag = this.content_elem.children[0].tagName.toLowerCase();
|
||||
this.getRowsHeight(rows);
|
||||
},
|
||||
getRowsHeight: function(rows) {
|
||||
var opts = this.options,
|
||||
prev_item_height = opts.item_height;
|
||||
opts.cluster_height = 0;
|
||||
if( ! rows.length) return;
|
||||
var nodes = this.content_elem.children;
|
||||
var node = nodes[Math.floor(nodes.length / 2)];
|
||||
opts.item_height = node.offsetHeight;
|
||||
// consider table's border-spacing
|
||||
if(opts.tag == 'tr' && getStyle('borderCollapse', this.content_elem) != 'collapse')
|
||||
opts.item_height += parseInt(getStyle('borderSpacing', this.content_elem), 10) || 0;
|
||||
// consider margins (and margins collapsing)
|
||||
if(opts.tag != 'tr') {
|
||||
var marginTop = parseInt(getStyle('marginTop', node), 10) || 0;
|
||||
var marginBottom = parseInt(getStyle('marginBottom', node), 10) || 0;
|
||||
opts.item_height += Math.max(marginTop, marginBottom);
|
||||
}
|
||||
opts.block_height = opts.item_height * opts.rows_in_block;
|
||||
opts.rows_in_cluster = opts.blocks_in_cluster * opts.rows_in_block;
|
||||
opts.cluster_height = opts.blocks_in_cluster * opts.block_height;
|
||||
return prev_item_height != opts.item_height;
|
||||
},
|
||||
// get current cluster number
|
||||
getClusterNum: function () {
|
||||
this.options.scroll_top = this.scroll_elem.scrollTop;
|
||||
return Math.floor(this.options.scroll_top / (this.options.cluster_height - this.options.block_height)) || 0;
|
||||
},
|
||||
// generate empty row if no data provided
|
||||
generateEmptyRow: function() {
|
||||
var opts = this.options;
|
||||
if( ! opts.tag || ! opts.show_no_data_row) return [];
|
||||
var empty_row = document.createElement(opts.tag),
|
||||
no_data_content = document.createTextNode(opts.no_data_text), td;
|
||||
empty_row.className = opts.no_data_class;
|
||||
if(opts.tag == 'tr') {
|
||||
td = document.createElement('td');
|
||||
// fixes #53
|
||||
td.colSpan = 100;
|
||||
td.appendChild(no_data_content);
|
||||
}
|
||||
empty_row.appendChild(td || no_data_content);
|
||||
return [empty_row.outerHTML];
|
||||
},
|
||||
// generate cluster for current scroll position
|
||||
generate: function (rows, cluster_num) {
|
||||
var opts = this.options,
|
||||
rows_len = rows.length;
|
||||
if (rows_len < opts.rows_in_block) {
|
||||
return {
|
||||
top_offset: 0,
|
||||
bottom_offset: 0,
|
||||
rows_above: 0,
|
||||
rows: rows_len ? rows : this.generateEmptyRow()
|
||||
}
|
||||
}
|
||||
var items_start = Math.max((opts.rows_in_cluster - opts.rows_in_block) * cluster_num, 0),
|
||||
items_end = items_start + opts.rows_in_cluster,
|
||||
top_offset = Math.max(items_start * opts.item_height, 0),
|
||||
bottom_offset = Math.max((rows_len - items_end) * opts.item_height, 0),
|
||||
this_cluster_rows = [],
|
||||
rows_above = items_start;
|
||||
if(top_offset < 1) {
|
||||
rows_above++;
|
||||
}
|
||||
for (var i = items_start; i < items_end; i++) {
|
||||
rows[i] && this_cluster_rows.push(rows[i]);
|
||||
}
|
||||
return {
|
||||
top_offset: top_offset,
|
||||
bottom_offset: bottom_offset,
|
||||
rows_above: rows_above,
|
||||
rows: this_cluster_rows
|
||||
}
|
||||
},
|
||||
renderExtraTag: function(class_name, height) {
|
||||
var tag = document.createElement(this.options.tag),
|
||||
clusterize_prefix = 'clusterize-';
|
||||
tag.className = [clusterize_prefix + 'extra-row', clusterize_prefix + class_name].join(' ');
|
||||
height && (tag.style.height = height + 'px');
|
||||
return tag.outerHTML;
|
||||
},
|
||||
// if necessary verify data changed and insert to DOM
|
||||
insertToDOM: function(rows, cache) {
|
||||
// explore row's height
|
||||
if( ! this.options.cluster_height) {
|
||||
this.exploreEnvironment(rows, cache);
|
||||
}
|
||||
var data = this.generate(rows, this.getClusterNum()),
|
||||
this_cluster_rows = data.rows.join(''),
|
||||
this_cluster_content_changed = this.checkChanges('data', this_cluster_rows, cache),
|
||||
top_offset_changed = this.checkChanges('top', data.top_offset, cache),
|
||||
only_bottom_offset_changed = this.checkChanges('bottom', data.bottom_offset, cache),
|
||||
callbacks = this.options.callbacks,
|
||||
layout = [];
|
||||
|
||||
if(this_cluster_content_changed || top_offset_changed) {
|
||||
if(data.top_offset) {
|
||||
this.options.keep_parity && layout.push(this.renderExtraTag('keep-parity'));
|
||||
layout.push(this.renderExtraTag('top-space', data.top_offset));
|
||||
}
|
||||
layout.push(this_cluster_rows);
|
||||
data.bottom_offset && layout.push(this.renderExtraTag('bottom-space', data.bottom_offset));
|
||||
callbacks.clusterWillChange && callbacks.clusterWillChange();
|
||||
this.html(layout.join(''));
|
||||
this.options.content_tag == 'ol' && this.content_elem.setAttribute('start', data.rows_above);
|
||||
callbacks.clusterChanged && callbacks.clusterChanged();
|
||||
} else if(only_bottom_offset_changed) {
|
||||
this.content_elem.lastChild.style.height = data.bottom_offset + 'px';
|
||||
}
|
||||
},
|
||||
// unfortunately ie <= 9 does not allow to use innerHTML for table elements, so make a workaround
|
||||
html: function(data) {
|
||||
var content_elem = this.content_elem;
|
||||
if(ie && ie <= 9 && this.options.tag == 'tr') {
|
||||
var div = document.createElement('div'), last;
|
||||
div.innerHTML = '<table><tbody>' + data + '</tbody></table>';
|
||||
while((last = content_elem.lastChild)) {
|
||||
content_elem.removeChild(last);
|
||||
}
|
||||
var rows_nodes = this.getChildNodes(div.firstChild.firstChild);
|
||||
while (rows_nodes.length) {
|
||||
content_elem.appendChild(rows_nodes.shift());
|
||||
}
|
||||
} else {
|
||||
content_elem.innerHTML = data;
|
||||
}
|
||||
},
|
||||
getChildNodes: function(tag) {
|
||||
var child_nodes = tag.children, nodes = [];
|
||||
for (var i = 0, ii = child_nodes.length; i < ii; i++) {
|
||||
nodes.push(child_nodes[i]);
|
||||
}
|
||||
return nodes;
|
||||
},
|
||||
checkChanges: function(type, value, cache) {
|
||||
var changed = value != cache[type];
|
||||
cache[type] = value;
|
||||
return changed;
|
||||
}
|
||||
}
|
||||
|
||||
// support functions
|
||||
function on(evt, element, fnc) {
|
||||
return element.addEventListener ? element.addEventListener(evt, fnc, false) : element.attachEvent("on" + evt, fnc);
|
||||
}
|
||||
function off(evt, element, fnc) {
|
||||
return element.removeEventListener ? element.removeEventListener(evt, fnc, false) : element.detachEvent("on" + evt, fnc);
|
||||
}
|
||||
function isArray(arr) {
|
||||
return Object.prototype.toString.call(arr) === '[object Array]';
|
||||
}
|
||||
function getStyle(prop, elem) {
|
||||
return window.getComputedStyle ? window.getComputedStyle(elem)[prop] : elem.currentStyle[prop];
|
||||
}
|
||||
|
||||
return Clusterize;
|
||||
}));
|
@ -1,16 +0,0 @@
|
||||
<div class="pos-bill-toolbar col-xs-9" style="display: flex; width: 70%;">
|
||||
<div class="party-area" style="flex: 1;">
|
||||
<span class="edit-customer-btn text-muted" style="display: inline;">
|
||||
<a class="btn-open no-decoration" title="Edit Customer">
|
||||
<i class="octicon octicon-pencil"></i>
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
<button class="btn btn-default list-customers-btn" style="margin-left: 12px">
|
||||
<i class="octicon octicon-organization"></i>
|
||||
</button>
|
||||
</button> {% if (allow_delete) { %}
|
||||
<button class="btn btn-default btn-danger" style="margin: 0 5px 0 5px">
|
||||
<i class="octicon octicon-trashcan"></i>
|
||||
</button> {% } %}
|
||||
</div>
|
@ -1,136 +0,0 @@
|
||||
<div class="pos">
|
||||
<div class="row">
|
||||
<div class="col-sm-5 pos-bill-wrapper">
|
||||
<div class="col-sm-12"><h6 class="form-section-heading uppercase">{{ __("Item Cart") }}</h6></div>
|
||||
<div class="pos-bill">
|
||||
<div class="item-cart">
|
||||
<div class="pos-list-row pos-bill-header text-muted h6">
|
||||
<span class="cell subject">
|
||||
<!--<input class="list-select-all" type="checkbox" title="{%= __("Select All") %}">-->
|
||||
{{ __("Item Name")}}
|
||||
</span>
|
||||
<span class="cell text-right">{{ __("Quantity") }}</span>
|
||||
<span class="cell text-right">{{ __("Discount") }}</span>
|
||||
<span class="cell text-right">{{ __("Rate") }}</span>
|
||||
</div>
|
||||
<div class="item-cart-items">
|
||||
<div class="no-items-message text-extra-muted">
|
||||
<span class="text-center">
|
||||
<i class="fa fa-2x fa-shopping-cart"></i>
|
||||
<p>{{ __("Tap items to add them here") }}</p>
|
||||
</span>
|
||||
</div>
|
||||
<div class="items">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="totals-area">
|
||||
<div class="pos-list-row net-total-area">
|
||||
<div class="cell"></div>
|
||||
<div class="cell text-right">{%= __("Net Total") %}</div>
|
||||
<div class="cell price-cell bold net-total text-right"></div>
|
||||
</div>
|
||||
<div class="pos-list-row tax-area">
|
||||
<div class="cell"></div>
|
||||
<div class="cell text-right">{%= __("Taxes") %}</div>
|
||||
<div class="cell price-cell text-right tax-table">
|
||||
</div>
|
||||
</div>
|
||||
{% if(allow_user_to_edit_discount) { %}
|
||||
<div class="pos-list-row discount-amount-area">
|
||||
<div class="cell"></div>
|
||||
<div class="cell text-right">{%= __("Discount") %}</div>
|
||||
<div class="cell price-cell discount-field-col">
|
||||
<div class="input-group input-group-sm">
|
||||
<span class="input-group-addon">%</span>
|
||||
<input type="text" class="form-control discount-percentage text-right">
|
||||
</div>
|
||||
<div class="input-group input-group-sm">
|
||||
<span class="input-group-addon">{%= get_currency_symbol(currency) %}</span>
|
||||
<input type="text" class="form-control discount-amount text-right" placeholder="{%= 0.00 %}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% } %}
|
||||
<div class="pos-list-row grand-total-area collapse-btn" style="border-bottom:1px solid #d1d8dd;">
|
||||
<div class="cell">
|
||||
<a class="">
|
||||
<i class="octicon octicon-chevron-down"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div class="cell text-right bold">{%= __("Grand Total") %}</div>
|
||||
<div class="cell price-cell grand-total text-right lead"></div>
|
||||
</div>
|
||||
<div class="pos-list-row qty-total-area collapse-btn" style="border-bottom:1px solid #d1d8dd;">
|
||||
<div class="cell">
|
||||
<a class="">
|
||||
<i class="octicon octicon-chevron-down"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div class="cell text-right bold">{%= __("Qty Total") %}</div>
|
||||
<div class="cell price-cell qty-total text-right lead"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" style="margin-top: 30px">
|
||||
<div class="col-sm-6 selected-item">
|
||||
|
||||
</div>
|
||||
<div class="col-xs-6 numeric_keypad hidden-xs" style="display:none">
|
||||
{% var chartData = ["Qty", "Disc", "Price"] %} {% for(var i=0; i
|
||||
<3; i++) { %} <div class="row text-right">
|
||||
{% for(var j=i*3; j
|
||||
<(i+1)*3; j++) { %} <button type="button" class="btn btn-default numeric-keypad" val="{{j+1}}">{{j+1}}</button>
|
||||
{% } %}
|
||||
<button type="button" {% if((!allow_user_to_edit_rate && __(chartData[i]) == __("Price")) || (!allow_user_to_edit_discount && __(chartData[i]) == __("Disc"))) { %} disabled {% } %} id="pos-item-{{ chartData[i].toLowerCase() }}" class="btn text-center btn-default numeric-keypad pos-operation">{{ __(chartData[i]) }}</button>
|
||||
</div>
|
||||
{% } %}
|
||||
<div class="row text-right">
|
||||
<button type="button" class="btn btn-default numeric-keypad numeric-del">{{ __("Del") }}</button>
|
||||
<button type="button" class="btn btn-default numeric-keypad" val="0">0</button>
|
||||
<button type="button" class="btn btn-default numeric-keypad" val=".">.</button>
|
||||
<button type="button" class="btn btn-primary numeric-keypad pos-pay">{{ __("Pay") }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-5 list-customers">
|
||||
<div class="col-sm-12"><h6 class="form-section-heading uppercase">{{ __("Customers in Queue") }}</h6></div>
|
||||
<div class="pos-list-row pos-bill-header">
|
||||
<div class="cell subject"><input class="list-select-all" type="checkbox">{{ __("Customer") }}</div>
|
||||
<div class="cell text-left">{{ __("Status") }}</div>
|
||||
<div class="cell text-right">{{ __("Amount") }}</div>
|
||||
<div class="cell text-right">{{ __("Grand Total") }}</div>
|
||||
</div>
|
||||
<div class="list-customers-table border-left border-right border-bottom">
|
||||
<div class="no-items-message text-extra-muted">
|
||||
<span class="text-center">
|
||||
<i class="fa fa-2x fa-user"></i>
|
||||
<p>{{ __("No Customers yet!") }}</p>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-7 pos-items-section">
|
||||
<div class="col-sm-12"><h6 class="form-section-heading uppercase">{{ __("Stock Items") }}</h6></div>
|
||||
<div class="row pos-item-area">
|
||||
|
||||
</div>
|
||||
<span id="customer-results" style="color:#68a;"></span>
|
||||
<div class="item-list-area">
|
||||
<div class="pos-list-row pos-bill-header text-muted h6">
|
||||
<div class="cell subject search-item-group">
|
||||
<div class="dropdown">
|
||||
<a class="text-muted dropdown-toggle" data-toggle="dropdown"><span class="dropdown-text">{{ __("All Item Groups") }}</span><i class="caret"></i></a>
|
||||
<ul class="dropdown-menu">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cell search-item"></div>
|
||||
</div>
|
||||
<div class="app-listing item-list image-view-container">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -1,34 +0,0 @@
|
||||
<div class="row pos-bill-row pos-bill-item" data-item-code="{%= item_code %}">
|
||||
<div class="col-xs-4"><h6>{%= item_code || "" %}{%= __(item_name) || "" %}</h6></div>
|
||||
<div class="col-xs-3">
|
||||
<div class="row pos-qty-row">
|
||||
<div class="col-xs-2 text-center pos-qty-btn" data-action="decrease-qty"><i class="fa fa-minus text-muted" style="font-size:12px"></i></div>
|
||||
<div class="col-xs-8">
|
||||
<div>
|
||||
<input type="tel" value="{%= qty %}" class="form-control pos-item-qty text-right">
|
||||
</div>
|
||||
{% if(actual_qty != null) { %}
|
||||
<div style="margin-top: 5px;" class="text-muted small text-right">
|
||||
{%= __("In Stock: ") %} <span>{%= actual_qty || 0.0 %}</span>
|
||||
</div>
|
||||
{% } %}
|
||||
</div>
|
||||
<div class="col-xs-2 text-center pos-qty-btn" data-action="increase-qty"><i class="fa fa-plus text-muted" style="font-size:12px"></i></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-2 text-right">
|
||||
<div class="row input-sm">
|
||||
<input type="tel" value="{%= discount_percentage %}" class="form-control text-right pos-item-disc">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-3 text-right">
|
||||
<div class="text-muted" style="margin-top: 5px;">
|
||||
{% if(enabled) { %}
|
||||
<input type="tel" value="{%= rate %}" class="form-control input-sm pos-item-price text-right">
|
||||
{% } else { %}
|
||||
<h6>{%= format_currency(rate) %}</h6>
|
||||
{% } %}
|
||||
</div>
|
||||
<p><h6>{%= amount %}</h6></p>
|
||||
</div>
|
||||
</div>
|
@ -1,9 +0,0 @@
|
||||
<div class="pos-list-row pos-bill-item {{ selected_class }}" data-item-code="{{ item_code }}">
|
||||
<div class="cell subject">
|
||||
<!--<input class="list-row-checkbox" type="checkbox" data-name="{{item_code}}">-->
|
||||
<a class="grey list-id" title="{{ item_name }}">{{ strip_html(__(item_name)) || item_code }}</a>
|
||||
</div>
|
||||
<div class="cell text-right">{%= qty %}</div>
|
||||
<div class="cell text-right">{%= discount_percentage %}</div>
|
||||
<div class="cell text-right">{%= format_currency(rate) %}</div>
|
||||
</div>
|
@ -1,9 +0,0 @@
|
||||
<div class="pos-list-row" invoice-name = "{{name}}">
|
||||
<div class="list-column cell subject" invoice-name = "{{name}}">
|
||||
<input class="list-delete text-left" type="checkbox" style = "margin-right:5px">
|
||||
<a class="grey list-id text-left customer-row" title="{{ customer }}">{%= customer %}</a>
|
||||
</div>
|
||||
<div class="list-column cell text-left customer-row"><span class="indicator {{data.indicator}}">{{ data.status }}</span></div>
|
||||
<div class="list-column cell text-right customer-row">{%= paid_amount %}</div>
|
||||
<div class="list-column cell text-right customer-row">{%= grand_total %}</div>
|
||||
</div>
|
@ -1,32 +0,0 @@
|
||||
<div class="pos-item-wrapper image-view-item" data-item-code="{{item_code}}">
|
||||
<div class="image-view-header doclist-row">
|
||||
<div class="list-value">
|
||||
<a class="grey list-id" data-name="{{item_code}}" title="{{ item_name || item_code}}">{{item_name || item_code}}<br>({{ __(item_stock) }})</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="image-view-body">
|
||||
<a data-item-code="{{ item_code }}"
|
||||
title="{{ item_name || item_code }}"
|
||||
>
|
||||
<div class="image-field"
|
||||
style="
|
||||
{% if (!item_image) { %}
|
||||
background-color: #fafbfc;
|
||||
{% } %}
|
||||
border: 0px;"
|
||||
>
|
||||
{% if (!item_image) { %}
|
||||
<span class="placeholder-text">
|
||||
{%= frappe.get_abbr(item_name || item_code) %}
|
||||
</span>
|
||||
{% } %}
|
||||
{% if (item_image) { %}
|
||||
<img src="{{ item_image }}" alt="{{item_name || item_code}}">
|
||||
{% } %}
|
||||
</div>
|
||||
<span class="price-info">
|
||||
{{item_price}} / {{item_uom}}
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
@ -1,22 +0,0 @@
|
||||
<div class="pos-selected-item-action" data-item-code="{%= item_code %}" data-idx="{%= idx %}">
|
||||
<div class="pos-list-row">
|
||||
<div class="cell">{{ __("Quantity") }}:</div>
|
||||
<input type="tel" class="form-control cell pos-item-qty" value="{%= qty %}"/>
|
||||
</div>
|
||||
<div class="pos-list-row">
|
||||
<div class="cell">{{ __("Price List Rate") }}:</div>
|
||||
<input type="tel" class="form-control cell" disabled value="{%= price_list_rate %}"/>
|
||||
</div>
|
||||
<div class="pos-list-row">
|
||||
<div class="cell">{{ __("Discount") }}: %</div>
|
||||
<input type="tel" class="form-control cell pos-item-disc" {% if !allow_user_to_edit_discount %} disabled {% endif %} value="{%= discount_percentage %}">
|
||||
</div>
|
||||
<div class="pos-list-row">
|
||||
<div class="cell">{{ __("Price") }}:</div>
|
||||
<input type="tel" class="form-control cell pos-item-price" {% if !allow_user_to_edit_rate %} disabled {% endif %} value="{%= rate %}"/>
|
||||
</div>
|
||||
<div class="pos-list-row">
|
||||
<div class="cell">{{ __("Amount") }}:</div>
|
||||
<input type="tel" class="form-control cell pos-amount" disabled value="{%= amount %}"/>
|
||||
</div>
|
||||
</div>
|
@ -1,4 +0,0 @@
|
||||
<div class="pos-list-row" style="padding-right: 0;">
|
||||
<div class="cell">{%= description %}</div>
|
||||
<div class="cell text-right bold">{%= tax_amount %}</div>
|
||||
</div>
|
1112
erpnext/public/scss/point-of-sale.scss
Normal file
1112
erpnext/public/scss/point-of-sale.scss
Normal file
File diff suppressed because it is too large
Load Diff
@ -328,7 +328,7 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
|
||||
frappe.msgprint({
|
||||
message: __('Work Orders Created: {0}',
|
||||
[r.message.map(function(d) {
|
||||
return repl('<a href="#Form/Work Order/%(name)s">%(name)s</a>', {name:d})
|
||||
return repl('<a href="/desk/Form/Work Order/%(name)s">%(name)s</a>', {name:d})
|
||||
}).join(', ')]),
|
||||
indicator: 'green'
|
||||
})
|
||||
@ -437,7 +437,7 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
|
||||
callback: function(r) {
|
||||
if(r.message) {
|
||||
frappe.msgprint(__('Material Request {0} submitted.',
|
||||
['<a href="#Form/Material Request/'+r.message.name+'">' + r.message.name+ '</a>']));
|
||||
['<a href="/desk/Form/Material Request/'+r.message.name+'">' + r.message.name+ '</a>']));
|
||||
}
|
||||
d.hide();
|
||||
me.frm.reload_doc();
|
||||
|
File diff suppressed because one or more lines are too long
@ -1,7 +1,4 @@
|
||||
/* global Clusterize */
|
||||
frappe.provide('erpnext.PointOfSale');
|
||||
{% include "erpnext/selling/page/point_of_sale/pos_controller.js" %}
|
||||
frappe.provide('erpnext.queries');
|
||||
|
||||
frappe.pages['point-of-sale'].on_page_load = function(wrapper) {
|
||||
frappe.ui.make_app_page({
|
||||
@ -10,8 +7,10 @@ frappe.pages['point-of-sale'].on_page_load = function(wrapper) {
|
||||
single_column: true
|
||||
});
|
||||
|
||||
wrapper.pos = new erpnext.PointOfSale.Controller(wrapper);
|
||||
window.cur_pos = wrapper.pos;
|
||||
frappe.require('assets/js/point-of-sale.min.js', function() {
|
||||
wrapper.pos = new erpnext.PointOfSale.Controller(wrapper);
|
||||
window.cur_pos = wrapper.pos;
|
||||
})
|
||||
};
|
||||
|
||||
frappe.pages['point-of-sale'].refresh = function(wrapper) {
|
||||
|
@ -1,23 +1,9 @@
|
||||
{% include "erpnext/selling/page/point_of_sale/onscan.js" %}
|
||||
{% include "erpnext/selling/page/point_of_sale/pos_item_selector.js" %}
|
||||
{% include "erpnext/selling/page/point_of_sale/pos_item_cart.js" %}
|
||||
{% include "erpnext/selling/page/point_of_sale/pos_item_details.js" %}
|
||||
{% include "erpnext/selling/page/point_of_sale/pos_payment.js" %}
|
||||
{% include "erpnext/selling/page/point_of_sale/pos_number_pad.js" %}
|
||||
{% include "erpnext/selling/page/point_of_sale/pos_past_order_list.js" %}
|
||||
{% include "erpnext/selling/page/point_of_sale/pos_past_order_summary.js" %}
|
||||
|
||||
erpnext.PointOfSale.Controller = class {
|
||||
constructor(wrapper) {
|
||||
this.wrapper = $(wrapper).find('.layout-main-section');
|
||||
this.page = wrapper.page;
|
||||
|
||||
this.load_assets();
|
||||
}
|
||||
|
||||
load_assets() {
|
||||
// after loading assets first check if opening entry has been made
|
||||
frappe.require(['assets/erpnext/css/pos.css'], this.check_opening_entry.bind(this));
|
||||
this.check_opening_entry();
|
||||
}
|
||||
|
||||
fetch_opening_entry() {
|
||||
@ -36,6 +22,7 @@ erpnext.PointOfSale.Controller = class {
|
||||
}
|
||||
|
||||
create_opening_voucher() {
|
||||
const me = this;
|
||||
const table_fields = [
|
||||
{
|
||||
fieldname: "mode_of_payment", fieldtype: "Link",
|
||||
@ -45,7 +32,7 @@ erpnext.PointOfSale.Controller = class {
|
||||
{
|
||||
fieldname: "opening_amount", fieldtype: "Currency",
|
||||
in_list_view: 1, label: "Opening Amount",
|
||||
options: "company:company_currency",
|
||||
options: "company:company_currency",
|
||||
change: function () {
|
||||
dialog.fields_dict.balance_details.df.data.some(d => {
|
||||
if (d.idx == this.doc.idx) {
|
||||
@ -93,7 +80,7 @@ erpnext.PointOfSale.Controller = class {
|
||||
fields: table_fields
|
||||
}
|
||||
],
|
||||
primary_action: async ({ company, pos_profile, balance_details }) => {
|
||||
primary_action: async function({ company, pos_profile, balance_details }) {
|
||||
if (!balance_details.length) {
|
||||
frappe.show_alert({
|
||||
message: __("Please add Mode of payments and opening balance details."),
|
||||
@ -103,7 +90,7 @@ erpnext.PointOfSale.Controller = class {
|
||||
}
|
||||
const method = "erpnext.selling.page.point_of_sale.point_of_sale.create_opening_voucher";
|
||||
const res = await frappe.call({ method, args: { pos_profile, company, balance_details }, freeze:true });
|
||||
!res.exc && this.prepare_app_defaults(res.message);
|
||||
!res.exc && me.prepare_app_defaults(res.message);
|
||||
dialog.hide();
|
||||
},
|
||||
primary_action_label: __('Submit')
|
||||
@ -134,7 +121,7 @@ erpnext.PointOfSale.Controller = class {
|
||||
set_opening_entry_status() {
|
||||
this.page.set_title_sub(
|
||||
`<span class="indicator orange">
|
||||
<a class="text-muted" href="#Form/POS%20Opening%20Entry/${this.pos_opening}">
|
||||
<a class="text-muted" href="/desk/Form/POS%20Opening%20Entry/${this.pos_opening}">
|
||||
Opened at ${moment(this.pos_opening_time).format("Do MMMM, h:mma")}
|
||||
</a>
|
||||
</span>`);
|
||||
@ -157,10 +144,10 @@ erpnext.PointOfSale.Controller = class {
|
||||
|
||||
prepare_dom() {
|
||||
this.wrapper.append(
|
||||
`<div class="app grid grid-cols-10 pt-8 gap-6"></div>`
|
||||
`<div class="point-of-sale-app"></div>`
|
||||
);
|
||||
|
||||
this.$components_wrapper = this.wrapper.find('.app');
|
||||
this.$components_wrapper = this.wrapper.find('.point-of-sale-app');
|
||||
}
|
||||
|
||||
prepare_components() {
|
||||
@ -190,7 +177,7 @@ erpnext.PointOfSale.Controller = class {
|
||||
}
|
||||
|
||||
toggle_recent_order() {
|
||||
const show = this.recent_order_list.$component.hasClass('d-none');
|
||||
const show = this.recent_order_list.$component.is(':hidden');
|
||||
this.toggle_recent_order_list(show);
|
||||
}
|
||||
|
||||
@ -199,7 +186,7 @@ erpnext.PointOfSale.Controller = class {
|
||||
|
||||
if (this.frm.doc.items.length == 0) {
|
||||
frappe.show_alert({
|
||||
message:__("You must add atleast one item to save it as draft."),
|
||||
message:__("You must add atleast one item to save it as draft."),
|
||||
indicator:'red'
|
||||
});
|
||||
frappe.utils.play_sound("error");
|
||||
@ -208,7 +195,7 @@ erpnext.PointOfSale.Controller = class {
|
||||
|
||||
this.frm.save(undefined, undefined, undefined, () => {
|
||||
frappe.show_alert({
|
||||
message:__("There was an error saving the document."),
|
||||
message:__("There was an error saving the document."),
|
||||
indicator:'red'
|
||||
});
|
||||
frappe.utils.play_sound("error");
|
||||
@ -256,7 +243,7 @@ erpnext.PointOfSale.Controller = class {
|
||||
|
||||
cart_item_clicked: (item_code, batch_no, uom) => {
|
||||
const item_row = this.frm.doc.items.find(
|
||||
i => i.item_code === item_code
|
||||
i => i.item_code === item_code
|
||||
&& i.uom === uom
|
||||
&& (!batch_no || (batch_no && i.batch_no === batch_no))
|
||||
);
|
||||
@ -356,10 +343,10 @@ erpnext.PointOfSale.Controller = class {
|
||||
|
||||
toggle_other_sections: (show) => {
|
||||
if (show) {
|
||||
this.item_details.$component.hasClass('d-none') ? '' : this.item_details.$component.addClass('d-none');
|
||||
this.item_selector.$component.addClass('d-none');
|
||||
this.item_details.$component.is(':visible') ? this.item_details.$component.css('display', 'none') : '';
|
||||
this.item_selector.$component.css('display', 'none');
|
||||
} else {
|
||||
this.item_selector.$component.removeClass('d-none');
|
||||
this.item_selector.$component.css('display', 'flex');
|
||||
}
|
||||
},
|
||||
|
||||
@ -388,7 +375,7 @@ erpnext.PointOfSale.Controller = class {
|
||||
this.order_summary.load_summary_of(doc);
|
||||
});
|
||||
},
|
||||
reset_summary: () => this.order_summary.show_summary_placeholder()
|
||||
reset_summary: () => this.order_summary.toggle_summary_placeholder(true)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -429,7 +416,7 @@ erpnext.PointOfSale.Controller = class {
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
toggle_recent_order_list(show) {
|
||||
this.toggle_components(!show);
|
||||
@ -539,7 +526,7 @@ erpnext.PointOfSale.Controller = class {
|
||||
const qty_needed = field === 'qty' ? value * item_row.conversion_factor : item_row.qty * value;
|
||||
await this.check_stock_availability(item_row, qty_needed, this.frm.doc.set_warehouse);
|
||||
}
|
||||
|
||||
|
||||
if (this.is_current_item_being_edited(item_row) || item_selected_from_selector) {
|
||||
await frappe.model.set_value(item_row.doctype, item_row.name, field, value);
|
||||
this.update_cart_html(item_row);
|
||||
@ -577,7 +564,7 @@ erpnext.PointOfSale.Controller = class {
|
||||
|
||||
this.check_serial_batch_selection_needed(item_row) && this.edit_item_details_of(item_row);
|
||||
this.update_cart_html(item_row);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
@ -588,7 +575,7 @@ erpnext.PointOfSale.Controller = class {
|
||||
get_item_from_frm(item_code, batch_no, uom) {
|
||||
const has_batch_no = batch_no;
|
||||
return this.frm.doc.items.find(
|
||||
i => i.item_code === item_code
|
||||
i => i.item_code === item_code
|
||||
&& (!has_batch_no || (has_batch_no && i.batch_no === batch_no))
|
||||
&& (i.uom === uom)
|
||||
);
|
||||
@ -617,7 +604,7 @@ erpnext.PointOfSale.Controller = class {
|
||||
const no_serial_selected = !item_row.serial_no;
|
||||
const no_batch_selected = !item_row.batch_no;
|
||||
|
||||
if ((serialized && no_serial_selected) || (batched && no_batch_selected) ||
|
||||
if ((serialized && no_serial_selected) || (batched && no_batch_selected) ||
|
||||
(serialized && batched && (no_batch_selected || no_serial_selected))) {
|
||||
return true;
|
||||
}
|
||||
|
@ -16,10 +16,10 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
|
||||
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="customer-cart-container"></section>`
|
||||
)
|
||||
|
||||
this.$component = this.wrapper.find('.item-cart');
|
||||
this.$component = this.wrapper.find('.customer-cart-container');
|
||||
}
|
||||
|
||||
init_child_components() {
|
||||
@ -29,7 +29,7 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
|
||||
init_customer_selector() {
|
||||
this.$component.append(
|
||||
`<div class="customer-section rounded flex flex-col m-8 mb-0"></div>`
|
||||
`<div class="customer-section"></div>`
|
||||
)
|
||||
this.$customer_section = this.$component.find('.customer-section');
|
||||
}
|
||||
@ -37,23 +37,23 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
reset_customer_selector() {
|
||||
const frm = this.events.get_frm();
|
||||
frm.set_value('customer', '');
|
||||
this.$customer_section.removeClass('border pr-4 pl-4');
|
||||
this.make_customer_selector();
|
||||
this.customer_field.set_focus();
|
||||
}
|
||||
|
||||
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">
|
||||
<div class="flex-1">Item</div>
|
||||
<div class="mr-4">Qty</div>
|
||||
<div class="rate-list-header mr-1 text-right">Amount</div>
|
||||
`<div class="cart-container">
|
||||
<div class="abs-cart-container">
|
||||
<div class="cart-label">Item Cart</div>
|
||||
<div class="cart-header">
|
||||
<div class="name-header">Item</div>
|
||||
<div class="qty-header">Qty</div>
|
||||
<div class="rate-amount-header">Amount</div>
|
||||
</div>
|
||||
<div class="cart-items-section flex flex-col flex-1 scroll-y rounded w-full"></div>
|
||||
<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 class="cart-items-section"></div>
|
||||
<div class="cart-totals-section"></div>
|
||||
<div class="numpad-section"></div>
|
||||
</div>
|
||||
</div>`
|
||||
);
|
||||
@ -72,50 +72,44 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
}
|
||||
|
||||
make_no_items_placeholder() {
|
||||
this.$cart_header.addClass('d-none');
|
||||
this.$cart_header.css('display', 'none');
|
||||
this.$cart_items_wrapper.html(
|
||||
`<div class="no-item-wrapper flex items-center h-18">
|
||||
<div class="flex-1 text-center text-grey">No items in cart</div>
|
||||
</div>`
|
||||
)
|
||||
this.$cart_items_wrapper.addClass('mt-4 border-grey border-dashed');
|
||||
`<div class="no-item-wrapper">No items in cart</div>`
|
||||
);
|
||||
}
|
||||
|
||||
get_discount_icon() {
|
||||
return (
|
||||
`<svg class="discount-icon" width="24" height="24" viewBox="0 0 24 24" stroke="currentColor" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M19 15.6213C19 15.2235 19.158 14.842 19.4393 14.5607L20.9393 13.0607C21.5251 12.4749 21.5251 11.5251 20.9393 10.9393L19.4393 9.43934C19.158 9.15804 19 8.7765 19 8.37868V6.5C19 5.67157 18.3284 5 17.5 5H15.6213C15.2235 5 14.842 4.84196 14.5607 4.56066L13.0607 3.06066C12.4749 2.47487 11.5251 2.47487 10.9393 3.06066L9.43934 4.56066C9.15804 4.84196 8.7765 5 8.37868 5H6.5C5.67157 5 5 5.67157 5 6.5V8.37868C5 8.7765 4.84196 9.15804 4.56066 9.43934L3.06066 10.9393C2.47487 11.5251 2.47487 12.4749 3.06066 13.0607L4.56066 14.5607C4.84196 14.842 5 15.2235 5 15.6213V17.5C5 18.3284 5.67157 19 6.5 19H8.37868C8.7765 19 9.15804 19.158 9.43934 19.4393L10.9393 20.9393C11.5251 21.5251 12.4749 21.5251 13.0607 20.9393L14.5607 19.4393C14.842 19.158 15.2235 19 15.6213 19H17.5C18.3284 19 19 18.3284 19 17.5V15.6213Z" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M15 9L9 15" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M10.5 9.5C10.5 10.0523 10.0523 10.5 9.5 10.5C8.94772 10.5 8.5 10.0523 8.5 9.5C8.5 8.94772 8.94772 8.5 9.5 8.5C10.0523 8.5 10.5 8.94772 10.5 9.5Z" fill="white" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M15.5 14.5C15.5 15.0523 15.0523 15.5 14.5 15.5C13.9477 15.5 13.5 15.0523 13.5 14.5C13.5 13.9477 13.9477 13.5 14.5 13.5C15.0523 13.5 15.5 13.9477 15.5 14.5Z" fill="white" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>`
|
||||
);
|
||||
}
|
||||
|
||||
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">
|
||||
+ Add Discount
|
||||
`<div class="add-discount-wrapper">
|
||||
${this.get_discount_icon()} Add Discount
|
||||
</div>
|
||||
<div class="border border-grey rounded">
|
||||
<div class="net-total flex justify-between items-center h-16 pr-8 pl-8 border-b-grey">
|
||||
<div class="flex flex-col">
|
||||
<div class="text-md text-dark-grey text-bold">Net Total</div>
|
||||
</div>
|
||||
<div class="flex flex-col text-right">
|
||||
<div class="text-md text-dark-grey text-bold">0.00</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="taxes"></div>
|
||||
<div class="grand-total flex justify-between items-center h-16 pr-8 pl-8 border-b-grey">
|
||||
<div class="flex flex-col">
|
||||
<div class="text-md text-dark-grey text-bold">Grand Total</div>
|
||||
</div>
|
||||
<div class="flex flex-col text-right">
|
||||
<div class="text-md text-dark-grey text-bold">0.00</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="checkout-btn flex items-center justify-center h-16 pr-8 pl-8 text-center text-grey no-select pointer rounded-b text-md text-bold">
|
||||
Checkout
|
||||
</div>
|
||||
<div class="edit-cart-btn flex items-center justify-center h-16 pr-8 pl-8 text-center text-grey no-select pointer d-none text-md text-bold">
|
||||
Edit Cart
|
||||
</div>
|
||||
</div>`
|
||||
<div class="net-total-container">
|
||||
<div class="net-total-label">Net Total</div>
|
||||
<div class="net-total-value">0.00</div>
|
||||
</div>
|
||||
<div class="taxes-container"></div>
|
||||
<div class="grand-total-container">
|
||||
<div>Grand Total</div>
|
||||
<div>0.00</div>
|
||||
</div>
|
||||
<div class="checkout-btn">Checkout</div>
|
||||
<div class="edit-cart-btn">Edit Cart</div>`
|
||||
)
|
||||
|
||||
this.$add_discount_elem = this.$component.find(".add-discount");
|
||||
this.$add_discount_elem = this.$component.find(".add-discount-wrapper");
|
||||
}
|
||||
|
||||
make_cart_numpad() {
|
||||
@ -137,39 +131,37 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
[ '', '', '', 'col-span-2' ],
|
||||
[ '', '', '', 'col-span-2' ],
|
||||
[ '', '', '', 'col-span-2' ],
|
||||
[ '', '', '', 'col-span-2 text-bold text-danger' ]
|
||||
[ '', '', '', 'col-span-2 remove-btn' ]
|
||||
],
|
||||
fieldnames_map: { 'Quantity': 'qty', 'Discount': 'discount_percentage' }
|
||||
})
|
||||
|
||||
this.$numpad_section.prepend(
|
||||
`<div class="flex mb-2 justify-between">
|
||||
`<div class="numpad-totals">
|
||||
<span class="numpad-net-total"></span>
|
||||
<span class="numpad-grand-total"></span>
|
||||
</div>`
|
||||
)
|
||||
|
||||
this.$numpad_section.append(
|
||||
`<div class="numpad-btn checkout-btn flex items-center justify-center h-16 pr-8 pl-8 bg-primary
|
||||
text-center text-white no-select pointer rounded text-md text-bold mt-4" data-button-value="checkout">
|
||||
Checkout
|
||||
</div>`
|
||||
`<div class="numpad-btn checkout-btn" data-button-value="checkout">Checkout</div>`
|
||||
)
|
||||
}
|
||||
|
||||
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');
|
||||
customer_info_is_visible ?
|
||||
me.toggle_customer_info(false) : me.reset_customer_selector();
|
||||
this.$customer_section.on('click', '.reset-customer-btn', function (e) {
|
||||
me.reset_customer_selector();
|
||||
});
|
||||
|
||||
this.$customer_section.on('click', '.customer-header', function(e) {
|
||||
// don't triggger the event if .add-remove-customer btn is clicked which is under .customer-header
|
||||
if ($(e.target).closest('.add-remove-customer').length) return;
|
||||
this.$customer_section.on('click', '.close-details-btn', function (e) {
|
||||
me.toggle_customer_info(false);
|
||||
});
|
||||
|
||||
const show = !me.$cart_container.hasClass('d-none');
|
||||
this.$customer_section.on('click', '.customer-display', function(e) {
|
||||
if ($(e.target).closest('.reset-customer-btn').length) return;
|
||||
|
||||
const show = me.$cart_container.is(':visible');
|
||||
me.toggle_customer_info(show);
|
||||
});
|
||||
|
||||
@ -178,7 +170,7 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
|
||||
me.toggle_item_highlight(this);
|
||||
|
||||
const payment_section_hidden = me.$totals_section.find('.edit-cart-btn').hasClass('d-none');
|
||||
const payment_section_hidden = !me.$totals_section.find('.edit-cart-btn').is(':visible');
|
||||
if (!payment_section_hidden) {
|
||||
// payment section is visible
|
||||
// edit cart first and then open item details section
|
||||
@ -193,23 +185,19 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
});
|
||||
|
||||
this.$component.on('click', '.checkout-btn', function() {
|
||||
if (!$(this).hasClass('bg-primary')) return;
|
||||
if ($(this).attr('style').indexOf('--blue-500') == -1) return;
|
||||
|
||||
me.events.checkout();
|
||||
me.toggle_checkout_btn(false);
|
||||
|
||||
me.$add_discount_elem.removeClass("d-none");
|
||||
});
|
||||
|
||||
this.$totals_section.on('click', '.edit-cart-btn', () => {
|
||||
this.events.edit_cart();
|
||||
this.toggle_checkout_btn(true);
|
||||
|
||||
this.$add_discount_elem.addClass("d-none");
|
||||
});
|
||||
|
||||
this.$component.on('click', '.add-discount', () => {
|
||||
const can_edit_discount = this.$add_discount_elem.find('.edit-discount').length;
|
||||
this.$component.on('click', '.add-discount-wrapper', () => {
|
||||
const can_edit_discount = this.$add_discount_elem.find('.edit-discount-btn').length;
|
||||
|
||||
if(!this.discount_field || can_edit_discount) this.show_discount_control();
|
||||
});
|
||||
@ -251,7 +239,7 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
frappe.ui.keys.add_shortcut({
|
||||
shortcut: "ctrl+enter",
|
||||
action: () => this.$component.find(".checkout-btn").click(),
|
||||
condition: () => this.$component.is(":visible") && this.$totals_section.find('.edit-cart-btn').hasClass('d-none'),
|
||||
condition: () => this.$component.is(":visible") && !this.$totals_section.find('.edit-cart-btn').is(':visible'),
|
||||
description: __("Checkout Order / Submit Order / New Order"),
|
||||
ignore_inputs: true,
|
||||
page: cur_page.page.page
|
||||
@ -259,14 +247,15 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
this.$component.find(".edit-cart-btn").attr("title", `${ctrl_label}+E`);
|
||||
frappe.ui.keys.on("ctrl+e", () => {
|
||||
const item_cart_visible = this.$component.is(":visible");
|
||||
if (item_cart_visible && this.$totals_section.find('.checkout-btn').hasClass('d-none')) {
|
||||
this.$component.find(".edit-cart-btn").click()
|
||||
const checkout_btn_invisible = !this.$totals_section.find('.checkout-btn').is('visible');
|
||||
if (item_cart_visible && checkout_btn_invisible) {
|
||||
this.$component.find(".edit-cart-btn").click();
|
||||
}
|
||||
});
|
||||
this.$component.find(".add-discount").attr("title", `${ctrl_label}+D`);
|
||||
this.$component.find(".add-discount-wrapper").attr("title", `${ctrl_label}+D`);
|
||||
frappe.ui.keys.add_shortcut({
|
||||
shortcut: "ctrl+d",
|
||||
action: () => this.$component.find(".add-discount").click(),
|
||||
action: () => this.$component.find(".add-discount-wrapper").click(),
|
||||
condition: () => this.$add_discount_elem.is(":visible"),
|
||||
description: __("Add Order Discount"),
|
||||
ignore_inputs: true,
|
||||
@ -282,24 +271,22 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
|
||||
toggle_item_highlight(item) {
|
||||
const $cart_item = $(item);
|
||||
const item_is_highlighted = $cart_item.hasClass("shadow");
|
||||
const item_is_highlighted = $cart_item.attr("style") == "background-color:var(--gray-50);";
|
||||
|
||||
if (!item || item_is_highlighted) {
|
||||
this.item_is_selected = false;
|
||||
this.$cart_container.find('.cart-item-wrapper').removeClass("shadow").css("opacity", "1");
|
||||
this.$cart_container.find('.cart-item-wrapper').css("background-color", "");
|
||||
} else {
|
||||
$cart_item.addClass("shadow");
|
||||
$cart_item.css("background-color", "var(--gray-50)");
|
||||
this.item_is_selected = true;
|
||||
this.$cart_container.find('.cart-item-wrapper').css("opacity", "1");
|
||||
this.$cart_container.find('.cart-item-wrapper').not(item).removeClass("shadow").css("opacity", "0.65");
|
||||
this.$cart_container.find('.cart-item-wrapper').not(item).css("background-color", "");
|
||||
}
|
||||
// highlight with inner shadow
|
||||
// $cart_item.addClass("shadow-inner bg-selected");
|
||||
// me.$cart_container.find('.cart-item-wrapper').not(this).removeClass("shadow-inner bg-selected");
|
||||
}
|
||||
|
||||
make_customer_selector() {
|
||||
this.$customer_section.html(`<div class="customer-search-field flex flex-1 items-center"></div>`);
|
||||
this.$customer_section.html(`
|
||||
<div class="customer-field"></div>
|
||||
`);
|
||||
const me = this;
|
||||
const query = { query: 'erpnext.controllers.queries.customer_query' };
|
||||
const allowed_customer_group = this.events.get_allowed_customer_group() || [];
|
||||
@ -332,7 +319,7 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
}
|
||||
},
|
||||
},
|
||||
parent: this.$customer_section.find('.customer-search-field'),
|
||||
parent: this.$customer_section.find('.customer-field'),
|
||||
render_input: true,
|
||||
});
|
||||
this.customer_field.toggle_label(false);
|
||||
@ -371,9 +358,9 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
}
|
||||
|
||||
show_discount_control() {
|
||||
this.$add_discount_elem.removeClass("pr-4 pl-4");
|
||||
this.$add_discount_elem.css({ 'padding': '0px', 'border': 'none' })
|
||||
this.$add_discount_elem.html(
|
||||
`<div class="add-discount-field flex flex-1 items-center"></div>`
|
||||
`<div class="add-discount-field"></div>`
|
||||
);
|
||||
const me = this;
|
||||
|
||||
@ -382,14 +369,19 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
label: __('Discount'),
|
||||
fieldtype: 'Data',
|
||||
placeholder: __('Enter discount percentage.'),
|
||||
input_class: 'input-xs',
|
||||
onchange: function() {
|
||||
const frm = me.events.get_frm();
|
||||
if (this.value.length || this.value === 0) {
|
||||
if (flt(this.value) != 0) {
|
||||
frappe.model.set_value(frm.doc.doctype, frm.doc.name, 'additional_discount_percentage', flt(this.value));
|
||||
me.hide_discount_control(this.value);
|
||||
} else {
|
||||
frappe.model.set_value(frm.doc.doctype, frm.doc.name, 'additional_discount_percentage', 0);
|
||||
me.$add_discount_elem.html(`+ Add Discount`);
|
||||
me.$add_discount_elem.css({
|
||||
'border': '1px dashed var(--gray-500)',
|
||||
'padding': 'var(--padding-sm) var(--padding-md)'
|
||||
});
|
||||
me.$add_discount_elem.html(`${me.get_discount_icon()} Add Discount`);
|
||||
me.discount_field = undefined;
|
||||
}
|
||||
},
|
||||
@ -403,38 +395,36 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
|
||||
hide_discount_control(discount) {
|
||||
if (!discount) {
|
||||
this.$add_discount_elem.removeClass("pr-4 pl-4");
|
||||
this.$add_discount_elem.css({ 'padding': '0px', 'border': 'none' });
|
||||
this.$add_discount_elem.html(
|
||||
`<div class="add-discount-field flex flex-1 items-center"></div>`
|
||||
`<div class="add-discount-field"></div>`
|
||||
);
|
||||
} else {
|
||||
this.$add_discount_elem.addClass('pr-4 pl-4');
|
||||
this.$add_discount_elem.css({
|
||||
'border': '1px dashed var(--dark-green-500)',
|
||||
'padding': 'var(--padding-sm) var(--padding-md)'
|
||||
});
|
||||
this.$add_discount_elem.html(
|
||||
`<svg class="mr-2" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1"
|
||||
stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M12 20h9"/><path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"/>
|
||||
</svg>
|
||||
<div class="edit-discount p-1 pr-3 pl-3 text-dark-grey rounded w-fit bg-green-200 mb-2">
|
||||
${String(discount).bold()}% off
|
||||
</div>
|
||||
`
|
||||
`<div class="edit-discount-btn">
|
||||
${this.get_discount_icon()} Additional ${String(discount).bold()}% discount applied
|
||||
</div>`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
update_customer_section() {
|
||||
const { customer, email_id='', mobile_no='', image } = this.customer_info || {};
|
||||
const { customer, email_id='', mobile_no='' } = this.customer_info || {};
|
||||
|
||||
if (customer) {
|
||||
this.$customer_section.addClass('border pr-4 pl-4').html(
|
||||
`<div class="customer-details flex flex-col">
|
||||
<div class="customer-header flex items-center rounded h-18 pointer">
|
||||
${get_customer_image()}
|
||||
<div class="customer-name flex flex-col flex-1 f-shrink-1 overflow-hidden whitespace-nowrap">
|
||||
<div class="text-md text-dark-grey text-bold">${customer}</div>
|
||||
this.$customer_section.html(
|
||||
`<div class="customer-details">
|
||||
<div class="customer-display">
|
||||
${this.get_customer_image()}
|
||||
<div class="customer-name-desc">
|
||||
<div class="customer-name">${customer}</div>
|
||||
${get_customer_description()}
|
||||
</div>
|
||||
<div class="f-shrink-0 add-remove-customer flex items-center pointer" data-customer="${escape(customer)}">
|
||||
<div class="reset-customer-btn" data-customer="${escape(customer)}">
|
||||
<svg width="32" height="32" viewBox="0 0 14 14" fill="none">
|
||||
<path d="M4.93764 4.93759L7.00003 6.99998M9.06243 9.06238L7.00003 6.99998M7.00003 6.99998L4.93764 9.06238L9.06243 4.93759" stroke="#8D99A6"/>
|
||||
</svg>
|
||||
@ -449,26 +439,24 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
|
||||
function get_customer_description() {
|
||||
if (!email_id && !mobile_no) {
|
||||
return `<div class="text-grey-200 italic">Click to add email / phone</div>`
|
||||
return `<div class="customer-desc">Click to add email / phone</div>`
|
||||
} else if (email_id && !mobile_no) {
|
||||
return `<div class="text-grey">${email_id}</div>`
|
||||
return `<div class="customer-desc">${email_id}</div>`
|
||||
} else if (mobile_no && !email_id) {
|
||||
return `<div class="text-grey">${mobile_no}</div>`
|
||||
return `<div class="customer-desc">${mobile_no}</div>`
|
||||
} else {
|
||||
return `<div class="text-grey">${email_id} | ${mobile_no}</div>`
|
||||
return `<div class="customer-desc">${email_id} - ${mobile_no}</div>`
|
||||
}
|
||||
}
|
||||
|
||||
function get_customer_image() {
|
||||
if (image) {
|
||||
return `<div class="icon flex items-center justify-center w-12 h-12 rounded bg-light-grey mr-4 text-grey-200">
|
||||
<img class="h-full" src="${image}" alt="${image}" style="object-fit: cover;">
|
||||
</div>`
|
||||
} else {
|
||||
return `<div class="icon flex items-center justify-center w-12 h-12 rounded bg-light-grey mr-4 text-grey-200 text-md">
|
||||
${frappe.get_abbr(customer)}
|
||||
</div>`
|
||||
}
|
||||
}
|
||||
|
||||
get_customer_image() {
|
||||
const { customer, image } = this.customer_info || {};
|
||||
if (image) {
|
||||
return `<div class="customer-image"><img src="${image}" alt="${image}""></div>`
|
||||
} else {
|
||||
return `<div class="customer-image customer-abbr">${frappe.get_abbr(customer)}</div>`
|
||||
}
|
||||
}
|
||||
|
||||
@ -484,57 +472,44 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
|
||||
render_net_total(value) {
|
||||
const currency = this.events.get_frm().doc.currency;
|
||||
this.$totals_section.find('.net-total').html(
|
||||
`<div class="flex flex-col">
|
||||
<div class="text-md text-dark-grey text-bold">Net Total</div>
|
||||
</div>
|
||||
<div class="flex flex-col text-right">
|
||||
<div class="text-md text-dark-grey text-bold">${format_currency(value, currency)}</div>
|
||||
</div>`
|
||||
this.$totals_section.find('.net-total-container').html(
|
||||
`<div>Net Total</div><div>${format_currency(value, currency)}</div>`
|
||||
)
|
||||
|
||||
this.$numpad_section.find('.numpad-net-total').html(`Net Total: <span class="text-bold">${format_currency(value, currency)}</span>`)
|
||||
this.$numpad_section.find('.numpad-net-total').html(
|
||||
`<div>Net Total: <span>${format_currency(value, currency)}</span></div>`
|
||||
);
|
||||
}
|
||||
|
||||
render_grand_total(value) {
|
||||
const currency = this.events.get_frm().doc.currency;
|
||||
this.$totals_section.find('.grand-total').html(
|
||||
`<div class="flex flex-col">
|
||||
<div class="text-md text-dark-grey text-bold">Grand Total</div>
|
||||
</div>
|
||||
<div class="flex flex-col text-right">
|
||||
<div class="text-md text-dark-grey text-bold">${format_currency(value, currency)}</div>
|
||||
</div>`
|
||||
this.$totals_section.find('.grand-total-container').html(
|
||||
`<div>Grand Total</div><div>${format_currency(value, currency)}</div>`
|
||||
)
|
||||
|
||||
this.$numpad_section.find('.numpad-grand-total').html(`Grand Total: <span class="text-bold">${format_currency(value, currency)}</span>`)
|
||||
this.$numpad_section.find('.numpad-grand-total').html(
|
||||
`<div>Grand Total: <span>${format_currency(value, currency)}</span></div>`
|
||||
)
|
||||
}
|
||||
|
||||
render_taxes(value, taxes) {
|
||||
if (taxes.length) {
|
||||
const currency = this.events.get_frm().doc.currency;
|
||||
this.$totals_section.find('.taxes').html(
|
||||
`<div class="flex items-center justify-between h-16 pr-8 pl-8 border-b-grey">
|
||||
<div class="flex overflow-hidden whitespace-nowrap">
|
||||
<div class="text-md text-dark-grey text-bold w-fit">Tax Charges</div>
|
||||
<div class="flex ml-4 text-dark-grey">
|
||||
${
|
||||
taxes.map((t, i) => {
|
||||
let margin_left = '';
|
||||
if (i !== 0) margin_left = 'ml-2';
|
||||
const description = /[0-9]+/.test(t.description) ? t.description : `${t.description} @ ${t.rate}%`;
|
||||
return `<span class="border-grey p-1 pl-2 pr-2 rounded ${margin_left}">${description}</span>`
|
||||
}).join('')
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col text-right f-shrink-0 ml-4">
|
||||
<div class="text-md text-dark-grey text-bold">${format_currency(value, currency)}</div>
|
||||
</div>
|
||||
</div>`
|
||||
this.$totals_section.find('.taxes-container').css('display', 'flex').html(
|
||||
`${
|
||||
taxes.map((t, i) => {
|
||||
const description = /[0-9]+/.test(t.description) ? t.description : `${t.description} @ ${t.rate}%`;
|
||||
return `<div class="tax-row">
|
||||
<div class="tax-label">
|
||||
${description}
|
||||
</div>
|
||||
<div class="tax-value">${format_currency(value, currency)}</div>
|
||||
</div>`
|
||||
}).join('')
|
||||
}`
|
||||
)
|
||||
} else {
|
||||
this.$totals_section.find('.taxes').html('')
|
||||
this.$totals_section.find('.taxes-container').css('display', 'none').html('');
|
||||
}
|
||||
}
|
||||
|
||||
@ -553,7 +528,7 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
const $item = this.get_cart_item(item);
|
||||
|
||||
if (remove_item) {
|
||||
$item && $item.remove();
|
||||
$item && $item.next().remove() && $item.remove();
|
||||
} else {
|
||||
const { item_code, batch_no, uom } = item;
|
||||
const search_field = batch_no ? 'batch_no' : 'item_code';
|
||||
@ -563,9 +538,9 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
this.render_cart_item(item_row, $item);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
const no_of_cart_items = this.$cart_items_wrapper.find('.cart-item-wrapper').length;
|
||||
this.highlight_checkout_btn(no_of_cart_items > 0);
|
||||
|
||||
this.update_empty_cart_section(no_of_cart_items);
|
||||
}
|
||||
|
||||
@ -575,32 +550,33 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
|
||||
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"
|
||||
`<div class="cart-item-wrapper"
|
||||
data-item-code="${escape(item_data.item_code)}" data-uom="${escape(item_data.uom)}"
|
||||
data-batch-no="${escape(item_data.batch_no || '')}">
|
||||
</div>`
|
||||
</div>
|
||||
<div class="seperator"></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">
|
||||
`${get_item_image_html()}
|
||||
<div class="item-name-desc">
|
||||
<div class="item-name">
|
||||
${item_data.item_name}
|
||||
</div>
|
||||
${get_description_html()}
|
||||
</div>
|
||||
${get_rate_discount_html()}
|
||||
</div>`
|
||||
${get_rate_discount_html()}`
|
||||
)
|
||||
|
||||
set_dynamic_rate_header_width();
|
||||
this.scroll_to_item($item_to_update);
|
||||
|
||||
function set_dynamic_rate_header_width() {
|
||||
const rate_cols = Array.from(me.$cart_items_wrapper.find(".rate-col"));
|
||||
me.$cart_header.find(".rate-list-header").css("width", "");
|
||||
me.$cart_items_wrapper.find(".rate-col").css("width", "");
|
||||
const rate_cols = Array.from(me.$cart_items_wrapper.find(".item-rate-amount"));
|
||||
me.$cart_header.find(".rate-amount-header").css("width", "");
|
||||
me.$cart_items_wrapper.find(".item-rate-amount").css("width", "");
|
||||
let max_width = rate_cols.reduce((max_width, elm) => {
|
||||
if ($(elm).width() > max_width)
|
||||
max_width = $(elm).width();
|
||||
@ -610,30 +586,26 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
max_width += 1;
|
||||
if (max_width == 1) max_width = "";
|
||||
|
||||
me.$cart_header.find(".rate-list-header").css("width", max_width);
|
||||
me.$cart_items_wrapper.find(".rate-col").css("width", max_width);
|
||||
me.$cart_header.find(".rate-amount-header").css("width", max_width);
|
||||
me.$cart_items_wrapper.find(".item-rate-amount").css("width", max_width);
|
||||
}
|
||||
|
||||
function get_rate_discount_html() {
|
||||
if (item_data.rate && item_data.amount && item_data.rate !== item_data.amount) {
|
||||
return `
|
||||
<div class="flex f-shrink-0 ml-4 items-center">
|
||||
<div class="flex w-8 h-8 rounded bg-light-grey mr-4 items-center justify-center font-bold f-shrink-0">
|
||||
<span>${item_data.qty || 0}</span>
|
||||
</div>
|
||||
<div class="rate-col flex flex-col f-shrink-0 text-right">
|
||||
<div class="text-md text-dark-grey text-bold">${format_currency(item_data.amount, currency)}</div>
|
||||
<div class="text-md-0 text-dark-grey">${format_currency(item_data.rate, currency)}</div>
|
||||
<div class="item-qty-rate">
|
||||
<div class="item-qty"><span>${item_data.qty || 0}</span></div>
|
||||
<div class="item-rate-amount">
|
||||
<div class="item-rate">${format_currency(item_data.amount, currency)}</div>
|
||||
<div class="item-amount">${format_currency(item_data.rate, currency)}</div>
|
||||
</div>
|
||||
</div>`
|
||||
} else {
|
||||
return `
|
||||
<div class="flex f-shrink-0 ml-4 text-right">
|
||||
<div class="flex w-8 h-8 rounded bg-light-grey mr-4 items-center justify-center font-bold f-shrink-0">
|
||||
<span>${item_data.qty || 0}</span>
|
||||
</div>
|
||||
<div class="rate-col flex flex-col f-shrink-0 text-right">
|
||||
<div class="text-md text-dark-grey text-bold">${format_currency(item_data.rate, currency)}</div>
|
||||
<div class="item-qty-rate">
|
||||
<div class="item-qty"><span>${item_data.qty || 0}</span></div>
|
||||
<div class="item-rate-amount">
|
||||
<div class="item-rate">${format_currency(item_data.rate, currency)}</div>
|
||||
</div>
|
||||
</div>`
|
||||
}
|
||||
@ -649,10 +621,19 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
}
|
||||
}
|
||||
item_data.description = frappe.ellipsis(item_data.description, 45);
|
||||
return `<div class="text-grey">${item_data.description}</div>`
|
||||
return `<div class="item-desc">${item_data.description}</div>`
|
||||
}
|
||||
return ``;
|
||||
}
|
||||
|
||||
function get_item_image_html() {
|
||||
const { image, item_name } = item_data;
|
||||
if (image) {
|
||||
return `<div class="item-image"><img src="${image}" alt="${image}""></div>`
|
||||
} else {
|
||||
return `<div class="item-image item-abbr">${frappe.get_abbr(item_name)}</div>`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scroll_to_item($item) {
|
||||
@ -668,20 +649,25 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
|
||||
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');
|
||||
this.$totals_section.find('.checkout-btn').css('display', 'flex');
|
||||
this.$totals_section.find('.edit-cart-btn').css('display', 'none');
|
||||
} else {
|
||||
this.$totals_section.find('.checkout-btn').addClass('d-none');
|
||||
this.$totals_section.find('.edit-cart-btn').removeClass('d-none');
|
||||
this.$totals_section.find('.checkout-btn').css('display', 'none');
|
||||
this.$totals_section.find('.edit-cart-btn').css('display', 'flex');
|
||||
}
|
||||
}
|
||||
|
||||
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');
|
||||
} else if (!toggle && has_primary_class) {
|
||||
this.$totals_section.find('.checkout-btn').removeClass('bg-primary text-white text-lg');
|
||||
if (toggle) {
|
||||
this.$add_discount_elem.css('display', 'flex');
|
||||
this.$cart_container.find('.checkout-btn').css({
|
||||
'background-color': 'var(--blue-500)'
|
||||
});
|
||||
} else {
|
||||
this.$add_discount_elem.css('display', 'none');
|
||||
this.$cart_container.find('.checkout-btn').css({
|
||||
'background-color': 'var(--blue-200)'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -689,8 +675,7 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
const $no_item_element = this.$cart_items_wrapper.find('.no-item-wrapper');
|
||||
|
||||
// if cart has items and no item is present
|
||||
no_of_cart_items > 0 && $no_item_element && $no_item_element.remove()
|
||||
&& 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 && $no_item_element.remove() && this.$cart_header.css('display', 'flex');
|
||||
|
||||
no_of_cart_items === 0 && !$no_item_element.length && this.make_no_items_placeholder();
|
||||
}
|
||||
@ -753,36 +738,36 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
}
|
||||
|
||||
highlight_numpad_btn($btn, curr_action) {
|
||||
const curr_action_is_highlighted = $btn.hasClass('shadow-inner');
|
||||
const curr_action_is_highlighted = $btn.hasClass('highlighted-numpad-btn');
|
||||
const curr_action_is_action = ['qty', 'discount_percentage', 'rate', 'done'].includes(curr_action);
|
||||
|
||||
if (!curr_action_is_highlighted) {
|
||||
$btn.addClass('shadow-inner bg-selected');
|
||||
$btn.addClass('highlighted-numpad-btn');
|
||||
}
|
||||
if (this.prev_action === curr_action && curr_action_is_highlighted) {
|
||||
// if Qty is pressed twice
|
||||
$btn.removeClass('shadow-inner bg-selected');
|
||||
$btn.removeClass('highlighted-numpad-btn');
|
||||
}
|
||||
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');
|
||||
prev_btn.removeClass('highlighted-numpad-btn');
|
||||
}
|
||||
if (!curr_action_is_action || curr_action === 'done') {
|
||||
// if numbers are clicked
|
||||
setTimeout(() => {
|
||||
$btn.removeClass('shadow-inner bg-selected');
|
||||
}, 100);
|
||||
$btn.removeClass('highlighted-numpad-btn');
|
||||
}, 200);
|
||||
}
|
||||
}
|
||||
|
||||
toggle_numpad(show) {
|
||||
if (show) {
|
||||
this.$totals_section.addClass('d-none');
|
||||
this.$numpad_section.removeClass('d-none');
|
||||
this.$totals_section.css('display', 'none');
|
||||
this.$numpad_section.css('display', 'flex');
|
||||
} else {
|
||||
this.$totals_section.removeClass('d-none');
|
||||
this.$numpad_section.addClass('d-none');
|
||||
this.$totals_section.css('display', 'flex');
|
||||
this.$numpad_section.css('display', 'none');
|
||||
}
|
||||
this.reset_numpad();
|
||||
}
|
||||
@ -790,7 +775,7 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
reset_numpad() {
|
||||
this.numpad_value = '';
|
||||
this.prev_action = undefined;
|
||||
this.$numpad_section.find('.shadow-inner').removeClass('shadow-inner bg-selected');
|
||||
this.$numpad_section.find('.highlighted-numpad-btn').removeClass('highlighted-numpad-btn');
|
||||
}
|
||||
|
||||
toggle_numpad_field_edit(fieldname) {
|
||||
@ -801,48 +786,60 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
|
||||
toggle_customer_info(show) {
|
||||
if (show) {
|
||||
this.$cart_container.addClass('d-none')
|
||||
this.$customer_section.addClass('flex-1 scroll-y').removeClass('mb-0 border pr-4 pl-4')
|
||||
this.$customer_section.find('.icon').addClass('w-24 h-24 text-2xl').removeClass('w-12 h-12 text-md')
|
||||
this.$customer_section.find('.customer-header').removeClass('h-18');
|
||||
this.$customer_section.find('.customer-details').addClass('sticky z-100 bg-white');
|
||||
const { customer } = this.customer_info || {};
|
||||
|
||||
this.$customer_section.find('.customer-name').html(
|
||||
`<div class="text-md text-dark-grey text-bold">${this.customer_info.customer}</div>
|
||||
<div class="last-transacted-on text-grey-200"></div>`
|
||||
)
|
||||
|
||||
this.$customer_section.find('.customer-details').append(
|
||||
`<div class="customer-form">
|
||||
<div class="text-grey mt-4 mb-6">CONTACT DETAILS</div>
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="email_id-field"></div>
|
||||
<div class="mobile_no-field"></div>
|
||||
<div class="loyalty_program-field"></div>
|
||||
<div class="loyalty_points-field"></div>
|
||||
this.$cart_container.css('display', 'none');
|
||||
this.$customer_section.css({
|
||||
'height': '100%',
|
||||
'padding-top': '0px',
|
||||
'overflow-x': 'hidden',
|
||||
'overflow-y': 'scroll'
|
||||
});
|
||||
this.$customer_section.find('.customer-details').html(
|
||||
`<div class="header">
|
||||
<div class="label">Contact Details</div>
|
||||
<div class="close-details-btn">
|
||||
<svg width="32" height="32" viewBox="0 0 14 14" fill="none">
|
||||
<path d="M4.93764 4.93759L7.00003 6.99998M9.06243 9.06238L7.00003 6.99998M7.00003 6.99998L4.93764 9.06238L9.06243 4.93759" stroke="#8D99A6"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="text-grey mt-4 mb-6">RECENT TRANSACTIONS</div>
|
||||
</div>`
|
||||
)
|
||||
</div>
|
||||
<div class="customer-display">
|
||||
${this.get_customer_image()}
|
||||
<div class="customer-name-desc">
|
||||
<div class="customer-name">${customer}</div>
|
||||
<div class="customer-desc"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="customer-fields-container">
|
||||
<div class="email_id-field"></div>
|
||||
<div class="mobile_no-field"></div>
|
||||
<div class="loyalty_program-field"></div>
|
||||
<div class="loyalty_points-field"></div>
|
||||
</div>
|
||||
<div class="transactions-label">Recent Transactions</div>`
|
||||
);
|
||||
// transactions need to be in diff div from sticky elem for scrolling
|
||||
this.$customer_section.append(`<div class="customer-transactions flex-1 rounded"></div>`)
|
||||
this.$customer_section.append(`<div class="customer-transactions"></div>`)
|
||||
|
||||
this.render_customer_info_form();
|
||||
this.render_customer_fields();
|
||||
this.fetch_customer_transactions();
|
||||
|
||||
} else {
|
||||
this.$cart_container.removeClass('d-none');
|
||||
this.$customer_section.removeClass('flex-1 scroll-y').addClass('mb-0 border pr-4 pl-4');
|
||||
this.$customer_section.find('.icon').addClass('w-12 h-12 text-md').removeClass('w-24 h-24 text-2xl');
|
||||
this.$customer_section.find('.customer-header').addClass('h-18')
|
||||
this.$customer_section.find('.customer-details').removeClass('sticky z-100 bg-white');
|
||||
this.$cart_container.css('display', 'flex');
|
||||
this.$customer_section.css({
|
||||
'height': '',
|
||||
'padding-top': '',
|
||||
'overflow-x': '',
|
||||
'overflow-y': ''
|
||||
});
|
||||
|
||||
this.update_customer_section();
|
||||
}
|
||||
}
|
||||
|
||||
render_customer_info_form() {
|
||||
const $customer_form = this.$customer_section.find('.customer-form');
|
||||
render_customer_fields() {
|
||||
const $customer_form = this.$customer_section.find('.customer-fields-container');
|
||||
|
||||
const dfs = [{
|
||||
fieldname: 'email_id',
|
||||
@ -864,7 +861,7 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
},{
|
||||
fieldname: 'loyalty_points',
|
||||
label: __('Loyalty Points'),
|
||||
fieldtype: 'Int',
|
||||
fieldtype: 'Data',
|
||||
read_only: 1
|
||||
}];
|
||||
|
||||
@ -916,41 +913,45 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
const transaction_container = this.$customer_section.find('.customer-transactions');
|
||||
|
||||
if (!res.length) {
|
||||
transaction_container.removeClass('flex-1 border rounded').html(
|
||||
`<div class="text-grey text-center">No recent transactions found</div>`
|
||||
transaction_container.html(
|
||||
`<div class="no-transactions-placeholder">No recent transactions found</div>`
|
||||
)
|
||||
return;
|
||||
};
|
||||
|
||||
const elapsed_time = moment(res[0].posting_date+" "+res[0].posting_time).fromNow();
|
||||
this.$customer_section.find('.last-transacted-on').html(`Last transacted ${elapsed_time}`);
|
||||
this.$customer_section.find('.customer-desc').html(`Last transacted ${elapsed_time}`);
|
||||
|
||||
res.forEach(invoice => {
|
||||
const posting_datetime = moment(invoice.posting_date+" "+invoice.posting_time).format("Do MMMM, h:mma");
|
||||
let indicator_color = '';
|
||||
|
||||
if (in_list(['Paid', 'Consolidated'], invoice.status)) (indicator_color = 'green');
|
||||
if (invoice.status === 'Draft') (indicator_color = 'red');
|
||||
if (invoice.status === 'Return') (indicator_color = 'grey');
|
||||
let indicator_color = {
|
||||
'Paid': 'green',
|
||||
'Draft': 'red',
|
||||
'Return': 'gray',
|
||||
'Consolidated': 'blue'
|
||||
};
|
||||
|
||||
transaction_container.append(
|
||||
`<div class="invoice-wrapper flex p-3 justify-between border-grey rounded pointer no-select" data-invoice-name="${escape(invoice.name)}">
|
||||
<div class="flex flex-col justify-end">
|
||||
<div class="text-dark-grey text-bold overflow-hidden whitespace-nowrap mb-2">${invoice.name}</div>
|
||||
<div class="flex items-center f-shrink-1 text-dark-grey overflow-hidden whitespace-nowrap">
|
||||
${posting_datetime}
|
||||
</div>
|
||||
`<div class="invoice-wrapper" data-invoice-name="${escape(invoice.name)}">
|
||||
<div class="invoice-name-date">
|
||||
<div class="invoice-name">${invoice.name}</div>
|
||||
<div class="invoice-date">${posting_datetime}</div>
|
||||
</div>
|
||||
<div class="flex flex-col text-right">
|
||||
<div class="f-shrink-0 text-md text-dark-grey text-bold ml-4">
|
||||
<div class="invoice-total-status">
|
||||
<div class="invoice-total">
|
||||
${format_currency(invoice.grand_total, invoice.currency, 0) || 0}
|
||||
</div>
|
||||
<div class="f-shrink-0 text-grey ml-4 text-bold indicator ${indicator_color}">${invoice.status}</div>
|
||||
<div class="invoice-status">
|
||||
<span class="indicator-pill whitespace-nowrap ${indicator_color[invoice.status]}">
|
||||
<span>${invoice.status}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>`
|
||||
</div>
|
||||
<div class="seperator"></div>`
|
||||
)
|
||||
});
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
load_invoice() {
|
||||
@ -973,20 +974,18 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
this.update_totals_section(frm);
|
||||
|
||||
if(frm.doc.docstatus === 1) {
|
||||
this.$totals_section.find('.checkout-btn').addClass('d-none');
|
||||
this.$totals_section.find('.edit-cart-btn').addClass('d-none');
|
||||
this.$totals_section.find('.grand-total').removeClass('border-b-grey');
|
||||
this.$totals_section.find('.checkout-btn').css('display', 'none');
|
||||
this.$totals_section.find('.edit-cart-btn').css('display', 'none');
|
||||
} else {
|
||||
this.$totals_section.find('.checkout-btn').removeClass('d-none');
|
||||
this.$totals_section.find('.edit-cart-btn').addClass('d-none');
|
||||
this.$totals_section.find('.grand-total').addClass('border-b-grey');
|
||||
this.$totals_section.find('.checkout-btn').css('display', 'flex');
|
||||
this.$totals_section.find('.edit-cart-btn').css('display', 'none');
|
||||
}
|
||||
|
||||
this.toggle_component(true);
|
||||
}
|
||||
|
||||
toggle_component(show) {
|
||||
show ? this.$component.removeClass('d-none') : this.$component.addClass('d-none');
|
||||
show ? this.$component.css('display', 'flex') : this.$component.css('display', 'none');
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -16,35 +16,36 @@ erpnext.PointOfSale.ItemDetails = class {
|
||||
|
||||
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>`
|
||||
`<section class="item-details-container"></section>`
|
||||
)
|
||||
|
||||
this.$component = this.wrapper.find('.item-details');
|
||||
this.$component = this.wrapper.find('.item-details-container');
|
||||
}
|
||||
|
||||
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">
|
||||
<div class="text-grey">ITEM DETAILS</div>
|
||||
<div class="close-btn text-grey hover-underline pointer no-select">Close</div>
|
||||
`<div class="item-details-header">
|
||||
<div class="label">Item Details</div>
|
||||
<div class="close-btn">
|
||||
<svg width="32" height="32" viewBox="0 0 14 14" fill="none">
|
||||
<path d="M4.93764 4.93759L7.00003 6.99998M9.06243 9.06238L7.00003 6.99998M7.00003 6.99998L4.93764 9.06238L9.06243 4.93759" stroke="#8D99A6"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="item-defaults flex">
|
||||
<div class="flex-1 flex flex-col justify-end mr-4 mb-2">
|
||||
<div class="item-name text-xl font-weight-450"></div>
|
||||
<div class="item-description text-md-0 text-grey-200"></div>
|
||||
<div class="item-price text-xl font-bold"></div>
|
||||
</div>
|
||||
<div class="item-image flex items-center justify-center w-46 h-46 bg-light-grey rounded ml-4 text-6xl text-grey-100"></div>
|
||||
</div>
|
||||
<div class="item-display">
|
||||
<div class="item-name-desc-price">
|
||||
<div class="item-name"></div>
|
||||
<div class="item-desc"></div>
|
||||
<div class="item-price"></div>
|
||||
</div>
|
||||
<div class="discount-section flex items-center"></div>
|
||||
<div class="text-grey mt-4 mb-6">STOCK DETAILS</div>
|
||||
<div class="form-container grid grid-cols-2 row-gap-2 col-gap-4 grid-auto-row"></div>
|
||||
</div>`
|
||||
<div class="item-image"></div>
|
||||
</div>
|
||||
<div class="discount-section"></div>
|
||||
<div class="form-container"></div>`
|
||||
)
|
||||
|
||||
this.$item_name = this.$component.find('.item-name');
|
||||
this.$item_description = this.$component.find('.item-description');
|
||||
this.$item_description = this.$component.find('.item-desc');
|
||||
this.$item_price = this.$component.find('.item-price');
|
||||
this.$item_image = this.$component.find('.item-image');
|
||||
this.$form_container = this.$component.find('.form-container');
|
||||
@ -52,7 +53,7 @@ erpnext.PointOfSale.ItemDetails = class {
|
||||
}
|
||||
|
||||
toggle_item_details_section(item) {
|
||||
const { item_code, batch_no, uom } = this.current_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;
|
||||
@ -104,11 +105,11 @@ erpnext.PointOfSale.ItemDetails = class {
|
||||
}
|
||||
|
||||
render_dom(item) {
|
||||
let { item_code ,item_name, description, image, price_list_rate } = item;
|
||||
let { item_name, description, image, price_list_rate } = item;
|
||||
|
||||
function get_description_html() {
|
||||
if (description) {
|
||||
description = description.indexOf('...') === -1 && description.length > 75 ? description.substr(0, 73) + '...' : description;
|
||||
description = description.indexOf('...') === -1 && description.length > 140 ? description.substr(0, 139) + '...' : description;
|
||||
return description;
|
||||
}
|
||||
return ``;
|
||||
@ -118,11 +119,9 @@ erpnext.PointOfSale.ItemDetails = class {
|
||||
this.$item_description.html(get_description_html());
|
||||
this.$item_price.html(format_currency(price_list_rate, this.currency));
|
||||
if (image) {
|
||||
this.$item_image.html(
|
||||
`<img class="h-full" src="${image}" alt="${image}" style="object-fit: cover;">`
|
||||
);
|
||||
this.$item_image.html(`<img src="${image}" alt="${image}">`);
|
||||
} else {
|
||||
this.$item_image.html(frappe.get_abbr(item_code));
|
||||
this.$item_image.html(`<div class="item-abbr">${frappe.get_abbr(item_name)}</div>`);
|
||||
}
|
||||
|
||||
}
|
||||
@ -130,12 +129,8 @@ erpnext.PointOfSale.ItemDetails = class {
|
||||
render_discount_dom(item) {
|
||||
if (item.discount_percentage) {
|
||||
this.$dicount_section.html(
|
||||
`<div class="text-grey line-through mr-4 text-md mb-2">
|
||||
${format_currency(item.price_list_rate, this.currency)}
|
||||
</div>
|
||||
<div class="p-1 pr-3 pl-3 rounded w-fit text-bold bg-green-200 mb-2">
|
||||
${item.discount_percentage}% off
|
||||
</div>`
|
||||
`<div class="item-rate">${format_currency(item.price_list_rate, this.currency)}</div>
|
||||
<div class="item-discount">${item.discount_percentage}% off</div>`
|
||||
)
|
||||
this.$item_price.html(format_currency(item.rate, this.currency));
|
||||
} else {
|
||||
@ -149,9 +144,7 @@ erpnext.PointOfSale.ItemDetails = class {
|
||||
|
||||
fields_to_display.forEach((fieldname, idx) => {
|
||||
this.$form_container.append(
|
||||
`<div class="">
|
||||
<div class="item_detail_field ${fieldname}-control" data-fieldname="${fieldname}"></div>
|
||||
</div>`
|
||||
`<div class="${fieldname}-control" data-fieldname="${fieldname}"></div>`
|
||||
)
|
||||
|
||||
const field_meta = this.item_meta.fields.find(df => df.fieldname === fieldname);
|
||||
@ -185,22 +178,15 @@ erpnext.PointOfSale.ItemDetails = class {
|
||||
|
||||
make_auto_serial_selection_btn(item) {
|
||||
if (item.has_serial_no) {
|
||||
this.$form_container.append(
|
||||
`<div class="grid-filler no-select"></div>`
|
||||
)
|
||||
if (!item.has_batch_no) {
|
||||
this.$form_container.append(
|
||||
`<div class="grid-filler no-select"></div>`
|
||||
)
|
||||
}
|
||||
this.$form_container.append(
|
||||
`<div class="auto-fetch-btn bg-grey-100 border border-grey text-bold rounded pt-3 pb-3 pl-6 pr-8 text-grey pointer no-select mt-2"
|
||||
style="height: 3.3rem">
|
||||
Auto Fetch Serial Numbers
|
||||
</div>`
|
||||
`<div class="btn btn-sm btn-secondary auto-fetch-btn">Auto Fetch Serial Numbers</div>`
|
||||
)
|
||||
this.$form_container.find('.serial_no-control').find('textarea').css('height', '9rem');
|
||||
this.$form_container.find('.serial_no-control').parent().addClass('row-span-2');
|
||||
this.$form_container.find('.serial_no-control').find('textarea').css('height', '6rem');
|
||||
}
|
||||
}
|
||||
|
||||
@ -294,8 +280,13 @@ erpnext.PointOfSale.ItemDetails = class {
|
||||
}
|
||||
|
||||
frappe.model.on("POS Invoice Item", "*", (fieldname, value, item_row) => {
|
||||
const field_control = me[`${fieldname}_control`];
|
||||
if (field_control) {
|
||||
const field_control = this[`${fieldname}_control`];
|
||||
const { item_code, batch_no, uom } = this.current_item;
|
||||
const item_code_is_same = item_code === item_row.item_code;
|
||||
const batch_is_same = batch_no == item_row.batch_no;
|
||||
const uom_is_same = uom === item_row.uom;
|
||||
|
||||
if (field_control && item_code_is_same && batch_is_same && uom_is_same) {
|
||||
field_control.set_value(value);
|
||||
cur_pos.update_cart_html(item_row);
|
||||
}
|
||||
@ -409,6 +400,6 @@ erpnext.PointOfSale.ItemDetails = class {
|
||||
}
|
||||
|
||||
toggle_component(show) {
|
||||
show ? this.$component.removeClass('d-none') : this.$component.addClass('d-none');
|
||||
show ? this.$component.css('display', 'flex') : this.$component.css('display', 'none');
|
||||
}
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
import onScan from 'onscan.js';
|
||||
|
||||
erpnext.PointOfSale.ItemSelector = class {
|
||||
constructor({ frm, wrapper, events, pos_profile }) {
|
||||
this.wrapper = wrapper;
|
||||
@ -17,18 +19,13 @@ erpnext.PointOfSale.ItemSelector = class {
|
||||
|
||||
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>
|
||||
`<section class="items-selector">
|
||||
<div class="filter-section">
|
||||
<div class="label">All Items</div>
|
||||
<div class="search-field"></div>
|
||||
<div class="item-group-field"></div>
|
||||
</div>
|
||||
<div class="items-container"></div>
|
||||
</section>`
|
||||
);
|
||||
|
||||
@ -51,7 +48,8 @@ erpnext.PointOfSale.ItemSelector = class {
|
||||
}
|
||||
|
||||
get_items({start = 0, page_length = 40, search_value=''}) {
|
||||
const price_list = this.events.get_frm().doc?.selling_price_list || this.price_list;
|
||||
const doc = this.events.get_frm().doc;
|
||||
const price_list = (doc && doc.selling_price_list) || this.price_list;
|
||||
let { item_group, pos_profile } = this;
|
||||
|
||||
!item_group && (item_group = this.parent_item_group);
|
||||
@ -80,27 +78,28 @@ erpnext.PointOfSale.ItemSelector = class {
|
||||
|
||||
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="${frappe.get_abbr(item.item_name)}" style="object-fit: cover;">
|
||||
</div>`
|
||||
return `<div class="item-display">
|
||||
<img src="${item_image}" alt="${frappe.get_abbr(item.item_name)}">
|
||||
</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-display abbr">${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)}"
|
||||
`<div class="item-wrapper"
|
||||
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">
|
||||
|
||||
<div class="item-detail">
|
||||
<div class="item-name">
|
||||
<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 class="item-rate">${format_currency(item.price_list_rate, item.currency, 0) || 0}</div>
|
||||
</div>
|
||||
</div>`
|
||||
)
|
||||
@ -108,6 +107,7 @@ erpnext.PointOfSale.ItemSelector = class {
|
||||
|
||||
make_search_bar() {
|
||||
const me = this;
|
||||
const doc = me.events.get_frm().doc;
|
||||
this.$component.find('.search-field').html('');
|
||||
this.$component.find('.item-group-field').html('');
|
||||
|
||||
@ -115,7 +115,7 @@ erpnext.PointOfSale.ItemSelector = class {
|
||||
df: {
|
||||
label: __('Search'),
|
||||
fieldtype: 'Data',
|
||||
placeholder: __('Search by item code, serial number, batch no or barcode')
|
||||
placeholder: __('Search by item code, serial number or barcode')
|
||||
},
|
||||
parent: this.$component.find('.search-field'),
|
||||
render_input: true,
|
||||
@ -135,7 +135,7 @@ erpnext.PointOfSale.ItemSelector = class {
|
||||
return {
|
||||
query: 'erpnext.selling.page.point_of_sale.point_of_sale.item_group_query',
|
||||
filters: {
|
||||
pos_profile: me.events.get_frm().doc?.pos_profile
|
||||
pos_profile: doc ? doc.pos_profile : ''
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -149,6 +149,7 @@ erpnext.PointOfSale.ItemSelector = class {
|
||||
|
||||
bind_events() {
|
||||
const me = this;
|
||||
window.onScan = onScan;
|
||||
onScan.attachTo(document, {
|
||||
onScan: (sScancode) => {
|
||||
if (this.search_field && this.$component.is(':visible')) {
|
||||
@ -252,23 +253,23 @@ erpnext.PointOfSale.ItemSelector = class {
|
||||
|
||||
resize_selector(minimize) {
|
||||
minimize ?
|
||||
this.$component.find('.search-field').removeClass('mr-8') :
|
||||
this.$component.find('.search-field').addClass('mr-8');
|
||||
this.$component.find('.filter-section').css('grid-template-columns', 'repeat(1, minmax(0, 1fr))') :
|
||||
this.$component.find('.filter-section').css('grid-template-columns', 'repeat(12, minmax(0, 1fr))');
|
||||
|
||||
minimize ?
|
||||
this.$component.find('.filter-section').addClass('flex-col') :
|
||||
this.$component.find('.filter-section').removeClass('flex-col');
|
||||
this.$component.find('.search-field').css('margin', 'var(--margin-sm) 0px') :
|
||||
this.$component.find('.search-field').css('margin', '0px var(--margin-sm)');
|
||||
|
||||
minimize ?
|
||||
this.$component.removeClass('col-span-6').addClass('col-span-2') :
|
||||
this.$component.removeClass('col-span-2').addClass('col-span-6')
|
||||
this.$component.css('grid-column', 'span 2 / span 2') :
|
||||
this.$component.css('grid-column', 'span 6 / 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')
|
||||
this.$items_container.css('grid-template-columns', 'repeat(1, minmax(0, 1fr))') :
|
||||
this.$items_container.css('grid-template-columns', 'repeat(4, minmax(0, 1fr))')
|
||||
}
|
||||
|
||||
toggle_component(show) {
|
||||
show ? this.$component.removeClass('d-none') : this.$component.addClass('d-none');
|
||||
show ? this.$component.css('display', 'flex') : this.$component.css('display', 'none');
|
||||
}
|
||||
}
|
@ -25,14 +25,13 @@ erpnext.PointOfSale.NumberPad = class {
|
||||
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>`
|
||||
return a2 + `<div class="numpad-btn ${class_to_append}" data-button-value="${fieldname}">${number}</div>`
|
||||
}, '')
|
||||
}, '');
|
||||
}
|
||||
|
||||
this.wrapper.html(
|
||||
`<div class="grid grid-cols-${cols} gap-4">
|
||||
`<div class="numpad-container">
|
||||
${get_keys()}
|
||||
</div>`
|
||||
)
|
||||
|
@ -14,17 +14,13 @@ erpnext.PointOfSale.PastOrderList = class {
|
||||
|
||||
prepare_dom() {
|
||||
this.wrapper.append(
|
||||
`<section class="col-span-4 flex flex-col shadow rounded past-order-list bg-white mx-h-70 h-100 d-none">
|
||||
<div class="flex flex-col rounded w-full scroll-y">
|
||||
<div class="filter-section flex flex-col p-8 pb-2 bg-white sticky z-100">
|
||||
<div class="search-field flex items-center text-grey"></div>
|
||||
<div class="status-field flex 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">RECENT ORDERS</div>
|
||||
<div class="invoices-container rounded border grid grid-cols-1"></div>
|
||||
</div>
|
||||
`<section class="past-order-list">
|
||||
<div class="filter-section">
|
||||
<div class="label">Recent Orders</div>
|
||||
<div class="search-field"></div>
|
||||
<div class="status-field"></div>
|
||||
</div>
|
||||
<div class="invoices-container"></div>
|
||||
</section>`
|
||||
);
|
||||
|
||||
@ -66,7 +62,7 @@ erpnext.PointOfSale.PastOrderList = class {
|
||||
options: `Draft\nPaid\nConsolidated\nReturn`,
|
||||
placeholder: __('Filter by invoice status'),
|
||||
onchange: function() {
|
||||
me.refresh_list(me.search_field.get_value(), this.value);
|
||||
if (me.$component.is(':visible')) me.refresh_list();
|
||||
}
|
||||
},
|
||||
parent: this.$component.find('.status-field'),
|
||||
@ -77,10 +73,6 @@ erpnext.PointOfSale.PastOrderList = class {
|
||||
this.status_field.set_value('Draft');
|
||||
}
|
||||
|
||||
toggle_component(show) {
|
||||
show ? this.$component.removeClass('d-none') && this.refresh_list() : this.$component.addClass('d-none');
|
||||
}
|
||||
|
||||
refresh_list() {
|
||||
frappe.dom.freeze();
|
||||
this.events.reset_summary();
|
||||
@ -106,23 +98,26 @@ erpnext.PointOfSale.PastOrderList = class {
|
||||
get_invoice_html(invoice) {
|
||||
const posting_datetime = moment(invoice.posting_date+" "+invoice.posting_time).format("Do MMMM, h:mma");
|
||||
return (
|
||||
`<div class="invoice-wrapper flex p-4 justify-between border-b-grey pointer no-select" data-invoice-name="${escape(invoice.name)}">
|
||||
<div class="flex flex-col justify-end">
|
||||
<div class="text-dark-grey text-bold overflow-hidden whitespace-nowrap mb-2">${invoice.name}</div>
|
||||
<div class="flex items-center">
|
||||
<div class="flex items-center f-shrink-1 text-dark-grey overflow-hidden whitespace-nowrap">
|
||||
<svg class="mr-2" width="12" height="12" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/>
|
||||
</svg>
|
||||
${invoice.customer}
|
||||
</div>
|
||||
`<div class="invoice-wrapper" data-invoice-name="${escape(invoice.name)}">
|
||||
<div class="invoice-name-date">
|
||||
<div class="invoice-name">${invoice.name}</div>
|
||||
<div class="invoice-date">
|
||||
<svg class="mr-2" width="12" height="12" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/>
|
||||
</svg>
|
||||
${invoice.customer}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col text-right">
|
||||
<div class="f-shrink-0 text-lg text-dark-grey text-bold ml-4">${format_currency(invoice.grand_total, invoice.currency, 0) || 0}</div>
|
||||
<div class="f-shrink-0 text-grey ml-4">${posting_datetime}</div>
|
||||
<div class="invoice-total-status">
|
||||
<div class="invoice-total">${format_currency(invoice.grand_total, invoice.currency, 0) || 0}</div>
|
||||
<div class="invoice-date">${posting_datetime}</div>
|
||||
</div>
|
||||
</div>`
|
||||
</div>
|
||||
<div class="seperator"></div>`
|
||||
);
|
||||
}
|
||||
|
||||
toggle_component(show) {
|
||||
show ? this.$component.css('display', 'flex') && this.refresh_list() : this.$component.css('display', 'none');
|
||||
}
|
||||
};
|
@ -8,85 +8,39 @@ erpnext.PointOfSale.PastOrderSummary = class {
|
||||
|
||||
init_component() {
|
||||
this.prepare_dom();
|
||||
this.init_child_components();
|
||||
this.init_email_print_dialog();
|
||||
this.bind_events();
|
||||
this.attach_shortcuts();
|
||||
}
|
||||
|
||||
prepare_dom() {
|
||||
this.wrapper.append(
|
||||
`<section class="col-span-6 flex flex-col items-center shadow rounded past-order-summary bg-white mx-h-70 h-100 d-none">
|
||||
<div class="no-summary-placeholder flex flex-1 items-center justify-center p-16">
|
||||
<div class="no-item-wrapper flex items-center h-18 pr-4 pl-4">
|
||||
<div class="flex-1 text-center text-grey">Select an invoice to load summary data</div>
|
||||
</div>
|
||||
`<section class="past-order-summary">
|
||||
<div class="no-summary-placeholder">
|
||||
Select an invoice to load summary data
|
||||
</div>
|
||||
<div class="summary-wrapper d-none flex-1 w-66 text-dark-grey relative">
|
||||
<div class="summary-container absolute flex flex-col pt-16 pb-16 pr-8 pl-8 w-full h-full"></div>
|
||||
<div class="invoice-summary-wrapper">
|
||||
<div class="abs-container">
|
||||
<div class="upper-section"></div>
|
||||
<div class="label">Items</div>
|
||||
<div class="items-container summary-container"></div>
|
||||
<div class="label">Totals</div>
|
||||
<div class="totals-container summary-container"></div>
|
||||
<div class="label">Payments</div>
|
||||
<div class="payments-container summary-container"></div>
|
||||
<div class="summary-btns"></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>`
|
||||
);
|
||||
|
||||
this.$component = this.wrapper.find('.past-order-summary');
|
||||
this.$summary_wrapper = this.$component.find('.summary-wrapper');
|
||||
this.$summary_container = this.$component.find('.summary-container');
|
||||
}
|
||||
|
||||
init_child_components() {
|
||||
this.init_upper_section();
|
||||
this.init_items_summary();
|
||||
this.init_totals_summary();
|
||||
this.init_payments_summary();
|
||||
this.init_summary_buttons();
|
||||
this.init_email_print_dialog();
|
||||
}
|
||||
|
||||
init_upper_section() {
|
||||
this.$summary_container.append(
|
||||
`<div class="flex upper-section justify-between w-full h-24"></div>`
|
||||
);
|
||||
|
||||
this.$summary_wrapper = this.$component.find('.invoice-summary-wrapper');
|
||||
this.$summary_container = this.$component.find('.abs-container');
|
||||
this.$upper_section = this.$summary_container.find('.upper-section');
|
||||
}
|
||||
|
||||
init_items_summary() {
|
||||
this.$summary_container.append(
|
||||
`<div class="flex flex-col flex-1 mt-6 w-full scroll-y">
|
||||
<div class="text-grey mb-4 sticky bg-white">ITEMS</div>
|
||||
<div class="items-summary-container border rounded flex flex-col w-full"></div>
|
||||
</div>`
|
||||
);
|
||||
|
||||
this.$items_summary_container = this.$summary_container.find('.items-summary-container');
|
||||
}
|
||||
|
||||
init_totals_summary() {
|
||||
this.$summary_container.append(
|
||||
`<div class="flex flex-col mt-6 w-full f-shrink-0">
|
||||
<div class="text-grey mb-4">TOTALS</div>
|
||||
<div class="summary-totals-container border rounded flex flex-col w-full"></div>
|
||||
</div>`
|
||||
);
|
||||
|
||||
this.$totals_summary_container = this.$summary_container.find('.summary-totals-container');
|
||||
}
|
||||
|
||||
init_payments_summary() {
|
||||
this.$summary_container.append(
|
||||
`<div class="flex flex-col mt-6 w-full f-shrink-0">
|
||||
<div class="text-grey mb-4">PAYMENTS</div>
|
||||
<div class="payments-summary-container border rounded flex flex-col w-full mb-4"></div>
|
||||
</div>`
|
||||
);
|
||||
|
||||
this.$payment_summary_container = this.$summary_container.find('.payments-summary-container');
|
||||
}
|
||||
|
||||
init_summary_buttons() {
|
||||
this.$summary_container.append(
|
||||
`<div class="summary-btns flex summary-btns justify-between w-full f-shrink-0"></div>`
|
||||
);
|
||||
|
||||
this.$items_container = this.$summary_container.find('.items-container');
|
||||
this.$totals_container = this.$summary_container.find('.totals-container');
|
||||
this.$payment_container = this.$summary_container.find('.payments-container');
|
||||
this.$summary_btns = this.$summary_container.find('.summary-btns');
|
||||
}
|
||||
|
||||
@ -121,132 +75,88 @@ erpnext.PointOfSale.PastOrderSummary = class {
|
||||
}
|
||||
|
||||
get_upper_section_html(doc) {
|
||||
const { status } = doc; let indicator_color = '';
|
||||
const { status } = doc;
|
||||
let indicator_color = '';
|
||||
|
||||
in_list(['Paid', 'Consolidated'], status) && (indicator_color = 'green');
|
||||
status === 'Draft' && (indicator_color = 'red');
|
||||
status === 'Return' && (indicator_color = 'grey');
|
||||
|
||||
return `<div class="flex flex-col items-start justify-end pr-4">
|
||||
<div class="text-lg text-bold pt-2">${doc.customer}</div>
|
||||
<div class="text-grey">${this.customer_email}</div>
|
||||
<div class="text-grey mt-auto">Sold by: ${doc.owner}</div>
|
||||
return `<div class="left-section">
|
||||
<div class="customer-name">${doc.customer}</div>
|
||||
<div class="customer-email">${this.customer_email}</div>
|
||||
<div class="cashier">Sold by: ${doc.owner}</div>
|
||||
</div>
|
||||
<div class="flex flex-col flex-1 items-end justify-between">
|
||||
<div class="text-2-5xl text-bold">${format_currency(doc.paid_amount, doc.currency)}</div>
|
||||
<div class="flex justify-between">
|
||||
<div class="text-grey mr-4">${doc.name}</div>
|
||||
<div class="text-grey text-bold indicator ${indicator_color}">${doc.status}</div>
|
||||
</div>
|
||||
<div class="right-section">
|
||||
<div class="paid-amount">${format_currency(doc.paid_amount, doc.currency)}</div>
|
||||
<div class="invoice-name">${doc.name}</div>
|
||||
<span class="indicator-pill whitespace-nowrap ${indicator_color}"><span>${doc.status}</span></span>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
get_item_html(doc, item_data) {
|
||||
return `<div class="item-row-wrapper">
|
||||
<div class="item-name">${item_data.item_name}</div>
|
||||
<div class="item-qty">${item_data.qty || 0}</div>
|
||||
<div class="item-rate-disc">${get_rate_discount_html()}</div>
|
||||
</div>`;
|
||||
|
||||
function get_rate_discount_html() {
|
||||
if (item_data.rate && item_data.price_list_rate && item_data.rate !== item_data.price_list_rate) {
|
||||
return `<span class="item-disc">(${item_data.discount_percentage}% off)</span>
|
||||
<div class="item-rate">${format_currency(item_data.rate, doc.currency)}</div>`;
|
||||
} else {
|
||||
return `<div class="item-rate">${format_currency(item_data.price_list_rate || item_data.rate, doc.currency)}</div>`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get_discount_html(doc) {
|
||||
if (doc.discount_amount) {
|
||||
return `<div class="total-summary-wrapper flex items-center h-12 pr-4 pl-4 pointer border-b-grey no-select">
|
||||
<div class="flex f-shrink-1 items-center">
|
||||
<div class="text-md-0 text-dark-grey text-bold overflow-hidden whitespace-nowrap mr-2">
|
||||
Discount
|
||||
</div>
|
||||
<span class="text-grey">(${doc.additional_discount_percentage} %)</span>
|
||||
</div>
|
||||
<div class="flex flex-col f-shrink-0 ml-auto text-right">
|
||||
<div class="text-md-0 text-dark-grey text-bold">${format_currency(doc.discount_amount, doc.currency)}</div>
|
||||
</div>
|
||||
</div>`;
|
||||
return `<div class="summary-row-wrapper">
|
||||
<div>Discount (${doc.additional_discount_percentage} %)</div>
|
||||
<div>${format_currency(doc.discount_amount, doc.currency)}</div>
|
||||
</div>`;
|
||||
} else {
|
||||
return ``;
|
||||
}
|
||||
}
|
||||
|
||||
get_net_total_html(doc) {
|
||||
return `<div class="total-summary-wrapper flex items-center h-12 pr-4 pl-4 pointer border-b-grey no-select">
|
||||
<div class="flex f-shrink-1 items-center">
|
||||
<div class="text-md-0 text-dark-grey text-bold overflow-hidden whitespace-nowrap">
|
||||
Net Total
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col f-shrink-0 ml-auto text-right">
|
||||
<div class="text-md-0 text-dark-grey text-bold">${format_currency(doc.net_total, doc.currency)}</div>
|
||||
</div>
|
||||
return `<div class="summary-row-wrapper">
|
||||
<div>Net Total</div>
|
||||
<div>${format_currency(doc.net_total, doc.currency)}</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
get_taxes_html(doc) {
|
||||
const taxes = doc.taxes.map((t, i) => {
|
||||
let margin_left = '';
|
||||
if (i !== 0) margin_left = 'ml-2';
|
||||
return `<span class="pl-2 pr-2 ${margin_left}">${t.description} @${t.rate}%</span>`;
|
||||
}).join('');
|
||||
if (!doc.taxes.length) return '';
|
||||
|
||||
return `
|
||||
<div class="total-summary-wrapper flex items-center justify-between h-12 pr-4 pl-4 border-b-grey">
|
||||
<div class="flex">
|
||||
<div class="text-md-0 text-dark-grey text-bold w-fit">Tax Charges</div>
|
||||
<div class="flex ml-6 text-dark-grey">${taxes}</div>
|
||||
</div>
|
||||
<div class="flex flex-col text-right">
|
||||
<div class="text-md-0 text-dark-grey text-bold">
|
||||
${format_currency(doc.base_total_taxes_and_charges, doc.currency)}
|
||||
</div>
|
||||
</div>
|
||||
<div class="taxes-wrapper">
|
||||
${
|
||||
doc.taxes.map((t, i) => {
|
||||
const description = /[0-9]+/.test(t.description) ? t.description : `${t.description} @ ${t.rate}%`;
|
||||
return `<div class="tax-row">
|
||||
<div class="tax-label">${description}</div>
|
||||
<div class="tax-value">${format_currency(t.tax_amount_after_discount_amount, doc.currency)}</div>
|
||||
</div>`
|
||||
}).join('')
|
||||
}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
get_grand_total_html(doc) {
|
||||
return `<div class="total-summary-wrapper flex items-center h-12 pr-4 pl-4 pointer border-b-grey no-select">
|
||||
<div class="flex f-shrink-1 items-center">
|
||||
<div class="text-md-0 text-dark-grey text-bold overflow-hidden whitespace-nowrap">
|
||||
Grand Total
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col f-shrink-0 ml-auto text-right">
|
||||
<div class="text-md-0 text-dark-grey text-bold">${format_currency(doc.grand_total, doc.currency)}</div>
|
||||
</div>
|
||||
return `<div class="summary-row-wrapper grand-total">
|
||||
<div>Grand Total</div>
|
||||
<div>${format_currency(doc.grand_total, doc.currency)}</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
get_item_html(doc, item_data) {
|
||||
return `<div class="item-summary-wrapper flex items-center h-12 pr-4 pl-4 border-b-grey pointer no-select">
|
||||
<div class="flex w-6 h-6 rounded bg-light-grey mr-4 items-center justify-center font-bold f-shrink-0">
|
||||
<span>${item_data.qty || 0}</span>
|
||||
</div>
|
||||
<div class="flex flex-col f-shrink-1">
|
||||
<div class="text-md text-dark-grey text-bold overflow-hidden whitespace-nowrap">
|
||||
${item_data.item_name}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex f-shrink-0 ml-auto text-right">
|
||||
${get_rate_discount_html()}
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
function get_rate_discount_html() {
|
||||
if (item_data.rate && item_data.price_list_rate && item_data.rate !== item_data.price_list_rate) {
|
||||
return `<span class="text-grey mr-2">
|
||||
(${item_data.discount_percentage}% off)
|
||||
</span>
|
||||
<div class="text-md-0 text-dark-grey text-bold">
|
||||
${format_currency(item_data.rate, doc.currency)}
|
||||
</div>`;
|
||||
} else {
|
||||
return `<div class="text-md-0 text-dark-grey text-bold">
|
||||
${format_currency(item_data.price_list_rate || item_data.rate, doc.currency)}
|
||||
</div>`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get_payment_html(doc, payment) {
|
||||
return `<div class="payment-summary-wrapper flex items-center h-12 pr-4 pl-4 pointer border-b-grey no-select">
|
||||
<div class="flex f-shrink-1 items-center">
|
||||
<div class="text-md-0 text-dark-grey text-bold overflow-hidden whitespace-nowrap">
|
||||
${payment.mode_of_payment}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col f-shrink-0 ml-auto text-right">
|
||||
<div class="text-md-0 text-dark-grey text-bold">${format_currency(payment.amount, doc.currency)}</div>
|
||||
</div>
|
||||
return `<div class="summary-row-wrapper payments">
|
||||
<div>${payment.mode_of_payment}</div>
|
||||
<div>${format_currency(payment.amount, doc.currency)}</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
@ -254,22 +164,22 @@ erpnext.PointOfSale.PastOrderSummary = class {
|
||||
this.$summary_container.on('click', '.return-btn', () => {
|
||||
this.events.process_return(this.doc.name);
|
||||
this.toggle_component(false);
|
||||
this.$component.find('.no-summary-placeholder').removeClass('d-none');
|
||||
this.$summary_wrapper.addClass('d-none');
|
||||
this.$component.find('.no-summary-placeholder').css('display', 'flex');
|
||||
this.$summary_wrapper.css('display', 'none');
|
||||
});
|
||||
|
||||
this.$summary_container.on('click', '.edit-btn', () => {
|
||||
this.events.edit_order(this.doc.name);
|
||||
this.toggle_component(false);
|
||||
this.$component.find('.no-summary-placeholder').removeClass('d-none');
|
||||
this.$summary_wrapper.addClass('d-none');
|
||||
this.$component.find('.no-summary-placeholder').css('display', 'flex');
|
||||
this.$summary_wrapper.css('display', 'none');
|
||||
});
|
||||
|
||||
this.$summary_container.on('click', '.new-btn', () => {
|
||||
this.events.new_order();
|
||||
this.toggle_component(false);
|
||||
this.$component.find('.no-summary-placeholder').removeClass('d-none');
|
||||
this.$summary_wrapper.addClass('d-none');
|
||||
this.$component.find('.no-summary-placeholder').css('display', 'flex');
|
||||
this.$summary_wrapper.css('display', 'none');
|
||||
});
|
||||
|
||||
this.$summary_container.on('click', '.email-btn', () => {
|
||||
@ -312,10 +222,6 @@ erpnext.PointOfSale.PastOrderSummary = class {
|
||||
});
|
||||
}
|
||||
|
||||
toggle_component(show) {
|
||||
show ? this.$component.removeClass('d-none') : this.$component.addClass('d-none');
|
||||
}
|
||||
|
||||
send_email() {
|
||||
const frm = this.events.get_frm();
|
||||
const recipients = this.email_dialog.get_values().recipients;
|
||||
@ -338,8 +244,10 @@ erpnext.PointOfSale.PastOrderSummary = class {
|
||||
if(!r.exc) {
|
||||
frappe.utils.play_sound("email");
|
||||
if(r.message["emails_not_sent_to"]) {
|
||||
frappe.msgprint(__("Email not sent to {0} (unsubscribed / disabled)",
|
||||
[ frappe.utils.escape_html(r.message["emails_not_sent_to"]) ]) );
|
||||
frappe.msgprint(__(
|
||||
"Email not sent to {0} (unsubscribed / disabled)",
|
||||
[ frappe.utils.escape_html(r.message["emails_not_sent_to"]) ]
|
||||
));
|
||||
} else {
|
||||
frappe.show_alert({
|
||||
message: __('Email sent successfully.'),
|
||||
@ -361,9 +269,7 @@ erpnext.PointOfSale.PastOrderSummary = class {
|
||||
m.visible_btns.forEach(b => {
|
||||
const class_name = b.split(' ')[0].toLowerCase();
|
||||
this.$summary_btns.append(
|
||||
`<div class="${class_name}-btn border rounded h-14 flex flex-1 items-center mr-4 justify-center text-md text-bold no-select pointer">
|
||||
${b}
|
||||
</div>`
|
||||
`<div class="summary-btn btn btn-default ${class_name}-btn">${b}</div>`
|
||||
);
|
||||
});
|
||||
}
|
||||
@ -371,29 +277,14 @@ erpnext.PointOfSale.PastOrderSummary = class {
|
||||
this.$summary_btns.children().last().removeClass('mr-4');
|
||||
}
|
||||
|
||||
show_summary_placeholder() {
|
||||
this.$summary_wrapper.addClass("d-none");
|
||||
this.$component.find('.no-summary-placeholder').removeClass('d-none');
|
||||
}
|
||||
|
||||
switch_to_post_submit_summary() {
|
||||
// switch to full width view
|
||||
this.$component.removeClass('col-span-6').addClass('col-span-10');
|
||||
this.$summary_wrapper.removeClass('w-66').addClass('w-40');
|
||||
|
||||
// switch place holder with summary container
|
||||
this.$component.find('.no-summary-placeholder').addClass('d-none');
|
||||
this.$summary_wrapper.removeClass('d-none');
|
||||
}
|
||||
|
||||
switch_to_recent_invoice_summary() {
|
||||
// switch full width view with 60% view
|
||||
this.$component.removeClass('col-span-10').addClass('col-span-6');
|
||||
this.$summary_wrapper.removeClass('w-40').addClass('w-66');
|
||||
|
||||
// switch place holder with summary container
|
||||
this.$component.find('.no-summary-placeholder').addClass('d-none');
|
||||
this.$summary_wrapper.removeClass('d-none');
|
||||
toggle_summary_placeholder(show) {
|
||||
if (show) {
|
||||
this.$summary_wrapper.css('display', 'none');
|
||||
this.$component.find('.no-summary-placeholder').css('display', 'flex');
|
||||
} else {
|
||||
this.$summary_wrapper.css('display', 'flex');
|
||||
this.$component.find('.no-summary-placeholder').css('display', 'none');
|
||||
}
|
||||
}
|
||||
|
||||
get_condition_btn_map(after_submission) {
|
||||
@ -408,14 +299,15 @@ erpnext.PointOfSale.PastOrderSummary = class {
|
||||
}
|
||||
|
||||
load_summary_of(doc, after_submission=false) {
|
||||
this.$summary_wrapper.removeClass("d-none");
|
||||
this.toggle_summary_placeholder(false)
|
||||
|
||||
after_submission ?
|
||||
this.switch_to_post_submit_summary() : this.switch_to_recent_invoice_summary();
|
||||
this.$summary_wrapper.css('grid-column', 'span 10 / span 10') :
|
||||
this.$summary_wrapper.css('grid-column', 'span 6 / span 6')
|
||||
|
||||
this.doc = doc;
|
||||
|
||||
this.attach_basic_info(doc);
|
||||
this.attach_document_info(doc);
|
||||
|
||||
this.attach_items_info(doc);
|
||||
|
||||
@ -428,7 +320,7 @@ erpnext.PointOfSale.PastOrderSummary = class {
|
||||
this.add_summary_btns(condition_btns_map);
|
||||
}
|
||||
|
||||
attach_basic_info(doc) {
|
||||
attach_document_info(doc) {
|
||||
frappe.db.get_value('Customer', this.doc.customer, 'email_id').then(({ message }) => {
|
||||
this.customer_email = message.email_id || '';
|
||||
const upper_section_dom = this.get_upper_section_html(doc);
|
||||
@ -437,19 +329,19 @@ erpnext.PointOfSale.PastOrderSummary = class {
|
||||
}
|
||||
|
||||
attach_items_info(doc) {
|
||||
this.$items_summary_container.html('');
|
||||
doc.items.forEach(item => {
|
||||
this.$items_container.html('');
|
||||
doc.items.forEach((item, i) => {
|
||||
const item_dom = this.get_item_html(doc, item);
|
||||
this.$items_summary_container.append(item_dom);
|
||||
this.$items_container.append(item_dom);
|
||||
});
|
||||
}
|
||||
|
||||
attach_payments_info(doc) {
|
||||
this.$payment_summary_container.html('');
|
||||
this.$payment_container.html('');
|
||||
doc.payments.forEach(p => {
|
||||
if (p.amount) {
|
||||
const payment_dom = this.get_payment_html(doc, p);
|
||||
this.$payment_summary_container.append(payment_dom);
|
||||
this.$payment_container.append(payment_dom);
|
||||
}
|
||||
});
|
||||
if (doc.redeem_loyalty_points && doc.loyalty_amount) {
|
||||
@ -457,20 +349,24 @@ erpnext.PointOfSale.PastOrderSummary = class {
|
||||
mode_of_payment: 'Loyalty Points',
|
||||
amount: doc.loyalty_amount,
|
||||
});
|
||||
this.$payment_summary_container.append(payment_dom);
|
||||
this.$payment_container.append(payment_dom);
|
||||
}
|
||||
}
|
||||
|
||||
attach_totals_info(doc) {
|
||||
this.$totals_summary_container.html('');
|
||||
this.$totals_container.html('');
|
||||
|
||||
const discount_dom = this.get_discount_html(doc);
|
||||
const net_total_dom = this.get_net_total_html(doc);
|
||||
const taxes_dom = this.get_taxes_html(doc);
|
||||
const discount_dom = this.get_discount_html(doc);
|
||||
const grand_total_dom = this.get_grand_total_html(doc);
|
||||
this.$totals_summary_container.append(discount_dom);
|
||||
this.$totals_summary_container.append(net_total_dom);
|
||||
this.$totals_summary_container.append(taxes_dom);
|
||||
this.$totals_summary_container.append(grand_total_dom);
|
||||
this.$totals_container.append(net_total_dom);
|
||||
this.$totals_container.append(taxes_dom);
|
||||
this.$totals_container.append(discount_dom);
|
||||
this.$totals_container.append(grand_total_dom);
|
||||
}
|
||||
|
||||
toggle_component(show) {
|
||||
show ? this.$component.css('display', 'flex') : this.$component.css('display', 'none');
|
||||
}
|
||||
};
|
@ -1,5 +1,3 @@
|
||||
{% include "erpnext/selling/page/point_of_sale/pos_number_pad.js" %}
|
||||
|
||||
erpnext.PointOfSale.Payment = class {
|
||||
constructor({ events, wrapper }) {
|
||||
this.wrapper = wrapper;
|
||||
@ -18,52 +16,37 @@ erpnext.PointOfSale.Payment = class {
|
||||
|
||||
prepare_dom() {
|
||||
this.wrapper.append(
|
||||
`<section class="col-span-6 flex shadow rounded payment-section bg-white mx-h-70 h-100 d-none">
|
||||
<div class="flex flex-col p-16 pt-8 pb-8 w-full">
|
||||
<div class="text-grey mb-6 payment-section no-select pointer">
|
||||
PAYMENT METHOD<span class="octicon octicon-chevron-down collapse-indicator"></span>
|
||||
</div>
|
||||
<div class="payment-modes flex flex-wrap"></div>
|
||||
<div class="invoice-details-section"></div>
|
||||
<div class="flex mt-auto justify-center w-full">
|
||||
<div class="flex flex-col justify-center flex-1 ml-4">
|
||||
<div class="flex w-full">
|
||||
<div class="totals-remarks items-end justify-end flex flex-1">
|
||||
<div class="remarks text-md-0 text-grey mr-auto"></div>
|
||||
<div class="totals flex justify-end pt-4"></div>
|
||||
</div>
|
||||
<div class="number-pad w-40 mb-4 ml-8 d-none"></div>
|
||||
</div>
|
||||
<div class="flex items-center justify-center mt-4 submit-order h-16 w-full rounded bg-primary text-md text-white no-select pointer text-bold">
|
||||
Complete Order
|
||||
</div>
|
||||
<div class="order-time flex items-center justify-end mt-2 pt-2 pb-2 w-full text-md-0 text-grey no-select pointer d-none"></div>
|
||||
</div>
|
||||
`<section class="payment-container">
|
||||
<div class="section-label payment-section">Payment Method</div>
|
||||
<div class="payment-modes"></div>
|
||||
<div class="fields-numpad-container">
|
||||
<div class="fields-section">
|
||||
<div class="section-label">Additional Information</div>
|
||||
<div class="invoice-fields"></div>
|
||||
</div>
|
||||
<div class="number-pad"></div>
|
||||
</div>
|
||||
<div class="totals-section">
|
||||
<div class="totals"></div>
|
||||
</div>
|
||||
<div class="submit-order-btn">Complete Order</div>
|
||||
</section>`
|
||||
)
|
||||
this.$component = this.wrapper.find('.payment-section');
|
||||
this.$component = this.wrapper.find('.payment-container');
|
||||
this.$payment_modes = this.$component.find('.payment-modes');
|
||||
this.$totals_remarks = this.$component.find('.totals-remarks');
|
||||
this.$totals_section = this.$component.find('.totals-section');
|
||||
this.$totals = this.$component.find('.totals');
|
||||
this.$remarks = this.$component.find('.remarks');
|
||||
this.$numpad = this.$component.find('.number-pad');
|
||||
this.$invoice_details_section = this.$component.find('.invoice-details-section');
|
||||
this.$invoice_fields_section = this.$component.find('.fields-section');
|
||||
}
|
||||
|
||||
make_invoice_fields_control() {
|
||||
frappe.db.get_doc("POS Settings", undefined).then((doc) => {
|
||||
const fields = doc.invoice_fields;
|
||||
if (!fields.length) return;
|
||||
|
||||
this.$invoice_details_section.html(
|
||||
`<div class="text-grey pb-6 mt-2 pointer no-select">
|
||||
ADDITIONAL INFORMATION<span class="octicon octicon-chevron-down collapse-indicator"></span>
|
||||
</div>
|
||||
<div class="invoice-fields grid grid-cols-2 gap-4 mb-6 d-none"></div>`
|
||||
);
|
||||
this.$invoice_fields = this.$invoice_details_section.find('.invoice-fields');
|
||||
|
||||
this.$invoice_fields = this.$invoice_fields_section.find('.invoice-fields');
|
||||
this.$invoice_fields.html('');
|
||||
const frm = this.events.get_frm();
|
||||
|
||||
fields.forEach(df => {
|
||||
@ -127,9 +110,9 @@ erpnext.PointOfSale.Payment = class {
|
||||
this.selected_mode.set_value(this.numpad_value);
|
||||
|
||||
function highlight_numpad_btn($btn) {
|
||||
$btn.addClass('shadow-inner bg-selected');
|
||||
$btn.addClass('shadow-base-inner bg-selected');
|
||||
setTimeout(() => {
|
||||
$btn.removeClass('shadow-inner bg-selected');
|
||||
$btn.removeClass('shadow-base-inner bg-selected');
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
@ -142,13 +125,16 @@ erpnext.PointOfSale.Payment = class {
|
||||
// if clicked element doesn't have .mode-of-payment class then return
|
||||
if (!$(e.target).is(mode_clicked)) return;
|
||||
|
||||
const scrollLeft = mode_clicked.offset().left - me.$payment_modes.offset().left + me.$payment_modes.scrollLeft();
|
||||
me.$payment_modes.animate({ scrollLeft });
|
||||
|
||||
const mode = mode_clicked.attr('data-mode');
|
||||
|
||||
// hide all control fields and shortcuts
|
||||
$(`.mode-of-payment-control`).addClass('d-none');
|
||||
$(`.cash-shortcuts`).addClass('d-none');
|
||||
me.$payment_modes.find(`.pay-amount`).removeClass('d-none');
|
||||
me.$payment_modes.find(`.loyalty-amount-name`).addClass('d-none');
|
||||
$(`.mode-of-payment-control`).css('display', 'none');
|
||||
$(`.cash-shortcuts`).css('display', 'none');
|
||||
me.$payment_modes.find(`.pay-amount`).css('display', 'inline');
|
||||
me.$payment_modes.find(`.loyalty-amount-name`).css('display', 'none');
|
||||
|
||||
// remove highlight from all mode-of-payments
|
||||
$('.mode-of-payment').removeClass('border-primary');
|
||||
@ -157,21 +143,20 @@ erpnext.PointOfSale.Payment = class {
|
||||
// clicked one is selected then unselect it
|
||||
mode_clicked.removeClass('border-primary');
|
||||
me.selected_mode = '';
|
||||
me.toggle_numpad(false);
|
||||
} else {
|
||||
// clicked one is not selected then select it
|
||||
mode_clicked.addClass('border-primary');
|
||||
mode_clicked.find('.mode-of-payment-control').removeClass('d-none');
|
||||
mode_clicked.find('.cash-shortcuts').removeClass('d-none');
|
||||
me.$payment_modes.find(`.${mode}-amount`).addClass('d-none');
|
||||
me.$payment_modes.find(`.${mode}-name`).removeClass('d-none');
|
||||
me.toggle_numpad(true);
|
||||
mode_clicked.find('.mode-of-payment-control').css('display', 'flex');
|
||||
mode_clicked.find('.cash-shortcuts').css('display', 'grid');
|
||||
me.$payment_modes.find(`.${mode}-amount`).css('display', 'none');
|
||||
me.$payment_modes.find(`.${mode}-name`).css('display', 'inline');
|
||||
|
||||
me.selected_mode = me[`${mode}_control`];
|
||||
const doc = me.events.get_frm().doc;
|
||||
me.selected_mode?.$input?.get(0).focus();
|
||||
const current_value = me.selected_mode?.get_value()
|
||||
!current_value && doc.grand_total > doc.paid_amount ? me.selected_mode?.set_value(doc.grand_total - doc.paid_amount) : '';
|
||||
me.selected_mode = me[`${mode}_control`];
|
||||
me.selected_mode && me.selected_mode.$input.get(0).focus();
|
||||
const current_value = me.selected_mode ? me.selected_mode.get_value() : undefined;
|
||||
!current_value && doc.grand_total > doc.paid_amount && me.selected_mode ?
|
||||
me.selected_mode.set_value(doc.grand_total - doc.paid_amount) : '';
|
||||
}
|
||||
})
|
||||
|
||||
@ -198,7 +183,7 @@ erpnext.PointOfSale.Payment = class {
|
||||
me.selected_mode.set_value(value);
|
||||
})
|
||||
|
||||
this.$component.on('click', '.submit-order', () => {
|
||||
this.$component.on('click', '.submit-order-btn', () => {
|
||||
const doc = this.events.get_frm().doc;
|
||||
const paid_amount = doc.paid_amount;
|
||||
const items = doc.items;
|
||||
@ -217,9 +202,9 @@ erpnext.PointOfSale.Payment = class {
|
||||
this.update_totals_section(frm.doc);
|
||||
|
||||
// need to re calculate cash shortcuts after discount is applied
|
||||
const is_cash_shortcuts_invisible = this.$payment_modes.find('.cash-shortcuts').hasClass('d-none');
|
||||
const is_cash_shortcuts_invisible = !this.$payment_modes.find('.cash-shortcuts').is(':visible');
|
||||
this.attach_cash_shortcuts(frm.doc);
|
||||
!is_cash_shortcuts_invisible && this.$payment_modes.find('.cash-shortcuts').removeClass('d-none');
|
||||
!is_cash_shortcuts_invisible && this.$payment_modes.find('.cash-shortcuts').css('display', 'grid');
|
||||
})
|
||||
|
||||
frappe.ui.form.on('POS Invoice', 'loyalty_amount', (frm) => {
|
||||
@ -235,29 +220,16 @@ erpnext.PointOfSale.Payment = class {
|
||||
this[`${mode}_control`].set_value(default_mop.amount);
|
||||
}
|
||||
});
|
||||
|
||||
this.$component.on('click', '.invoice-details-section', function(e) {
|
||||
if ($(e.target).closest('.invoice-fields').length) return;
|
||||
|
||||
me.$payment_modes.addClass('d-none');
|
||||
me.$invoice_fields.toggleClass("d-none");
|
||||
me.toggle_numpad(false);
|
||||
});
|
||||
this.$component.on('click', '.payment-section', () => {
|
||||
this.$invoice_fields.addClass("d-none");
|
||||
this.$payment_modes.toggleClass('d-none');
|
||||
this.toggle_numpad(true);
|
||||
})
|
||||
}
|
||||
|
||||
attach_shortcuts() {
|
||||
const ctrl_label = frappe.utils.is_mac() ? '⌘' : 'Ctrl';
|
||||
this.$component.find('.submit-order').attr("title", `${ctrl_label}+Enter`);
|
||||
this.$component.find('.submit-order-btn').attr("title", `${ctrl_label}+Enter`);
|
||||
frappe.ui.keys.on("ctrl+enter", () => {
|
||||
const payment_is_visible = this.$component.is(":visible");
|
||||
const active_mode = this.$payment_modes.find(".border-primary");
|
||||
if (payment_is_visible && active_mode.length) {
|
||||
this.$component.find('.submit-order').click();
|
||||
this.$component.find('.submit-order-btn').click();
|
||||
}
|
||||
});
|
||||
|
||||
@ -287,15 +259,13 @@ erpnext.PointOfSale.Payment = class {
|
||||
}
|
||||
|
||||
toggle_numpad(show) {
|
||||
if (show) {
|
||||
this.$numpad.removeClass('d-none');
|
||||
this.$remarks.addClass('d-none');
|
||||
this.$totals_remarks.addClass('w-60 justify-center').removeClass('justify-end w-full');
|
||||
} else {
|
||||
this.$numpad.addClass('d-none');
|
||||
this.$remarks.removeClass('d-none');
|
||||
this.$totals_remarks.removeClass('w-60 justify-center').addClass('justify-end w-full');
|
||||
}
|
||||
// if (show) {
|
||||
// this.$numpad.css('display', 'flex');
|
||||
// this.$totals_section.addClass('w-60 justify-center').removeClass('justify-end w-full');
|
||||
// } else {
|
||||
// this.$numpad.css('display', 'none');
|
||||
// this.$totals_section.removeClass('w-60 justify-center').addClass('justify-end w-full');
|
||||
// }
|
||||
}
|
||||
|
||||
render_payment_section() {
|
||||
@ -327,7 +297,7 @@ erpnext.PointOfSale.Payment = class {
|
||||
fieldtype: 'Data',
|
||||
onchange: function() {}
|
||||
},
|
||||
parent: this.$totals_remarks.find(`.remarks`),
|
||||
parent: this.$totals_section.find(`.remarks`),
|
||||
render_input: true,
|
||||
});
|
||||
this[`remark_control`].set_value('');
|
||||
@ -348,12 +318,11 @@ erpnext.PointOfSale.Payment = class {
|
||||
const amount = p.amount > 0 ? format_currency(p.amount, currency) : '';
|
||||
|
||||
return (
|
||||
`<div class="w-half ${margin} bg-white">
|
||||
<div class="mode-of-payment rounded border border-grey text-grey text-md
|
||||
mb-4 p-8 pt-4 pb-4 no-select pointer" data-mode="${mode}" data-payment-type="${payment_type}">
|
||||
`<div class="payment-mode-wrapper">
|
||||
<div class="mode-of-payment" data-mode="${mode}" data-payment-type="${payment_type}">
|
||||
${p.mode_of_payment}
|
||||
<div class="${mode}-amount pay-amount inline float-right text-bold">${amount}</div>
|
||||
<div class="${mode} mode-of-payment-control mt-4 flex flex-1 items-center d-none"></div>
|
||||
<div class="${mode}-amount pay-amount">${amount}</div>
|
||||
<div class="${mode} mode-of-payment-control"></div>
|
||||
</div>
|
||||
</div>`
|
||||
)
|
||||
@ -405,12 +374,10 @@ erpnext.PointOfSale.Payment = class {
|
||||
|
||||
this.$payment_modes.find('.cash-shortcuts').remove();
|
||||
this.$payment_modes.find('[data-payment-type="Cash"]').find('.mode-of-payment-control').after(
|
||||
`<div class="cash-shortcuts grid grid-cols-3 gap-2 flex-1 text-center text-md-0 mb-2 d-none">
|
||||
`<div class="cash-shortcuts">
|
||||
${
|
||||
shortcuts.map(s => {
|
||||
return `<div class="shortcut rounded bg-light-grey text-dark-grey pt-2 pb-2 no-select pointer" data-value="${s}">
|
||||
${format_currency(s, currency, 0)}
|
||||
</div>`
|
||||
return `<div class="shortcut" data-value="${s}">${format_currency(s, currency, 0)}</div>`
|
||||
}).join('')
|
||||
}
|
||||
</div>`
|
||||
@ -457,13 +424,12 @@ erpnext.PointOfSale.Payment = class {
|
||||
const margin = this.$payment_modes.children().length % 2 === 0 ? 'pr-2' : 'pl-2';
|
||||
const amount = doc.loyalty_amount > 0 ? format_currency(doc.loyalty_amount, doc.currency) : '';
|
||||
this.$payment_modes.append(
|
||||
`<div class="w-half ${margin} bg-white">
|
||||
<div class="mode-of-payment rounded border border-grey text-grey text-md
|
||||
mb-4 p-8 pt-4 pb-4 no-select pointer" data-mode="loyalty-amount" data-payment-type="loyalty-amount">
|
||||
`<div class="payment-mode-wrapper">
|
||||
<div class="mode-of-payment" data-mode="loyalty-amount" data-payment-type="loyalty-amount">
|
||||
Redeem Loyalty Points
|
||||
<div class="loyalty-amount-amount pay-amount inline float-right text-bold">${amount}</div>
|
||||
<div class="loyalty-amount-name inline float-right text-bold text-md-0 d-none">${loyalty_program}</div>
|
||||
<div class="loyalty-amount mode-of-payment-control mt-4 flex flex-1 items-center d-none"></div>
|
||||
<div class="loyalty-amount-amount pay-amount">${amount}</div>
|
||||
<div class="loyalty-amount-name">${loyalty_program}</div>
|
||||
<div class="loyalty-amount mode-of-payment-control"></div>
|
||||
</div>
|
||||
</div>`
|
||||
)
|
||||
@ -520,18 +486,24 @@ erpnext.PointOfSale.Payment = class {
|
||||
const label = change ? __('Change') : __('To Be Paid');
|
||||
|
||||
this.$totals.html(
|
||||
`<div>
|
||||
<div class="pr-8 border-r-grey">Paid Amount</div>
|
||||
<div class="pr-8 border-r-grey text-bold text-2xl">${format_currency(paid_amount, currency)}</div>
|
||||
`<div class="col">
|
||||
<div class="total-label">Grand Total</div>
|
||||
<div class="value">${format_currency(doc.grand_total, currency)}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="pl-8">${label}</div>
|
||||
<div class="pl-8 text-green-400 text-bold text-2xl">${format_currency(change || remaining, currency)}</div>
|
||||
<div class="seperator-y"></div>
|
||||
<div class="col">
|
||||
<div class="total-label">Paid Amount</div>
|
||||
<div class="value">${format_currency(paid_amount, currency)}</div>
|
||||
</div>
|
||||
<div class="seperator-y"></div>
|
||||
<div class="col">
|
||||
<div class="total-label">${label}</div>
|
||||
<div class="value">${format_currency(change || remaining, currency)}</div>
|
||||
</div>`
|
||||
)
|
||||
}
|
||||
|
||||
toggle_component(show) {
|
||||
show ? this.$component.removeClass('d-none') : this.$component.addClass('d-none');
|
||||
show ? this.$component.css('display', 'flex') : this.$component.css('display', 'none');
|
||||
}
|
||||
}
|
@ -21,7 +21,6 @@
|
||||
<h3>{%= __("Next Steps") %}</h3>
|
||||
<ul class="list-unstyled">
|
||||
<li><a class="text-muted" href="#">{%= __("Go to the Desktop and start using ERPNext") %}</a></li>
|
||||
<li><a class="text-muted" href="#modules/Learn">{%= __("View a list of all the help videos") %}</a></li>
|
||||
<li><a class="text-muted" href="https://erpnext.com/docs/user" target="_blank">{%= __("Read the ERPNext Manual") %}</a></li>
|
||||
<li><a class="text-muted" href="https://discuss.erpnext.com" target="_blank">{%= __("Community Forum") %}</a></li>
|
||||
</ul>
|
||||
|
@ -198,7 +198,7 @@ erpnext.stock.move_item = function(item, source, target, actual_qty, rate, callb
|
||||
freeze: true,
|
||||
callback: function(r) {
|
||||
frappe.show_alert(__('Stock Entry {0} created',
|
||||
['<a href="#Form/Stock Entry/'+r.message.name+'">' + r.message.name+ '</a>']));
|
||||
['<a href="/desk/Form/Stock Entry/'+r.message.name+'">' + r.message.name+ '</a>']));
|
||||
dialog.hide();
|
||||
callback(r);
|
||||
},
|
||||
|
@ -102,7 +102,7 @@ frappe.ui.form.on('Batch', {
|
||||
},
|
||||
callback: (r) => {
|
||||
frappe.show_alert(__('Stock Entry {0} created',
|
||||
['<a href="#Form/Stock Entry/'+r.message.name+'">' + r.message.name+ '</a>']));
|
||||
['<a href="/desk/Form/Stock Entry/'+r.message.name+'">' + r.message.name+ '</a>']));
|
||||
frm.refresh();
|
||||
},
|
||||
});
|
||||
|
@ -85,7 +85,7 @@ frappe.ui.form.on("Item", {
|
||||
}
|
||||
if (frm.doc.variant_of) {
|
||||
frm.set_intro(__('This Item is a Variant of {0} (Template).',
|
||||
[`<a href="#Form/Item/${frm.doc.variant_of}">${frm.doc.variant_of}</a>`]), true);
|
||||
[`<a href="/desk/Form/Item/${frm.doc.variant_of}">${frm.doc.variant_of}</a>`]), true);
|
||||
}
|
||||
|
||||
if (frappe.defaults.get_default("item_naming_by")!="Naming Series" || frm.doc.variant_of) {
|
||||
@ -649,7 +649,7 @@ $.extend(erpnext.item, {
|
||||
if (r.message) {
|
||||
var variant = r.message;
|
||||
frappe.msgprint_dialog = frappe.msgprint(__("Item Variant {0} already exists with same attributes",
|
||||
[repl('<a href="#Form/Item/%(item_encoded)s" class="strong variant-click">%(item)s</a>', {
|
||||
[repl('<a href="/desk/Form/Item/%(item_encoded)s" class="strong variant-click">%(item)s</a>', {
|
||||
item_encoded: encodeURIComponent(variant),
|
||||
item: variant
|
||||
})]
|
||||
|
@ -860,7 +860,7 @@ class Item(WebsiteGenerator):
|
||||
|
||||
rows = ''
|
||||
for docname, attr_list in not_included.items():
|
||||
link = "<a href='#Form/Item/{0}'>{0}</a>".format(frappe.bold(_(docname)))
|
||||
link = "<a href='/app/Form/Item/{0}'>{0}</a>".format(frappe.bold(_(docname)))
|
||||
rows += table_row(link, body(attr_list))
|
||||
|
||||
error_description = _('The following deleted attributes exist in Variants but not in the Template. You can either delete the Variants or keep the attribute(s) in template.')
|
||||
|
@ -14,6 +14,6 @@ frappe.ui.form.on("Item Price", {
|
||||
frm.add_fetch("item_code", "stock_uom", "uom");
|
||||
|
||||
frm.set_df_property("bulk_import_help", "options",
|
||||
'<a href="#data-import-tool/Item Price">' + __("Import in Bulk") + '</a>');
|
||||
'<a href="/desk/data-import-tool/Item Price">' + __("Import in Bulk") + '</a>');
|
||||
}
|
||||
});
|
||||
|
@ -184,7 +184,7 @@ frappe.ui.form.on("Issue", {
|
||||
let url = window.location.href
|
||||
let arr = url.split("/");
|
||||
let result = arr[0] + "//" + arr[2]
|
||||
frappe.msgprint(`New issue created: <a href="${result}/desk#Form/Issue/${r.message}">${r.message}</a>`)
|
||||
frappe.msgprint(`New issue created: <a href="${result}//desk/Form/Issue/${r.message}">${r.message}</a>`)
|
||||
frm.reload_doc();
|
||||
dialog.hide();
|
||||
});
|
||||
|
@ -207,7 +207,7 @@ class Issue(Document):
|
||||
"comment_type": "Info",
|
||||
"reference_doctype": "Issue",
|
||||
"reference_name": replicated_issue.name,
|
||||
"content": " - Split the Issue from <a href='#Form/Issue/{0}'>{1}</a>".format(self.name, frappe.bold(self.name)),
|
||||
"content": " - Split the Issue from <a href='/app/Form/Issue/{0}'>{1}</a>".format(self.name, frappe.bold(self.name)),
|
||||
}).insert(ignore_permissions=True)
|
||||
|
||||
return replicated_issue.name
|
||||
|
@ -26,12 +26,12 @@ class FindItemBot(BotParser):
|
||||
for warehouse in warehouses:
|
||||
qty = frappe.db.get_value("Bin", {'item_code': item[0], 'warehouse': warehouse.name}, 'actual_qty')
|
||||
if qty:
|
||||
out.append(_('{0} units of [{1}](#Form/Item/{1}) found in [{2}](#Form/Warehouse/{2})').format(qty,
|
||||
out.append(_('{0} units of [{1}](/app/Form/Item/{1}) found in [{2}](/app/Form/Warehouse/{2})').format(qty,
|
||||
item[0], warehouse.name))
|
||||
found = True
|
||||
|
||||
if not found:
|
||||
out.append(_('[{0}](#Form/Item/{0}) is out of stock').format(item[0]))
|
||||
out.append(_('[{0}](/app/Form/Item/{0}) is out of stock').format(item[0]))
|
||||
|
||||
return "\n\n".join(out)
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
||||
"snyk": "^1.290.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"onscan.js": "^1.5.2"
|
||||
},
|
||||
"scripts": {
|
||||
"snyk-protect": "snyk protect",
|
||||
|
@ -1217,6 +1217,11 @@ onetime@^2.0.0:
|
||||
dependencies:
|
||||
mimic-fn "^1.0.0"
|
||||
|
||||
onscan.js@^1.5.2:
|
||||
version "1.5.2"
|
||||
resolved "https://registry.yarnpkg.com/onscan.js/-/onscan.js-1.5.2.tgz#14ed636e5f4c3f0a78bacbf9a505dad3140ee341"
|
||||
integrity sha512-9oGYy2gXYRjvXO9GYqqVca0VuCTAmWhbmX3egBSBP13rXiMNb+dKPJzKFEeECGqPBpf0m40Zoo+GUQ7eCackdw==
|
||||
|
||||
opn@^5.5.0:
|
||||
version "5.5.0"
|
||||
resolved "https://registry.yarnpkg.com/opn/-/opn-5.5.0.tgz#fc7164fab56d235904c51c3b27da6758ca3b9bfc"
|
||||
|
Loading…
x
Reference in New Issue
Block a user