diff --git a/inn/hooks.py b/inn/hooks.py index aa1315d..38cab28 100644 --- a/inn/hooks.py +++ b/inn/hooks.py @@ -1,7 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals -from . import __version__ as app_version - app_name = "inn" app_title = "Inn Hotels" app_publisher = "Core Initiative" @@ -15,11 +11,11 @@ # ------------------ # include js, css files in header of desk.html -# app_include_css = "/assets/inn/css/inn.css" +app_include_css = "inn.bundle.css" # app_include_js = "/assets/inn/js/inn.js" # include js, css files in header of web template -# web_include_css = "/assets/inn/css/inn.css" +web_include_css = "inn.bundle.css" # web_include_js = "/assets/inn/js/inn.js" # include js in page @@ -135,6 +131,9 @@ jinja = { "methods": [ "inn.inn_hotels.doctype.inn_reservation.inn_reservation.get_total_deposit", - "inn.inn_hotels.doctype.inn_reservation.inn_reservation.get_date" + "inn.inn_hotels.doctype.inn_reservation.inn_reservation.get_date", + + "inn.inn_hotels.doctype.inn_pos_usage.inn_pos_usage.print_list_order", + "inn.inn_hotels.page.pos_extended.pos_extended.get_table_number" ] } \ No newline at end of file diff --git a/inn/inn_hotels/doctype/inn_point_of_sale_table/__init__.py b/inn/inn_hotels/doctype/inn_point_of_sale_table/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/inn/inn_hotels/doctype/inn_point_of_sale_table/inn_point_of_sale_table.js b/inn/inn_hotels/doctype/inn_point_of_sale_table/inn_point_of_sale_table.js new file mode 100644 index 0000000..c1f581f --- /dev/null +++ b/inn/inn_hotels/doctype/inn_point_of_sale_table/inn_point_of_sale_table.js @@ -0,0 +1,8 @@ +// Copyright (c) 2024, Core Initiative and contributors +// For license information, please see license.txt + +// frappe.ui.form.on("Inn Point Of Sale Table", { +// refresh(frm) { + +// }, +// }); diff --git a/inn/inn_hotels/doctype/inn_point_of_sale_table/inn_point_of_sale_table.json b/inn/inn_hotels/doctype/inn_point_of_sale_table/inn_point_of_sale_table.json new file mode 100644 index 0000000..b18dbf4 --- /dev/null +++ b/inn/inn_hotels/doctype/inn_point_of_sale_table/inn_point_of_sale_table.json @@ -0,0 +1,52 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "field:table_name", + "creation": "2024-02-05 14:06:01.644083", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "table_name", + "status" + ], + "fields": [ + { + "fieldname": "table_name", + "fieldtype": "Data", + "label": "Table Name", + "unique": 1 + }, + { + "fieldname": "status", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Status", + "options": "Empty\nReserved\nOccupied" + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2024-02-05 14:09:01.664573", + "modified_by": "Administrator", + "module": "Inn Hotels", + "name": "Inn Point Of Sale Table", + "naming_rule": "By fieldname", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/inn/inn_hotels/doctype/inn_point_of_sale_table/inn_point_of_sale_table.py b/inn/inn_hotels/doctype/inn_point_of_sale_table/inn_point_of_sale_table.py new file mode 100644 index 0000000..f4eddff --- /dev/null +++ b/inn/inn_hotels/doctype/inn_point_of_sale_table/inn_point_of_sale_table.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, Core Initiative and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class InnPointOfSaleTable(Document): + pass diff --git a/inn/inn_hotels/doctype/inn_point_of_sale_table/test_inn_point_of_sale_table.py b/inn/inn_hotels/doctype/inn_point_of_sale_table/test_inn_point_of_sale_table.py new file mode 100644 index 0000000..4132bec --- /dev/null +++ b/inn/inn_hotels/doctype/inn_point_of_sale_table/test_inn_point_of_sale_table.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, Core Initiative and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestInnPointOfSaleTable(FrappeTestCase): + pass diff --git a/inn/inn_hotels/doctype/inn_pos_usage/__init__.py b/inn/inn_hotels/doctype/inn_pos_usage/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/inn/inn_hotels/doctype/inn_pos_usage/inn_pos_usage.js b/inn/inn_hotels/doctype/inn_pos_usage/inn_pos_usage.js new file mode 100644 index 0000000..5b693f1 --- /dev/null +++ b/inn/inn_hotels/doctype/inn_pos_usage/inn_pos_usage.js @@ -0,0 +1,8 @@ +// Copyright (c) 2024, Core Initiative and contributors +// For license information, please see license.txt + +// frappe.ui.form.on("Inn POS Usage", { +// refresh(frm) { + +// }, +// }); diff --git a/inn/inn_hotels/doctype/inn_pos_usage/inn_pos_usage.json b/inn/inn_hotels/doctype/inn_pos_usage/inn_pos_usage.json new file mode 100644 index 0000000..3074a8d --- /dev/null +++ b/inn/inn_hotels/doctype/inn_pos_usage/inn_pos_usage.json @@ -0,0 +1,76 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2024-02-12 11:06:40.509746", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "table", + "pos_invoice", + "print_status", + "processed_item", + "new_item" + ], + "fields": [ + { + "fieldname": "table", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Table", + "options": "Inn Point Of Sale Table", + "reqd": 1 + }, + { + "fieldname": "pos_invoice", + "fieldtype": "Link", + "in_list_view": 1, + "label": "POS Invoice", + "options": "POS Invoice", + "reqd": 1 + }, + { + "default": "0", + "fieldname": "print_status", + "fieldtype": "Int", + "hidden": 1, + "label": "Print Status", + "reqd": 1 + }, + { + "fieldname": "processed_item", + "fieldtype": "Table", + "label": "Processed Item", + "options": "Inn POS Usage Item" + }, + { + "fieldname": "new_item", + "fieldtype": "Table", + "label": "New Item", + "options": "Inn POS Usage Item" + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2024-02-13 12:38:30.114825", + "modified_by": "Administrator", + "module": "Inn Hotels", + "name": "Inn POS Usage", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/inn/inn_hotels/doctype/inn_pos_usage/inn_pos_usage.py b/inn/inn_hotels/doctype/inn_pos_usage/inn_pos_usage.py new file mode 100644 index 0000000..3ab43f0 --- /dev/null +++ b/inn/inn_hotels/doctype/inn_pos_usage/inn_pos_usage.py @@ -0,0 +1,28 @@ +# Copyright (c) 2024, Core Initiative and contributors +# For license information, please see license.txt + +import frappe +from frappe.model.document import Document + + +class InnPOSUsage(Document): + pass + + +@frappe.whitelist() +def print_list_order(pos_invoice): + order_dict = frappe.get_last_doc('Inn POS Usage', filters={'pos_invoice': pos_invoice}) + res = { + "table": order_dict.table, + "items": order_dict.new_item + } + return res + +@frappe.whitelist() +def get_table_order(pos_invoice): + order_dict = frappe.get_value('POS Invoice', filters={'pos_invoice': pos_invoice}, fields=['new_item']) + order_list = [] + for item in order_dict: + order_list.append(item.item) + + return order_list \ No newline at end of file diff --git a/inn/inn_hotels/doctype/inn_pos_usage/test_inn_pos_usage.py b/inn/inn_hotels/doctype/inn_pos_usage/test_inn_pos_usage.py new file mode 100644 index 0000000..7584f4f --- /dev/null +++ b/inn/inn_hotels/doctype/inn_pos_usage/test_inn_pos_usage.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, Core Initiative and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestInnPOSUsage(FrappeTestCase): + pass diff --git a/inn/inn_hotels/doctype/inn_pos_usage_item/__init__.py b/inn/inn_hotels/doctype/inn_pos_usage_item/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/inn/inn_hotels/doctype/inn_pos_usage_item/inn_pos_usage_item.json b/inn/inn_hotels/doctype/inn_pos_usage_item/inn_pos_usage_item.json new file mode 100644 index 0000000..0949ed3 --- /dev/null +++ b/inn/inn_hotels/doctype/inn_pos_usage_item/inn_pos_usage_item.json @@ -0,0 +1,40 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2024-02-13 12:37:13.404550", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "item_name", + "quantity" + ], + "fields": [ + { + "fieldname": "item_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Item Name", + "reqd": 1 + }, + { + "fieldname": "quantity", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Quantity", + "reqd": 1 + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2024-02-13 12:38:42.267641", + "modified_by": "Administrator", + "module": "Inn Hotels", + "name": "Inn POS Usage Item", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/inn/inn_hotels/doctype/inn_pos_usage_item/inn_pos_usage_item.py b/inn/inn_hotels/doctype/inn_pos_usage_item/inn_pos_usage_item.py new file mode 100644 index 0000000..ec9d8a5 --- /dev/null +++ b/inn/inn_hotels/doctype/inn_pos_usage_item/inn_pos_usage_item.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, Core Initiative and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class InnPOSUsageItem(Document): + pass diff --git a/inn/inn_hotels/doctype/inn_room_booking/inn_room_card_owner.py b/inn/inn_hotels/doctype/inn_room_booking/inn_room_card_owner.py new file mode 100644 index 0000000..96f0eb7 --- /dev/null +++ b/inn/inn_hotels/doctype/inn_room_booking/inn_room_card_owner.py @@ -0,0 +1,221 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Core Initiative and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.model.document import Document +from dateutil.parser import parse + +@frappe.whitelist() +def count_all_room(start_date, end_date): + default_availability = frappe.db.count("Inn Room") + try: + start_date = parse(start_date, False).date() + except ValueError: + raise frappe.ValidationError("{start_date} is not a valid date string") + + try: + end_date = parse(end_date, False).date() + except ValueError: + raise frappe.ValidationError("{end_date} is not a valid date string") + + delta = end_date - start_date + if delta.days < 0: + raise frappe.ValidationError("start date must before end date") + + return default_availability * delta.days + +@frappe.whitelist() +def count_sold_room(start_date, end_date): + try: + start_date = parse(start_date, False).date() + except ValueError: + raise frappe.ValidationError("{start_date} is not a valid date string") + + try: + end_date = parse(end_date, False).date() + except ValueError: + raise frappe.ValidationError("{end_date} is not a valid date string") + + delta = end_date - start_date + if delta.days < 0: + raise frappe.ValidationError("start date must before end date") + + + total_sold = 0 + # calculate reservation start before start_date and reservation end after start date + current_sold = frappe.db.get_values(doctype="Inn Room Booking", filters={"start": ["<", start_date], "end": [">", start_date], "room_availability": "Room Sold"}, fieldname=["start", "end"], as_dict=True) + for ii in current_sold: + room_end_date = ii.end + if room_end_date > end_date: + room_end_date = end_date + + days_sold = (room_end_date - start_date).days + total_sold += days_sold + + + # calculate reservation start after start_date and reservations start before before_date + current_sold = frappe.db.get_values(doctype="Inn Room Booking", filters=[["start", "between", [start_date, end_date]], ["room_availability", "=", "Room Sold"]], fieldname=["start", "end"], as_dict=True) + for ii in current_sold: + room_start_date = ii.start + room_end_date = ii.end + if room_end_date > end_date: + room_end_date = end_date + + days_sold = (room_end_date - room_start_date).days + total_sold += days_sold + + return total_sold + +@frappe.whitelist() +def count_available_room(start_date, end_date): + + default_availability = frappe.db.count("Inn Room") + try: + start_date = parse(start_date, False).date() + except ValueError: + raise frappe.ValidationError("{start_date} is not a valid date string") + + try: + end_date = parse(end_date, False).date() + except ValueError: + raise frappe.ValidationError("{end_date} is not a valid date string") + + delta = end_date - start_date + if delta.days < 0: + raise frappe.ValidationError("start date must before end date") + + + all_room = default_availability * delta.days + + total_sold = 0 + # calculate reservation start before start_date and reservation end after start date + current_used = frappe.db.get_values(doctype="Inn Room Booking", filters={"start": ["<", start_date], "end": [">", start_date]}, fieldname=["start", "end"], as_dict=True) + for ii in current_used: + room_end_date = ii.end + if room_end_date > end_date: + room_end_date = end_date + + days_sold = (room_end_date - start_date).days + total_sold += days_sold + + + # calculate reservation start after start_date and reservations start before before_date + current_used = frappe.db.get_values(doctype="Inn Room Booking", filters=[["start", "between", [start_date, end_date]]], fieldname=["start", "end"], as_dict=True) + for ii in current_used: + room_start_date = ii.start + room_end_date = ii.end + if room_end_date > end_date: + room_end_date = end_date + + days_sold = (room_end_date - room_start_date).days + total_sold += days_sold + + return all_room - total_sold + +@frappe.whitelist() +def count_ooo_room(start_date, end_date): + try: + start_date = parse(start_date, False) + except ValueError: + raise frappe.ValidationError("{start_date} is not a valid date string") + + try: + end_date = parse(end_date, False) + except ValueError: + raise frappe.ValidationError("{end_date} is not a valid date string") + + delta = end_date - start_date + if delta.days < 0: + raise frappe.ValidationError("start date must before end date") + + + total_sold = 0 + # calculate reservation start before start_date and reservation end after start date + current_ooo = frappe.db.get_values(doctype="Inn Room Booking", filters={"start": ["<", start_date], "end": [">", start_date], "room_availability": "Out of Order"}, fieldname=["start", "end"], as_dict=True) + for ii in current_ooo: + room_end_date = ii.end + if room_end_date > end_date: + room_end_date = end_date + + days_sold = (room_end_date - start_date).days + total_sold += days_sold + + + # calculate reservation start after start_date and reservations start before before_date + current_ooo = frappe.db.get_values(doctype="Inn Room Booking", filters=[["start", "between", [start_date, end_date]], ["room_availability", "=", "Out of Order"]], fieldname=["start", "end"], as_dict=True) + for ii in current_ooo: + room_start_date = ii.start + room_end_date = ii.end + if room_end_date > end_date: + room_end_date = end_date + + days_sold = (room_end_date - room_start_date).days + total_sold += days_sold + + return total_sold + +def calculate_total_rate_and_sold(start_date, end_date): + try: + start_date = parse(start_date, False).date() + except ValueError: + raise frappe.ValidationError("{start_date} is not a valid date string") + + try: + end_date = parse(end_date, False).date() + except ValueError: + raise frappe.ValidationError("{end_date} is not a valid date string") + + delta = end_date - start_date + if delta.days < 0: + raise frappe.ValidationError("start date must before end date") + + total_sold = 0 + total_rate = 0 + cached_rate = {} + # calculate reservation start before start_date and reservation end after start date + current_sold = frappe.db.get_values(doctype="Inn Reservation", filters={"arrival": ["<", start_date], "expected_departure": [">", start_date]}, fieldname=["arrival", "expected_departure", "room_rate"], as_dict=True) + for ii in current_sold: + room_end_date = ii.expected_departure + if room_end_date > end_date: + room_end_date = end_date + + days_sold = (room_end_date - start_date).days + total_sold += days_sold + + if ii.room_rate not in cached_rate: + room_rate = frappe.db.get_value(doctype="Inn Room Rate", filters={"name": ii.room_rate}, fieldname="final_total_rate_amount") + cached_rate[ii.room_rate] = room_rate.final_total_rate_amount + + total_rate = cached_rate[ii.room_rate] * days_sold + + + # calculate reservation start after start_date and reservations start before before_date + current_sold = frappe.db.get_values(doctype="Inn Reservation", filters=[["expected_arrival", "between", [start_date, end_date]]], fieldname=["expected_arrival", "expected_departure", "room_rate"], as_dict=True) + for ii in current_sold: + room_start_date = ii.expected_arrival + room_end_date = ii.expected_departure + if room_end_date > end_date: + room_end_date = end_date + + days_sold = (room_end_date - room_start_date).days + total_sold += days_sold + + + if ii.room_rate not in cached_rate: + room_rate = frappe.db.get_value(doctype="Inn Room Rate", filters={"name": ii.room_rate}, fieldname="final_total_rate_amount") + cached_rate[ii.room_rate] = room_rate + total_rate += cached_rate[ii.room_rate] * days_sold + + return total_rate, total_sold + +@frappe.whitelist() +def calculate_average_rate(start_date, end_date): + total_rate, total_sold = calculate_total_rate_and_sold(start_date, end_date) + return total_rate / total_sold + +@frappe.whitelist() +def calculate_total_rate(start_date, end_date): + total_rate, _ = calculate_total_rate_and_sold(start_date, end_date) + return total_rate \ No newline at end of file diff --git a/inn/inn_hotels/page/__init__.py b/inn/inn_hotels/page/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/inn/inn_hotels/page/pos_extended/__init__.py b/inn/inn_hotels/page/pos_extended/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/inn/inn_hotels/page/pos_extended/pos_extended.js b/inn/inn_hotels/page/pos_extended/pos_extended.js new file mode 100644 index 0000000..e0e3213 --- /dev/null +++ b/inn/inn_hotels/page/pos_extended/pos_extended.js @@ -0,0 +1,291 @@ +frappe.provide('erpnext.PointOfSale') +frappe.provide('inn.PointOfSale') + +frappe.pages['pos-extended'].on_page_load = function (wrapper) { + var page = frappe.ui.make_app_page({ + parent: wrapper, + title: 'Point of Sales', + single_column: true + }); + + + var file = location.pathname.split("/").pop(); + + var link = document.createElement("link"); + link.href = file.substr(0, file.lastIndexOf(".")) + ".css"; + link.type = "text/css"; + link.rel = "stylesheet"; + link.media = "screen,print"; + + document.getElementsByTagName("head")[0].appendChild(link); + + frappe.require(["point-of-sale.bundle.js", "inn-pos.bundle.js"], function () { + inn.PointOfSale.Controller = class MyPosController extends erpnext.PointOfSale.Controller { + constructor(wrapper) { + super(wrapper); + } + init_payments() { + this.payment = new erpnext.PointOfSale.Payment({ + wrapper: this.$components_wrapper, + events: { + get_frm: () => this.frm || {}, + + get_customer_details: () => this.customer_details || {}, + + toggle_other_sections: (show) => { + if (show) { + this.item_details.$component.is(':visible') ? this.item_details.$component.css('display', 'none') : ''; + this.item_selector.toggle_component(false); + } else { + this.item_selector.toggle_component(true); + } + }, + + submit_invoice: () => { + this.frm.savesubmit() + .then((r) => { + this.toggle_components(false); + this.order_summary.toggle_component(true); + this.order_summary.load_summary_of(this.frm.doc, true); + + frappe.call({ + method: "inn.inn_hotels.page.pos_extended.pos_extended.clean_table_number", + args: { + invoice_name: this.frm.doc.name + } + }) + + frappe.show_alert({ + indicator: 'green', + message: __('POS invoice {0} created succesfully', [r.doc.name]) + }); + }); + } + } + }); + } + + save_draft_invoice() { + if (!this.$components_wrapper.is(":visible")) return; + + if (this.frm.doc.items.length == 0) { + frappe.show_alert({ + message: __("You must add atleast one item to save it as draft."), + indicator: 'red' + }); + frappe.utils.play_sound("error"); + return; + } + + if (!("table_number" in this.cart)) { + frappe.show_alert({ + message: __("You must assign table to this order."), + indicator: 'red' + }); + frappe.utils.play_sound("error"); + return; + } + + + this.frm.save(undefined, undefined, undefined, () => { + frappe.show_alert({ + message: __("There was an error saving the document."), + indicator: 'red' + }); + frappe.utils.play_sound("error"); + }).then(() => { + frappe.run_serially([ + () => frappe.call({ + method: "inn.inn_hotels.page.pos_extended.pos_extended.save_pos_usage", + args: { + invoice_name: this.frm.doc.name, + table: this.cart.table_number, + action: "save_draft" + } + }), + () => frappe.dom.freeze(), + () => this.make_new_invoice(), + () => frappe.dom.unfreeze(), + ]); + }); + } + + prepare_menu() { + this.page.add_menu_item(("Open Form View"), this.open_form_view.bind(this), false, 'Ctrl+F'); + this.page.add_menu_item(("Toggle Recent Orders"), this.toggle_recent_order.bind(this), false, 'Ctrl+O'); + this.page.add_menu_item(("Save as Draft"), this.save_draft_invoice.bind(this), false, 'Ctrl+S'); + this.page.add_menu_item(('Close the POS'), this.close_pos.bind(this), false, 'Shift+Ctrl+C'); + } + + init_item_cart() { + this.cart = new inn.PointOfSale.PosExtendItemCart({ + wrapper: this.$components_wrapper, + settings: this.settings, + events: { + get_frm: () => this.frm, + + cart_item_clicked: (item) => { + const item_row = this.get_item_from_frm(item); + this.item_details.toggle_item_details_section(item_row); + }, + + numpad_event: (value, action) => this.update_item_field(value, action), + + checkout: () => this.save_and_checkout(), + + edit_cart: () => this.payment.edit_cart(), + + customer_details_updated: (details) => { + this.customer_details = details; + // will add/remove LP payment method + this.payment.render_loyalty_points_payment_mode(); + }, + + print_captain_order: () => this.print_captain_order(), + print_table_order: () => this.print_table_order(), + } + }) + } + + async print_table_order() { + const me = this + let success = false + + await frappe.call({ + method: "inn.inn_hotels.page.pos_extended.pos_extended.save_pos_usage", + args: { + invoice_name: this.frm.doc.name, + table: this.cart.table_number, + action: "print_table" + }, + async: false, + callback: r => { + success = true + } + + }) + if (success) { + frappe.utils.print( + me.frm.doc.doctype, + me.frm.doc.name, + "POS Extended Table Order", + me.frm.doc.letter_head, + me.frm.doc.language || frappe.boot.lang + ); + } + } + + async print_captain_order() { + const me = this + let success = false + + if (!this.$components_wrapper.is(":visible")) return; + + if (this.frm.doc.items.length == 0) { + frappe.show_alert({ + message: __("You must add atleast one item to save it as draft."), + indicator: 'red' + }); + frappe.utils.play_sound("error"); + return; + } + + if (!("table_number" in this.cart)) { + frappe.show_alert({ + message: __("You must assign table to this order."), + indicator: 'red' + }); + frappe.utils.play_sound("error"); + return; + } + + await this.frm.save(undefined, undefined, undefined, () => { + frappe.show_alert({ + message: __("There was an error saving the document."), + indicator: 'red' + }); + frappe.utils.play_sound("error"); + }); + + await frappe.call({ + method: "inn.inn_hotels.page.pos_extended.pos_extended.save_pos_usage", + args: { + invoice_name: this.frm.doc.name, + table: this.cart.table_number, + action: "print_captain" + }, + async: false, + callback: r => { + success = true + } + }) + if (success) { + frappe.utils.print( + me.frm.doc.doctype, + me.frm.doc.name, + "POS Extended Captain Order", + me.frm.doc.letter_head, + me.frm.doc.language || frappe.boot.lang + ); + } + } + + print_bill(draft) { + console.log(draft) + frappe.utils.print( + draft.doctype, + draft.name, + "POS Extended Bill", + draft.letter_head, + draft.language || frappe.boot.lang + ); + } + + init_order_summary() { + this.order_summary = new inn.PointOfSale.PosExtendPastOrderSummary({ + wrapper: this.$components_wrapper, + events: { + get_frm: () => this.frm, + + process_return: (name) => { + this.recent_order_list.toggle_component(false); + frappe.db.get_doc('POS Invoice', name).then((doc) => { + frappe.run_serially([ + () => this.make_return_invoice(doc), + () => this.cart.load_invoice(), + () => this.item_selector.toggle_component(true) + ]); + }); + }, + edit_order: (name) => { + this.recent_order_list.toggle_component(false); + frappe.run_serially([ + () => this.frm.refresh(name), + () => this.frm.call('reset_mode_of_payments'), + () => this.cart.load_invoice(), + () => this.item_selector.toggle_component(true) + ]); + }, + delete_order: (name) => { + frappe.model.delete_doc(this.frm.doc.doctype, name, () => { + this.recent_order_list.refresh_list(); + }); + }, + new_order: () => { + frappe.run_serially([ + () => frappe.dom.freeze(), + () => this.make_new_invoice(), + () => this.item_selector.toggle_component(true), + () => frappe.dom.unfreeze(), + ]); + }, + print_bill: (doc) => this.print_bill(doc), + } + }) + } + }; + + wrapper.pos = new inn.PointOfSale.Controller(wrapper); + window.cur_pos = wrapper.pos; + }) +} \ No newline at end of file diff --git a/inn/inn_hotels/page/pos_extended/pos_extended.json b/inn/inn_hotels/page/pos_extended/pos_extended.json new file mode 100644 index 0000000..d0b33ba --- /dev/null +++ b/inn/inn_hotels/page/pos_extended/pos_extended.json @@ -0,0 +1,23 @@ +{ + "content": null, + "creation": "2024-01-31 12:21:34.357359", + "docstatus": 0, + "doctype": "Page", + "idx": 0, + "modified": "2024-01-31 12:21:54.868306", + "modified_by": "Administrator", + "module": "Inn Hotels", + "name": "pos-extended", + "owner": "Administrator", + "page_name": "pos-extended", + "roles": [ + { + "role": "Administrator" + } + ], + "script": null, + "standard": "Yes", + "style": null, + "system_page": 0, + "title": "Point of Sales Extended" +} \ No newline at end of file diff --git a/inn/inn_hotels/page/pos_extended/pos_extended.py b/inn/inn_hotels/page/pos_extended/pos_extended.py new file mode 100644 index 0000000..d7e004a --- /dev/null +++ b/inn/inn_hotels/page/pos_extended/pos_extended.py @@ -0,0 +1,129 @@ + +import frappe + +PRINT_STATUS_DRAFT = 0 +PRINT_STATUS_CAPTAIN = 1 +PRINT_STATUS_TABLE = 2 +ORDER_FINISHED = 3 + +NEW_ORDER = 1 + +@frappe.whitelist() +def save_pos_usage(invoice_name, table, action): + if action not in ["save_draft", "print_captain", "print_table"]: + raise TypeError("argument error: action not found") + + + new = False + doc = "" + if not frappe.db.exists({"doctype": "Inn POS Usage", "pos_invoice": invoice_name}): + inn_table = frappe.get_doc("Inn Point Of Sale Table", table) + inn_table.status = "Occupied" + inn_table.save() + + doc = frappe.new_doc("Inn POS Usage") + doc.table = table + doc.pos_invoice = invoice_name + new = True + + else: + doc = frappe.get_last_doc("Inn POS Usage", filters={"pos_invoice": invoice_name}) + + if action in ["save_draft", "print_captain"] and doc.print_status == PRINT_STATUS_TABLE and not new: + # case if customer want to add the order + # then new item will be added to processed item + # then new item will be empty to reset the tracked item + items = doc.new_item + doc.new_item = {} + + new_item = {x.item_name : x for x in items} + tracked_item = {x.item_name: x for x in doc.processed_item} + + for i in new_item: + if i in tracked_item: + tracked_item[i].quantity += new_item[i].quantity + else: + tracked_item[i] = new_item[i] + tracked_item[i].parentfield = "processed_item" + + tracked_item[i].save() + + doc.print_status = PRINT_STATUS_DRAFT + doc.processed_item = {tracked_item[x] for x in tracked_item} + + if action == "save_draft" and doc.print_status == PRINT_STATUS_DRAFT: + doc.print_status = PRINT_STATUS_DRAFT + elif action == "print_captain" and doc.print_status == PRINT_STATUS_DRAFT: + doc.print_status = PRINT_STATUS_CAPTAIN + elif action == "print_table" and doc.print_status == PRINT_STATUS_CAPTAIN: + doc.print_status = PRINT_STATUS_TABLE + else: + raise TypeError("print error: status not match") + + if action in ["save_draft", "print_captain"]: + # add untracked child + all_item = frappe.db.get_values(doctype="POS Invoice Item", filters={"parenttype":"POS Invoice", "parent":invoice_name}, fieldname=["item_name", "qty"], as_dict=True) + + if not 'tracked_item' in locals(): + tracked_item = {x.item_name: x for x in doc.processed_item} + + new_item_name = {item.item_name: item for item in doc.new_item} + doc.save() + + + add_item = False + for item in all_item: + item_name = "" + quantity = 0 + if item.item_name in tracked_item: + diff = item.qty - tracked_item[item.item_name].quantity + if diff > 0: + add_item = True + item_name = item.item_name + quantity = diff + + else: + add_item = True + item_name = item.item_name + quantity = item.qty + + if add_item: + if item_name in new_item_name: + new_item_name[item_name].quantity = quantity + new_item_name[item_name].save() + + else: + new_item = frappe.new_doc("Inn POS Usage Item") + new_item.item_name = item_name + new_item.quantity = quantity + new_item.parent = doc.name + new_item.parenttype = doc.doctype + new_item.parentfield = "new_item" + new_item.insert() + add_item = False + + + elif action == "print_table": + # no change. print table will use same data as print_captain + doc.save() + + pass + + + return {"message": "success"} + +@frappe.whitelist() +def get_table_number(invoice_name): + return frappe.get_value(doctype="Inn POS Usage", filters={"pos_invoice": invoice_name }, fieldname=["table"]) + + +@frappe.whitelist() +def clean_table_number(invoice_name): + table_name = frappe.get_last_doc(doctype="Inn POS Usage", filters={"pos_invoice": invoice_name}) + table_name.print_status = ORDER_FINISHED + table_name.save() + + doc_table = frappe.get_doc("Inn Point Of Sale Table", table_name.table) + doc_table.status = "Empty" + doc_table.save() + return diff --git a/inn/inn_hotels/page/pos_extended/pos_extended_itemcart.js b/inn/inn_hotels/page/pos_extended/pos_extended_itemcart.js new file mode 100644 index 0000000..c17a1d5 --- /dev/null +++ b/inn/inn_hotels/page/pos_extended/pos_extended_itemcart.js @@ -0,0 +1,138 @@ +frappe.require(["point-of-sale.bundle.js", "inn-pos.bundle.js"], function () { + inn.PointOfSale.PosExtendItemCart = class PosExtendItemCart extends erpnext.PointOfSale.ItemCart { + constructor({ wrapper, events, settings }) { + super({ wrapper, events, settings }); + } + init_child_components() { + this.init_customer_selector(); + this.init_table_selector(); + this.init_cart_components(); + } + + init_table_selector() { + this.$component.append( + `
` + ) + this.$table_section = this.$component.find('.table-section'); + this.make_table_selector(); + } + + + make_table_selector() { + this.$table_section.html(`
`) + const me = this; + this.table_field = frappe.ui.form.make_control({ + df: { + label: __('No Table'), + fieldtype: 'Link', + options: 'Inn Point Of Sale Table', + placeholder: __('Search table number'), + get_query: function () { + return { + filters: [ + ["Inn Point Of Sale Table", "status", "=", "Empty"] + ] + } + }, + onchange: function () { + me["table_number"] = this.get_value() + me.events.get_frm().dirty() + } + }, + parent: this.$table_section.find('.table-field'), + render_input: true, + }); + this.table_field.toggle_label(false); + } + + load_invoice() { + super.load_invoice() + + const frm = this.events.get_frm() + const me = this + let table_number = undefined + + frappe.call({ + method: "inn.inn_hotels.page.pos_extended.pos_extended.get_table_number", + args: { + invoice_name: frm.doc.name + }, + async: false, + callback: function (r) { + table_number = r.message + me.$table_section.find("input").val(table_number) + me.table_number = table_number + } + }) + } + + make_cart_totals_section() { + this.$totals_section = this.$component.find('.cart-totals-section'); + + this.$totals_section.append( + `
+ ${this.get_discount_icon()} ${__('Add Discount')} +
+
+
${__('Total Items')}
+
0.00
+
+
+
${__("Net Total")}
+
0.00
+
+
+
+
${__('Grand Total')}
+
0.00
+
+ +
${__('Checkout')}
+
${__('Edit Cart')}
` + ) + + this.$add_discount_elem = this.$component.find(".add-discount-wrapper"); + } + + highlight_checkout_btn(toggle) { + super.highlight_checkout_btn(toggle) + if (toggle) { + this.$cart_container.find('.caption-order-btn').css({ + 'background-color': 'var(--gray-800)' + }); + this.$cart_container.find('.table-order-btn').css({ + 'background-color': 'var(--gray-800)' + }); + } else { + this.$cart_container.find('.caption-order-btn').css({ + 'background-color': 'var(--gray-300)' + }); + this.$cart_container.find('.table-order-btn').css({ + 'background-color': 'var(--gray-300)' + }); + } + } + + bind_events() { + super.bind_events() + + const me = this; + this.$component.on("click", ".caption-order-btn", async function () { + if ($(this).attr('style').indexOf('--gray-800') == -1) return; + + await me.events.print_captain_order(); + }) + + this.$component.on("click", ".table-order-btn", async function () { + if ($(this).attr('style').indexOf('--gray-800') == -1) return; + + await me.events.print_table_order(); + }) + } + } +}) + +export default inn.PointOfSale.PosExtendItemCart \ No newline at end of file diff --git a/inn/inn_hotels/page/pos_extended/pos_extended_pastordersummary.js b/inn/inn_hotels/page/pos_extended/pos_extended_pastordersummary.js new file mode 100644 index 0000000..692aaad --- /dev/null +++ b/inn/inn_hotels/page/pos_extended/pos_extended_pastordersummary.js @@ -0,0 +1,83 @@ +frappe.require(["point-of-sale.bundle.js"], function () { + + inn.PointOfSale.PosExtendPastOrderSummary = class PosExtendPastOrderSummary extends erpnext.PointOfSale.PastOrderSummary { + constructor({ wrapper, events }) { + super({ wrapper, events }) + } + + async attach_document_info(doc) { + await frappe.db.get_value('Customer', this.doc.customer, 'email_id').then(({ message }) => { + doc.customer_email = message.email_id || ''; + }); + + const upper_section_dom = this.get_upper_section_html(doc); + this.$upper_section.html(upper_section_dom); + } + + bind_events() { + super.bind_events(); + + this.$summary_container.on('click', '.show-btn', () => { + console.log(this.doc) + this.events.print_bill(this.doc) + }); + } + + get_condition_btn_map(after_submission) { + if (after_submission) + return [{ condition: true, visible_btns: ['Print Receipt', 'Email Receipt', 'New Order'] }]; + + return [ + { condition: this.doc.docstatus === 0, visible_btns: ['Show Bill', 'Edit Order', 'Delete Order'] }, + { condition: !this.doc.is_return && this.doc.docstatus === 1, visible_btns: ['Print Receipt', 'Email Receipt', 'Return'] }, + { condition: this.doc.is_return && this.doc.docstatus === 1, visible_btns: ['Print Receipt', 'Email Receipt'] } + ]; + } + + print_receipt() { + const frm = this.events.get_frm(); + frappe.utils.print( + this.doc.doctype, + this.doc.name, + "POS Extended Invoice", + this.doc.letter_head, + this.doc.language || frappe.boot.lang + ); + } + + get_upper_section_html(doc) { + const { status } = doc; + let indicator_color = ''; + + in_list(['Paid', 'Consolidated'], status) && (indicator_color = 'green'); + status === 'Draft' && (indicator_color = 'red'); + status === 'Return' && (indicator_color = 'grey'); + + var table_number = undefined + + frappe.call({ + method: "inn.inn_hotels.page.pos_extended.pos_extended.get_table_number", + args: { + invoice_name: doc.name + }, + async: false, + callback: function (r) { + table_number = r.message + } + }) + + return `
+
${doc.customer}
+
${doc.customer_email}
+
${__('Table')}: ${table_number}
+
${__('Sold by')}: ${doc.owner}
+
+
+ +
${doc.name}
+ ${doc.status} +
`; + } + } + +}) \ No newline at end of file diff --git a/inn/inn_hotels/page/pos_extended/pos_extended_printformat.js b/inn/inn_hotels/page/pos_extended/pos_extended_printformat.js new file mode 100644 index 0000000..e69de29 diff --git a/inn/inn_hotels/print_format/pos_extended_bill/__init__.py b/inn/inn_hotels/print_format/pos_extended_bill/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/inn/inn_hotels/print_format/pos_extended_bill/pos_extended_bill.json b/inn/inn_hotels/print_format/pos_extended_bill/pos_extended_bill.json new file mode 100644 index 0000000..373ec7c --- /dev/null +++ b/inn/inn_hotels/print_format/pos_extended_bill/pos_extended_bill.json @@ -0,0 +1,31 @@ +{ + "absolute_value": 0, + "align_labels_right": 0, + "creation": "2024-02-12 15:19:53.428647", + "custom_format": 1, + "default_print_language": "en", + "disabled": 0, + "doc_type": "POS Invoice", + "docstatus": 0, + "doctype": "Print Format", + "font_size": 14, + "html": "\n\n{% if letter_head %}\n {{ letter_head }}\n{% endif %}\n\n

\n\t{{ doc.company }}
\n\t{{ _(\"Bill\") }}
\n

\n

\n\t{{ _(\"Receipt No\") }}: {{ doc.name }}
\n\t{{ _(\"Cashier\") }}: {{ doc.owner }}
\n\t{{ _(\"Customer\") }}: {{ doc.customer_name }}
\n\t{% set table_number = get_table_number(doc.name) %}\n\n\t{{ _(\"Table\") }}: {{ table_number if table_number else \"None\" }}
\n\t{{ _(\"Date\") }}: {{ doc.get_formatted(\"posting_date\") }}
\n\t{{ _(\"Time\") }}: {{ doc.get_formatted(\"posting_time\") }}
\n

\n\n
\n\n\t\n\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\n\t\n\t\t{%- for item in doc.items -%}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{%- endfor -%}\n\t\n
{{ _(\"Item\") }}{{ _(\"Qty\") }}{{ _(\"Amount\") }}
\n\t\t\t\t{{ item.item_code }}\n\t\t\t\t{%- if item.item_name != item.item_code -%}\n\t\t\t\t\t
{{ item.item_name }}\n\t\t\t\t{%- endif -%}\n\t\t\t\t{%- if item.serial_no -%}\n\t\t\t\t\t
{{ _(\"SR.No\") }}:
\n\t\t\t\t\t{{ item.serial_no | replace(\"\\n\", \", \") }}\n\t\t\t\t{%- endif -%}\n\t\t\t
{{ item.qty }}
@ {{ item.get_formatted(\"rate\") }}
{{ item.get_formatted(\"amount\") }}
\n\n\t\n\t\t\n\t\t\t{% if doc.flags.show_inclusive_tax_in_print %}\n\t\t\t\t\n\t\t\t\t\n\t\t\t{% else %}\n\t\t\t\t\n\t\t\t\t\n\t\t\t{% endif %}\n\t\t\n\t\t{%- for row in doc.taxes -%}\n\t\t {%- if not row.included_in_print_rate or doc.flags.show_inclusive_tax_in_print -%}\n\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t {%- endif -%}\n\t\t{%- endfor -%}\n\n\t\t{%- if doc.discount_amount -%}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{%- endif -%}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{%- if doc.rounded_total -%}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{%- endif -%}\n\t\n
\n\t\t\t\t\t{{ _(\"Total Excl. Tax\") }}\n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"net_total\", doc) }}\n\t\t\t\t\n\t\t\t\t\t{{ _(\"Total\") }}\n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"total\", doc) }}\n\t\t\t\t
\n\t\t\t\t {% if '%' in row.description %}\n\t\t\t\t\t {{ row.description }}\n\t\t\t\t\t{% else %}\n\t\t\t\t\t {{ row.description }}@{{ row.rate }}%\n\t\t\t\t\t{% endif %}\n\t\t\t\t\n\t\t\t\t\t{{ row.get_formatted(\"tax_amount\", doc) }}\n\t\t\t\t
\n\t\t\t\t{{ _(\"Discount\") }}\n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"discount_amount\") }}\n\t\t\t
\n\t\t\t\t{{ _(\"Grand Total\") }}\n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"grand_total\") }}\n\t\t\t
\n\t\t\t\t{{ _(\"Rounded Total\") }}\n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"rounded_total\") }}\n\t\t\t
\n
\n

{{ doc.terms or \"\" }}

", + "idx": 0, + "line_breaks": 0, + "margin_bottom": 15.0, + "margin_left": 15.0, + "margin_right": 15.0, + "margin_top": 15.0, + "modified": "2024-02-13 16:00:18.302763", + "modified_by": "Administrator", + "module": "Inn Hotels", + "name": "POS Extended Bill", + "owner": "Administrator", + "page_number": "Hide", + "print_format_builder": 0, + "print_format_builder_beta": 0, + "print_format_type": "Jinja", + "raw_printing": 0, + "show_section_headings": 0, + "standard": "Yes" +} \ No newline at end of file diff --git a/inn/inn_hotels/print_format/pos_extended_captain_order/__init__.py b/inn/inn_hotels/print_format/pos_extended_captain_order/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/inn/inn_hotels/print_format/pos_extended_captain_order/pos_extended_captain_order.json b/inn/inn_hotels/print_format/pos_extended_captain_order/pos_extended_captain_order.json new file mode 100644 index 0000000..acb7339 --- /dev/null +++ b/inn/inn_hotels/print_format/pos_extended_captain_order/pos_extended_captain_order.json @@ -0,0 +1,33 @@ +{ + "absolute_value": 0, + "align_labels_right": 0, + "creation": "2024-02-13 10:57:02.837376", + "css": ".print-format table, .print-format tr, \n.print-format td, .print-format div, .print-format p {\n\tfont-family: Monospace;\n\tline-height: 200%;\n\tvertical-align: middle;\n}\n@media screen {\n\t.print-format {\n\t\twidth: 4in;\n\t\tpadding: 0.25in;\n\t\tmin-height: 8in;\n\t}\n}", + "custom_format": 1, + "default_print_language": "en", + "disabled": 0, + "doc_type": "POS Invoice", + "docstatus": 0, + "doctype": "Print Format", + "font": "Default", + "font_size": 0, + "html": "{% set list = print_list_order(doc.name) %}\n

\n\tOrder ID: {{doc.name}}
\n\tTable No: {{list.table}}
\n

\n
\n\n\t\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\n\t\n\t\t{% for item in list[\"items\"] %}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{% endfor %}\n\t\n
QtyItem
{{ item.quantity }}{{ item.item_name }}
\n", + "idx": 0, + "line_breaks": 0, + "margin_bottom": 0.0, + "margin_left": 0.0, + "margin_right": 0.0, + "margin_top": 0.0, + "modified": "2024-02-13 14:10:30.526511", + "modified_by": "Administrator", + "module": "Inn Hotels", + "name": "POS Extended Captain Order", + "owner": "Administrator", + "page_number": "Hide", + "print_format_builder": 0, + "print_format_builder_beta": 0, + "print_format_type": "Jinja", + "raw_printing": 0, + "show_section_headings": 0, + "standard": "Yes" +} \ No newline at end of file diff --git a/inn/inn_hotels/print_format/pos_extended_invoice/__init__.py b/inn/inn_hotels/print_format/pos_extended_invoice/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/inn/inn_hotels/print_format/pos_extended_invoice/pos_extended_invoice.json b/inn/inn_hotels/print_format/pos_extended_invoice/pos_extended_invoice.json new file mode 100644 index 0000000..6f6edb4 --- /dev/null +++ b/inn/inn_hotels/print_format/pos_extended_invoice/pos_extended_invoice.json @@ -0,0 +1,31 @@ +{ + "absolute_value": 0, + "align_labels_right": 0, + "creation": "2024-02-12 14:59:39.188116", + "custom_format": 1, + "default_print_language": "en", + "disabled": 0, + "doc_type": "POS Invoice", + "docstatus": 0, + "doctype": "Print Format", + "font_size": 14, + "html": "\n\n{% if letter_head %}\n {{ letter_head }}\n{% endif %}\n\n

\n\t{{ doc.company }}
\n\t{{ doc.select_print_heading or _(\"Invoice\") }}
\n

\n

\n\t{{ _(\"Receipt No\") }}: {{ doc.name }}
\n\t{{ _(\"Cashier\") }}: {{ doc.owner }}
\n\t{{ _(\"Customer\") }}: {{ doc.customer_name }}
\n\t{{ _(\"Table\") }}: {{ frappe.db.get_value(doctype=\"Inn POS Usage\", filters={\"pos_invoice\": doc.name}, fieldname=[\"table\"]) }}
\n\t{{ _(\"Date\") }}: {{ doc.get_formatted(\"posting_date\") }}
\n\t{{ _(\"Time\") }}: {{ doc.get_formatted(\"posting_time\") }}
\n

\n\n
\n\n\t\n\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\n\t\n\t\t{%- for item in doc.items -%}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{%- endfor -%}\n\t\n
{{ _(\"Item\") }}{{ _(\"Qty\") }}{{ _(\"Amount\") }}
\n\t\t\t\t{{ item.item_code }}\n\t\t\t\t{%- if item.item_name != item.item_code -%}\n\t\t\t\t\t
{{ item.item_name }}\n\t\t\t\t{%- endif -%}\n\t\t\t\t{%- if item.serial_no -%}\n\t\t\t\t\t
{{ _(\"SR.No\") }}:
\n\t\t\t\t\t{{ item.serial_no | replace(\"\\n\", \", \") }}\n\t\t\t\t{%- endif -%}\n\t\t\t
{{ item.qty }}
@ {{ item.get_formatted(\"rate\") }}
{{ item.get_formatted(\"amount\") }}
\n\n\t\n\t\t\n\t\t\t{% if doc.flags.show_inclusive_tax_in_print %}\n\t\t\t\t\n\t\t\t\t\n\t\t\t{% else %}\n\t\t\t\t\n\t\t\t\t\n\t\t\t{% endif %}\n\t\t\n\t\t{%- for row in doc.taxes -%}\n\t\t {%- if not row.included_in_print_rate or doc.flags.show_inclusive_tax_in_print -%}\n\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t {%- endif -%}\n\t\t{%- endfor -%}\n\n\t\t{%- if doc.discount_amount -%}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{%- endif -%}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{%- if doc.rounded_total -%}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{%- endif -%}\n\t\t{%- for row in doc.payments -%}\n\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t{%- endfor -%}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{%- if doc.change_amount -%}\n\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t{%- endif -%}\n\t\n
\n\t\t\t\t\t{{ _(\"Total Excl. Tax\") }}\n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"net_total\", doc) }}\n\t\t\t\t\n\t\t\t\t\t{{ _(\"Total\") }}\n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"total\", doc) }}\n\t\t\t\t
\n\t\t\t\t {% if '%' in row.description %}\n\t\t\t\t\t {{ row.description }}\n\t\t\t\t\t{% else %}\n\t\t\t\t\t {{ row.description }}@{{ row.rate }}%\n\t\t\t\t\t{% endif %}\n\t\t\t\t\n\t\t\t\t\t{{ row.get_formatted(\"tax_amount\", doc) }}\n\t\t\t\t
\n\t\t\t\t{{ _(\"Discount\") }}\n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"discount_amount\") }}\n\t\t\t
\n\t\t\t\t{{ _(\"Grand Total\") }}\n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"grand_total\") }}\n\t\t\t
\n\t\t\t\t{{ _(\"Rounded Total\") }}\n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"rounded_total\") }}\n\t\t\t
\n\t\t\t\t {{ row.mode_of_payment }}\n\t\t\t\t\n\t\t\t\t\t{{ row.get_formatted(\"amount\", doc) }}\n\t\t\t\t
\n\t\t\t\t{{ _(\"Paid Amount\") }}\n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"paid_amount\") }}\n\t\t\t
\n\t\t\t\t\t{{ _(\"Change Amount\") }}\n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"change_amount\") }}\n\t\t\t\t
\n
\n

{{ doc.terms or \"\" }}

\n

{{ _(\"Thank you, please visit again.\") }}

", + "idx": 0, + "line_breaks": 0, + "margin_bottom": 15.0, + "margin_left": 15.0, + "margin_right": 15.0, + "margin_top": 15.0, + "modified": "2024-02-12 15:04:16.995259", + "modified_by": "Administrator", + "module": "Inn Hotels", + "name": "POS Extended Invoice", + "owner": "Administrator", + "page_number": "Hide", + "print_format_builder": 0, + "print_format_builder_beta": 0, + "print_format_type": "Jinja", + "raw_printing": 0, + "show_section_headings": 0, + "standard": "Yes" +} \ No newline at end of file diff --git a/inn/inn_hotels/print_format/pos_extended_table_order/__init__.py b/inn/inn_hotels/print_format/pos_extended_table_order/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/inn/inn_hotels/print_format/pos_extended_table_order/pos_extended_table_order.json b/inn/inn_hotels/print_format/pos_extended_table_order/pos_extended_table_order.json new file mode 100644 index 0000000..6bc6300 --- /dev/null +++ b/inn/inn_hotels/print_format/pos_extended_table_order/pos_extended_table_order.json @@ -0,0 +1,33 @@ +{ + "absolute_value": 0, + "align_labels_right": 0, + "creation": "2024-02-13 14:12:57.400528", + "css": ".print-format table, .print-format tr, \n.print-format td, .print-format div, .print-format p {\n\tfont-family: Monospace;\n\tline-height: 200%;\n\tvertical-align: middle;\n}\n@media screen {\n\t.print-format {\n\t\twidth: 4in;\n\t\tpadding: 0.25in;\n\t\tmin-height: 8in;\n\t}\n}", + "custom_format": 1, + "default_print_language": "en", + "disabled": 0, + "doc_type": "POS Invoice", + "docstatus": 0, + "doctype": "Print Format", + "font": "Default", + "font_size": 0, + "html": "{% set list = print_list_order(doc.name) %}\n

\n\tOrder ID: {{doc.name}}
\n\tTable No: {{list.table}}
\n

\n
\n\n\t\n\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\n\t\n\t\t{% for item in list[\"items\"] %}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{% endfor %}\n\t\n
QtyItemServed?
{{ item.quantity }}{{ item.item_name }}[     ]
\n", + "idx": 0, + "line_breaks": 0, + "margin_bottom": 0.0, + "margin_left": 0.0, + "margin_right": 0.0, + "margin_top": 0.0, + "modified": "2024-02-13 14:14:53.144872", + "modified_by": "Administrator", + "module": "Inn Hotels", + "name": "POS Extended Table Order", + "owner": "Administrator", + "page_number": "Hide", + "print_format_builder": 0, + "print_format_builder_beta": 0, + "print_format_type": "Jinja", + "raw_printing": 0, + "show_section_headings": 0, + "standard": "Yes" +} \ No newline at end of file diff --git a/inn/public/.gitignore b/inn/public/.gitignore new file mode 100644 index 0000000..2597551 --- /dev/null +++ b/inn/public/.gitignore @@ -0,0 +1 @@ +dist* \ No newline at end of file diff --git a/inn/public/css/inn.bundle.scss b/inn/public/css/inn.bundle.scss new file mode 100644 index 0000000..cbfad8a --- /dev/null +++ b/inn/public/css/inn.bundle.scss @@ -0,0 +1,72 @@ +@import "./style.bundle"; + +.pos-card { + background-color: var(--fg-color); + box-shadow: var(--shadow-base); + border-radius: var(--border-radius-md); +} + +.table-section { + @extend .pos-card; + display: flex; + flex-direction: column; + padding: var(--padding-md) var(--padding-lg); + overflow: visible; + margin-top: var(--margin-md); + + > .table-details { + display: flex; + flex-direction:column ; + background-color: var(--fg-color); + + > .header { + display: flex; + margin-bottom: var(--margin-md); + } + } +} + +.table-number { + font-size: var(--text-md); + font-weight: 500; + color: var(--gray-600); + margin-top: auto; +} + +.pointer-no-select { + cursor: pointer; + user-select: none; +} + +.primary-action-pos { + @extend .pointer-no-select; + display: flex; + align-items: center; + justify-content: center; + padding: var(--padding-sm); + margin-top: var(--margin-sm); + border-radius: var(--border-radius-md); + font-size: var(--text-lg); + font-weight: 700; +} + +.print-button-order { + background-color: var(--gray-300); + width: 100%; + color: white; +} + + +.print-order-section{ + display: flex; + + > .caption-order-btn { + @extend .primary-action-pos; + @extend .print-button-order; + } + + > .table-order-btn { + @extend .primary-action-pos; + @extend .print-button-order; + } +} \ No newline at end of file diff --git a/inn/public/css/style.bundle.scss b/inn/public/css/style.bundle.scss new file mode 100644 index 0000000..e69de29 diff --git a/inn/public/js/inn-pos.bundle.js b/inn/public/js/inn-pos.bundle.js new file mode 100644 index 0000000..66819b7 --- /dev/null +++ b/inn/public/js/inn-pos.bundle.js @@ -0,0 +1,3 @@ +import "../../inn_hotels/page/pos_extended/pos_extended_itemcart.js"; +import "../../inn_hotels/page/pos_extended/pos_extended_pastordersummary.js"; +import "../../inn_hotels/page/pos_extended/pos_extended_printformat.js";