From 542bc017184db1187be0dcab948dbee0ba753a91 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Wed, 18 Nov 2020 15:00:34 +0530 Subject: [PATCH 01/18] fix(minor): update routes --- .../opening_invoice_creation_tool.py | 6 +++--- erpnext/assets/doctype/asset/asset.py | 8 ++++---- .../supplier_quotation/supplier_quotation.py | 2 +- erpnext/config/education.py | 2 +- erpnext/config/projects.py | 6 +++--- .../assessment_result_tool.js | 2 +- .../course_scheduling_tool.js | 2 +- .../program_enrollment/program_enrollment.py | 2 +- .../tally_migration/tally_migration.js | 4 ++-- .../clinical_procedure/clinical_procedure.js | 2 +- .../inpatient_record/inpatient_record.py | 2 +- .../patient_appointment.py | 4 ++-- erpnext/healthcare/utils.py | 10 +++++----- .../employee_transfer/employee_transfer.py | 2 +- .../leave_allocation/leave_allocation.py | 2 +- .../leave_application/leave_application.py | 2 +- .../hr/doctype/shift_request/shift_request.py | 2 +- erpnext/hr/utils.py | 2 +- erpnext/manufacturing/doctype/bom/bom.js | 2 +- .../doctype/bom/bom_item_preview.html | 4 ++-- .../production_plan/production_plan.py | 4 ++-- .../bom_stock_report/bom_stock_report.js | 4 ++-- .../doctype/payroll_period/payroll_period.py | 2 +- erpnext/projects/doctype/task/task_list.js | 2 +- erpnext/public/js/call_popup/call_popup.js | 4 ++-- erpnext/public/js/communication.js | 2 +- .../js/education/assessment_result_tool.html | 6 +++--- .../doctype/sales_order/sales_order.js | 4 ++-- .../page/point_of_sale/pos_controller.js | 20 +++++++++---------- .../welcome_to_erpnext.html | 1 - erpnext/stock/dashboard/item_dashboard.js | 2 +- erpnext/stock/doctype/batch/batch.js | 2 +- erpnext/stock/doctype/item/item.js | 4 ++-- erpnext/stock/doctype/item/item.py | 2 +- .../stock/doctype/item_price/item_price.js | 2 +- erpnext/support/doctype/issue/issue.js | 2 +- erpnext/support/doctype/issue/issue.py | 2 +- erpnext/utilities/bot.py | 4 ++-- 38 files changed, 68 insertions(+), 69 deletions(-) diff --git a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py index d51856a8a4..0e1d140b2e 100644 --- a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py +++ b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py @@ -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, "Error Log"), indicator="red", title=_("Error Occured")) + .format(errors, "Error Log"), indicator="red", title=_("Error Occured")) return names def publish(index, total, doctype): diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 30abc66a02..1793dad494 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -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("{0}").format(movement_entry.name)) + frappe.msgprint(_("Asset Movement record {0} created").format("{0}").format(movement_entry.name)) @frappe.whitelist() def get_item_details(item_code, asset_category): diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py index ae5611f3c4..6a4c02c075 100644 --- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py +++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py @@ -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, - " Request for Quotation {0} ".format(doc.name))) + " Request for Quotation {0} ".format(doc.name))) quote_status = _('Received') for item in doc.items: diff --git a/erpnext/config/education.py b/erpnext/config/education.py index 4efaaa65cd..1c8ab10f53 100644 --- a/erpnext/config/education.py +++ b/erpnext/config/education.py @@ -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", diff --git a/erpnext/config/projects.py b/erpnext/config/projects.py index 47700d10b2..ab4db96477 100644 --- a/erpnext/config/projects.py +++ b/erpnext/config/projects.py @@ -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(): }, ] }, - + ] diff --git a/erpnext/education/doctype/assessment_result_tool/assessment_result_tool.js b/erpnext/education/doctype/assessment_result_tool/assessment_result_tool.js index 3cd451209f..e213309c5e 100644 --- a/erpnext/education/doctype/assessment_result_tool/assessment_result_tool.js +++ b/erpnext/education/doctype/assessment_result_tool/assessment_result_tool.js @@ -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); } }); } diff --git a/erpnext/education/doctype/course_scheduling_tool/course_scheduling_tool.js b/erpnext/education/doctype/course_scheduling_tool/course_scheduling_tool.js index 20503f919c..f408dae7bd 100644 --- a/erpnext/education/doctype/course_scheduling_tool/course_scheduling_tool.js +++ b/erpnext/education/doctype/course_scheduling_tool/course_scheduling_tool.js @@ -25,7 +25,7 @@ frappe.ui.form.on('Course Scheduling Tool', { ${__("Course")}${__("Date")} ${course_schedules.map( - c => `${c.name} + c => `${c.name} ${c.schedule_date}` ).join('')} diff --git a/erpnext/education/doctype/program_enrollment/program_enrollment.py b/erpnext/education/doctype/program_enrollment/program_enrollment.py index 6fbcd8aa97..3045db7f32 100644 --- a/erpnext/education/doctype/program_enrollment/program_enrollment.py +++ b/erpnext/education/doctype/program_enrollment/program_enrollment.py @@ -87,7 +87,7 @@ class ProgramEnrollment(Document): fees.submit() fee_list.append(fees.name) if fee_list: - fee_list = ["""%s""" % \ + fee_list = ["""%s""" % \ (fee, fee) for fee in fee_list] msgprint(_("Fee Records Created - {0}").format(comma_and(fee_list))) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js index fd16d1e84a..e8641114be 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js @@ -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("%(tally_document)s", { + repl("%(tally_document)s", { tally_document: frm.docname }), - "Error Log" + "Error Log" ] ), title: __("Tally Migration Error"), diff --git a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js index eb7d4bdeba..19bddbb191 100644 --- a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js +++ b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js @@ -86,7 +86,7 @@ frappe.ui.form.on('Clinical Procedure', { if (r.message) { frappe.show_alert({ message: __('Stock Entry {0} created', - ['' + r.message + '']), + ['' + r.message + '']), indicator: 'green' }); } diff --git a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py index bc76970601..c7ab447860 100644 --- a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py +++ b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py @@ -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) \ - + """ {0}""".format(ip_record[0].name)) + + """ {0}""".format(ip_record[0].name)) frappe.throw(msg) def admit(self, service_unit, check_in, expected_discharge=None): diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py index e685b20a8c..90d9023278 100755 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py @@ -63,7 +63,7 @@ class PatientAppointment(Document): if overlaps: overlapping_details = _('Appointment overlaps with ') - overlapping_details += "{0}
".format(overlaps[0][0]) + overlapping_details += "{0}
".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 += " {0}".format(self.patient) + msg += " {0}".format(self.patient) frappe.throw(msg, title=_('Customer Not Found')) def update_prescription_details(self): diff --git a/erpnext/healthcare/utils.py b/erpnext/healthcare/utils.py index 96282f50a9..248692332c 100644 --- a/erpnext/healthcare/utils.py +++ b/erpnext/healthcare/utils.py @@ -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 += " {0}".format(patient.name) + msg += " {0}".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 += '''Healthcare Settings''' + msg += '''Healthcare Settings''' 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) \ - + '''Healthcare Settings''') + + '''Healthcare Settings''') 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) \ - + ''' {0}'''.format(practitioner)) + + ''' {0}'''.format(practitioner)) frappe.throw(msg, title=_('Missing Configuration')) @@ -654,6 +654,6 @@ def render_doc_as_html(doctype, docname, exclude_fields = []): >
" \ + section_html + html +'
' if doc_html: - doc_html = "
" %(doctype, docname) + doc_html + '
' + doc_html = "
" %(doctype, docname) + doc_html + '
' return {'html': doc_html} diff --git a/erpnext/hr/doctype/employee_transfer/employee_transfer.py b/erpnext/hr/doctype/employee_transfer/employee_transfer.py index c730e022a5..37d616f14d 100644 --- a/erpnext/hr/doctype/employee_transfer/employee_transfer.py +++ b/erpnext/hr/doctype/employee_transfer/employee_transfer.py @@ -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 {0}\ + frappe.throw(_("Please delete the Employee {0}\ to cancel this document").format(self.new_employee_id)) #mark the employee as active employee.status = "Active" diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.py b/erpnext/hr/doctype/leave_allocation/leave_allocation.py index 03fe3fa035..32c5456239 100755 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.py +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.py @@ -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') + ': {0}' + frappe.throw(_('Reference') + ': {0}' .format(leave_allocation[0][0]), OverlapError) def validate_back_dated_allocation(self): diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index 3f25f58383..35c3ea77ea 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -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'])) \ - + """ {0}""".format(d["name"]) + + """ {0}""".format(d["name"]) frappe.throw(msg, OverlapError) def get_total_leaves_on_half_day(self): diff --git a/erpnext/hr/doctype/shift_request/shift_request.py b/erpnext/hr/doctype/shift_request/shift_request.py index 1c2801bf08..473193d5ac 100644 --- a/erpnext/hr/doctype/shift_request/shift_request.py +++ b/erpnext/hr/doctype/shift_request/shift_request.py @@ -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'])) \ - + """ {0}""".format(d["name"]) + + """ {0}""".format(d["name"]) frappe.throw(msg, OverlapError) \ No newline at end of file diff --git a/erpnext/hr/utils.py b/erpnext/hr/utils.py index 8d95924681..3b8d73b0f0 100644 --- a/erpnext/hr/utils.py +++ b/erpnext/hr/utils.py @@ -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)) \ - + """ {1}""".format(doc.doctype, overlap_doc) \ + + """ {1}""".format(doc.doctype, overlap_doc) \ + _(") for {0}").format(exists_for) frappe.throw(msg) diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js index 1c4b7a1e1c..55f7a1b8a9 100644 --- a/erpnext/manufacturing/doctype/bom/bom.js +++ b/erpnext/manufacturing/doctype/bom/bom.js @@ -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}', [ `variants`, - `${frm.doc.item}`, + `${frm.doc.item}`, ]), true); frm.$wrapper.find(".variants-intro").on("click", () => { diff --git a/erpnext/manufacturing/doctype/bom/bom_item_preview.html b/erpnext/manufacturing/doctype/bom/bom_item_preview.html index c782f7bf0e..6cd5f8cb3c 100644 --- a/erpnext/manufacturing/doctype/bom/bom_item_preview.html +++ b/erpnext/manufacturing/doctype/bom/bom_item_preview.html @@ -12,11 +12,11 @@

{% if data.value %} - + {{ __("Open BOM {0}", [data.value.bold()]) }} {% endif %} {% if data.item_code %} - + {{ __("Open Item {0}", [data.item_code.bold()]) }} {% endif %}

diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index 3833e86d27..8f9dd05217 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -319,7 +319,7 @@ class ProductionPlan(Document): frappe.flags.mute_messages = False if wo_list: - wo_list = ["""%s""" % \ + wo_list = ["""%s""" % \ (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 = ["""{1}""".format(m.name, m.name) \ + material_request_list = ["""{1}""".format(m.name, m.name) \ for m in material_request_list] msgprint(_("{0} created").format(comma_and(material_request_list))) else : diff --git a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.js b/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.js index 2ac6fa073b..2f18d1704b 100644 --- a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.js +++ b/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.js @@ -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 = `${data['Item']}` + value = `${data['Item']}` } else { - value = `${data['Item']}` + value = `${data['Item']}` } } return value diff --git a/erpnext/payroll/doctype/payroll_period/payroll_period.py b/erpnext/payroll/doctype/payroll_period/payroll_period.py index d7893d0657..1c8cc53deb 100644 --- a/erpnext/payroll/doctype/payroll_period/payroll_period.py +++ b/erpnext/payroll/doctype/payroll_period/payroll_period.py @@ -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)) \ - + """ {1}""".format(self.doctype, overlap_doc[0].name) \ + + """ {1}""".format(self.doctype, overlap_doc[0].name) \ + _(") for {0}").format(self.company) frappe.throw(msg) diff --git a/erpnext/projects/doctype/task/task_list.js b/erpnext/projects/doctype/task/task_list.js index 941fe97546..7c620317de 100644 --- a/erpnext/projects/doctype/task/task_list.js +++ b/erpnext/projects/doctype/task/task_list.js @@ -26,7 +26,7 @@ frappe.listview_settings['Task'] = { }, gantt_custom_popup_html: function(ganttobj, task) { var html = `
${ganttobj.name}
`; + href="/desk/Form/Task/${ganttobj.id}""> ${ganttobj.name} `; if(task.project) html += `

Project: ${task.project}

`; html += `

Progress: ${ganttobj.progress}

`; diff --git a/erpnext/public/js/call_popup/call_popup.js b/erpnext/public/js/call_popup/call_popup.js index 5e4d4a585f..378d6d131d 100644 --- a/erpnext/public/js/call_popup/call_popup.js +++ b/erpnext/public/js/call_popup/call_popup.js @@ -85,7 +85,7 @@ class CallPopup {
+ href="/desk/Form/Call Log/${this.call_log.name}"> ${__('View call log')} `, @@ -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(` - + ${__('View all issues from {0}', [issue.customer])} `); diff --git a/erpnext/public/js/communication.js b/erpnext/public/js/communication.js index 26e5ab8b32..38778e2ab0 100644 --- a/erpnext/public/js/communication.js +++ b/erpnext/public/js/communication.js @@ -84,7 +84,7 @@ frappe.ui.form.on("Communication", { frm.reload_doc(); frappe.show_alert({ message: __("Opportunity {0} created", - ['' + r.message + '']), + ['' + r.message + '']), indicator: 'green' }); } diff --git a/erpnext/public/js/education/assessment_result_tool.html b/erpnext/public/js/education/assessment_result_tool.html index 9fc17f7be1..b591010ec8 100644 --- a/erpnext/public/js/education/assessment_result_tool.html +++ b/erpnext/public/js/education/assessment_result_tool.html @@ -19,7 +19,7 @@ {% for s in students %} - @@ -29,7 +29,7 @@ {% if(s.assessment_details) { %} - {{s.assessment_details[c.assessment_criteria][1]}} + {{s.assessment_details[c.assessment_criteria][1]}} {% } %} - + diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index 73cc0b836e..96d265c989 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -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('%(name)s', {name:d}) + return repl('%(name)s', {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.', - ['' + r.message.name+ ''])); + ['' + r.message.name+ ''])); } d.hide(); me.frm.reload_doc(); diff --git a/erpnext/selling/page/point_of_sale/pos_controller.js b/erpnext/selling/page/point_of_sale/pos_controller.js index 970d840665..288084d245 100644 --- a/erpnext/selling/page/point_of_sale/pos_controller.js +++ b/erpnext/selling/page/point_of_sale/pos_controller.js @@ -45,7 +45,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) { @@ -134,7 +134,7 @@ erpnext.PointOfSale.Controller = class { set_opening_entry_status() { this.page.set_title_sub( ` - + Opened at ${moment(this.pos_opening_time).format("Do MMMM, h:mma")} `); @@ -199,7 +199,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 +208,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 +256,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)) ); @@ -429,7 +429,7 @@ erpnext.PointOfSale.Controller = class { }) } - + toggle_recent_order_list(show) { this.toggle_components(!show); @@ -539,7 +539,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 +577,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 +588,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 +617,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; } diff --git a/erpnext/setup/page/welcome_to_erpnext/welcome_to_erpnext.html b/erpnext/setup/page/welcome_to_erpnext/welcome_to_erpnext.html index 5808ce73ee..7166ba3786 100644 --- a/erpnext/setup/page/welcome_to_erpnext/welcome_to_erpnext.html +++ b/erpnext/setup/page/welcome_to_erpnext/welcome_to_erpnext.html @@ -21,7 +21,6 @@

{%= __("Next Steps") %}

diff --git a/erpnext/stock/dashboard/item_dashboard.js b/erpnext/stock/dashboard/item_dashboard.js index 9bd03d45cb..faa9b5df2f 100644 --- a/erpnext/stock/dashboard/item_dashboard.js +++ b/erpnext/stock/dashboard/item_dashboard.js @@ -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', - ['' + r.message.name+ ''])); + ['' + r.message.name+ ''])); dialog.hide(); callback(r); }, diff --git a/erpnext/stock/doctype/batch/batch.js b/erpnext/stock/doctype/batch/batch.js index 71a3e7abca..7b2edff7e0 100644 --- a/erpnext/stock/doctype/batch/batch.js +++ b/erpnext/stock/doctype/batch/batch.js @@ -102,7 +102,7 @@ frappe.ui.form.on('Batch', { }, callback: (r) => { frappe.show_alert(__('Stock Entry {0} created', - ['' + r.message.name+ ''])); + ['' + r.message.name+ ''])); frm.refresh(); }, }); diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js index faf4accc73..43e18d16fc 100644 --- a/erpnext/stock/doctype/item/item.js +++ b/erpnext/stock/doctype/item/item.js @@ -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).', - [`${frm.doc.variant_of}`]), true); + [`${frm.doc.variant_of}`]), 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('%(item)s', { + [repl('%(item)s', { item_encoded: encodeURIComponent(variant), item: variant })] diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index 3b62c38b86..1993d56a6c 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -860,7 +860,7 @@ class Item(WebsiteGenerator): rows = '' for docname, attr_list in not_included.items(): - link = "{0}".format(frappe.bold(_(docname))) + link = "{0}".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.') diff --git a/erpnext/stock/doctype/item_price/item_price.js b/erpnext/stock/doctype/item_price/item_price.js index 2729f4b15e..773fddcf96 100644 --- a/erpnext/stock/doctype/item_price/item_price.js +++ b/erpnext/stock/doctype/item_price/item_price.js @@ -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", - '' + __("Import in Bulk") + ''); + '' + __("Import in Bulk") + ''); } }); diff --git a/erpnext/support/doctype/issue/issue.js b/erpnext/support/doctype/issue/issue.js index 940b940aba..e58ac22d0b 100644 --- a/erpnext/support/doctype/issue/issue.js +++ b/erpnext/support/doctype/issue/issue.js @@ -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: ${r.message}`) + frappe.msgprint(`New issue created: ${r.message}`) frm.reload_doc(); dialog.hide(); }); diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index 62b39cced5..e4e7b2543b 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -207,7 +207,7 @@ class Issue(Document): "comment_type": "Info", "reference_doctype": "Issue", "reference_name": replicated_issue.name, - "content": " - Split the Issue from {1}".format(self.name, frappe.bold(self.name)), + "content": " - Split the Issue from {1}".format(self.name, frappe.bold(self.name)), }).insert(ignore_permissions=True) return replicated_issue.name diff --git a/erpnext/utilities/bot.py b/erpnext/utilities/bot.py index 0e5e95d1a8..b2e74da921 100644 --- a/erpnext/utilities/bot.py +++ b/erpnext/utilities/bot.py @@ -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) From 0fb2e02b5626e9dfd4fcebf6511c62b6d05f1352 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Mon, 9 Nov 2020 23:51:24 +0530 Subject: [PATCH 02/18] feat: pos item selector new ui --- erpnext/public/build.json | 3 +- erpnext/public/scss/point-of-sale.scss | 145 ++++++++++++++++++ .../page/point_of_sale/pos_controller.js | 4 +- .../page/point_of_sale/pos_item_selector.js | 42 +++-- 4 files changed, 168 insertions(+), 26 deletions(-) create mode 100644 erpnext/public/scss/point-of-sale.scss diff --git a/erpnext/public/build.json b/erpnext/public/build.json index 2695502269..78128a105d 100644 --- a/erpnext/public/build.json +++ b/erpnext/public/build.json @@ -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" diff --git a/erpnext/public/scss/point-of-sale.scss b/erpnext/public/scss/point-of-sale.scss new file mode 100644 index 0000000000..1d01496277 --- /dev/null +++ b/erpnext/public/scss/point-of-sale.scss @@ -0,0 +1,145 @@ +.point-of-sale-app { + display: grid; + grid-template-columns: repeat(10, minmax(0, 1fr)); + gap: var(--margin-md); + + section { + min-height: 45rem; + max-height: calc(100vh - 200px); + } + + .frappe-control { + margin: 0 !important; + padding: 5px 5px; + width: 100%; + } + + .form-group { + margin-bottom: 0px !important; + } + + .pointer-no-select { + cursor: pointer; + user-select: none; + } + + .nowrap { + overflow: hidden; + white-space: nowrap; + } + + .image { + height: 100% !important; + object-fit: cover; + } + + .abbr { + background-color: var(--gray-50); + font-size: var(--text-3xl); + } + + .label { + display: flex; + align-items: center; + font-weight: 700; + font-size: var(--text-lg); + } + + .pos-card { + background-color: var(--fg-color); + box-shadow: var(--shadow-base); + border-radius: var(--border-radius-md); + } + + > .items-selector { + grid-column: span 6 / span 6; + display: flex; + flex-direction: column; + overflow-y: scroll; + overflow-x: hidden; + + > .filter-section { + display: grid; + grid-template-columns: repeat(12, minmax(0, 1fr)); + background-color: var(--fg-color); + position: sticky; + top: -1px; + z-index: 1; + padding: var(--padding-md); + padding-bottom: var(--padding-sm); + align-items: center; + + > .label { + @extend .label; + grid-column: span 4 / span 4; + padding: var(--padding-xs); + padding-top: 0px; + } + + > .search-field { + grid-column: span 5 / span 5; + display: flex; + align-items: center; + } + + > .item-group-field { + grid-column: span 3 / span 3; + display: flex; + align-items: center; + } + } + + > .items-container { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: var(--margin-lg); + flex: 1 1 0%; + padding: var(--padding-lg); + padding-top: var(--padding-xs); + + > .item-wrapper { + @extend .pointer-no-select; + border-radius: var(--border-radius-md); + box-shadow: var(--shadow-base); + + .item-display { + display: flex; + align-items: center; + justify-content: center; + border-radius: var(--border-radius-md); + margin: var(--margin-sm); + margin-bottom: 0px; + min-height: 8rem; + height: 8rem; + color: var(--gray-500); + + > img { + @extend .image; + } + } + + > .item-detail { + display: flex; + flex-direction: column; + justify-content: center; + min-height: 3.5rem; + height: 3.5rem; + padding-left: var(--padding-sm); + padding-right: var(--padding-sm); + + > .item-name { + @extend .nowrap; + display: flex; + align-items: center; + font-size: var(--text-md); + } + + > .item-rate { + font-weight: 700; + } + } + + } + } + } +} \ No newline at end of file diff --git a/erpnext/selling/page/point_of_sale/pos_controller.js b/erpnext/selling/page/point_of_sale/pos_controller.js index 288084d245..8cbd59086c 100644 --- a/erpnext/selling/page/point_of_sale/pos_controller.js +++ b/erpnext/selling/page/point_of_sale/pos_controller.js @@ -157,10 +157,10 @@ erpnext.PointOfSale.Controller = class { prepare_dom() { this.wrapper.append( - `
` + `
` ); - this.$components_wrapper = this.wrapper.find('.app'); + this.$components_wrapper = this.wrapper.find('.point-of-sale-app'); } prepare_components() { diff --git a/erpnext/selling/page/point_of_sale/pos_item_selector.js b/erpnext/selling/page/point_of_sale/pos_item_selector.js index 49d42814ab..4195d93d4e 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_selector.js +++ b/erpnext/selling/page/point_of_sale/pos_item_selector.js @@ -17,18 +17,13 @@ erpnext.PointOfSale.ItemSelector = class { prepare_dom() { this.wrapper.append( - `
-
-
-
-
-
-
-
ALL ITEMS
-
-
-
+ `
+
+
All Items
+
+
+
` ); @@ -80,27 +75,28 @@ erpnext.PointOfSale.ItemSelector = class { function get_item_image_html() { if (item_image) { - return `
- ${frappe.get_abbr(item.item_name)} -
` + return `
+ ${frappe.get_abbr(item.item_name)} +
`; } else { - return `
- ${frappe.get_abbr(item.item_name)} -
` + return `
${frappe.get_abbr(item.item_name)}
`; } } return ( - `
+ ${get_item_image_html()} -
-
+ +
+
${frappe.ellipsis(item.item_name, 18)}
-
${format_currency(item.price_list_rate, item.currency, 0) || 0}
+
${format_currency(item.price_list_rate, item.currency, 0) || 0}
` ) @@ -115,7 +111,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, From 210baafad4e0fa12fc70999d5830998ba3e7c26c Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Wed, 11 Nov 2020 12:04:00 +0530 Subject: [PATCH 03/18] feat: pos customer selector new ui --- erpnext/public/scss/point-of-sale.scss | 169 ++++++++++++++ .../page/point_of_sale/pos_item_cart.js | 217 ++++++++++-------- 2 files changed, 285 insertions(+), 101 deletions(-) diff --git a/erpnext/public/scss/point-of-sale.scss b/erpnext/public/scss/point-of-sale.scss index 1d01496277..4d5bc21de5 100644 --- a/erpnext/public/scss/point-of-sale.scss +++ b/erpnext/public/scss/point-of-sale.scss @@ -51,6 +51,10 @@ border-radius: var(--border-radius-md); } + .seperator { + border-bottom: 1px solid var(--gray-300); + } + > .items-selector { grid-column: span 6 / span 6; display: flex; @@ -142,4 +146,169 @@ } } } + + > .customer-cart-container { + grid-column: span 4 / span 4; + display: flex; + flex-direction: column; + + > .customer-section { + @extend .pos-card; + display: flex; + flex-direction: column; + padding: var(--padding-md); + + > .customer-field { + display: flex; + align-items: center; + } + + > .customer-details { + display: flex; + flex-direction: column; + position: sticky; + top: -1px; + z-index: 1; + background-color: var(--fg-color); + + > .header { + display: flex; + margin-bottom: var(--margin-md); + justify-content: space-between; + padding-top: var(--padding-md); + + > .label { + @extend .label; + } + + > .close-details-btn { + display: flex; + align-items: center; + cursor: pointer; + } + } + + > .customer-display { + display: flex; + align-items: center; + cursor: pointer; + + > .customer-image { + display: flex; + align-items: center; + justify-content: center; + width: 3rem; + height: 3rem; + border-radius: 50%; + color: var(--gray-500); + margin-right: var(--margin-md); + + > img { + @extend .image; + border-radius: 50%; + } + } + + > .customer-abbr { + @extend .abbr; + font-size: var(--text-2xl); + } + + > .customer-name-desc { + @extend .nowrap; + display: flex; + flex-direction: column; + margin-right: auto; + + >.customer-name { + font-weight: 700; + font-size: var(--text-lg); + } + + >.customer-desc { + color: var(--gray-600); + font-weight: 500; + font-size: var(--text-sm); + } + } + + > .reset-customer-btn { + display: flex; + align-items: center; + cursor: pointer; + } + + } + + > .customer-fields-container { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + margin-top: var(--margin-sm); + } + + > .transactions-label { + @extend .label; + margin-top: var(--margin-sm); + margin-bottom: var(--margin-sm); + } + } + } + + > .cart-container { + + } + } + + .invoice-wrapper { + @extend .pointer-no-select; + display: flex; + justify-content: space-between; + border-radius: var(--border-radius-md); + padding: var(--padding-sm); + + &:hover { + background-color: var(--gray-50); + } + + > .invoice-name-date { + display: flex; + flex-direction: column; + justify-content: end; + + > .invoice-name { + @extend .nowrap; + font-size: var(--text-md); + font-weight: 700; + margin-bottom: var(--margin-xs); + } + + > .invoice-date { + @extend .nowrap; + font-size: var(--text-sm); + display: flex; + align-items: center; + } + } + + > .invoice-total-status { + display: flex; + flex-direction: column; + font-weight: 500; + font-size: var(--text-sm); + margin-left: var(--margin-md); + + > .invoice-total { + margin-bottom: var(--margin-xs); + font-size: var(--text-base); + font-weight: 700; + text-align: right; + } + + > .invoice-status { + display: flex; + align-items: center; + justify-content: right; + } + } + } } \ No newline at end of file diff --git a/erpnext/selling/page/point_of_sale/pos_item_cart.js b/erpnext/selling/page/point_of_sale/pos_item_cart.js index 7799dacacb..11453f7cf0 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_cart.js +++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js @@ -16,10 +16,10 @@ erpnext.PointOfSale.ItemCart = class { prepare_dom() { this.wrapper.append( - `
` + `
` ) - 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( - `
` + `
` ) this.$customer_section = this.$component.find('.customer-section'); } @@ -37,21 +37,20 @@ 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( - `
+ `
Item
Qty
Amount
-
+
@@ -88,7 +87,7 @@ erpnext.PointOfSale.ItemCart = class { `
+ Add Discount
-
+
Net Total
@@ -106,7 +105,7 @@ erpnext.PointOfSale.ItemCart = class {
0.00
-
+
Checkout
@@ -151,7 +150,7 @@ erpnext.PointOfSale.ItemCart = class { this.$numpad_section.append( `
+ text-center text-white no-select pointer rounded-md text-md text-bold mt-4" data-button-value="checkout"> Checkout
` ) @@ -159,15 +158,17 @@ erpnext.PointOfSale.ItemCart = class { 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); + }); + + this.$customer_section.on('click', '.customer-display', function(e) { + // don't triggger the event if .reset-customer-btn btn is clicked which is under .customer-header + if ($(e.target).closest('.reset-customer-btn').length) return; const show = !me.$cart_container.hasClass('d-none'); me.toggle_customer_info(show); @@ -282,24 +283,26 @@ 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.hasClass("shadow-base"); 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').removeClass("shadow-base").css("opacity", "1"); } else { - $cart_item.addClass("shadow"); + $cart_item.addClass("shadow-base"); 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).removeClass("shadow-base").css("opacity", "0.65"); } - // 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"); + // highlight with inner shadow-base + // $cart_item.addClass("shadow-base-inner bg-selected"); + // me.$cart_container.find('.cart-item-wrapper').not(this).removeClass("shadow-base-inner bg-selected"); } make_customer_selector() { - this.$customer_section.html(`
`); + this.$customer_section.html(` +
+ `); const me = this; const query = { query: 'erpnext.controllers.queries.customer_query' }; const allowed_customer_group = this.events.get_allowed_customer_group() || []; @@ -313,6 +316,7 @@ erpnext.PointOfSale.ItemCart = class { label: __('Customer'), fieldtype: 'Link', options: 'Customer', + input_class: 'input-xs', placeholder: __('Search by customer name, phone, email.'), get_query: () => query, onchange: function() { @@ -332,7 +336,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); @@ -414,7 +418,7 @@ erpnext.PointOfSale.ItemCart = class { stroke-linecap="round" stroke-linejoin="round"> -
+
${String(discount).bold()}% off
` @@ -423,18 +427,18 @@ erpnext.PointOfSale.ItemCart = class { } 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( - `
-
- ${get_customer_image()} -
-
${customer}
+ this.$customer_section.html( + `
+
+ ${this.get_customer_image()} +
+
${customer}
${get_customer_description()}
-
+
@@ -449,26 +453,23 @@ erpnext.PointOfSale.ItemCart = class { function get_customer_description() { if (!email_id && !mobile_no) { - return `
Click to add email / phone
` + return `
Click to add email / phone
` } else if (email_id && !mobile_no) { - return `
${email_id}
` + return `
${email_id}
` } else if (mobile_no && !email_id) { - return `
${mobile_no}
` + return `
${mobile_no}
` } else { - return `
${email_id} | ${mobile_no}
` + return `
${email_id} - ${mobile_no}
` } } - function get_customer_image() { - if (image) { - return `
- ${image} -
` - } else { - return `
- ${frappe.get_abbr(customer)} -
` - } + } + get_customer_image() { + const { customer, image } = this.customer_info || {}; + if (image) { + return `
${image}
` + } else { + return `
${frappe.get_abbr(customer)}
` } } @@ -523,7 +524,7 @@ erpnext.PointOfSale.ItemCart = class { let margin_left = ''; if (i !== 0) margin_left = 'ml-2'; const description = /[0-9]+/.test(t.description) ? t.description : `${t.description} @ ${t.rate}%`; - return `${description}` + return `${description}` }).join('') }
@@ -575,7 +576,7 @@ erpnext.PointOfSale.ItemCart = class { if (!$item_to_update.length) { this.$cart_items_wrapper.append( - `
` @@ -618,7 +619,7 @@ erpnext.PointOfSale.ItemCart = class { if (item_data.rate && item_data.amount && item_data.rate !== item_data.amount) { return `
-
+
${item_data.qty || 0}
@@ -629,7 +630,7 @@ erpnext.PointOfSale.ItemCart = class { } else { return `
-
+
${item_data.qty || 0}
@@ -753,25 +754,25 @@ 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('shadow-base-inner'); const curr_action_is_action = ['qty', 'discount_percentage', 'rate', 'done'].includes(curr_action); if (!curr_action_is_highlighted) { - $btn.addClass('shadow-inner bg-selected'); + $btn.addClass('shadow-base-inner bg-selected'); } if (this.prev_action === curr_action && curr_action_is_highlighted) { // if Qty is pressed twice - $btn.removeClass('shadow-inner bg-selected'); + $btn.removeClass('shadow-base-inner bg-selected'); } if (this.prev_action && this.prev_action !== curr_action && curr_action_is_action) { // Order: Qty -> Rate then remove Qty highlight const prev_btn = $(`[data-button-value='${this.prev_action}']`); - prev_btn.removeClass('shadow-inner bg-selected'); + prev_btn.removeClass('shadow-base-inner bg-selected'); } if (!curr_action_is_action || curr_action === 'done') { // if numbers are clicked setTimeout(() => { - $btn.removeClass('shadow-inner bg-selected'); + $btn.removeClass('shadow-base-inner bg-selected'); }, 100); } } @@ -790,7 +791,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('.shadow-base-inner').removeClass('shadow-base-inner bg-selected'); } toggle_numpad_field_edit(fieldname) { @@ -801,48 +802,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( - `
${this.customer_info.customer}
-
` - ) - - this.$customer_section.find('.customer-details').append( - `
-
CONTACT DETAILS
-
- -
-
-
+ this.$cart_container.addClass('d-none'); + this.$customer_section.css({ + 'height': '100%', + 'padding-top': '0px', + 'overflow-x': 'hidden', + 'overflow-y': 'scroll' + }); + this.$customer_section.find('.customer-details').html( + `
+
Contact Details
+
+ + +
-
RECENT TRANSACTIONS
-
` - ) +
+
+ ${this.get_customer_image()} +
+
${customer}
+
+
+
+
+ +
+
+
+
+
Recent Transactions
` + ); // transactions need to be in diff div from sticky elem for scrolling - this.$customer_section.append(`
`) + this.$customer_section.append(`
`) - 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.$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 +877,7 @@ erpnext.PointOfSale.ItemCart = class { },{ fieldname: 'loyalty_points', label: __('Loyalty Points'), - fieldtype: 'Int', + fieldtype: 'Data', read_only: 1 }]; @@ -916,14 +929,14 @@ erpnext.PointOfSale.ItemCart = class { const transaction_container = this.$customer_section.find('.customer-transactions'); if (!res.length) { - transaction_container.removeClass('flex-1 border rounded').html( - `
No recent transactions found
` + transaction_container.html( + `
No recent transactions found
` ) 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"); @@ -934,20 +947,22 @@ erpnext.PointOfSale.ItemCart = class { if (invoice.status === 'Return') (indicator_color = 'grey'); transaction_container.append( - `
-
-
${invoice.name}
-
- ${posting_datetime} -
+ `
+
+
${invoice.name}
+
${posting_datetime}
-
-
+
+
${format_currency(invoice.grand_total, invoice.currency, 0) || 0}
-
${invoice.status}
+
+ + ${invoice.status} +
-
` +
+
` ) }); }) From cc208839cfd4b23fa01a1c76d90675cf2a3e6e94 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Wed, 11 Nov 2020 20:47:20 +0530 Subject: [PATCH 04/18] feat: pos cart with new ui --- erpnext/public/scss/point-of-sale.scss | 269 +++++++++++++++++ .../page/point_of_sale/pos_item_cart.js | 276 ++++++++---------- .../page/point_of_sale/pos_number_pad.js | 5 +- 3 files changed, 396 insertions(+), 154 deletions(-) diff --git a/erpnext/public/scss/point-of-sale.scss b/erpnext/public/scss/point-of-sale.scss index 4d5bc21de5..37ca8fd13d 100644 --- a/erpnext/public/scss/point-of-sale.scss +++ b/erpnext/public/scss/point-of-sale.scss @@ -52,9 +52,31 @@ } .seperator { + margin-left: var(--margin-sm); + margin-right: var(--margin-sm); border-bottom: 1px solid var(--gray-300); } + .primary-action { + @extend .pointer-no-select; + display: flex; + align-items: center; + justify-content: center; + padding: var(--padding-sm); + margin: var(--margin-xs); + margin-top: var(--margin-sm); + margin-bottom: var(--margin-xs); + border-radius: var(--border-radius-md); + font-size: var(--text-lg); + font-weight: 700; + } + + .highlighted-numpad-btn { + box-shadow: inset 0 0px 4px 0px rgba(0, 0, 0, 0.15) !important; + font-weight: 700; + background-color: var(--gray-50); + } + > .items-selector { grid-column: span 6 / span 6; display: flex; @@ -255,7 +277,254 @@ } > .cart-container { + @extend .pos-card; + display: flex; + flex-direction: column; + align-items: center; + margin-top: var(--margin-md); + position: relative; + height: 100%; + > .abs-cart-container { + position: absolute; + display: flex; + flex-direction: column; + padding: var(--padding-md); + width: 100%; + height: 100%; + + > .cart-label { + @extend .label; + padding-bottom: var(--padding-md); + padding-left: var(--margin-sm); + } + + > .cart-header { + display: flex; + width: 100%; + font-size: var(--text-md); + padding-left: var(--padding-xs); + padding-right: var(--padding-xs); + padding-bottom: var(--padding-md); + + > .name-header { + flex: 1 1 0%; + margin-left: var(--margin-xs); + } + + > .qty-header { + margin-right: var(--margin-lg); + } + + > .rate-amount-header { + margin-right: var(--margin-xs); + text-align: right; + } + } + + .no-item-wrapper { + display: flex; + align-items: center; + justify-content: center; + background-color: var(--gray-50); + border-radius: var(--border-radius-md); + font-size: var(--text-md); + font-weight: 500; + width: 100%; + height: 100%; + } + + > .cart-items-section { + display: flex; + flex-direction: column; + flex: 1 1 0%; + overflow-y: scroll; + + > .cart-item-wrapper { + @extend .pointer-no-select; + display: flex; + align-items: center; + padding: var(--padding-sm); + border-radius: var(--border-radius-md); + + &:hover { + background-color: var(--gray-50); + } + + > .item-image { + display: flex; + align-items: center; + justify-content: center; + width: 2rem; + height: 2rem; + border-radius: var(--border-radius-md); + color: var(--gray-500); + margin-right: var(--margin-md); + + > img { + @extend .image; + } + } + + > .item-abbr { + @extend .abbr; + font-size: var(--text-lg); + } + + + > .item-name-desc { + @extend .nowrap; + display: flex; + flex-direction: column; + flex: 1 1 0%; + flex-shrink: 1; + + > .item-name { + font-weight: 700; + } + + > .item-desc { + font-size: var(--text-sm); + color: var(--gray-600); + font-weight: 500; + } + } + + > .item-qty-rate { + display: flex; + flex-shrink: 0; + text-align: right; + margin-left: var(--margin-md); + + > .item-qty { + display: flex; + align-items: center; + margin-right: var(--margin-lg); + font-weight: 700; + } + + > .item-rate-amount { + display: flex; + flex-direction: column; + flex-shrink: 0; + text-align: right; + + > .item-rate { + font-weight: 700; + } + + > .item-amount { + font-size: var(--text-md); + font-weight: 600; + } + } + } + + } + } + + > .cart-totals-section { + display: flex; + flex-direction: column; + flex-shrink: 0; + width: 100%; + margin-top: var(--margin-md); + + > .net-total-container { + display: flex; + align-items: center; + justify-content: space-between; + padding: var(--padding-sm); + font-weight: 500; + font-size: var(--text-md); + } + + > .taxes-container { + display: none; + align-items: center; + justify-content: space-between; + padding: var(--padding-sm); + font-weight: 500; + font-size: var(--text-md); + + > .tax-label { + display: flex; + align-items: center; + + > .tax-desc { + margin-left: var(--margin-md); + } + } + } + + > .grand-total-container { + display: flex; + align-items: center; + justify-content: space-between; + padding: var(--padding-sm); + font-weight: 700; + font-size: var(--text-lg); + } + + > .checkout-btn { + @extend .primary-action; + background-color: var(--blue-200); + color: white; + } + } + + > .numpad-section { + display: none; + flex-direction: column; + flex-shrink: 0; + margin-top: var(--margin-sm); + padding: var(--padding-sm); + padding-bottom: 0px; + width: 100%; + + > .numpad-totals { + display: flex; + justify-content: space-between; + margin-bottom: var(--margin-md); + font-size: var(--text-md); + font-weight: 700; + } + + > .numpad-container { + display: grid; + grid-template-columns: repeat(5, minmax(0, 1fr)); + gap: var(--margin-md); + margin-bottom: var(--margin-md); + + > .numpad-btn { + @extend .pointer-no-select; + border-radius: var(--border-radius-md); + display: flex; + align-items: center; + justify-content: center; + padding: var(--padding-md); + box-shadow: var(--shadow-sm); + } + + > .col-span-2 { + grid-column: span 2 / span 2; + } + + > .remove-btn { + font-weight: 700; + color: var(--red-500); + } + } + + > .checkout-btn { + @extend .primary-action; + margin: 0px; + margin-bottom: var(--margin-sm); + background-color: var(--blue-200); + color: white; + } + } + } } } diff --git a/erpnext/selling/page/point_of_sale/pos_item_cart.js b/erpnext/selling/page/point_of_sale/pos_item_cart.js index 11453f7cf0..e0a2ee7767 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_cart.js +++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js @@ -43,16 +43,17 @@ erpnext.PointOfSale.ItemCart = class { init_cart_components() { this.$component.append( - `
-
-
-
Item
-
Qty
-
Amount
+ `
+
+
Item Cart
+
+
Item
+
Qty
+
Amount
-
-
-
+
+
+
` ); @@ -71,13 +72,10 @@ 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( - `
-
No items in cart
-
` - ) - this.$cart_items_wrapper.addClass('mt-4 border-grey border-dashed'); + `
No items in cart
` + ); } make_cart_totals_section() { @@ -87,30 +85,18 @@ erpnext.PointOfSale.ItemCart = class { `
+ Add Discount
-
-
-
-
Net Total
-
-
-
0.00
-
-
-
-
-
-
Grand Total
-
-
-
0.00
-
-
-
- Checkout -
-
- Edit Cart -
+
+
Net Total
+
0.00
+
+
+
+
Grand Total
+
0.00
+
+
Checkout
+
+ Edit Cart
` ) @@ -136,23 +122,20 @@ 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( - `
+ `
` ) this.$numpad_section.append( - `
- Checkout -
` + `
Checkout
` ) } @@ -167,10 +150,9 @@ erpnext.PointOfSale.ItemCart = class { }); this.$customer_section.on('click', '.customer-display', function(e) { - // don't triggger the event if .reset-customer-btn btn is clicked which is under .customer-header - if ($(e.target).closest('.reset-customer-btn').length) return; + if ($(this).find('.reset-customer-btn').length == 0) return; - const show = !me.$cart_container.hasClass('d-none'); + const show = me.$cart_container.is(':visible'); me.toggle_customer_info(show); }); @@ -283,20 +265,15 @@ erpnext.PointOfSale.ItemCart = class { toggle_item_highlight(item) { const $cart_item = $(item); - const item_is_highlighted = $cart_item.hasClass("shadow-base"); - if (!item || item_is_highlighted) { + if (!item) { this.item_is_selected = false; - this.$cart_container.find('.cart-item-wrapper').removeClass("shadow-base").css("opacity", "1"); + this.$cart_container.find('.cart-item-wrapper').css("background-color", ""); } else { - $cart_item.addClass("shadow-base"); + $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-base").css("opacity", "0.65"); + this.$cart_container.find('.cart-item-wrapper').not(item).css("background-color", ""); } - // highlight with inner shadow-base - // $cart_item.addClass("shadow-base-inner bg-selected"); - // me.$cart_container.find('.cart-item-wrapper').not(this).removeClass("shadow-base-inner bg-selected"); } make_customer_selector() { @@ -464,6 +441,7 @@ erpnext.PointOfSale.ItemCart = class { } } + get_customer_image() { const { customer, image } = this.customer_info || {}; if (image) { @@ -485,57 +463,47 @@ erpnext.PointOfSale.ItemCart = class { render_net_total(value) { const currency = this.events.get_frm().doc.currency; - this.$totals_section.find('.net-total').html( - `
-
Net Total
-
-
-
${format_currency(value, currency)}
-
` + this.$totals_section.find('.net-total-container').html( + `
Net Total
${format_currency(value, currency)}
` ) - this.$numpad_section.find('.numpad-net-total').html(`Net Total: ${format_currency(value, currency)}`) + this.$numpad_section.find('.numpad-net-total').html( + `
Net Total: ${format_currency(value, currency)}
` + ); } render_grand_total(value) { const currency = this.events.get_frm().doc.currency; - this.$totals_section.find('.grand-total').html( - `
-
Grand Total
-
-
-
${format_currency(value, currency)}
-
` + this.$totals_section.find('.grand-total-container').html( + `
Grand Total
${format_currency(value, currency)}
` ) - this.$numpad_section.find('.numpad-grand-total').html(`Grand Total: ${format_currency(value, currency)}`) + this.$numpad_section.find('.numpad-grand-total').html( + `
Grand Total: ${format_currency(value, currency)}
` + ) } render_taxes(value, taxes) { if (taxes.length) { const currency = this.events.get_frm().doc.currency; - this.$totals_section.find('.taxes').html( - `
-
-
Tax Charges
-
- ${ - 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 `${description}` - }).join('') - } -
+ this.$totals_section.find('.taxes-container').css('display', 'flex').html( + `
+
Tax Charges
+
+ ${ + taxes.map((t, i) => { + let margin_left = ''; + if (i !== 0) margin_left = '10px'; + const description = /[0-9]+/.test(t.description) ? t.description : `${t.description} @ ${t.rate}%`; + return `${description}` + }).join('') + }
-
-
${format_currency(value, currency)}
-
-
` +
+
${format_currency(value, currency)}
` ) } else { - this.$totals_section.find('.taxes').html('') + this.$totals_section.find('.taxes-container').css('display', 'none').html(''); } } @@ -564,9 +532,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); } @@ -576,32 +544,33 @@ erpnext.PointOfSale.ItemCart = class { if (!$item_to_update.length) { this.$cart_items_wrapper.append( - `
-
` +
+
` ) $item_to_update = this.get_cart_item(item_data); } $item_to_update.html( - `
-
+ `${get_item_image_html()} +
+
${item_data.item_name}
${get_description_html()}
- ${get_rate_discount_html()} -
` + ${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(); @@ -611,30 +580,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 ` -
-
- ${item_data.qty || 0} -
-
-
${format_currency(item_data.amount, currency)}
-
${format_currency(item_data.rate, currency)}
+
+
${item_data.qty || 0}
+
+
${format_currency(item_data.amount, currency)}
+
${format_currency(item_data.rate, currency)}
` } else { return ` -
-
- ${item_data.qty || 0} -
-
-
${format_currency(item_data.rate, currency)}
+
+
${item_data.qty || 0}
+
+
${format_currency(item_data.rate, currency)}
` } @@ -650,10 +615,19 @@ erpnext.PointOfSale.ItemCart = class { } } item_data.description = frappe.ellipsis(item_data.description, 45); - return `
${item_data.description}
` + return `
${item_data.description}
` } return ``; } + + function get_item_image_html() { + const { image, item_name } = item_data; + if (image) { + return `
${image}
` + } else { + return `
${frappe.get_abbr(item_name)}
` + } + } } scroll_to_item($item) { @@ -669,20 +643,23 @@ 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.$cart_container.find('.checkout-btn').css({ + 'background-color': 'var(--blue-500)' + }); + } else { + this.$cart_container.find('.checkout-btn').css({ + 'background-color': 'var(--blue-200)' + }); } } @@ -690,8 +667,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(); } @@ -754,36 +730,36 @@ erpnext.PointOfSale.ItemCart = class { } highlight_numpad_btn($btn, curr_action) { - const curr_action_is_highlighted = $btn.hasClass('shadow-base-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-base-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-base-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-base-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-base-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(); } @@ -791,7 +767,7 @@ erpnext.PointOfSale.ItemCart = class { reset_numpad() { this.numpad_value = ''; this.prev_action = undefined; - this.$numpad_section.find('.shadow-base-inner').removeClass('shadow-base-inner bg-selected'); + this.$numpad_section.find('.highlighted-numpad-btn').removeClass('highlighted-numpad-btn'); } toggle_numpad_field_edit(fieldname) { @@ -804,7 +780,7 @@ erpnext.PointOfSale.ItemCart = class { if (show) { const { customer } = this.customer_info || {}; - this.$cart_container.addClass('d-none'); + this.$cart_container.css('display', 'none'); this.$customer_section.css({ 'height': '100%', 'padding-top': '0px', @@ -842,7 +818,7 @@ erpnext.PointOfSale.ItemCart = class { this.fetch_customer_transactions(); } else { - this.$cart_container.removeClass('d-none'); + this.$cart_container.css('display', 'flex'); this.$customer_section.css({ 'height': '', 'padding-top': '', @@ -988,20 +964,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'); } } diff --git a/erpnext/selling/page/point_of_sale/pos_number_pad.js b/erpnext/selling/page/point_of_sale/pos_number_pad.js index 4b8e841805..edde7d84df 100644 --- a/erpnext/selling/page/point_of_sale/pos_number_pad.js +++ b/erpnext/selling/page/point_of_sale/pos_number_pad.js @@ -25,14 +25,13 @@ erpnext.PointOfSale.NumberPad = class { const fieldname = fieldnames && fieldnames[number] ? fieldnames[number] : typeof number === 'string' ? frappe.scrub(number) : number; - return a2 + `
${number}
` + return a2 + `
${number}
` }, '') }, ''); } this.wrapper.html( - `
+ `
${get_keys()}
` ) From fc3315a6d7938212009955802cbd7181083e0025 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Wed, 11 Nov 2020 22:59:10 +0530 Subject: [PATCH 05/18] feat: pos item details with new ui --- erpnext/public/scss/point-of-sale.scss | 114 +++++++++++++++++- .../page/point_of_sale/pos_item_cart.js | 11 +- .../page/point_of_sale/pos_item_details.js | 83 ++++++------- .../page/point_of_sale/pos_item_selector.js | 16 +-- 4 files changed, 160 insertions(+), 64 deletions(-) diff --git a/erpnext/public/scss/point-of-sale.scss b/erpnext/public/scss/point-of-sale.scss index 37ca8fd13d..573b9dc2b7 100644 --- a/erpnext/public/scss/point-of-sale.scss +++ b/erpnext/public/scss/point-of-sale.scss @@ -119,7 +119,6 @@ display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--margin-lg); - flex: 1 1 0%; padding: var(--padding-lg); padding-top: var(--padding-xs); @@ -471,6 +470,13 @@ background-color: var(--blue-200); color: white; } + + > .edit-cart-btn { + @extend .primary-action; + display: none; + background-color: var(--gray-100); + font-weight: 500; + } } > .numpad-section { @@ -542,7 +548,7 @@ > .invoice-name-date { display: flex; flex-direction: column; - justify-content: end; + justify-content: flex-end; > .invoice-name { @extend .nowrap; @@ -580,4 +586,108 @@ } } } + + > .item-details-container { + @extend .pos-card; + grid-column: span 4 / span 4; + display: none; + flex-direction: column; + padding: var(--padding-lg); + padding-top: var(--padding-md); + + > .item-details-header { + display: flex; + justify-content: space-between; + margin-bottom: var(--margin-md); + + > .close-btn { + @extend .pointer-no-select; + } + } + + > .item-display { + display: flex; + + > .item-name-desc-price { + flex: 1 1 0%; + display: flex; + flex-direction: column; + justify-content: flex-end; + margin-right: var(--margin-md); + + > .item-name { + font-size: var(--text-3xl); + font-weight: 600; + } + + > .item-desc { + font-size: var(--text-md); + font-weight: 500; + } + + > .item-price { + font-size: var(--text-3xl); + font-weight: 700; + } + } + + > .item-image { + display: flex; + align-items: center; + justify-content: center; + width: 11rem; + height: 11rem; + border-radius: var(--border-radius-md); + margin-left: var(--margin-md); + color: var(--gray-500); + + > img { + @extend .image; + } + + > .item-abbr { + @extend .abbr; + display: flex; + align-items: center; + justify-content: center; + border-radius: var(--border-radius-md); + font-size: var(--text-3xl); + width: 100%; + height: 100%; + } + } + } + + > .discount-section { + display: flex; + align-items: center; + margin-bottom: var(--margin-sm); + + > .item-rate { + font-weight: 500; + margin-right: var(--margin-sm); + text-decoration: line-through; + } + + > .item-discount { + padding: 3px var(--padding-sm); + border-radius: var(--border-radius-sm); + background-color: var(--green-100); + color: var(--green-700); + font-size: var(--text-sm); + font-weight: 700; + } + } + + > .form-container { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + column-gap: var(--padding-xs); + + > .auto-fetch-btn { + @extend .pointer-no-select; + margin: auto var(--margin-xs); + } + } + } } \ No newline at end of file diff --git a/erpnext/selling/page/point_of_sale/pos_item_cart.js b/erpnext/selling/page/point_of_sale/pos_item_cart.js index e0a2ee7767..b560ba4a87 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_cart.js +++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js @@ -95,9 +95,7 @@ erpnext.PointOfSale.ItemCart = class {
0.00
Checkout
-
- Edit Cart -
` +
Edit Cart
` ) this.$add_discount_elem = this.$component.find(".add-discount"); @@ -176,7 +174,7 @@ 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); @@ -265,8 +263,9 @@ erpnext.PointOfSale.ItemCart = class { toggle_item_highlight(item) { const $cart_item = $(item); + const item_is_highlighted = $cart_item.attr("style") == "background-color:var(--gray-50);"; - if (!item) { + if (!item || item_is_highlighted) { this.item_is_selected = false; this.$cart_container.find('.cart-item-wrapper').css("background-color", ""); } else { @@ -522,7 +521,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'; diff --git a/erpnext/selling/page/point_of_sale/pos_item_details.js b/erpnext/selling/page/point_of_sale/pos_item_details.js index a4de9f165d..546154345b 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_details.js +++ b/erpnext/selling/page/point_of_sale/pos_item_details.js @@ -16,35 +16,36 @@ erpnext.PointOfSale.ItemDetails = class { prepare_dom() { this.wrapper.append( - `
` + `
` ) - this.$component = this.wrapper.find('.item-details'); + this.$component = this.wrapper.find('.item-details-container'); } init_child_components() { this.$component.html( - `
-
-
ITEM DETAILS
-
Close
+ `
+
Item Details
+
+ + +
-
-
-
-
-
-
-
+
+
+
+
+
+
-
-
STOCK DETAILS
-
-
` +
+
+
+
` ) 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( - `${image}` - ); + this.$item_image.html(`${image}`); } else { - this.$item_image.html(frappe.get_abbr(item_code)); + this.$item_image.html(`
${frappe.get_abbr(item_name)}
`); } } @@ -130,12 +129,8 @@ erpnext.PointOfSale.ItemDetails = class { render_discount_dom(item) { if (item.discount_percentage) { this.$dicount_section.html( - `
- ${format_currency(item.price_list_rate, this.currency)} -
-
- ${item.discount_percentage}% off -
` + `
${format_currency(item.price_list_rate, this.currency)}
+
${item.discount_percentage}% off
` ) 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( - `
-
-
` + `
` ) 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( - `
` - ) if (!item.has_batch_no) { this.$form_container.append( `
` ) } this.$form_container.append( - `
- Auto Fetch Serial Numbers -
` + `
Auto Fetch Serial Numbers
` ) - 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'); } } \ No newline at end of file diff --git a/erpnext/selling/page/point_of_sale/pos_item_selector.js b/erpnext/selling/page/point_of_sale/pos_item_selector.js index 4195d93d4e..0bac84481d 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_selector.js +++ b/erpnext/selling/page/point_of_sale/pos_item_selector.js @@ -248,20 +248,16 @@ erpnext.PointOfSale.ItemSelector = class { resize_selector(minimize) { minimize ? - this.$component.find('.search-field').removeClass('mr-8') : - this.$component.find('.search-field').addClass('mr-8'); - - minimize ? - this.$component.find('.filter-section').addClass('flex-col') : - this.$component.find('.filter-section').removeClass('flex-col'); + 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.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) { From 7202e9f65845c602878381c167a870fea54b83a5 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Fri, 13 Nov 2020 17:33:20 +0530 Subject: [PATCH 06/18] refactor: pos payment with new ui --- erpnext/public/scss/point-of-sale.scss | 199 ++++++++++++++++-- .../page/point_of_sale/pos_controller.js | 6 +- .../page/point_of_sale/pos_item_cart.js | 57 +++-- .../page/point_of_sale/pos_item_selector.js | 2 +- .../selling/page/point_of_sale/pos_payment.js | 162 +++++++------- 5 files changed, 297 insertions(+), 129 deletions(-) diff --git a/erpnext/public/scss/point-of-sale.scss b/erpnext/public/scss/point-of-sale.scss index 573b9dc2b7..61be422e48 100644 --- a/erpnext/public/scss/point-of-sale.scss +++ b/erpnext/public/scss/point-of-sale.scss @@ -5,12 +5,12 @@ section { min-height: 45rem; + height: calc(100vh - 200px); max-height: calc(100vh - 200px); } .frappe-control { margin: 0 !important; - padding: 5px 5px; width: 100%; } @@ -63,9 +63,7 @@ align-items: center; justify-content: center; padding: var(--padding-sm); - margin: var(--margin-xs); margin-top: var(--margin-sm); - margin-bottom: var(--margin-xs); border-radius: var(--border-radius-md); font-size: var(--text-lg); font-weight: 700; @@ -91,21 +89,21 @@ position: sticky; top: -1px; z-index: 1; - padding: var(--padding-md); + padding: var(--padding-lg); padding-bottom: var(--padding-sm); align-items: center; > .label { @extend .label; grid-column: span 4 / span 4; - padding: var(--padding-xs); - padding-top: 0px; + padding-bottom: var(--padding-xs); } > .search-field { grid-column: span 5 / span 5; display: flex; align-items: center; + margin-right: var(--padding-sm); } > .item-group-field { @@ -177,7 +175,7 @@ @extend .pos-card; display: flex; flex-direction: column; - padding: var(--padding-md); + padding: var(--padding-md) var(--padding-lg); > .customer-field { display: flex; @@ -288,36 +286,33 @@ position: absolute; display: flex; flex-direction: column; - padding: var(--padding-md); + padding: var(--padding-lg); width: 100%; height: 100%; > .cart-label { @extend .label; padding-bottom: var(--padding-md); - padding-left: var(--margin-sm); } > .cart-header { display: flex; width: 100%; font-size: var(--text-md); - padding-left: var(--padding-xs); - padding-right: var(--padding-xs); padding-bottom: var(--padding-md); > .name-header { flex: 1 1 0%; - margin-left: var(--margin-xs); } > .qty-header { margin-right: var(--margin-lg); + text-align: center; } > .rate-amount-header { - margin-right: var(--margin-xs); text-align: right; + margin-right: var(--margin-sm); } } @@ -429,11 +424,38 @@ width: 100%; margin-top: var(--margin-md); + > .add-discount-wrapper { + @extend .pointer-no-select; + display: none; + align-items: center; + border-radius: var(--border-radius-md); + border: 1px dashed var(--gray-500); + padding: var(--padding-sm) var(--padding-md); + margin-bottom: var(--margin-sm); + + > .add-discount-field { + width: 100%; + } + + > .discount-icon { + margin-right: var(--margin-sm); + } + + > .edit-discount-btn { + padding: 3px var(--padding-sm); + border-radius: var(--border-radius-sm); + background-color: var(--green-100); + color: var(--green-700); + font-size: var(--text-sm); + font-weight: 700; + } + } + > .net-total-container { display: flex; align-items: center; justify-content: space-between; - padding: var(--padding-sm); + padding: var(--padding-sm) 0px; font-weight: 500; font-size: var(--text-md); } @@ -442,7 +464,7 @@ display: none; align-items: center; justify-content: space-between; - padding: var(--padding-sm); + padding: var(--padding-sm) 0px; font-weight: 500; font-size: var(--text-md); @@ -460,7 +482,7 @@ display: flex; align-items: center; justify-content: space-between; - padding: var(--padding-sm); + padding: var(--padding-sm) 0px; font-weight: 700; font-size: var(--text-lg); } @@ -474,8 +496,15 @@ > .edit-cart-btn { @extend .primary-action; display: none; - background-color: var(--gray-100); + background-color: var(--gray-300); font-weight: 500; + transition: all 0.15s ease-in-out; + + &:hover { + background-color: var(--gray-600); + color: white; + font-weight: 700; + } } } @@ -682,12 +711,144 @@ > .form-container { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); - column-gap: var(--padding-xs); + column-gap: var(--padding-md); > .auto-fetch-btn { @extend .pointer-no-select; - margin: auto var(--margin-xs); + margin: var(--margin-xs); } } } + + > .payment-container { + @extend .pos-card; + grid-column: span 6 / span 6; + display: none; + flex-direction: column; + padding: var(--padding-lg); + + .border-primary { + border: 1px solid var(--blue-500); + } + + .submit-order-btn { + @extend .primary-action; + background-color: var(--blue-500); + color: white; + } + + .section-label { + @extend .label; + @extend .pointer-no-select; + margin-bottom: var(--margin-md); + } + + > .payment-modes { + display: flex; + margin-bottom: var(--margin-md); + overflow-x: scroll; + overflow-y: hidden; + + > .payment-mode-wrapper { + min-width: 40%; + padding: var(--padding-xs); + + > .mode-of-payment { + @extend .pos-card; + @extend .pointer-no-select; + padding: var(--padding-md) var(--padding-lg); + + > .pay-amount { + display: inline; + float: right; + font-weight: 700; + } + + > .mode-of-payment-control { + display: none; + align-items: center; + margin-top: var(--margin-sm); + margin-bottom: var(--margin-xs); + } + + > .loyalty-amount-name { + display: none; + float: right; + font-weight: 700; + } + + > .cash-shortcuts { + display: none; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: var(--margin-sm); + font-size: var(--text-sm); + text-align: center; + + > .shortcut { + @extend .pointer-no-select; + border-radius: var(--border-radius-sm); + background-color: var(--gray-100); + font-weight: 500; + padding: var(--padding-xs) var(--padding-sm); + transition: all 0.15s ease-in-out; + + &:hover { + background-color: var(--gray-300); + } + } + } + } + } + } + + .invoice-fields { + display: none; + grid-template-columns: repeat(2, minmax(0, 1fr)); + column-gap: var(--padding-md); + } + + > .totals-section { + display: flex; + margin-top: auto; + margin-bottom: var(--margin-sm); + justify-content: center; + flex-direction: column; + + > .totals { + display: flex; + padding-top: var(--padding-md); + background-color: var(--gray-100); + justify-content: center; + padding: var(--padding-md); + border-radius: var(--border-radius-md); + + > .col { + flex-grow: 1; + text-align: center; + + > .total-label { + font-size: var(--text-md); + font-weight: 500; + color: var(--gray-600); + } + + > .value { + font-size: var(--text-2xl); + font-weight: 700; + } + } + + > .seperator-y { + margin-left: var(--margin-sm); + margin-right: var(--margin-sm); + border-right: 1px solid var(--gray-300); + } + } + + > .number-pad { + display: none; + } + } + + } } \ No newline at end of file diff --git a/erpnext/selling/page/point_of_sale/pos_controller.js b/erpnext/selling/page/point_of_sale/pos_controller.js index 8cbd59086c..4875ec061f 100644 --- a/erpnext/selling/page/point_of_sale/pos_controller.js +++ b/erpnext/selling/page/point_of_sale/pos_controller.js @@ -356,10 +356,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'); } }, diff --git a/erpnext/selling/page/point_of_sale/pos_item_cart.js b/erpnext/selling/page/point_of_sale/pos_item_cart.js index b560ba4a87..867cd2df63 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_cart.js +++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js @@ -78,12 +78,23 @@ erpnext.PointOfSale.ItemCart = class { ); } + get_discount_icon() { + return ( + ` + + + + + ` + ); + } + make_cart_totals_section() { this.$totals_section = this.$component.find('.cart-totals-section'); this.$totals_section.append( - `
- + Add Discount + `
+ ${this.get_discount_icon()} Add Discount
Net Total
@@ -98,7 +109,7 @@ erpnext.PointOfSale.ItemCart = class {
Edit Cart
` ) - this.$add_discount_elem = this.$component.find(".add-discount"); + this.$add_discount_elem = this.$component.find(".add-discount-wrapper"); } make_cart_numpad() { @@ -178,19 +189,15 @@ erpnext.PointOfSale.ItemCart = class { 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(); }); @@ -244,10 +251,10 @@ erpnext.PointOfSale.ItemCart = class { 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, @@ -292,7 +299,6 @@ erpnext.PointOfSale.ItemCart = class { label: __('Customer'), fieldtype: 'Link', options: 'Customer', - input_class: 'input-xs', placeholder: __('Search by customer name, phone, email.'), get_query: () => query, onchange: function() { @@ -351,9 +357,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( - `
` + `
` ); const me = this; @@ -362,14 +368,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; } }, @@ -383,21 +394,19 @@ 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( - `
` + `
` ); } else { - this.$add_discount_elem.addClass('pr-4 pl-4'); this.$add_discount_elem.html( - ` -
+
${String(discount).bold()}% off -
- ` +
` ); } } @@ -652,10 +661,12 @@ erpnext.PointOfSale.ItemCart = class { highlight_checkout_btn(toggle) { 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)' }); diff --git a/erpnext/selling/page/point_of_sale/pos_item_selector.js b/erpnext/selling/page/point_of_sale/pos_item_selector.js index 0bac84481d..e2a2365e32 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_selector.js +++ b/erpnext/selling/page/point_of_sale/pos_item_selector.js @@ -261,6 +261,6 @@ erpnext.PointOfSale.ItemSelector = 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'); } } \ No newline at end of file diff --git a/erpnext/selling/page/point_of_sale/pos_payment.js b/erpnext/selling/page/point_of_sale/pos_payment.js index e4d8965ac2..9f8c2dff1d 100644 --- a/erpnext/selling/page/point_of_sale/pos_payment.js +++ b/erpnext/selling/page/point_of_sale/pos_payment.js @@ -18,38 +18,28 @@ erpnext.PointOfSale.Payment = class { prepare_dom() { this.wrapper.append( - `
-
-
- PAYMENT METHOD -
-
-
-
-
-
-
-
-
-
-
-
-
- Complete Order -
-
-
-
+ `
+ +
+
+
+
+
+
+
Complete Order
` ) - 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('.invoice-fields-section'); } make_invoice_fields_control() { @@ -57,13 +47,16 @@ erpnext.PointOfSale.Payment = class { const fields = doc.invoice_fields; if (!fields.length) return; - this.$invoice_details_section.html( - `
- ADDITIONAL INFORMATION + this.$invoice_fields_section.html( + ` -
` +
` ); - this.$invoice_fields = this.$invoice_details_section.find('.invoice-fields'); + this.$invoice_fields = this.$invoice_fields_section.find('.invoice-fields'); const frm = this.events.get_frm(); fields.forEach(df => { @@ -127,9 +120,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 +135,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 scrollRight = mode_clicked.offset().right - me.$payment_modes.offset().right + me.$payment_modes.scrollRight(); + // me.$payment_modes.animate({ scrollRight }); + 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'); @@ -161,10 +157,10 @@ erpnext.PointOfSale.Payment = class { } 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'); + 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.toggle_numpad(true); me.selected_mode = me[`${mode}_control`]; @@ -198,7 +194,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 +213,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) => { @@ -236,28 +232,28 @@ erpnext.PointOfSale.Payment = class { } }); - this.$component.on('click', '.invoice-details-section', function(e) { + this.$component.on('click', '.invoice-fields-section', function(e) { if ($(e.target).closest('.invoice-fields').length) return; - me.$payment_modes.addClass('d-none'); - me.$invoice_fields.toggleClass("d-none"); + me.$payment_modes.css('display', 'none'); + me.$invoice_fields.css('display', 'grid'); me.toggle_numpad(false); }); this.$component.on('click', '.payment-section', () => { - this.$invoice_fields.addClass("d-none"); - this.$payment_modes.toggleClass('d-none'); + this.$invoice_fields.css('display', 'none'); + this.$payment_modes.css('display', 'flex'); 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 +283,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 +321,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 +342,11 @@ erpnext.PointOfSale.Payment = class { const amount = p.amount > 0 ? format_currency(p.amount, currency) : ''; return ( - `
-
+ `
+
${p.mode_of_payment} -
${amount}
-
+
${amount}
+
` ) @@ -405,12 +398,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( - `
+ `
${ shortcuts.map(s => { - return `
- ${format_currency(s, currency, 0)} -
` + return `
${format_currency(s, currency, 0)}
` }).join('') }
` @@ -457,13 +448,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( - `
-
+ `
+
Redeem Loyalty Points -
${amount}
-
${loyalty_program}
-
+
${amount}
+
${loyalty_program}
+
` ) @@ -520,18 +510,24 @@ erpnext.PointOfSale.Payment = class { const label = change ? __('Change') : __('To Be Paid'); this.$totals.html( - `
-
Paid Amount
-
${format_currency(paid_amount, currency)}
+ `
+
Grand Total
+
${format_currency(doc.grand_total, currency)}
-
-
${label}
-
${format_currency(change || remaining, currency)}
+
+
+
Paid Amount
+
${format_currency(paid_amount, currency)}
+
+
+
+
${label}
+
${format_currency(change || remaining, currency)}
` ) } 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'); } } \ No newline at end of file From b0b6aa9124bab869f70022aedf1d3985408123b5 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Mon, 23 Nov 2020 12:57:06 +0530 Subject: [PATCH 07/18] refactor: past order list and summary with new ui --- erpnext/public/scss/point-of-sale.scss | 249 +++++++++++++- .../page/point_of_sale/pos_controller.js | 2 +- .../page/point_of_sale/pos_item_cart.js | 43 ++- .../page/point_of_sale/pos_item_selector.js | 2 +- .../page/point_of_sale/pos_past_order_list.js | 51 ++- .../point_of_sale/pos_past_order_summary.js | 310 ++++++------------ 6 files changed, 385 insertions(+), 272 deletions(-) diff --git a/erpnext/public/scss/point-of-sale.scss b/erpnext/public/scss/point-of-sale.scss index 61be422e48..60043bf110 100644 --- a/erpnext/public/scss/point-of-sale.scss +++ b/erpnext/public/scss/point-of-sale.scss @@ -75,7 +75,14 @@ background-color: var(--gray-50); } + .sticky-element { + position: sticky; + top: -1px; + z-index: 1; + } + > .items-selector { + @extend .pos-card; grid-column: span 6 / span 6; display: flex; flex-direction: column; @@ -83,12 +90,10 @@ overflow-x: hidden; > .filter-section { + @extend .sticky-element; display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); background-color: var(--fg-color); - position: sticky; - top: -1px; - z-index: 1; padding: var(--padding-lg); padding-bottom: var(--padding-sm); align-items: center; @@ -124,6 +129,10 @@ @extend .pointer-no-select; border-radius: var(--border-radius-md); box-shadow: var(--shadow-base); + + &:hover { + transform: scale(1.02, 1.02); + } .item-display { display: flex; @@ -180,14 +189,13 @@ > .customer-field { display: flex; align-items: center; + padding-top: var(--padding-xs); } > .customer-details { + @extend .sticky-element; display: flex; flex-direction: column; - position: sticky; - top: -1px; - z-index: 1; background-color: var(--fg-color); > .header { @@ -263,6 +271,7 @@ display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); margin-top: var(--margin-sm); + gap: var(--padding-sm); } > .transactions-label { @@ -445,7 +454,7 @@ padding: 3px var(--padding-sm); border-radius: var(--border-radius-sm); background-color: var(--green-100); - color: var(--green-700); + color: var(--dark-green-500); font-size: var(--text-sm); font-weight: 700; } @@ -462,19 +471,14 @@ > .taxes-container { display: none; - align-items: center; - justify-content: space-between; + flex-direction: column; padding: var(--padding-sm) 0px; font-weight: 500; font-size: var(--text-md); - > .tax-label { + > .tax-row { display: flex; - align-items: center; - - > .tax-desc { - margin-left: var(--margin-md); - } + justify-content: space-between; } } @@ -702,7 +706,7 @@ padding: 3px var(--padding-sm); border-radius: var(--border-radius-sm); background-color: var(--green-100); - color: var(--green-700); + color: var(--dark-green-500); font-size: var(--text-sm); font-weight: 700; } @@ -849,6 +853,219 @@ display: none; } } + } + > .past-order-list { + @extend .pos-card; + grid-column: span 4 / span 4; + display: none; + flex-direction: column; + overflow-y: scroll; + overflow-x: hidden; + + > .filter-section { + @extend .sticky-element; + display: flex; + flex-direction: column; + background-color: var(--fg-color); + padding: var(--padding-lg); + + > .search-field { + width: 100%; + display: flex; + align-items: center; + margin-top: var(--margin-sm); + margin-bottom: var(--margin-xs); + } + + > .status-field { + width: 100%; + display: flex; + align-items: center; + } + } + + > .invoices-container { + padding: var(--padding-lg); + padding-top: 0px; + } + } + + > .past-order-summary { + // @extend .pos-card; + display: none; + grid-column: span 6 / span 6; + flex-direction: column; + align-items: center; + justify-content: center; + padding: var(--padding-lg); + + > .no-summary-placeholder { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + height: 100%; + background-color: var(--gray-50); + font-weight: 500; + border-radius: var(--border-radius-md); + } + + > .invoice-summary-wrapper { + @extend .pos-card; + display: none; + position: relative; + width: 31rem; + height: 100%; + + > .abs-container { + position: absolute; + display: flex; + flex-direction: column; + width: 100%; + height: 100%; + padding: var(--padding-lg); + + > .upper-section { + display: flex; + justify-content: space-between; + width: 100%; + margin-bottom: var(--margin-md); + + > .left-section { + display: flex; + flex-direction: column; + align-items: flex-start; + justify-content: flex-end; + padding-right: var(--padding-sm); + + > .customer-name { + font-size: var(--text-2xl); + font-weight: 700; + } + + > .customer-email { + font-size: var(--text-md); + font-weight: 500; + color: var(--gray-600); + } + + > .cashier { + font-size: var(--text-md); + font-weight: 500; + color: var(--gray-600); + margin-top: auto; + } + } + + > .right-section { + display: flex; + flex-direction: column; + align-items: flex-end; + justify-content: space-between; + + > .paid-amount { + font-size: var(--text-2xl); + font-weight: 700; + } + + > .invoice-name { + font-size: var(--text-md); + font-weight: 500; + color: var(--gray-600); + margin-bottom: var(--margin-sm); + } + } + } + + > .summary-container { + display: flex; + flex-direction: column; + border-radius: var(--border-radius-md); + background-color: var(--gray-50); + margin: var(--margin-md) 0px; + + > .summary-row-wrapper { + display: flex; + align-items: center; + justify-content: space-between; + padding: var(--padding-sm) var(--padding-md); + // border-bottom: 1px solid var(--gray-300); + } + + > .taxes-wrapper { + display: flex; + flex-direction: column; + padding: var(--padding-sm) var(--padding-md); + // border-bottom: 1px solid var(--gray-300); + + > .tax-row { + display: flex; + justify-content: space-between; + } + } + + > .item-row-wrapper { + display: flex; + align-items: center; + padding: var(--padding-sm) var(--padding-md); + // border-bottom: 1px solid var(--gray-300); + + > .item-name { + @extend .nowrap; + font-weight: 500; + margin-right: var(--margin-md); + } + + > .item-qty { + font-weight: 500; + margin-left: auto; + } + + > .item-rate-disc { + display: flex; + text-align: right; + margin-left: var(--margin-md); + + > .item-disc { + color: var(--dark-green-500); + } + + > .item-rate { + font-weight: 500; + margin-left: var(--margin-md); + } + } + } + + > .grand-total { + // font-size: var(--text-lg); + font-weight: 700; + padding: var(--padding-md); + } + + > .payments { + font-weight: 700; + } + } + + + > .summary-btns { + display: flex; + justify-content: space-between; + + > .summary-btn { + flex: 1; + margin: 0px var(--margin-xs); + } + + > .new-btn { + background-color: var(--blue-500); + color:white; + font-weight: 500; + } + } + } + } } } \ No newline at end of file diff --git a/erpnext/selling/page/point_of_sale/pos_controller.js b/erpnext/selling/page/point_of_sale/pos_controller.js index 4875ec061f..9a7ddd9a57 100644 --- a/erpnext/selling/page/point_of_sale/pos_controller.js +++ b/erpnext/selling/page/point_of_sale/pos_controller.js @@ -190,7 +190,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); } diff --git a/erpnext/selling/page/point_of_sale/pos_item_cart.js b/erpnext/selling/page/point_of_sale/pos_item_cart.js index 867cd2df63..955111439b 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_cart.js +++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js @@ -159,7 +159,7 @@ erpnext.PointOfSale.ItemCart = class { }); this.$customer_section.on('click', '.customer-display', function(e) { - if ($(this).find('.reset-customer-btn').length == 0) return; + if ($(e.target).closest('.reset-customer-btn').length) return; const show = me.$cart_container.is(':visible'); me.toggle_customer_info(show); @@ -495,20 +495,17 @@ erpnext.PointOfSale.ItemCart = class { if (taxes.length) { const currency = this.events.get_frm().doc.currency; this.$totals_section.find('.taxes-container').css('display', 'flex').html( - `
-
Tax Charges
-
- ${ - taxes.map((t, i) => { - let margin_left = ''; - if (i !== 0) margin_left = '10px'; - const description = /[0-9]+/.test(t.description) ? t.description : `${t.description} @ ${t.rate}%`; - return `${description}` - }).join('') - } -
-
-
${format_currency(value, currency)}
` + `${ + taxes.map((t, i) => { + const description = /[0-9]+/.test(t.description) ? t.description : `${t.description} @ ${t.rate}%`; + return `
+
+ ${description} +
+
${format_currency(value, currency)}
+
` + }).join('') + }` ) } else { this.$totals_section.find('.taxes-container').css('display', 'none').html(''); @@ -926,11 +923,12 @@ erpnext.PointOfSale.ItemCart = class { 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( `
@@ -943,8 +941,9 @@ erpnext.PointOfSale.ItemCart = class { ${format_currency(invoice.grand_total, invoice.currency, 0) || 0}
- - ${invoice.status} + + ${invoice.status} +
diff --git a/erpnext/selling/page/point_of_sale/pos_item_selector.js b/erpnext/selling/page/point_of_sale/pos_item_selector.js index e2a2365e32..38fe645a65 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_selector.js +++ b/erpnext/selling/page/point_of_sale/pos_item_selector.js @@ -17,7 +17,7 @@ erpnext.PointOfSale.ItemSelector = class { prepare_dom() { this.wrapper.append( - `
+ `
All Items
diff --git a/erpnext/selling/page/point_of_sale/pos_past_order_list.js b/erpnext/selling/page/point_of_sale/pos_past_order_list.js index b256247924..166d9cf0ce 100644 --- a/erpnext/selling/page/point_of_sale/pos_past_order_list.js +++ b/erpnext/selling/page/point_of_sale/pos_past_order_list.js @@ -14,17 +14,13 @@ erpnext.PointOfSale.PastOrderList = class { prepare_dom() { this.wrapper.append( - `
-
-
-
-
-
-
-
RECENT ORDERS
-
-
+ `
+
+
Recent Orders
+
+
+
` ); @@ -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 ( - `
-
-
${invoice.name}
-
-
- - - - ${invoice.customer} -
+ `
+
+
${invoice.name}
+
+ + + + ${invoice.customer}
-
-
${format_currency(invoice.grand_total, invoice.currency, 0) || 0}
-
${posting_datetime}
+
+
${format_currency(invoice.grand_total, invoice.currency, 0) || 0}
+
${posting_datetime}
-
` +
+
` ); } + + toggle_component(show) { + show ? this.$component.css('display', 'flex') && this.refresh_list() : this.$component.css('display', 'none'); + } }; \ No newline at end of file diff --git a/erpnext/selling/page/point_of_sale/pos_past_order_summary.js b/erpnext/selling/page/point_of_sale/pos_past_order_summary.js index 6fd4c26bea..2e94ecefa1 100644 --- a/erpnext/selling/page/point_of_sale/pos_past_order_summary.js +++ b/erpnext/selling/page/point_of_sale/pos_past_order_summary.js @@ -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( - `
-
-
-
Select an invoice to load summary data
-
+ `
+
+ Select an invoice to load summary data
-
-
+
+
+
+
Items
+
+
Totals
+
+
Payments
+
+
+
` ); 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( - `
` - ); - + 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( - `
-
ITEMS
-
-
` - ); - - this.$items_summary_container = this.$summary_container.find('.items-summary-container'); - } - - init_totals_summary() { - this.$summary_container.append( - `
-
TOTALS
-
-
` - ); - - this.$totals_summary_container = this.$summary_container.find('.summary-totals-container'); - } - - init_payments_summary() { - this.$summary_container.append( - `
-
PAYMENTS
-
-
` - ); - - this.$payment_summary_container = this.$summary_container.find('.payments-summary-container'); - } - - init_summary_buttons() { - this.$summary_container.append( - `
` - ); - + 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 `
-
${doc.customer}
-
${this.customer_email}
-
Sold by: ${doc.owner}
+ return `
+
${doc.customer}
+
${this.customer_email}
+
Sold by: ${doc.owner}
-
-
${format_currency(doc.paid_amount, doc.currency)}
-
-
${doc.name}
-
${doc.status}
-
+
+ +
${doc.name}
+ ${doc.status}
`; } + get_item_html(doc, item_data) { + return `
+
${item_data.item_name}
+
${item_data.qty || 0}
+
${get_rate_discount_html()}
+
`; + + function get_rate_discount_html() { + if (item_data.rate && item_data.price_list_rate && item_data.rate !== item_data.price_list_rate) { + return `(${item_data.discount_percentage}% off) +
${format_currency(item_data.rate, doc.currency)}
`; + } else { + return `
${format_currency(item_data.price_list_rate || item_data.rate, doc.currency)}
`; + } + } + } + get_discount_html(doc) { if (doc.discount_amount) { - return `
-
-
- Discount -
- (${doc.additional_discount_percentage} %) -
-
-
${format_currency(doc.discount_amount, doc.currency)}
-
-
`; + return `
+
Discount (${doc.additional_discount_percentage} %)
+
${format_currency(doc.discount_amount, doc.currency)}
+
`; } else { return ``; } } get_net_total_html(doc) { - return `
-
-
- Net Total -
-
-
-
${format_currency(doc.net_total, doc.currency)}
-
+ return `
+
Net Total
+
${format_currency(doc.net_total, doc.currency)}
`; } get_taxes_html(doc) { - const taxes = doc.taxes.map((t, i) => { - let margin_left = ''; - if (i !== 0) margin_left = 'ml-2'; - return `${t.description} @${t.rate}%`; - }).join(''); + if (!doc.taxes.length) return ''; return ` -
-
-
Tax Charges
-
${taxes}
-
-
-
- ${format_currency(doc.base_total_taxes_and_charges, doc.currency)} -
-
+
+ ${ + doc.taxes.map((t, i) => { + const description = /[0-9]+/.test(t.description) ? t.description : `${t.description} @ ${t.rate}%`; + return `
+
${description}
+
${format_currency(t.tax_amount_after_discount_amount, doc.currency)}
+
` + }).join('') + }
`; } get_grand_total_html(doc) { - return `
-
-
- Grand Total -
-
-
-
${format_currency(doc.grand_total, doc.currency)}
-
+ return `
+
Grand Total
+
${format_currency(doc.grand_total, doc.currency)}
`; } - get_item_html(doc, item_data) { - return `
-
- ${item_data.qty || 0} -
-
-
- ${item_data.item_name} -
-
-
- ${get_rate_discount_html()} -
-
`; - - function get_rate_discount_html() { - if (item_data.rate && item_data.price_list_rate && item_data.rate !== item_data.price_list_rate) { - return ` - (${item_data.discount_percentage}% off) - -
- ${format_currency(item_data.rate, doc.currency)} -
`; - } else { - return `
- ${format_currency(item_data.price_list_rate || item_data.rate, doc.currency)} -
`; - } - } - } - get_payment_html(doc, payment) { - return `
-
-
- ${payment.mode_of_payment} -
-
-
-
${format_currency(payment.amount, doc.currency)}
-
+ return `
+
${payment.mode_of_payment}
+
${format_currency(payment.amount, doc.currency)}
`; } @@ -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( - `
- ${b} -
` + `
${b}
` ); }); } @@ -372,28 +278,20 @@ erpnext.PointOfSale.PastOrderSummary = class { } show_summary_placeholder() { - this.$summary_wrapper.addClass("d-none"); - this.$component.find('.no-summary-placeholder').removeClass('d-none'); + this.$summary_wrapper.css('display', 'none'); + this.$component.find('.no-summary-placeholder').css('display', 'flex'); } 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'); + this.$component.find('.no-summary-placeholder').css('display', 'none'); + this.$summary_wrapper.css('display', 'flex'); } 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'); + this.$component.find('.no-summary-placeholder').css('display', 'none'); + this.$summary_wrapper.css('display', 'flex'); } get_condition_btn_map(after_submission) { @@ -410,8 +308,8 @@ erpnext.PointOfSale.PastOrderSummary = class { load_summary_of(doc, after_submission=false) { this.$summary_wrapper.removeClass("d-none"); - after_submission ? - this.switch_to_post_submit_summary() : this.switch_to_recent_invoice_summary(); + // after_submission ? + // this.switch_to_post_submit_summary() : this.switch_to_recent_invoice_summary(); this.doc = doc; @@ -437,19 +335,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 +355,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'); } }; \ No newline at end of file From d0d6fc2e175977dfc011faa8a4e803887763379d Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Mon, 23 Nov 2020 13:33:40 +0530 Subject: [PATCH 08/18] fix: cannot open past order summary --- erpnext/public/css/pos.css | 217 ------------------ erpnext/public/scss/point-of-sale.scss | 5 +- .../page/point_of_sale/pos_controller.js | 2 +- .../page/point_of_sale/pos_item_cart.js | 12 +- .../page/point_of_sale/pos_item_selector.js | 6 +- .../point_of_sale/pos_past_order_summary.js | 30 +-- .../selling/page/point_of_sale/pos_payment.js | 4 +- 7 files changed, 30 insertions(+), 246 deletions(-) delete mode 100644 erpnext/public/css/pos.css diff --git a/erpnext/public/css/pos.css b/erpnext/public/css/pos.css deleted file mode 100644 index 47f577131a..0000000000 --- a/erpnext/public/css/pos.css +++ /dev/null @@ -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; } \ No newline at end of file diff --git a/erpnext/public/scss/point-of-sale.scss b/erpnext/public/scss/point-of-sale.scss index 60043bf110..8291020622 100644 --- a/erpnext/public/scss/point-of-sale.scss +++ b/erpnext/public/scss/point-of-sale.scss @@ -108,7 +108,7 @@ grid-column: span 5 / span 5; display: flex; align-items: center; - margin-right: var(--padding-sm); + margin: 0px var(--margin-sm); } > .item-group-field { @@ -898,7 +898,7 @@ flex-direction: column; align-items: center; justify-content: center; - padding: var(--padding-lg); + // padding: var(--padding-lg); > .no-summary-placeholder { display: flex; @@ -1002,6 +1002,7 @@ > .tax-row { display: flex; justify-content: space-between; + font-size: var(--text-md); } } diff --git a/erpnext/selling/page/point_of_sale/pos_controller.js b/erpnext/selling/page/point_of_sale/pos_controller.js index 9a7ddd9a57..13c37493ce 100644 --- a/erpnext/selling/page/point_of_sale/pos_controller.js +++ b/erpnext/selling/page/point_of_sale/pos_controller.js @@ -388,7 +388,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) } }) } diff --git a/erpnext/selling/page/point_of_sale/pos_item_cart.js b/erpnext/selling/page/point_of_sale/pos_item_cart.js index 955111439b..a30f5b884d 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_cart.js +++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js @@ -170,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 @@ -239,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 @@ -247,8 +247,9 @@ 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-wrapper").attr("title", `${ctrl_label}+D`); @@ -638,6 +639,9 @@ erpnext.PointOfSale.ItemCart = class { scroll_to_item($item) { if ($item.length === 0) return; const scrollTop = $item.offset().top - this.$cart_items_wrapper.offset().top + this.$cart_items_wrapper.scrollTop(); + console.log($item.offset()); + console.log(this.$cart_items_wrapper.offset()); + console.log(scrollTop); this.$cart_items_wrapper.animate({ scrollTop }); } diff --git a/erpnext/selling/page/point_of_sale/pos_item_selector.js b/erpnext/selling/page/point_of_sale/pos_item_selector.js index 38fe645a65..3588e308c8 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_selector.js +++ b/erpnext/selling/page/point_of_sale/pos_item_selector.js @@ -248,9 +248,13 @@ erpnext.PointOfSale.ItemSelector = class { resize_selector(minimize) { minimize ? - this.$component.find('.filter-section').css('grid-template-columns', 'repeat(1, minmax(0, 1fr))') : + 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('.search-field').css('margin', 'var(--margin-sm) 0px') : + this.$component.find('.search-field').css('margin', '0px var(--margin-sm)'); + minimize ? this.$component.css('grid-column', 'span 2 / span 2') : this.$component.css('grid-column', 'span 6 / span 6') diff --git a/erpnext/selling/page/point_of_sale/pos_past_order_summary.js b/erpnext/selling/page/point_of_sale/pos_past_order_summary.js index 2e94ecefa1..8d1cd3195a 100644 --- a/erpnext/selling/page/point_of_sale/pos_past_order_summary.js +++ b/erpnext/selling/page/point_of_sale/pos_past_order_summary.js @@ -277,21 +277,14 @@ erpnext.PointOfSale.PastOrderSummary = class { this.$summary_btns.children().last().removeClass('mr-4'); } - show_summary_placeholder() { - this.$summary_wrapper.css('display', 'none'); - this.$component.find('.no-summary-placeholder').css('display', 'flex'); - } - - switch_to_post_submit_summary() { - // switch place holder with summary container - this.$component.find('.no-summary-placeholder').css('display', 'none'); - this.$summary_wrapper.css('display', 'flex'); - } - - switch_to_recent_invoice_summary() { - // switch place holder with summary container - this.$component.find('.no-summary-placeholder').css('display', 'none'); - this.$summary_wrapper.css('display', 'flex'); + 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) { @@ -308,12 +301,11 @@ erpnext.PointOfSale.PastOrderSummary = class { load_summary_of(doc, after_submission=false) { this.$summary_wrapper.removeClass("d-none"); - // after_submission ? - // this.switch_to_post_submit_summary() : this.switch_to_recent_invoice_summary(); + this.toggle_summary_placeholder(false) this.doc = doc; - this.attach_basic_info(doc); + this.attach_document_info(doc); this.attach_items_info(doc); @@ -326,7 +318,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); diff --git a/erpnext/selling/page/point_of_sale/pos_payment.js b/erpnext/selling/page/point_of_sale/pos_payment.js index 9f8c2dff1d..5354ac17ef 100644 --- a/erpnext/selling/page/point_of_sale/pos_payment.js +++ b/erpnext/selling/page/point_of_sale/pos_payment.js @@ -135,8 +135,8 @@ erpnext.PointOfSale.Payment = class { // if clicked element doesn't have .mode-of-payment class then return if (!$(e.target).is(mode_clicked)) return; - // const scrollRight = mode_clicked.offset().right - me.$payment_modes.offset().right + me.$payment_modes.scrollRight(); - // me.$payment_modes.animate({ scrollRight }); + 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'); From fada84cc4777aadff14ab496b31b9ece5d7a89a6 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Mon, 23 Nov 2020 13:40:46 +0530 Subject: [PATCH 09/18] fix: remove console and asset loading --- erpnext/public/scss/point-of-sale.scss | 2 +- erpnext/selling/page/point_of_sale/pos_controller.js | 7 +------ erpnext/selling/page/point_of_sale/pos_item_cart.js | 3 --- 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/erpnext/public/scss/point-of-sale.scss b/erpnext/public/scss/point-of-sale.scss index 8291020622..6d094561a7 100644 --- a/erpnext/public/scss/point-of-sale.scss +++ b/erpnext/public/scss/point-of-sale.scss @@ -1042,7 +1042,7 @@ > .grand-total { // font-size: var(--text-lg); font-weight: 700; - padding: var(--padding-md); + // padding: var(--padding-md); } > .payments { diff --git a/erpnext/selling/page/point_of_sale/pos_controller.js b/erpnext/selling/page/point_of_sale/pos_controller.js index 13c37493ce..5b0a1b6dc8 100644 --- a/erpnext/selling/page/point_of_sale/pos_controller.js +++ b/erpnext/selling/page/point_of_sale/pos_controller.js @@ -12,12 +12,7 @@ erpnext.PointOfSale.Controller = class { 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() { diff --git a/erpnext/selling/page/point_of_sale/pos_item_cart.js b/erpnext/selling/page/point_of_sale/pos_item_cart.js index a30f5b884d..527de00dae 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_cart.js +++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js @@ -639,9 +639,6 @@ erpnext.PointOfSale.ItemCart = class { scroll_to_item($item) { if ($item.length === 0) return; const scrollTop = $item.offset().top - this.$cart_items_wrapper.offset().top + this.$cart_items_wrapper.scrollTop(); - console.log($item.offset()); - console.log(this.$cart_items_wrapper.offset()); - console.log(scrollTop); this.$cart_items_wrapper.animate({ scrollTop }); } From f719380a0ddca883da8e471977aed9c2b38e6899 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Mon, 23 Nov 2020 15:51:03 +0530 Subject: [PATCH 10/18] fix: discount and taxes spacing --- erpnext/public/scss/point-of-sale.scss | 17 +++++++------- .../page/point_of_sale/pos_item_cart.js | 22 +++++++++---------- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/erpnext/public/scss/point-of-sale.scss b/erpnext/public/scss/point-of-sale.scss index 6d094561a7..7769eb1521 100644 --- a/erpnext/public/scss/point-of-sale.scss +++ b/erpnext/public/scss/point-of-sale.scss @@ -446,17 +446,15 @@ width: 100%; } - > .discount-icon { + .discount-icon { margin-right: var(--margin-sm); } - > .edit-discount-btn { - padding: 3px var(--padding-sm); - border-radius: var(--border-radius-sm); - background-color: var(--green-100); + .edit-discount-btn { + display: flex; + align-items: center; + font-weight: 500; color: var(--dark-green-500); - font-size: var(--text-sm); - font-weight: 700; } } @@ -472,13 +470,13 @@ > .taxes-container { display: none; flex-direction: column; - padding: var(--padding-sm) 0px; font-weight: 500; font-size: var(--text-md); > .tax-row { display: flex; justify-content: space-between; + line-height: var(--text-3xl); } } @@ -996,13 +994,14 @@ > .taxes-wrapper { display: flex; flex-direction: column; - padding: var(--padding-sm) var(--padding-md); + // padding: var(--padding-sm) var(--padding-md); // border-bottom: 1px solid var(--gray-300); > .tax-row { display: flex; justify-content: space-between; font-size: var(--text-md); + line-height: var(--text-3xl); } } diff --git a/erpnext/selling/page/point_of_sale/pos_item_cart.js b/erpnext/selling/page/point_of_sale/pos_item_cart.js index 527de00dae..37c2330f22 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_cart.js +++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js @@ -80,11 +80,11 @@ erpnext.PointOfSale.ItemCart = class { get_discount_icon() { return ( - ` - - - - + ` + + + + ` ); } @@ -400,13 +400,13 @@ erpnext.PointOfSale.ItemCart = class { `
` ); } else { + this.$add_discount_elem.css({ + 'border': '1px dashed var(--dark-green-500)', + 'padding': 'var(--padding-sm) var(--padding-md)' + }); this.$add_discount_elem.html( - ` - - -
- ${String(discount).bold()}% off + `
+ ${this.get_discount_icon()} Additional ${String(discount).bold()}% discount applied
` ); } From fa0ef218dd91d898d25b50761885b04aa7b2e1bc Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Mon, 23 Nov 2020 16:05:55 +0530 Subject: [PATCH 11/18] fix: customer details spacing --- erpnext/public/scss/point-of-sale.scss | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/public/scss/point-of-sale.scss b/erpnext/public/scss/point-of-sale.scss index 7769eb1521..b9f6f6bd97 100644 --- a/erpnext/public/scss/point-of-sale.scss +++ b/erpnext/public/scss/point-of-sale.scss @@ -270,13 +270,14 @@ > .customer-fields-container { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); - margin-top: var(--margin-sm); - gap: var(--padding-sm); + margin-top: var(--margin-md); + column-gap: var(--padding-sm); + row-gap: var(--padding-xs); } > .transactions-label { @extend .label; - margin-top: var(--margin-sm); + margin-top: var(--margin-md); margin-bottom: var(--margin-sm); } } @@ -579,13 +580,12 @@ > .invoice-name-date { display: flex; flex-direction: column; - justify-content: flex-end; + justify-content: space-around; > .invoice-name { @extend .nowrap; font-size: var(--text-md); font-weight: 700; - margin-bottom: var(--margin-xs); } > .invoice-date { From 69ef766a93befb5a90ee26cb3796c016f3cb1769 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Mon, 23 Nov 2020 16:38:32 +0530 Subject: [PATCH 12/18] feat: number pad in payment screen --- erpnext/public/scss/point-of-sale.scss | 38 +++++++++++++--- .../selling/page/point_of_sale/pos_payment.js | 44 +++++-------------- 2 files changed, 44 insertions(+), 38 deletions(-) diff --git a/erpnext/public/scss/point-of-sale.scss b/erpnext/public/scss/point-of-sale.scss index b9f6f6bd97..c35ca81a94 100644 --- a/erpnext/public/scss/point-of-sale.scss +++ b/erpnext/public/scss/point-of-sale.scss @@ -747,7 +747,8 @@ > .payment-modes { display: flex; - margin-bottom: var(--margin-md); + padding-bottom: var(--padding-sm); + margin-bottom: var(--margin-xs); overflow-x: scroll; overflow-y: hidden; @@ -803,10 +804,37 @@ } } - .invoice-fields { - display: none; - grid-template-columns: repeat(2, minmax(0, 1fr)); - column-gap: var(--padding-md); + > .fields-numpad-container { + display: flex; + flex: 1; + + > .fields-section { + flex: 1; + } + + > .number-pad { + flex: 1; + display: flex; + justify-content: flex-end; + align-items: flex-end; + + .numpad-container { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: var(--margin-md); + margin-bottom: var(--margin-md); + + > .numpad-btn { + @extend .pointer-no-select; + border-radius: var(--border-radius-md); + display: flex; + align-items: center; + justify-content: center; + padding: var(--padding-md); + box-shadow: var(--shadow-sm); + } + } + } } > .totals-section { diff --git a/erpnext/selling/page/point_of_sale/pos_payment.js b/erpnext/selling/page/point_of_sale/pos_payment.js index 5354ac17ef..fc8f8d8c70 100644 --- a/erpnext/selling/page/point_of_sale/pos_payment.js +++ b/erpnext/selling/page/point_of_sale/pos_payment.js @@ -19,17 +19,17 @@ erpnext.PointOfSale.Payment = class { prepare_dom() { this.wrapper.append( `
- +
-
+
+
+ +
+
+
+
-
Complete Order
` @@ -39,7 +39,7 @@ erpnext.PointOfSale.Payment = class { this.$totals_section = this.$component.find('.totals-section'); this.$totals = this.$component.find('.totals'); this.$numpad = this.$component.find('.number-pad'); - this.$invoice_fields_section = this.$component.find('.invoice-fields-section'); + this.$invoice_fields_section = this.$component.find('.fields-section'); } make_invoice_fields_control() { @@ -47,15 +47,6 @@ erpnext.PointOfSale.Payment = class { const fields = doc.invoice_fields; if (!fields.length) return; - this.$invoice_fields_section.html( - ` -
` - ); this.$invoice_fields = this.$invoice_fields_section.find('.invoice-fields'); const frm = this.events.get_frm(); @@ -153,7 +144,6 @@ 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'); @@ -161,13 +151,14 @@ erpnext.PointOfSale.Payment = class { 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.toggle_numpad(true); 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.numpad_value = ''; } }) @@ -231,19 +222,6 @@ erpnext.PointOfSale.Payment = class { this[`${mode}_control`].set_value(default_mop.amount); } }); - - this.$component.on('click', '.invoice-fields-section', function(e) { - if ($(e.target).closest('.invoice-fields').length) return; - - me.$payment_modes.css('display', 'none'); - me.$invoice_fields.css('display', 'grid'); - me.toggle_numpad(false); - }); - this.$component.on('click', '.payment-section', () => { - this.$invoice_fields.css('display', 'none'); - this.$payment_modes.css('display', 'flex'); - this.toggle_numpad(true); - }) } attach_shortcuts() { From eeee7e94279de65e18149bfd17fe906eaf19b2c8 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Mon, 23 Nov 2020 16:38:49 +0530 Subject: [PATCH 13/18] fix: post submission invoice recipt screen --- .../selling/page/point_of_sale/pos_past_order_summary.js | 6 ++++-- erpnext/selling/page/point_of_sale/pos_payment.js | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/erpnext/selling/page/point_of_sale/pos_past_order_summary.js b/erpnext/selling/page/point_of_sale/pos_past_order_summary.js index 8d1cd3195a..28076db63b 100644 --- a/erpnext/selling/page/point_of_sale/pos_past_order_summary.js +++ b/erpnext/selling/page/point_of_sale/pos_past_order_summary.js @@ -299,10 +299,12 @@ 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.$summary_wrapper.css('grid-column', 'span 10 / span 10') : + this.$summary_wrapper.css('grid-column', 'span 6 / span 6') + this.doc = doc; this.attach_document_info(doc); diff --git a/erpnext/selling/page/point_of_sale/pos_payment.js b/erpnext/selling/page/point_of_sale/pos_payment.js index fc8f8d8c70..6476b4925a 100644 --- a/erpnext/selling/page/point_of_sale/pos_payment.js +++ b/erpnext/selling/page/point_of_sale/pos_payment.js @@ -46,8 +46,9 @@ erpnext.PointOfSale.Payment = class { frappe.db.get_doc("POS Settings", undefined).then((doc) => { const fields = doc.invoice_fields; if (!fields.length) return; - + this.$invoice_fields = this.$invoice_fields_section.find('.invoice-fields'); + this.$invoice_fields.html(''); const frm = this.events.get_frm(); fields.forEach(df => { From 937dc467a092da7331cb18cc4c02f68164101284 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Mon, 23 Nov 2020 17:12:21 +0530 Subject: [PATCH 14/18] fix: no transactions placeholder --- erpnext/public/scss/point-of-sale.scss | 17 +++++++++++++++-- .../selling/page/point_of_sale/pos_item_cart.js | 4 ++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/erpnext/public/scss/point-of-sale.scss b/erpnext/public/scss/point-of-sale.scss index c35ca81a94..f388093548 100644 --- a/erpnext/public/scss/point-of-sale.scss +++ b/erpnext/public/scss/point-of-sale.scss @@ -281,6 +281,19 @@ margin-bottom: var(--margin-sm); } } + + > .customer-transactions { + height: 100%; + + > .no-transactions-placeholder { + height: 100%; + display: flex; + align-items: center; + justify-content: center; + background-color: var(--gray-50); + border-radius: var(--border-radius-md); + } + } } > .cart-container { @@ -900,7 +913,7 @@ width: 100%; display: flex; align-items: center; - margin-top: var(--margin-sm); + margin-top: var(--margin-md); margin-bottom: var(--margin-xs); } @@ -1022,7 +1035,7 @@ > .taxes-wrapper { display: flex; flex-direction: column; - // padding: var(--padding-sm) var(--padding-md); + padding: 0px var(--padding-md); // border-bottom: 1px solid var(--gray-300); > .tax-row { diff --git a/erpnext/selling/page/point_of_sale/pos_item_cart.js b/erpnext/selling/page/point_of_sale/pos_item_cart.js index 37c2330f22..efe716de6c 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_cart.js +++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js @@ -914,7 +914,7 @@ erpnext.PointOfSale.ItemCart = class { if (!res.length) { transaction_container.html( - `
No recent transactions found
` + `
No recent transactions found
` ) return; }; @@ -951,7 +951,7 @@ erpnext.PointOfSale.ItemCart = class {
` ) }); - }) + }); } load_invoice() { From 96feae608db8d12ca6560b3921e429eca9727c47 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Mon, 23 Nov 2020 17:53:34 +0530 Subject: [PATCH 15/18] refactor: build point of sale min js --- erpnext/public/build.json | 10 ++++++++++ erpnext/selling/page/point_of_sale/onscan.js | 1 - .../selling/page/point_of_sale/point_of_sale.js | 9 ++++----- .../selling/page/point_of_sale/pos_controller.js | 14 +++----------- .../page/point_of_sale/pos_item_selector.js | 9 +++++++-- erpnext/selling/page/point_of_sale/pos_payment.js | 13 +++++-------- package.json | 1 + yarn.lock | 5 +++++ 8 files changed, 35 insertions(+), 27 deletions(-) delete mode 100644 erpnext/selling/page/point_of_sale/onscan.js diff --git a/erpnext/public/build.json b/erpnext/public/build.json index 78128a105d..b12045c4c8 100644 --- a/erpnext/public/build.json +++ b/erpnext/public/build.json @@ -56,5 +56,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" ] } diff --git a/erpnext/selling/page/point_of_sale/onscan.js b/erpnext/selling/page/point_of_sale/onscan.js deleted file mode 100644 index 428dc75cf8..0000000000 --- a/erpnext/selling/page/point_of_sale/onscan.js +++ /dev/null @@ -1 +0,0 @@ -!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t()):e.onScan=t()}(this,function(){var d={attachTo:function(e,t){if(void 0!==e.scannerDetectionData)throw new Error("onScan.js is already initialized for DOM element "+e);var n={onScan:function(e,t){},onScanError:function(e){},onKeyProcess:function(e,t){},onKeyDetect:function(e,t){},onPaste:function(e,t){},keyCodeMapper:function(e){return d.decodeKeyEvent(e)},onScanButtonLongPress:function(){},scanButtonKeyCode:!1,scanButtonLongPressTime:500,timeBeforeScanTest:100,avgTimeByChar:30,minLength:6,suffixKeyCodes:[9,13],prefixKeyCodes:[],ignoreIfFocusOn:!1,stopPropagation:!1,preventDefault:!1,captureEvents:!1,reactToKeydown:!0,reactToPaste:!1,singleScanQty:1};return t=this._mergeOptions(n,t),e.scannerDetectionData={options:t,vars:{firstCharTime:0,lastCharTime:0,accumulatedString:"",testTimer:!1,longPressTimeStart:0,longPressed:!1}},!0===t.reactToPaste&&e.addEventListener("paste",this._handlePaste,t.captureEvents),!1!==t.scanButtonKeyCode&&e.addEventListener("keyup",this._handleKeyUp,t.captureEvents),!0!==t.reactToKeydown&&!1===t.scanButtonKeyCode||e.addEventListener("keydown",this._handleKeyDown,t.captureEvents),this},detachFrom:function(e){e.scannerDetectionData.options.reactToPaste&&e.removeEventListener("paste",this._handlePaste),!1!==e.scannerDetectionData.options.scanButtonKeyCode&&e.removeEventListener("keyup",this._handleKeyUp),e.removeEventListener("keydown",this._handleKeyDown),e.scannerDetectionData=void 0},getOptions:function(e){return e.scannerDetectionData.options},setOptions:function(e,t){switch(e.scannerDetectionData.options.reactToPaste){case!0:!1===t.reactToPaste&&e.removeEventListener("paste",this._handlePaste);break;case!1:!0===t.reactToPaste&&e.addEventListener("paste",this._handlePaste)}switch(e.scannerDetectionData.options.scanButtonKeyCode){case!1:!1!==t.scanButtonKeyCode&&e.addEventListener("keyup",this._handleKeyUp);break;default:!1===t.scanButtonKeyCode&&e.removeEventListener("keyup",this._handleKeyUp)}return e.scannerDetectionData.options=this._mergeOptions(e.scannerDetectionData.options,t),this._reinitialize(e),this},decodeKeyEvent:function(e){var t=this._getNormalizedKeyNum(e);switch(!0){case 48<=t&&t<=90:case 106<=t&&t<=111:if(void 0!==e.key&&""!==e.key)return e.key;var n=String.fromCharCode(t);switch(e.shiftKey){case!1:n=n.toLowerCase();break;case!0:n=n.toUpperCase()}return n;case 96<=t&&t<=105:return t-96}return""},simulate:function(e,t){return this._reinitialize(e),Array.isArray(t)?t.forEach(function(e){var t={};"object"!=typeof e&&"function"!=typeof e||null===e?t.keyCode=parseInt(e):t=e;var n=new KeyboardEvent("keydown",t);document.dispatchEvent(n)}):this._validateScanCode(e,t),this},_reinitialize:function(e){var t=e.scannerDetectionData.vars;t.firstCharTime=0,t.lastCharTime=0,t.accumulatedString=""},_isFocusOnIgnoredElement:function(e){var t=e.scannerDetectionData.options.ignoreIfFocusOn;if(!t)return!1;var n=document.activeElement;if(Array.isArray(t)){for(var a=0;at.length*i.avgTimeByChar:c={message:"Receieved code was not entered in time"};break;default:return i.onScan.call(e,t,o),n=new CustomEvent("scan",{detail:{scanCode:t,qty:o}}),e.dispatchEvent(n),d._reinitialize(e),!0}return c.scanCode=t,c.scanDuration=s-r,c.avgTimeByChar=i.avgTimeByChar,c.minLength=i.minLength,i.onScanError.call(e,c),n=new CustomEvent("scanError",{detail:c}),e.dispatchEvent(n),d._reinitialize(e),!1},_mergeOptions:function(e,t){var n,a={};for(n in e)Object.prototype.hasOwnProperty.call(e,n)&&(a[n]=e[n]);for(n in t)Object.prototype.hasOwnProperty.call(t,n)&&(a[n]=t[n]);return a},_getNormalizedKeyNum:function(e){return e.which||e.keyCode},_handleKeyDown:function(e){var t=d._getNormalizedKeyNum(e),n=this.scannerDetectionData.options,a=this.scannerDetectionData.vars,i=!1;if(!1!==n.onKeyDetect.call(this,t,e)&&!d._isFocusOnIgnoredElement(this))if(!1===n.scanButtonKeyCode||t!=n.scanButtonKeyCode){switch(!0){case a.firstCharTime&&-1!==n.suffixKeyCodes.indexOf(t):e.preventDefault(),e.stopImmediatePropagation(),i=!0;break;case!a.firstCharTime&&-1!==n.prefixKeyCodes.indexOf(t):e.preventDefault(),e.stopImmediatePropagation(),i=!1;break;default:var o=n.keyCodeMapper.call(this,e);if(null===o)return;a.accumulatedString+=o,n.preventDefault&&e.preventDefault(),n.stopPropagation&&e.stopImmediatePropagation(),i=!1}a.firstCharTime||(a.firstCharTime=Date.now()),a.lastCharTime=Date.now(),a.testTimer&&clearTimeout(a.testTimer),i?(d._validateScanCode(this,a.accumulatedString),a.testTimer=!1):a.testTimer=setTimeout(d._validateScanCode,n.timeBeforeScanTest,this,a.accumulatedString),n.onKeyProcess.call(this,o,e)}else a.longPressed||(a.longPressTimer=setTimeout(n.onScanButtonLongPress,n.scanButtonLongPressTime,this),a.longPressed=!0)},_handlePaste:function(e){if(!d._isFocusOnIgnoredElement(this)){e.preventDefault(),oOptions.stopPropagation&&e.stopImmediatePropagation();var t=(event.clipboardData||window.clipboardData).getData("text");this.scannerDetectionData.options.onPaste.call(this,t,event);var n=this.scannerDetectionData.vars;n.firstCharTime=0,n.lastCharTime=0,d._validateScanCode(this,t)}},_handleKeyUp:function(e){d._isFocusOnIgnoredElement(this)||d._getNormalizedKeyNum(e)==this.scannerDetectionData.options.scanButtonKeyCode&&(clearTimeout(this.scannerDetectionData.vars.longPressTimer),this.scannerDetectionData.vars.longPressed=!1)},isScanInProgressFor:function(e){return 0 { + 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."), @@ -98,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') diff --git a/erpnext/selling/page/point_of_sale/pos_item_selector.js b/erpnext/selling/page/point_of_sale/pos_item_selector.js index 3588e308c8..0d0e36cb3e 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_selector.js +++ b/erpnext/selling/page/point_of_sale/pos_item_selector.js @@ -1,3 +1,5 @@ +import onScan from 'onscan.js'; + erpnext.PointOfSale.ItemSelector = class { constructor({ frm, wrapper, events, pos_profile }) { this.wrapper = wrapper; @@ -46,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); @@ -104,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(''); @@ -131,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 : '' } } }, @@ -145,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')) { diff --git a/erpnext/selling/page/point_of_sale/pos_payment.js b/erpnext/selling/page/point_of_sale/pos_payment.js index 6476b4925a..365c27b12f 100644 --- a/erpnext/selling/page/point_of_sale/pos_payment.js +++ b/erpnext/selling/page/point_of_sale/pos_payment.js @@ -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; @@ -153,13 +151,12 @@ erpnext.PointOfSale.Payment = class { 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.numpad_value = ''; + 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) : ''; } }) diff --git a/package.json b/package.json index 1b2dc9efcf..d12661b5cc 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "snyk": "^1.290.1" }, "dependencies": { + "onscan.js": "^1.5.2" }, "scripts": { "snyk-protect": "snyk protect", diff --git a/yarn.lock b/yarn.lock index 97a063597d..e5a2da1e40 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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" From c1afc95e3cdcf59a8c91eaa8d303e0698f7a19d4 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Mon, 23 Nov 2020 17:56:54 +0530 Subject: [PATCH 16/18] chore: remove unused code --- erpnext/public/build.json | 10 - .../public/js/payment/payment_details.html | 11 - erpnext/public/js/payment/payments.js | 239 ------------- erpnext/public/js/payment/pos_payment.html | 42 --- erpnext/public/js/pos/clusterize.js | 330 ------------------ erpnext/public/js/pos/customer_toolbar.html | 16 - erpnext/public/js/pos/pos.html | 136 -------- erpnext/public/js/pos/pos_bill_item.html | 34 -- erpnext/public/js/pos/pos_bill_item_new.html | 9 - erpnext/public/js/pos/pos_invoice_list.html | 9 - erpnext/public/js/pos/pos_item.html | 32 -- erpnext/public/js/pos/pos_selected_item.html | 22 -- erpnext/public/js/pos/pos_tax_row.html | 4 - 13 files changed, 894 deletions(-) delete mode 100644 erpnext/public/js/payment/payment_details.html delete mode 100644 erpnext/public/js/payment/payments.js delete mode 100644 erpnext/public/js/payment/pos_payment.html delete mode 100644 erpnext/public/js/pos/clusterize.js delete mode 100644 erpnext/public/js/pos/customer_toolbar.html delete mode 100644 erpnext/public/js/pos/pos.html delete mode 100644 erpnext/public/js/pos/pos_bill_item.html delete mode 100644 erpnext/public/js/pos/pos_bill_item_new.html delete mode 100644 erpnext/public/js/pos/pos_invoice_list.html delete mode 100755 erpnext/public/js/pos/pos_item.html delete mode 100644 erpnext/public/js/pos/pos_selected_item.html delete mode 100644 erpnext/public/js/pos/pos_tax_row.html diff --git a/erpnext/public/build.json b/erpnext/public/build.json index b12045c4c8..ec15b88939 100644 --- a/erpnext/public/build.json +++ b/erpnext/public/build.json @@ -28,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", diff --git a/erpnext/public/js/payment/payment_details.html b/erpnext/public/js/payment/payment_details.html deleted file mode 100644 index 3e6394483e..0000000000 --- a/erpnext/public/js/payment/payment_details.html +++ /dev/null @@ -1,11 +0,0 @@ -
-
{{mode_of_payment}}
-
-
- - - - -
-
-
\ No newline at end of file diff --git a/erpnext/public/js/payment/payments.js b/erpnext/public/js/payment/payments.js deleted file mode 100644 index 0d656bc1fb..0000000000 --- a/erpnext/public/js/payment/payments.js +++ /dev/null @@ -1,239 +0,0 @@ -// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -// License: GNU General Public License v3. See license.txt - -erpnext.payments = erpnext.stock.StockController.extend({ - make_payment: function() { - var me = this; - - this.dialog = new frappe.ui.Dialog({ - title: 'Payment' - }); - - this.dialog.show(); - this.$body = this.dialog.body; - this.set_payment_primary_action(); - this.make_keyboard(); - this.select_text() - }, - - select_text: function(){ - var me = this; - $(this.$body).find('.form-control').click(function(){ - $(this).select(); - }) - }, - - set_payment_primary_action: function(){ - var me = this; - - this.dialog.set_primary_action(__("Submit"), function() { - // Allow no ZERO payment - $.each(me.frm.doc.payments, function (index, data) { - if (data.amount != 0) { - me.dialog.hide(); - me.submit_invoice(); - return; - } - }); - }) - }, - - make_keyboard: function(){ - var me = this; - $(this.$body).empty(); - $(this.$body).html(frappe.render_template('pos_payment', this.frm.doc)) - this.show_payment_details(); - this.bind_keyboard_event() - this.clear_amount() - }, - - make_multimode_payment: function(){ - var me = this; - - if(this.frm.doc.change_amount > 0){ - me.payment_val = me.doc.outstanding_amount - } - - this.payments = frappe.model.add_child(this.frm.doc, 'Multi Mode Payment', "payments"); - this.payments.mode_of_payment = this.dialog.fields_dict.mode_of_payment.get_value(); - this.payments.amount = flt(this.payment_val); - }, - - show_payment_details: function(){ - var me = this; - var multimode_payments = $(this.$body).find('.multimode-payments').empty(); - if(this.frm.doc.payments.length){ - $.each(this.frm.doc.payments, function(index, data){ - $(frappe.render_template('payment_details', { - mode_of_payment: data.mode_of_payment, - amount: data.amount, - idx: data.idx, - currency: me.frm.doc.currency, - type: data.type - })).appendTo(multimode_payments) - - if (data.type == 'Cash' && data.amount == me.frm.doc.paid_amount) { - me.idx = data.idx; - me.selected_mode = $(me.$body).find(repl("input[idx='%(idx)s']",{'idx': me.idx})); - me.highlight_selected_row(); - me.bind_amount_change_event(); - } - }) - }else{ - $("

No payment mode selected in pos profile

").appendTo(multimode_payments) - } - }, - - set_outstanding_amount: function(){ - this.selected_mode = $(this.$body).find(repl("input[idx='%(idx)s']",{'idx': this.idx})); - this.highlight_selected_row() - this.payment_val = 0.0 - if(this.frm.doc.outstanding_amount > 0 && flt(this.selected_mode.val()) == 0.0){ - //When user first time click on row - this.payment_val = flt(this.frm.doc.outstanding_amount / this.frm.doc.conversion_rate, precision("outstanding_amount")) - this.selected_mode.val(format_currency(this.payment_val, this.frm.doc.currency)); - this.update_payment_amount() - }else if(flt(this.selected_mode.val()) > 0){ - //If user click on existing row which has value - this.payment_val = flt(this.selected_mode.val()); - } - this.selected_mode.select() - this.bind_amount_change_event(); - }, - - bind_keyboard_event: function(){ - var me = this; - this.payment_val = ''; - this.bind_form_control_event(); - this.bind_numeric_keys_event(); - }, - - bind_form_control_event: function(){ - var me = this; - $(this.$body).find('.pos-payment-row').click(function(){ - me.idx = $(this).attr("idx"); - me.set_outstanding_amount() - }) - - $(this.$body).find('.form-control').click(function(){ - me.idx = $(this).attr("idx"); - me.set_outstanding_amount(); - me.update_paid_amount(true); - }) - - $(this.$body).find('.write_off_amount').change(function(){ - me.write_off_amount(flt($(this).val()), precision("write_off_amount")); - }) - - $(this.$body).find('.change_amount').change(function(){ - me.change_amount(flt($(this).val()), precision("change_amount")); - }) - }, - - highlight_selected_row: function(){ - var me = this; - var selected_row = $(this.$body).find(repl(".pos-payment-row[idx='%(idx)s']",{'idx': this.idx})); - $(this.$body).find('.pos-payment-row').removeClass('selected-payment-mode') - selected_row.addClass('selected-payment-mode') - $(this.$body).find('.amount').attr('disabled', true); - this.selected_mode.attr('disabled', false); - }, - - bind_numeric_keys_event: function(){ - var me = this; - $(this.$body).find('.pos-keyboard-key').click(function(){ - me.payment_val += $(this).text(); - me.selected_mode.val(format_currency(me.payment_val, me.frm.doc.currency)) - me.idx = me.selected_mode.attr("idx") - me.update_paid_amount() - }) - - $(this.$body).find('.delete-btn').click(function(){ - me.payment_val = cstr(flt(me.selected_mode.val())).slice(0, -1); - me.selected_mode.val(format_currency(me.payment_val, me.frm.doc.currency)); - me.idx = me.selected_mode.attr("idx") - me.update_paid_amount(); - }) - - }, - - bind_amount_change_event: function(){ - var me = this; - this.selected_mode.change(function(){ - me.payment_val = flt($(this).val()) || 0.0; - me.selected_mode.val(format_currency(me.payment_val, me.frm.doc.currency)) - me.idx = me.selected_mode.attr("idx") - me.update_payment_amount() - }) - }, - - clear_amount: function() { - var me = this; - $(this.$body).find('.clr').click(function(e){ - e.stopPropagation(); - me.idx = $(this).attr("idx"); - me.selected_mode = $(me.$body).find(repl("input[idx='%(idx)s']",{'idx': me.idx})); - me.payment_val = 0.0; - me.selected_mode.val(0.0); - me.highlight_selected_row(); - me.update_payment_amount(); - }) - }, - - write_off_amount: function(write_off_amount) { - var me = this; - - this.frm.doc.write_off_amount = flt(write_off_amount, precision("write_off_amount")); - this.frm.doc.base_write_off_amount = flt(this.frm.doc.write_off_amount * this.frm.doc.conversion_rate, - precision("base_write_off_amount")); - this.calculate_outstanding_amount(false) - this.show_amounts() - }, - - change_amount: function(change_amount) { - var me = this; - - this.frm.doc.change_amount = flt(change_amount, precision("change_amount")); - this.calculate_write_off_amount() - this.show_amounts() - }, - - update_paid_amount: function(update_write_off) { - var me = this; - if(in_list(['change_amount', 'write_off_amount'], this.idx)){ - var value = me.selected_mode.val(); - if(me.idx == 'change_amount'){ - me.change_amount(value) - } else{ - if(flt(value) == 0 && update_write_off && me.frm.doc.outstanding_amount > 0) { - value = flt(me.frm.doc.outstanding_amount / me.frm.doc.conversion_rate, precision(me.idx)); - } - me.write_off_amount(value) - } - }else{ - this.update_payment_amount() - } - }, - - update_payment_amount: function(){ - var me = this; - - $.each(this.frm.doc.payments, function(index, data){ - if(cint(me.idx) == cint(data.idx)){ - data.amount = flt(me.selected_mode.val(), 2) - } - }) - - this.calculate_outstanding_amount(false); - this.show_amounts(); - }, - - show_amounts: function(){ - var me = this; - $(this.$body).find(".write_off_amount").val(format_currency(this.frm.doc.write_off_amount, this.frm.doc.currency)); - $(this.$body).find('.paid_amount').text(format_currency(this.frm.doc.paid_amount, this.frm.doc.currency)); - $(this.$body).find('.change_amount').val(format_currency(this.frm.doc.change_amount, this.frm.doc.currency)) - $(this.$body).find('.outstanding_amount').text(format_currency(this.frm.doc.outstanding_amount, frappe.get_doc(":Company", this.frm.doc.company).default_currency)) - this.update_invoice(); - } -}) \ No newline at end of file diff --git a/erpnext/public/js/payment/pos_payment.html b/erpnext/public/js/payment/pos_payment.html deleted file mode 100644 index cb6971b46b..0000000000 --- a/erpnext/public/js/payment/pos_payment.html +++ /dev/null @@ -1,42 +0,0 @@ -
-
-

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

-
-
-
-

{{ __("Paid") }}

-
-
-

{{ __("Outstanding") }}

{%= format_currency(outstanding_amount, currency) %}

-
-
-

{{ __("Change") }} -

-
-
-

{{ __("Write off") }} -

-
-
-
-
-
-
-
-
-
- {% for(var i=0; i<3; i++) { %} -
- {% for(var j=i*3; j<(i+1)*3; j++) { %} - - {% } %} -
- {% } %} -
- - - -
-
-
-
diff --git a/erpnext/public/js/pos/clusterize.js b/erpnext/public/js/pos/clusterize.js deleted file mode 100644 index 075c9ca4ae..0000000000 --- a/erpnext/public/js/pos/clusterize.js +++ /dev/null @@ -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 = '', - 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 = '' + data + '
'; - 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; -})); \ No newline at end of file diff --git a/erpnext/public/js/pos/customer_toolbar.html b/erpnext/public/js/pos/customer_toolbar.html deleted file mode 100644 index 3ba5ccbc67..0000000000 --- a/erpnext/public/js/pos/customer_toolbar.html +++ /dev/null @@ -1,16 +0,0 @@ -
-
- - - - - -
- - {% if (allow_delete) { %} - {% } %} -
\ No newline at end of file diff --git a/erpnext/public/js/pos/pos.html b/erpnext/public/js/pos/pos.html deleted file mode 100644 index 89e2940c89..0000000000 --- a/erpnext/public/js/pos/pos.html +++ /dev/null @@ -1,136 +0,0 @@ -
-
-
-
{{ __("Item Cart") }}
-
-
-
- - - {{ __("Item Name")}} - - {{ __("Quantity") }} - {{ __("Discount") }} - {{ __("Rate") }} -
-
-
- - -

{{ __("Tap items to add them here") }}

-
-
-
-
-
-
-
-
-
-
-
{%= __("Net Total") %}
-
-
-
-
-
{%= __("Taxes") %}
-
-
-
- {% if(allow_user_to_edit_discount) { %} -
-
-
{%= __("Discount") %}
-
-
- % - -
-
- {%= get_currency_symbol(currency) %} - -
-
-
- {% } %} -
-
- - - -
-
{%= __("Grand Total") %}
-
-
-
-
- - - -
-
{%= __("Qty Total") %}
-
-
-
-
-
- -
- -
-
-
-
{{ __("Customers in Queue") }}
-
-
{{ __("Customer") }}
-
{{ __("Status") }}
-
{{ __("Amount") }}
-
{{ __("Grand Total") }}
-
-
-
- - -

{{ __("No Customers yet!") }}

-
-
-
-
-
-
{{ __("Stock Items") }}
-
- -
- -
-
- -
-
-
- -
-
-
-
diff --git a/erpnext/public/js/pos/pos_bill_item.html b/erpnext/public/js/pos/pos_bill_item.html deleted file mode 100644 index 21868a6cae..0000000000 --- a/erpnext/public/js/pos/pos_bill_item.html +++ /dev/null @@ -1,34 +0,0 @@ -
-
{%= item_code || "" %}{%= __(item_name) || "" %}
-
-
-
-
-
- -
- {% if(actual_qty != null) { %} -
- {%= __("In Stock: ") %} {%= actual_qty || 0.0 %} -
- {% } %} -
-
-
-
-
-
- -
-
-
-
- {% if(enabled) { %} - - {% } else { %} -
{%= format_currency(rate) %}
- {% } %} -
-

{%= amount %}

-
-
diff --git a/erpnext/public/js/pos/pos_bill_item_new.html b/erpnext/public/js/pos/pos_bill_item_new.html deleted file mode 100644 index cb626cefce..0000000000 --- a/erpnext/public/js/pos/pos_bill_item_new.html +++ /dev/null @@ -1,9 +0,0 @@ -
- -
{%= qty %}
-
{%= discount_percentage %}
-
{%= format_currency(rate) %}
-
diff --git a/erpnext/public/js/pos/pos_invoice_list.html b/erpnext/public/js/pos/pos_invoice_list.html deleted file mode 100644 index 13aa52055a..0000000000 --- a/erpnext/public/js/pos/pos_invoice_list.html +++ /dev/null @@ -1,9 +0,0 @@ -
- -
{{ data.status }}
-
{%= paid_amount %}
-
{%= grand_total %}
-
diff --git a/erpnext/public/js/pos/pos_item.html b/erpnext/public/js/pos/pos_item.html deleted file mode 100755 index 52f3cf698a..0000000000 --- a/erpnext/public/js/pos/pos_item.html +++ /dev/null @@ -1,32 +0,0 @@ - \ No newline at end of file diff --git a/erpnext/public/js/pos/pos_selected_item.html b/erpnext/public/js/pos/pos_selected_item.html deleted file mode 100644 index 03c73411a4..0000000000 --- a/erpnext/public/js/pos/pos_selected_item.html +++ /dev/null @@ -1,22 +0,0 @@ -
-
-
{{ __("Quantity") }}:
- -
-
-
{{ __("Price List Rate") }}:
- -
-
-
{{ __("Discount") }}: %
- -
-
-
{{ __("Price") }}:
- -
-
-
{{ __("Amount") }}:
- -
-
\ No newline at end of file diff --git a/erpnext/public/js/pos/pos_tax_row.html b/erpnext/public/js/pos/pos_tax_row.html deleted file mode 100644 index 3752a89bbd..0000000000 --- a/erpnext/public/js/pos/pos_tax_row.html +++ /dev/null @@ -1,4 +0,0 @@ -
-
{%= description %}
-
{%= tax_amount %}
-
From 7f1d7894c204b3dcadca47453c4556af2a72b86f Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Tue, 24 Nov 2020 14:04:49 +0530 Subject: [PATCH 17/18] fix: cannot find erpnext.payments --- erpnext/public/js/payment/payments.js | 239 ++++++++++++++++++++++++++ 1 file changed, 239 insertions(+) create mode 100644 erpnext/public/js/payment/payments.js diff --git a/erpnext/public/js/payment/payments.js b/erpnext/public/js/payment/payments.js new file mode 100644 index 0000000000..0d656bc1fb --- /dev/null +++ b/erpnext/public/js/payment/payments.js @@ -0,0 +1,239 @@ +// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +// License: GNU General Public License v3. See license.txt + +erpnext.payments = erpnext.stock.StockController.extend({ + make_payment: function() { + var me = this; + + this.dialog = new frappe.ui.Dialog({ + title: 'Payment' + }); + + this.dialog.show(); + this.$body = this.dialog.body; + this.set_payment_primary_action(); + this.make_keyboard(); + this.select_text() + }, + + select_text: function(){ + var me = this; + $(this.$body).find('.form-control').click(function(){ + $(this).select(); + }) + }, + + set_payment_primary_action: function(){ + var me = this; + + this.dialog.set_primary_action(__("Submit"), function() { + // Allow no ZERO payment + $.each(me.frm.doc.payments, function (index, data) { + if (data.amount != 0) { + me.dialog.hide(); + me.submit_invoice(); + return; + } + }); + }) + }, + + make_keyboard: function(){ + var me = this; + $(this.$body).empty(); + $(this.$body).html(frappe.render_template('pos_payment', this.frm.doc)) + this.show_payment_details(); + this.bind_keyboard_event() + this.clear_amount() + }, + + make_multimode_payment: function(){ + var me = this; + + if(this.frm.doc.change_amount > 0){ + me.payment_val = me.doc.outstanding_amount + } + + this.payments = frappe.model.add_child(this.frm.doc, 'Multi Mode Payment', "payments"); + this.payments.mode_of_payment = this.dialog.fields_dict.mode_of_payment.get_value(); + this.payments.amount = flt(this.payment_val); + }, + + show_payment_details: function(){ + var me = this; + var multimode_payments = $(this.$body).find('.multimode-payments').empty(); + if(this.frm.doc.payments.length){ + $.each(this.frm.doc.payments, function(index, data){ + $(frappe.render_template('payment_details', { + mode_of_payment: data.mode_of_payment, + amount: data.amount, + idx: data.idx, + currency: me.frm.doc.currency, + type: data.type + })).appendTo(multimode_payments) + + if (data.type == 'Cash' && data.amount == me.frm.doc.paid_amount) { + me.idx = data.idx; + me.selected_mode = $(me.$body).find(repl("input[idx='%(idx)s']",{'idx': me.idx})); + me.highlight_selected_row(); + me.bind_amount_change_event(); + } + }) + }else{ + $("

No payment mode selected in pos profile

").appendTo(multimode_payments) + } + }, + + set_outstanding_amount: function(){ + this.selected_mode = $(this.$body).find(repl("input[idx='%(idx)s']",{'idx': this.idx})); + this.highlight_selected_row() + this.payment_val = 0.0 + if(this.frm.doc.outstanding_amount > 0 && flt(this.selected_mode.val()) == 0.0){ + //When user first time click on row + this.payment_val = flt(this.frm.doc.outstanding_amount / this.frm.doc.conversion_rate, precision("outstanding_amount")) + this.selected_mode.val(format_currency(this.payment_val, this.frm.doc.currency)); + this.update_payment_amount() + }else if(flt(this.selected_mode.val()) > 0){ + //If user click on existing row which has value + this.payment_val = flt(this.selected_mode.val()); + } + this.selected_mode.select() + this.bind_amount_change_event(); + }, + + bind_keyboard_event: function(){ + var me = this; + this.payment_val = ''; + this.bind_form_control_event(); + this.bind_numeric_keys_event(); + }, + + bind_form_control_event: function(){ + var me = this; + $(this.$body).find('.pos-payment-row').click(function(){ + me.idx = $(this).attr("idx"); + me.set_outstanding_amount() + }) + + $(this.$body).find('.form-control').click(function(){ + me.idx = $(this).attr("idx"); + me.set_outstanding_amount(); + me.update_paid_amount(true); + }) + + $(this.$body).find('.write_off_amount').change(function(){ + me.write_off_amount(flt($(this).val()), precision("write_off_amount")); + }) + + $(this.$body).find('.change_amount').change(function(){ + me.change_amount(flt($(this).val()), precision("change_amount")); + }) + }, + + highlight_selected_row: function(){ + var me = this; + var selected_row = $(this.$body).find(repl(".pos-payment-row[idx='%(idx)s']",{'idx': this.idx})); + $(this.$body).find('.pos-payment-row').removeClass('selected-payment-mode') + selected_row.addClass('selected-payment-mode') + $(this.$body).find('.amount').attr('disabled', true); + this.selected_mode.attr('disabled', false); + }, + + bind_numeric_keys_event: function(){ + var me = this; + $(this.$body).find('.pos-keyboard-key').click(function(){ + me.payment_val += $(this).text(); + me.selected_mode.val(format_currency(me.payment_val, me.frm.doc.currency)) + me.idx = me.selected_mode.attr("idx") + me.update_paid_amount() + }) + + $(this.$body).find('.delete-btn').click(function(){ + me.payment_val = cstr(flt(me.selected_mode.val())).slice(0, -1); + me.selected_mode.val(format_currency(me.payment_val, me.frm.doc.currency)); + me.idx = me.selected_mode.attr("idx") + me.update_paid_amount(); + }) + + }, + + bind_amount_change_event: function(){ + var me = this; + this.selected_mode.change(function(){ + me.payment_val = flt($(this).val()) || 0.0; + me.selected_mode.val(format_currency(me.payment_val, me.frm.doc.currency)) + me.idx = me.selected_mode.attr("idx") + me.update_payment_amount() + }) + }, + + clear_amount: function() { + var me = this; + $(this.$body).find('.clr').click(function(e){ + e.stopPropagation(); + me.idx = $(this).attr("idx"); + me.selected_mode = $(me.$body).find(repl("input[idx='%(idx)s']",{'idx': me.idx})); + me.payment_val = 0.0; + me.selected_mode.val(0.0); + me.highlight_selected_row(); + me.update_payment_amount(); + }) + }, + + write_off_amount: function(write_off_amount) { + var me = this; + + this.frm.doc.write_off_amount = flt(write_off_amount, precision("write_off_amount")); + this.frm.doc.base_write_off_amount = flt(this.frm.doc.write_off_amount * this.frm.doc.conversion_rate, + precision("base_write_off_amount")); + this.calculate_outstanding_amount(false) + this.show_amounts() + }, + + change_amount: function(change_amount) { + var me = this; + + this.frm.doc.change_amount = flt(change_amount, precision("change_amount")); + this.calculate_write_off_amount() + this.show_amounts() + }, + + update_paid_amount: function(update_write_off) { + var me = this; + if(in_list(['change_amount', 'write_off_amount'], this.idx)){ + var value = me.selected_mode.val(); + if(me.idx == 'change_amount'){ + me.change_amount(value) + } else{ + if(flt(value) == 0 && update_write_off && me.frm.doc.outstanding_amount > 0) { + value = flt(me.frm.doc.outstanding_amount / me.frm.doc.conversion_rate, precision(me.idx)); + } + me.write_off_amount(value) + } + }else{ + this.update_payment_amount() + } + }, + + update_payment_amount: function(){ + var me = this; + + $.each(this.frm.doc.payments, function(index, data){ + if(cint(me.idx) == cint(data.idx)){ + data.amount = flt(me.selected_mode.val(), 2) + } + }) + + this.calculate_outstanding_amount(false); + this.show_amounts(); + }, + + show_amounts: function(){ + var me = this; + $(this.$body).find(".write_off_amount").val(format_currency(this.frm.doc.write_off_amount, this.frm.doc.currency)); + $(this.$body).find('.paid_amount').text(format_currency(this.frm.doc.paid_amount, this.frm.doc.currency)); + $(this.$body).find('.change_amount').val(format_currency(this.frm.doc.change_amount, this.frm.doc.currency)) + $(this.$body).find('.outstanding_amount').text(format_currency(this.frm.doc.outstanding_amount, frappe.get_doc(":Company", this.frm.doc.company).default_currency)) + this.update_invoice(); + } +}) \ No newline at end of file From ac164c66ed3494368fcb074a5ec472b661914065 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Tue, 24 Nov 2020 16:09:11 +0530 Subject: [PATCH 18/18] perf: get recent transaction only when component is visible --- erpnext/selling/page/point_of_sale/pos_past_order_list.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/selling/page/point_of_sale/pos_past_order_list.js b/erpnext/selling/page/point_of_sale/pos_past_order_list.js index 166d9cf0ce..ec392313f5 100644 --- a/erpnext/selling/page/point_of_sale/pos_past_order_list.js +++ b/erpnext/selling/page/point_of_sale/pos_past_order_list.js @@ -62,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'),