diff --git a/inn/__init__.py b/inn/__init__.py index 635d0fc2..afdd45db 100644 --- a/inn/__init__.py +++ b/inn/__init__.py @@ -1,4 +1,4 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -__version__ = '1.1.26' +__version__ = "1.1.9" diff --git a/inn/inn_hotels/doctype/ar_city_ledger_invoice/ar_city_ledger_invoice.js b/inn/inn_hotels/doctype/ar_city_ledger_invoice/ar_city_ledger_invoice.js index 4952d16a..8c43c9d2 100644 --- a/inn/inn_hotels/doctype/ar_city_ledger_invoice/ar_city_ledger_invoice.js +++ b/inn/inn_hotels/doctype/ar_city_ledger_invoice/ar_city_ledger_invoice.js @@ -1,228 +1,254 @@ // Copyright (c) 2020, Core Initiative and contributors // For license information, please see license.txt -frappe.ui.form.on('AR City Ledger Invoice', { - onload: function(frm) { - make_payment_visibility(frm); - }, - refresh: function(frm) { - filter_folio(frm); - make_payment_visibility(frm); - }, - inn_channel: function (frm) { - filter_folio(frm); - }, - inn_group: function (frm) { - filter_folio(frm); - }, - customer_id: function (frm) { - filter_folio(frm); - }, - make_payment: function (frm) { - frappe.confirm(__("Please make sure that the payment details (Payment Date, Amount and Mode of Payment) are correct, and Outstanding Amount is zero. Are you want to continue?"), function() { - if (frm.doc.outstanding != 0.0) { - frappe.msgprint('Outstanding amount must be zero in order to Make Payment. Please correct the payment details before Making Payment.'); - } - else { - frappe.call({ - method: "inn.inn_hotels.doctype.ar_city_ledger_invoice.ar_city_ledger_invoice.make_payment", - args: { - id: frm.doc.name, - }, - callback: (r) => { - if (r.message === 1) { - frappe.show_alert(__("This AR City Ledger Invoice are successfully paid.")); - frm.reload_doc(); - } - } - }); - } - }); - - } +frappe.ui.form.on("AR City Ledger Invoice", { + onload: function (frm) { + make_payment_visibility(frm); + }, + refresh: function (frm) { + filter_folio(frm); + make_payment_visibility(frm); + }, + inn_channel: function (frm) { + filter_folio(frm); + }, + inn_group: function (frm) { + filter_folio(frm); + }, + customer_id: function (frm) { + filter_folio(frm); + }, + make_payment: function (frm) { + frappe.confirm( + __( + "Please make sure that the payment details (Payment Date, Amount and Mode of Payment) are correct, and Outstanding Amount is zero. Are you want to continue?" + ), + function () { + if (frm.doc.outstanding != 0.0) { + frappe.msgprint( + "Outstanding amount must be zero in order to Make Payment. Please correct the payment details before Making Payment." + ); + } else { + frappe.call({ + method: + "inn.inn_hotels.doctype.ar_city_ledger_invoice.ar_city_ledger_invoice.make_payment", + args: { + id: frm.doc.name, + }, + callback: (r) => { + if (r.message === 1) { + frappe.show_alert( + __("This AR City Ledger Invoice are successfully paid.") + ); + frm.reload_doc(); + } + }, + }); + } + } + ); + }, }); -frappe.ui.form.on('AR City Ledger Invoice Folio', { - folio_id: function (frm, cdt, cdn) { - let child = locals[cdt][cdn]; - autofill_by_folio(child); - }, - folio_remove: function (frm) { - calculate_payments(frm); - }, +frappe.ui.form.on("AR City Ledger Invoice Folio", { + folio_id: function (frm, cdt, cdn) { + let child = locals[cdt][cdn]; + autofill_by_folio(child); + }, + folio_remove: function (frm) { + calculate_payments(frm); + }, }); -frappe.ui.form.on('AR City Ledger Invoice Payments', { - payments_add: function (frm) { - console.log("masuk sini"); - if (!frm.doc.folio || frm.doc.folio.length === 0) { - frappe.msgprint("Please add Folio to be Collected first"); - frm.doc.payments = []; - frm.refresh_field('payments'); - } - }, - payments_remove: function (frm) { - calculate_payments(frm); - }, - payment_amount: function (frm, cdt, cdn) { - let child = locals[cdt][cdn]; - if (child.payment_amount) { - calculate_payments(frm); - } - }, - mode_of_payment: function (frm, cdt, cdn) { - let child = locals[cdt][cdn]; - if (child.mode_of_payment) { - autofill_payments_account(child); - } - } +frappe.ui.form.on("AR City Ledger Invoice Payments", { + payments_add: function (frm) { + console.log("masuk sini"); + if (!frm.doc.folio || frm.doc.folio.length === 0) { + frappe.msgprint("Please add Folio to be Collected first"); + frm.doc.payments = []; + frm.refresh_field("payments"); + } + }, + payments_remove: function (frm) { + calculate_payments(frm); + }, + payment_amount: function (frm, cdt, cdn) { + let child = locals[cdt][cdn]; + if (child.payment_amount) { + calculate_payments(frm); + } + }, + mode_of_payment: function (frm, cdt, cdn) { + let child = locals[cdt][cdn]; + if (child.mode_of_payment) { + autofill_payments_account(child); + } + }, }); function filter_folio(frm) { - let field = frm.fields_dict['folio'].grid.fields_map['folio_id']; - let channel = frm.doc.inn_channel; - let group = frm.doc.inn_group; - let customer_id = frm.doc.customer_id; - frappe.call({ - method: 'inn.inn_hotels.doctype.ar_city_ledger.ar_city_ledger.get_folio_from_ar_city_ledger', - args: { - selector: 'Folio', - channel: channel, - group: group, - customer_id: customer_id - }, - callback: (r) => { - if (r.message) { - field.get_query = function () { - return { - filters: [ - ['Inn Folio', 'name', 'in', r.message] - ] - } - } - } - } - }); + let field = frm.fields_dict["folio"].grid.fields_map["folio_id"]; + let channel = frm.doc.inn_channel; + let group = frm.doc.inn_group; + let customer_id = frm.doc.customer_id; + frappe.call({ + method: + "inn.inn_hotels.doctype.ar_city_ledger.ar_city_ledger.get_folio_from_ar_city_ledger", + args: { + selector: "Folio", + channel: channel, + group: group, + customer_id: customer_id, + }, + callback: (r) => { + if (r.message) { + field.get_query = function () { + return { + filters: [["Inn Folio", "name", "in", r.message]], + }; + }; + } + }, + }); } function autofill_by_folio(child) { - if (child.folio_id !== undefined) { - frappe.call({ - method: 'inn.inn_hotels.doctype.ar_city_ledger.ar_city_ledger.get_ar_city_ledger_by_folio', - args: { - folio_id: child.folio_id - }, - callback: (r) => { - child.customer_id = r.message.customer_id; - child.amount = r.message.total_amount; - child.open = r.message.folio_open; - child.close = r.message.folio_close; - child.ar_city_ledger_id = r.message.name; - cur_frm.refresh_field('folio'); + if (child.folio_id !== undefined) { + frappe.call({ + method: + "inn.inn_hotels.doctype.ar_city_ledger.ar_city_ledger.get_ar_city_ledger_by_folio", + args: { + folio_id: child.folio_id, + }, + callback: (r) => { + child.customer_id = r.message.customer_id; + child.amount = r.message.total_amount; + child.open = r.message.folio_open; + child.close = r.message.folio_close; + child.ar_city_ledger_id = r.message.name; + cur_frm.refresh_field("folio"); - if (child.amount != 0) { - calculate_payments(cur_frm); - } - } - }); - } + if (child.amount != 0) { + calculate_payments(cur_frm); + } + }, + }); + } } -cur_frm.set_query("mode_of_payment", "payments", function(doc, cdt, cdn) { - var d = locals[cdt][cdn]; - return{ - filters: [ - ['Mode of Payment', 'mode_of_payment', '!=', 'City Ledger'], - ] - } +cur_frm.set_query("mode_of_payment", "payments", function (doc, cdt, cdn) { + var d = locals[cdt][cdn]; + return { + filters: [["Mode of Payment", "mode_of_payment", "!=", "City Ledger"]], + }; }); function calculate_payments(frm) { - let total_amount = 0.0; - let total_paid = 0.0; - let outstanding = 0.0; - let folios = []; - let payments = []; - if (frm.doc.folio) { - folios = frm.doc.folio; - } - if (frm.doc.payments) { - payments = frm.doc.payments; - } + let total_amount = 0.0; + let total_paid = 0.0; + let outstanding = 0.0; + let folios = []; + let payments = []; + if (frm.doc.folio) { + folios = frm.doc.folio; + } + if (frm.doc.payments) { + payments = frm.doc.payments; + } - if (folios.length > 0) { - for (let i = 0; i < folios.length; i++) { - if (folios[i].amount !== undefined) { - total_amount += folios[i].amount; - } - } - } - else { - total_amount = 0.0; - } + if (folios.length > 0) { + for (let i = 0; i < folios.length; i++) { + if (folios[i].amount !== undefined) { + total_amount += folios[i].amount; + } + } + } else { + total_amount = 0.0; + } - if (payments.length > 0) { - for (let i = 0; i < payments.length; i++) { - if (payments[i].payment_amount !== undefined) { - total_paid += payments[i].payment_amount - } - } - } - else { - total_paid = 0.0; - } + if (payments.length > 0) { + for (let i = 0; i < payments.length; i++) { + if (payments[i].payment_amount !== undefined) { + total_paid += payments[i].payment_amount; + } + } + } else { + total_paid = 0.0; + } - outstanding = total_amount - total_paid; + outstanding = total_amount - total_paid; - frm.set_value('total_amount', total_amount); - frm.set_value('total_paid', total_paid); - frm.set_value('outstanding', outstanding); + frm.set_value("total_amount", total_amount); + frm.set_value("total_paid", total_paid); + frm.set_value("outstanding", outstanding); } function autofill_payments_account(child) { - frappe.call({ - method: 'inn.inn_hotels.doctype.ar_city_ledger_invoice.ar_city_ledger_invoice.get_payments_accounts', - args: { - mode_of_payment: child.mode_of_payment - }, - callback: (r) => { - if (r.message) { - child.account = r.message[0]; - child.account_against = r.message[1]; - cur_frm.refresh_field('payments'); - } - } - }); + frappe.call({ + method: + "inn.inn_hotels.doctype.ar_city_ledger_invoice.ar_city_ledger_invoice.get_payments_accounts", + args: { + mode_of_payment: child.mode_of_payment, + }, + callback: (r) => { + if (r.message) { + child.account = r.message[0]; + child.account_against = r.message[1]; + cur_frm.refresh_field("payments"); + } + }, + }); } function make_payment_visibility(frm) { - if (frm.doc.__islocal === 1) { - frm.set_df_property('sb5', 'hidden', 1); - } - else if (frm.doc.payments && frm.doc.payments.length === 0) { - frm.set_df_property('sb5', 'hidden', 1); - } - else if (frm.doc.status == 'Paid') { - frm.set_df_property('sb5', 'hidden', 1); - disable_form(frm); - } - else { - frm.set_df_property('sb5', 'hidden', 0); - } + if (frm.doc.__islocal === 1) { + frm.set_df_property("sb5", "hidden", 1); + } else if (frm.doc.payments && frm.doc.payments.length === 0) { + frm.set_df_property("sb5", "hidden", 1); + } else if (frm.doc.status == "Paid") { + frm.set_df_property("sb5", "hidden", 1); + disable_form(frm); + } else { + frm.set_df_property("sb5", "hidden", 0); + } } function disable_form(frm) { - frm.disable_save(); - frm.set_df_property('issued_date', 'read_only', 1); - frm.set_df_property('due_date', 'read_only', 1); - frm.set_df_property('inn_channel', 'read_only', 1); - frm.set_df_property('inn_group', 'read_only', 1); - frm.set_df_property('customer_id', 'read_only', 1); - frm.get_field("folio").grid.only_sortable(); - frappe.meta.get_docfield('AR City Ledger Invoice Folio', 'folio_id', frm.doc.name).read_only = 1; - frm.get_field("payments").grid.only_sortable(); - frappe.meta.get_docfield('AR City Ledger Invoice Payments', 'payment_reference_date', frm.doc.name).read_only = 1; - frappe.meta.get_docfield('AR City Ledger Invoice Payments', 'mode_of_payment', frm.doc.name).read_only = 1; - frappe.meta.get_docfield('AR City Ledger Invoice Payments', 'payment_amount', frm.doc.name).read_only = 1; - frappe.meta.get_docfield('AR City Ledger Invoice Payments', 'payment_reference_no', frm.doc.name).read_only = 1; - frappe.meta.get_docfield('AR City Ledger Invoice Payments', 'payment_clearance_date', frm.doc.name).read_only = 1; - frm.set_intro('This AR City Ledger Invoice has been Paid.'); -} \ No newline at end of file + frm.disable_save(); + frm.set_df_property("issued_date", "read_only", 1); + frm.set_df_property("due_date", "read_only", 1); + frm.set_df_property("inn_channel", "read_only", 1); + frm.set_df_property("inn_group", "read_only", 1); + frm.set_df_property("customer_id", "read_only", 1); + frm.get_field("folio").grid.only_sortable(); + frappe.meta.get_docfield( + "AR City Ledger Invoice Folio", + "folio_id", + frm.doc.name + ).read_only = 1; + frm.get_field("payments").grid.only_sortable(); + frappe.meta.get_docfield( + "AR City Ledger Invoice Payments", + "payment_reference_date", + frm.doc.name + ).read_only = 1; + frappe.meta.get_docfield( + "AR City Ledger Invoice Payments", + "mode_of_payment", + frm.doc.name + ).read_only = 1; + frappe.meta.get_docfield( + "AR City Ledger Invoice Payments", + "payment_amount", + frm.doc.name + ).read_only = 1; + frappe.meta.get_docfield( + "AR City Ledger Invoice Payments", + "payment_reference_no", + frm.doc.name + ).read_only = 1; + frappe.meta.get_docfield( + "AR City Ledger Invoice Payments", + "payment_clearance_date", + frm.doc.name + ).read_only = 1; + frm.set_intro("This AR City Ledger Invoice has been Paid."); +} diff --git a/inn/inn_hotels/doctype/ar_city_ledger_invoice/ar_city_ledger_invoice.py b/inn/inn_hotels/doctype/ar_city_ledger_invoice/ar_city_ledger_invoice.py index 0f5202b6..92fdb55e 100644 --- a/inn/inn_hotels/doctype/ar_city_ledger_invoice/ar_city_ledger_invoice.py +++ b/inn/inn_hotels/doctype/ar_city_ledger_invoice/ar_city_ledger_invoice.py @@ -6,72 +6,92 @@ import frappe from frappe.model.document import Document + class ARCityLedgerInvoice(Document): - pass + pass + @frappe.whitelist() def get_payments_accounts(mode_of_payment): - account = frappe.db.get_value('Mode of Payment Account', {'parent': mode_of_payment, 'company': frappe.get_doc( - "Global Defaults").default_company}, "default_account") - against = frappe.db.get_list('Account', filters={'account_number': '1133.001'})[0].name - return account, against + hotel_settings = frappe.get_single("Inn Hotels Setting") + account = frappe.db.get_value( + "Mode of Payment Account", + { + "parent": mode_of_payment, + "company": frappe.get_doc("Global Defaults").default_company, + }, + "default_account", + ) + against = frappe.db.get_list( + "Account", + filters={"name": hotel_settings.ar_city_ledger_invoice_payment_account}, + )[0].name + return account, against + @frappe.whitelist() def make_payment(id): - doc = frappe.get_doc('AR City Ledger Invoice', id) - arc_id = [] - folio_list = doc.folio - if len(folio_list) == 0: - frappe.msgprint("Please add the Folio to be Collected first before making payment") - else: - for folio in folio_list: - arc_id.append(folio.ar_city_ledger_id) - payments = doc.get('payments') - return_status = 1 + doc = frappe.get_doc("AR City Ledger Invoice", id) + arc_id = [] + folio_list = doc.folio + if len(folio_list) == 0: + frappe.msgprint( + "Please add the Folio to be Collected first before making payment" + ) + else: + for folio in folio_list: + arc_id.append(folio.ar_city_ledger_id) + payments = doc.get("payments") + return_status = 1 - for payment in payments: - remark = 'AR City Ledger Invoice Payments: ' + payment.name - doc_je = frappe.new_doc('Journal Entry') - doc_je.title = payment.name - doc_je.voucher_type = 'Journal Entry' - doc_je.naming_series = 'ACC-JV-.YYYY.-' - doc_je.posting_date = payment.payment_reference_date - doc_je.company = frappe.get_doc('Global Defaults').default_company - doc_je.total_amount_currency = frappe.get_doc('Global Defaults').default_currency - doc_je.remark = remark - doc_je.user_remark = remark + for payment in payments: + remark = "AR City Ledger Invoice Payments: " + payment.name + doc_je = frappe.new_doc("Journal Entry") + doc_je.title = payment.name + doc_je.voucher_type = "Journal Entry" + doc_je.naming_series = "ACC-JV-.YYYY.-" + doc_je.posting_date = payment.payment_reference_date + doc_je.company = frappe.get_doc("Global Defaults").default_company + doc_je.total_amount_currency = frappe.get_doc( + "Global Defaults" + ).default_currency + doc_je.remark = remark + doc_je.user_remark = remark - doc_jea_debit = frappe.new_doc('Journal Entry Account') - doc_jea_debit.account = payment.account - doc_jea_debit.debit = payment.payment_amount - doc_jea_debit.debit_in_account_currency = payment.payment_amount - doc_jea_debit.party_type = 'Customer' - doc_jea_debit.party = doc.customer_id - doc_jea_debit.user_remark = remark + doc_jea_debit = frappe.new_doc("Journal Entry Account") + doc_jea_debit.account = payment.account + doc_jea_debit.debit = payment.payment_amount + doc_jea_debit.debit_in_account_currency = payment.payment_amount + doc_jea_debit.party_type = "Customer" + doc_jea_debit.party = doc.customer_id + doc_jea_debit.user_remark = remark - doc_jea_credit = frappe.new_doc('Journal Entry Account') - doc_jea_credit.account = payment.account_against - doc_jea_credit.credit = payment.payment_amount - doc_jea_credit.credit_in_account_currency = payment.payment_amount - doc_jea_credit.party_type = 'Customer' - doc_jea_credit.party = doc.customer_id - doc_jea_credit.user_remark = remark + doc_jea_credit = frappe.new_doc("Journal Entry Account") + doc_jea_credit.account = payment.account_against + doc_jea_credit.credit = payment.payment_amount + doc_jea_credit.credit_in_account_currency = payment.payment_amount + doc_jea_credit.party_type = "Customer" + doc_jea_credit.party = doc.customer_id + doc_jea_credit.user_remark = remark - doc_je.append('accounts', doc_jea_debit) - doc_je.append('accounts', doc_jea_credit) + doc_je.append("accounts", doc_jea_debit) + doc_je.append("accounts", doc_jea_credit) - doc_je.save() - doc_je.submit() + doc_je.save() + doc_je.submit() - if frappe.db.get_value('Journal Entry', {'title': payment.name}, 'remark') == remark: - return_status = 2 + if ( + frappe.db.get_value("Journal Entry", {"title": payment.name}, "remark") + == remark + ): + return_status = 2 - if return_status == 1: - doc.status = 'Paid' - doc.save() - for arc in arc_id: - doc_arc_ledger = frappe.get_doc('AR City Ledger', arc) - doc_arc_ledger.is_paid = 1 - doc_arc_ledger.save() + if return_status == 1: + doc.status = "Paid" + doc.save() + for arc in arc_id: + doc_arc_ledger = frappe.get_doc("AR City Ledger", arc) + doc_arc_ledger.is_paid = 1 + doc_arc_ledger.save() - return return_status \ No newline at end of file + return return_status diff --git a/inn/inn_hotels/doctype/inn_account_setting/__init__.py b/inn/inn_hotels/doctype/inn_account_setting/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/inn/inn_hotels/doctype/inn_account_setting/inn_account_setting.json b/inn/inn_hotels/doctype/inn_account_setting/inn_account_setting.json new file mode 100644 index 00000000..9c4851ee --- /dev/null +++ b/inn/inn_hotels/doctype/inn_account_setting/inn_account_setting.json @@ -0,0 +1,70 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2025-01-21 10:18:10.048150", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "account_name", + "parent_number", + "account_number", + "is_group", + "account_currency", + "account_type", + "root_type" + ], + "fields": [ + { + "fieldname": "account_name", + "fieldtype": "Data", + "label": "Account Name" + }, + { + "fieldname": "account_number", + "fieldtype": "Data", + "label": "Account Number" + }, + { + "default": "0", + "fieldname": "is_group", + "fieldtype": "Check", + "label": "Is Group" + }, + { + "fieldname": "account_type", + "fieldtype": "Select", + "label": "Account Type", + "options": "\nAccumulated Depreciation\nAsset Received But Not Billed\nBank\nCash\nChargeable\nCapital Work in Progress\nCost of Goods Sold\nCurrent Asset\nCurrent Liability\nDepreciation\nDirect Expense\nDirect Income\nEquity\nExpense Account\nExpenses Included In Asset Valuation\nExpenses Included In Valuation\nFixed Asset\nIncome Account\nIndirect Expense\nIndirect Income\nLiability\nPayable\nReceivable\nRound Off\nRound Off for Opening\nStock\nStock Adjustment\nStock Received But Not Billed\nService Received But Not Billed\nTax\nTemporary" + }, + { + "fieldname": "root_type", + "fieldtype": "Select", + "label": "Root Type", + "options": "\nAsset\nLiability\nIncome\nExpense\nEquity" + }, + { + "fieldname": "parent_number", + "fieldtype": "Data", + "label": "Parent Number" + }, + { + "fieldname": "account_currency", + "fieldtype": "Link", + "label": "Currency", + "options": "Currency" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2025-01-21 11:02:48.635981", + "modified_by": "Administrator", + "module": "Inn Hotels", + "name": "Inn Account Setting", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/inn/inn_hotels/doctype/inn_account_setting/inn_account_setting.py b/inn/inn_hotels/doctype/inn_account_setting/inn_account_setting.py new file mode 100644 index 00000000..c82c8307 --- /dev/null +++ b/inn/inn_hotels/doctype/inn_account_setting/inn_account_setting.py @@ -0,0 +1,9 @@ +# Copyright (c) 2025, Core Initiative and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class InnAccountSetting(Document): + pass diff --git a/inn/inn_hotels/doctype/inn_dayend_close/inn_dayend_close.json b/inn/inn_hotels/doctype/inn_dayend_close/inn_dayend_close.json index f23d2bb4..cd3e532f 100644 --- a/inn/inn_hotels/doctype/inn_dayend_close/inn_dayend_close.json +++ b/inn/inn_hotels/doctype/inn_dayend_close/inn_dayend_close.json @@ -86,7 +86,7 @@ } ], "links": [], - "modified": "2024-03-14 11:35:48.623600", + "modified": "2025-01-22 19:31:51.872837", "modified_by": "Administrator", "module": "Inn Hotels", "name": "Inn Dayend Close", diff --git a/inn/inn_hotels/doctype/inn_dayend_close/inn_dayend_close.py b/inn/inn_hotels/doctype/inn_dayend_close/inn_dayend_close.py index 6e305bd3..3d069be6 100644 --- a/inn/inn_hotels/doctype/inn_dayend_close/inn_dayend_close.py +++ b/inn/inn_hotels/doctype/inn_dayend_close/inn_dayend_close.py @@ -9,351 +9,574 @@ from frappe.model.document import Document from inn.inn_hotels.doctype.inn_audit_log.inn_audit_log import get_last_audit_date from inn.inn_hotels.doctype.inn_folio.inn_folio import check_void_request -from inn.inn_hotels.doctype.inn_dayend_close.inn_dayend_close_helper import _fill_party_account +from inn.inn_hotels.doctype.inn_dayend_close.inn_dayend_close_helper import ( + _fill_party_account, +) + class InnDayendClose(Document): - pass + pass + @frappe.whitelist() def is_there_open_dayend_close(): - if frappe.get_all('Inn Dayend Close', {'status': 'Open'}): - return 1 - else: - return 2 + if frappe.get_all("Inn Dayend Close", {"status": "Open"}): + return 1 + else: + return 2 + @frappe.whitelist() def process_dayend_close(doc_id): - need_resolve_flag = False - - COMMISSION_TRANSACTION_TYPE = frappe.db.get_single_value("Inn Hotels Setting", fieldname="profit_sharing_transaction_type") - - # Create Journal Entry Pairing for Every Eligible Inn Folio Transactions - folio_list = frappe.get_all('Inn Folio', filters={'status': ['in', ['Open', 'Closed']], 'journal_entry_id_closed': ['=', '']}, fields=["name", "reservation_id"]) - for item in folio_list: - need_resolve_list = check_void_request(item.name) - if len(need_resolve_list) > 0: - need_resolve_flag = True - break - - if need_resolve_flag: - return "There are transaction requested to be voided not yet responded. Please resolve the request first." - else: - print('Folio List Size: ',len(folio_list)) - for item in folio_list: - print(datetime.datetime.now(),': Folio ', item.name) - doc_folio = frappe.get_doc('Inn Folio', item.name) - if doc_folio.reservation_id: - reservation = frappe.get_doc('Inn Reservation', doc_folio.reservation_id) - - if reservation.status == 'In House': - actual_room = frappe.get_doc('Inn Room', reservation.actual_room_id) - actual_room.room_status = 'Occupied Dirty' - actual_room.save() - - trx_list : list[Document] = doc_folio.get('folio_transaction') - for trx in trx_list: - if trx.is_void == 0 and trx.journal_entry_id is None: - if trx.remark is None: - remark = trx.transaction_type + ' ' + trx.parent - elif trx.remark == '': - remark = trx.transaction_type + ' ' + trx.parent - else: - remark = trx.remark - customer_name = frappe.db.get_value('Inn Folio', trx.parent, 'customer_id') - doc_je = frappe.new_doc('Journal Entry') - doc_je.title = doc_folio.name - doc_je.voucher_type = 'Journal Entry' - doc_je.naming_series = 'ACC-JV-.YYYY.-' - doc_je.posting_date = get_last_audit_date() - doc_je.company = frappe.get_doc('Global Defaults').default_company - doc_je.total_amount_currency = frappe.get_doc('Global Defaults').default_currency - doc_je.remark = remark - doc_je.user_remark = remark - - doc_jea_debit = frappe.new_doc('Journal Entry Account') - doc_jea_debit.account = trx.debit_account - doc_jea_debit.debit = trx.amount - doc_jea_debit.debit_in_account_currency = trx.amount - doc_jea_debit.party_type, doc_jea_debit.party = _fill_party_account(doc_jea_debit.account, customer_name) - doc_jea_debit.user_remark = remark - - if trx.transaction_type == COMMISSION_TRANSACTION_TYPE and doc_jea_debit.party_type == "Supplier": - channel_id = frappe.db.get_value("Inn Reservation", item.reservation_id, fieldname="channel") - channel_vendor = frappe.db.get_value("Inn Channel", channel_id, channel_id) - doc_jea_debit.party = channel_vendor - - doc_jea_credit = frappe.new_doc('Journal Entry Account') - doc_jea_credit.account = trx.credit_account - doc_jea_credit.credit = trx.amount - doc_jea_credit.credit_in_account_currency = trx.amount - doc_jea_credit.party_type, doc_jea_credit.party = _fill_party_account(doc_jea_credit.account, customer_name) - doc_jea_credit.user_remark = remark - - if trx.transaction_type == COMMISSION_TRANSACTION_TYPE and doc_jea_credit.party_type == "Supplier": - channel_id = frappe.db.get_value("Inn Reservation", item.reservation_id, fieldname="channel") - channel_vendor = frappe.db.get_value(doctype="Inn Channel", filters={"name": channel_id}, fieldname="supplier") - doc_jea_credit.party = channel_vendor - - doc_je.append('accounts', doc_jea_debit) - doc_je.append('accounts', doc_jea_credit) - - doc_je.save() - doc_je.submit() - - trx.journal_entry_id = doc_je.name - trx.save() - - # Create Journal Entry Pairing for Every Eligible Inn Folio - closed_folio_list = frappe.get_all('Inn Folio', filters={ - 'status': 'Closed', - 'total_credit': ['!=', 0], - 'total_debit': ['!=', 0], - 'journal_entry_id_closed': ['=', ''] - }) - for item in closed_folio_list: - doc_folio = frappe.get_doc('Inn Folio', item.name) - cust_name = doc_folio.customer_id - # Get all Closed folio with close date == last audit date - if doc_folio.journal_entry_id_closed is None and doc_folio.close == get_last_audit_date(): - closed_folio_remark = 'Closed Folio Transaction' - # Get all transactions that not void - closed_trx_list = frappe.get_all('Inn Folio Transaction', - filters={'parent': item.name,'is_void': 0}, - fields=['*']) - # Folio must not be empty, Because Journal Entry Table Account not allowed to be empty - if len(closed_trx_list) > 0: - doc_je = frappe.new_doc('Journal Entry') - doc_je.title = doc_folio.name - doc_je.voucher_type = 'Journal Entry' - doc_je.naming_series = 'ACC-JV-.YYYY.-' - doc_je.posting_date = get_last_audit_date() - doc_je.company = frappe.get_doc('Global Defaults').default_company - doc_je.total_amount_currency = frappe.get_doc('Global Defaults').default_currency - doc_je.remark = closed_folio_remark - doc_je.user_remark = closed_folio_remark - - for trx in closed_trx_list: - if trx.flag == 'Debit': - doc_jea_debit = frappe.new_doc('Journal Entry Account') - doc_jea_debit.account = trx.debit_account - doc_jea_debit.debit = trx.amount - doc_jea_debit.credit_in_account_currency = trx.amount #amount flipped to credit - doc_jea_debit.party_type, doc_jea_debit.party = _fill_party_account(doc_jea_debit.account, cust_name) - doc_jea_debit.user_remark = closed_folio_remark - doc_je.append('accounts', doc_jea_debit) - print(f"DEBIT {doc_jea_debit.debit} - {doc_jea_debit.credit_in_account_currency} {trx.remark}") - elif trx.flag == 'Credit': - doc_jea_credit = frappe.new_doc('Journal Entry Account') - doc_jea_credit.account = trx.credit_account - doc_jea_credit.credit = trx.amount - doc_jea_credit.debit_in_account_currency = trx.amount #amount flipped to debit - doc_jea_credit.party_type, doc_jea_credit.party = _fill_party_account(doc_jea_credit.account, cust_name) - doc_jea_credit.user_remark = closed_folio_remark - doc_je.append('accounts', doc_jea_credit) - print(f"CREDIT {doc_jea_credit.credit} - {doc_jea_credit.debit_in_account_currency} {trx.remark}") - - doc_je.save() - doc_je.submit() - doc_folio.journal_entry_id_closed = doc_je.name - doc_folio.save() - - # Create Journal Entry for Inn Restaurant Finished Order - # Get all finished order that not transfered to folio and not paired with journal entry yet - # create_je_for_inn_restaurant_finished_order() - - - doc_audit_log = frappe.new_doc('Inn Audit Log') - doc_audit_log.naming_series = 'AL.DD.-.MM.-.YYYY.-' - doc_audit_log.audit_date = get_last_audit_date() + datetime.timedelta(days = 1) - doc_audit_log.posting_date = datetime.datetime.now() - doc_audit_log.posted_by =frappe.session.user - doc_audit_log.insert() - - doc = frappe.get_doc('Inn Dayend Close', doc_id) - doc.status = 'Closed' - doc.save() - - return doc.status + need_resolve_flag = False + + # Fetch transaction types from Inn Hotels Setting + hotel_settings = frappe.get_doc("Inn Hotels Setting") + transaction_types = { + "credit_card_administration_fee": hotel_settings.credit_card_administration_fee, + "package": hotel_settings.package, + "room_charge": hotel_settings.room_charge, + "breakfast_charge": hotel_settings.breakfast_charge, + "refund": hotel_settings.refund, + "dp_kamar": hotel_settings.dp_kamar, + "room_payment": hotel_settings.room_payment, + "deposit": hotel_settings.deposit, + "down_payment": hotel_settings.down_payment, + "payment": hotel_settings.payment, + "additional_charge": hotel_settings.additional_charge, + "restaurant_food": hotel_settings.restaurant_food, + "restaurant_beverages": hotel_settings.restaurant_beverages, + "restaurant_other": hotel_settings.restaurant_other, + "room_service_food": hotel_settings.room_service_food, + "room_service_beverage": hotel_settings.room_service_beverage, + "fbs_service_10": hotel_settings.fbs_service_10, + "round_off": hotel_settings.round_off, + "laundry": hotel_settings.laundry, + "cancellation_fee": hotel_settings.cancellation_fee, + "late_checkout": hotel_settings.late_checkout, + "early_checkin": hotel_settings.early_checkin, + } + if hotel_settings.include_tax: + transaction_types["room_charge_tax_service"] = ( + hotel_settings.room_charge_tax_service + ) + + transaction_types["breakfast_charge_tax_service"] = ( + hotel_settings.breakfast_charge_tax_service + ) + + transaction_types["fbs_tax_11"] = hotel_settings.fbs_tax_11 + + COMMISSION_TRANSACTION_TYPE = hotel_settings.profit_sharing_transaction_type + + # Create Journal Entry Pairing for Every Eligible Inn Folio Transactions + folio_list = frappe.get_all( + "Inn Folio", + filters={ + "status": ["in", ["Open", "Closed"]], + "journal_entry_id_closed": ["=", ""], + }, + fields=["name", "reservation_id"], + ) + for item in folio_list: + need_resolve_list = check_void_request(item.name) + if len(need_resolve_list) > 0: + need_resolve_flag = True + break + + if need_resolve_flag: + return "There are transaction requested to be voided not yet responded. Please resolve the request first." + else: + print("Folio List Size: ", len(folio_list)) + for item in folio_list: + print(datetime.datetime.now(), ": Folio ", item.name) + doc_folio = frappe.get_doc("Inn Folio", item.name) + if doc_folio.reservation_id: + reservation = frappe.get_doc( + "Inn Reservation", doc_folio.reservation_id + ) + + if reservation.status == "In House": + actual_room = frappe.get_doc("Inn Room", reservation.actual_room_id) + actual_room.room_status = "Occupied Dirty" + actual_room.save() + + trx_list: list[Document] = doc_folio.get("folio_transaction") + for trx in trx_list: + if trx.is_void == 0 and trx.journal_entry_id is None: + if trx.remark is None: + remark = trx.transaction_type + " " + trx.parent + elif trx.remark == "": + remark = trx.transaction_type + " " + trx.parent + else: + remark = trx.remark + customer_name = frappe.db.get_value( + "Inn Folio", trx.parent, "customer_id" + ) + doc_je = frappe.new_doc("Journal Entry") + doc_je.title = doc_folio.name + doc_je.voucher_type = "Journal Entry" + doc_je.naming_series = "ACC-JV-.YYYY.-" + doc_je.posting_date = get_last_audit_date() + doc_je.company = frappe.get_doc("Global Defaults").default_company + doc_je.total_amount_currency = frappe.get_doc( + "Global Defaults" + ).default_currency + doc_je.remark = remark + doc_je.user_remark = remark + + doc_jea_debit = frappe.new_doc("Journal Entry Account") + doc_jea_debit.account = trx.debit_account + doc_jea_debit.debit = trx.amount + doc_jea_debit.debit_in_account_currency = trx.amount + doc_jea_debit.party_type, doc_jea_debit.party = _fill_party_account( + doc_jea_debit.account, customer_name + ) + doc_jea_debit.user_remark = remark + + if ( + trx.transaction_type == COMMISSION_TRANSACTION_TYPE + and doc_jea_debit.party_type == "Supplier" + ): + channel_id = frappe.db.get_value( + "Inn Reservation", item.reservation_id, fieldname="channel" + ) + channel_vendor = frappe.db.get_value( + "Inn Channel", channel_id, channel_id + ) + doc_jea_debit.party = channel_vendor + + doc_jea_credit = frappe.new_doc("Journal Entry Account") + doc_jea_credit.account = trx.credit_account + doc_jea_credit.credit = trx.amount + doc_jea_credit.credit_in_account_currency = trx.amount + doc_jea_credit.party_type, doc_jea_credit.party = ( + _fill_party_account(doc_jea_credit.account, customer_name) + ) + doc_jea_credit.user_remark = remark + + if ( + trx.transaction_type == COMMISSION_TRANSACTION_TYPE + and doc_jea_credit.party_type == "Supplier" + ): + channel_id = frappe.db.get_value( + "Inn Reservation", item.reservation_id, fieldname="channel" + ) + channel_vendor = frappe.db.get_value( + doctype="Inn Channel", + filters={"name": channel_id}, + fieldname="supplier", + ) + doc_jea_credit.party = channel_vendor + + doc_je.append("accounts", doc_jea_debit) + doc_je.append("accounts", doc_jea_credit) + + doc_je.save() + doc_je.submit() + + trx.journal_entry_id = doc_je.name + trx.save() + + # Create Journal Entry Pairing for Every Eligible Inn Folio + closed_folio_list = frappe.get_all( + "Inn Folio", + filters={ + "status": "Closed", + "total_credit": ["!=", 0], + "total_debit": ["!=", 0], + "journal_entry_id_closed": ["=", ""], + }, + ) + for item in closed_folio_list: + doc_folio = frappe.get_doc("Inn Folio", item.name) + cust_name = doc_folio.customer_id + # Get all Closed folio with close date == last audit date + if ( + doc_folio.journal_entry_id_closed is None + and doc_folio.close == get_last_audit_date() + ): + closed_folio_remark = "Closed Folio Transaction" + # Get all transactions that not void + closed_trx_list = frappe.get_all( + "Inn Folio Transaction", + filters={"parent": item.name, "is_void": 0}, + fields=["*"], + ) + # Folio must not be empty, Because Journal Entry Table Account not allowed to be empty + if len(closed_trx_list) > 0: + doc_je = frappe.new_doc("Journal Entry") + doc_je.title = doc_folio.name + doc_je.voucher_type = "Journal Entry" + doc_je.naming_series = "ACC-JV-.YYYY.-" + doc_je.posting_date = get_last_audit_date() + doc_je.company = frappe.get_doc("Global Defaults").default_company + doc_je.total_amount_currency = frappe.get_doc( + "Global Defaults" + ).default_currency + doc_je.remark = closed_folio_remark + doc_je.user_remark = closed_folio_remark + + for trx in closed_trx_list: + if trx.flag == "Debit": + doc_jea_debit = frappe.new_doc("Journal Entry Account") + doc_jea_debit.account = trx.debit_account + doc_jea_debit.debit = trx.amount + doc_jea_debit.credit_in_account_currency = ( + trx.amount + ) # amount flipped to credit + doc_jea_debit.party_type, doc_jea_debit.party = ( + _fill_party_account(doc_jea_debit.account, cust_name) + ) + doc_jea_debit.user_remark = closed_folio_remark + doc_je.append("accounts", doc_jea_debit) + print( + f"DEBIT {doc_jea_debit.debit} - {doc_jea_debit.credit_in_account_currency} {trx.remark}" + ) + elif trx.flag == "Credit": + doc_jea_credit = frappe.new_doc("Journal Entry Account") + doc_jea_credit.account = trx.credit_account + doc_jea_credit.credit = trx.amount + doc_jea_credit.debit_in_account_currency = ( + trx.amount + ) # amount flipped to debit + doc_jea_credit.party_type, doc_jea_credit.party = ( + _fill_party_account(doc_jea_credit.account, cust_name) + ) + doc_jea_credit.user_remark = closed_folio_remark + doc_je.append("accounts", doc_jea_credit) + print( + f"CREDIT {doc_jea_credit.credit} - {doc_jea_credit.debit_in_account_currency} {trx.remark}" + ) + + doc_je.save() + doc_je.submit() + doc_folio.journal_entry_id_closed = doc_je.name + doc_folio.save() + + # Create Journal Entry for Inn Restaurant Finished Order + # Get all finished order that not transfered to folio and not paired with journal entry yet + # create_je_for_inn_restaurant_finished_order(transaction_types) + + doc_audit_log = frappe.new_doc("Inn Audit Log") + doc_audit_log.naming_series = "AL.DD.-.MM.-.YYYY.-" + doc_audit_log.audit_date = get_last_audit_date() + datetime.timedelta(days=1) + doc_audit_log.posting_date = datetime.datetime.now() + doc_audit_log.posted_by = frappe.session.user + doc_audit_log.insert() + + doc = frappe.get_doc("Inn Dayend Close", doc_id) + doc.status = "Closed" + doc.save() + + return doc.status + @frappe.whitelist() def load_child(date): - audit_date = datetime.datetime.strptime(date, '%Y-%m-%d').date() - # return get_arrived_today(audit_date), get_departed_today(audit_date), get_closed_today(audit_date), get_ongoing_order_need_to_be_finished() - return get_arrived_today(audit_date), get_departed_today(audit_date), get_closed_today(audit_date) + audit_date = datetime.datetime.strptime(date, "%Y-%m-%d").date() + # return get_arrived_today(audit_date), get_departed_today(audit_date), get_closed_today(audit_date), get_ongoing_order_need_to_be_finished() + return ( + get_arrived_today(audit_date), + get_departed_today(audit_date), + get_closed_today(audit_date), + ) + def get_arrived_today(date): - return_list = [] - list = frappe.get_all('Inn Reservation', filters={'status': 'Reserved'}, fields=['*']) - for item in list: - if item.expected_arrival == date: - new_arrived = frappe.new_doc('Inn Expected Arrived Today') - new_arrived.reservation_id = item.name - new_arrived.folio_id = frappe.get_doc('Inn Folio', {'reservation_id': item.name}).name - new_arrived.customer_id = item.customer_id - new_arrived.description = 'Must Check In Today' - return_list.append(new_arrived) - return return_list + return_list = [] + list = frappe.get_all( + "Inn Reservation", filters={"status": "Reserved"}, fields=["*"] + ) + for item in list: + if item.expected_arrival == date: + new_arrived = frappe.new_doc("Inn Expected Arrived Today") + new_arrived.reservation_id = item.name + new_arrived.folio_id = frappe.get_doc( + "Inn Folio", {"reservation_id": item.name} + ).name + new_arrived.customer_id = item.customer_id + new_arrived.description = "Must Check In Today" + return_list.append(new_arrived) + return return_list + def get_departed_today(date): - return_list = [] - list = frappe.get_all('Inn Reservation', filters={'status': 'In House'}, fields=['*']) - for item in list: - if item.departure.date() == date: - new_departed = frappe.new_doc('Inn Expected Departed Today') - new_departed.reservation_id = item.name - new_departed.folio_id = frappe.get_doc('Inn Folio', {'reservation_id': item.name}).name - new_departed.customer_id = item.customer_id - new_departed.description = 'Must Check Out Today' - return_list.append(new_departed) - return return_list + return_list = [] + list = frappe.get_all( + "Inn Reservation", filters={"status": "In House"}, fields=["*"] + ) + for item in list: + if item.departure.date() == date: + new_departed = frappe.new_doc("Inn Expected Departed Today") + new_departed.reservation_id = item.name + new_departed.folio_id = frappe.get_doc( + "Inn Folio", {"reservation_id": item.name} + ).name + new_departed.customer_id = item.customer_id + new_departed.description = "Must Check Out Today" + return_list.append(new_departed) + return return_list + def get_closed_today(date): - return_list = [] - list = frappe.get_all('Inn Folio', filters={'status': 'Open', 'type': ['in', ['Master', 'Desk']]}, fields=['*']) - for item in list: - if item.close == date: - new_closed = frappe.new_doc('Inn Expected Closed Today') - new_closed.type = item.type - new_closed.folio_id = item.name - new_closed.customer_id = item.customer_id - new_closed.description = 'Must Close Today' - return_list.append(new_closed) - return return_list + return_list = [] + list = frappe.get_all( + "Inn Folio", + filters={"status": "Open", "type": ["in", ["Master", "Desk"]]}, + fields=["*"], + ) + for item in list: + if item.close == date: + new_closed = frappe.new_doc("Inn Expected Closed Today") + new_closed.type = item.type + new_closed.folio_id = item.name + new_closed.customer_id = item.customer_id + new_closed.description = "Must Close Today" + return_list.append(new_closed) + return return_list + def get_ongoing_order_need_to_be_finished(): - return_list = [] - list = frappe.get_all('Inn Restaurant Ongoing Order', fields=['*']) - for item in list: - new_order_need_to_finish = frappe.new_doc('Inn Restaurant Order Expected to be Finished') - new_order_need_to_finish.ongoing_order_id = item.name - new_order_need_to_finish.restaurant = item.restaurant - new_order_need_to_finish.customer = item.customer - new_order_need_to_finish.description = 'Restaurant Order need to be finished today' - return_list.append(new_order_need_to_finish) - return return_list + return_list = [] + list = frappe.get_all("Inn Restaurant Ongoing Order", fields=["*"]) + for item in list: + new_order_need_to_finish = frappe.new_doc( + "Inn Restaurant Order Expected to be Finished" + ) + new_order_need_to_finish.ongoing_order_id = item.name + new_order_need_to_finish.restaurant = item.restaurant + new_order_need_to_finish.customer = item.customer + new_order_need_to_finish.description = ( + "Restaurant Order need to be finished today" + ) + return_list.append(new_order_need_to_finish) + return return_list + def create_journal_entry(title, remark, debit_account, credit_account, amount): - print("Journal Entry Title: " + title) - customer_name = 'Customer Restaurant' - doc_je = frappe.new_doc('Journal Entry') - doc_je.title = title - doc_je.voucher_type = 'Journal Entry' - doc_je.naming_series = 'ACC-JV-.YYYY.-' - doc_je.posting_date = get_last_audit_date() - doc_je.company = frappe.get_doc('Global Defaults').default_company - doc_je.total_amount_currency = frappe.get_doc('Global Defaults').default_currency - doc_je.remark = remark - doc_je.user_remark = remark - - doc_jea_debit = frappe.new_doc('Journal Entry Account') - doc_jea_debit.account = debit_account - doc_jea_debit.debit = amount - doc_jea_debit.debit_in_account_currency = amount - doc_jea_debit.party_type, doc_jea_debit.party = _fill_party_account(doc_jea_debit.account, customer_name) - doc_jea_debit.user_remark = remark - - doc_jea_credit = frappe.new_doc('Journal Entry Account') - doc_jea_credit.account = credit_account - doc_jea_credit.credit = amount - doc_jea_credit.credit_in_account_currency = amount - doc_jea_credit.party_type, doc_jea_credit.party = _fill_party_account(doc_jea_credit.account, customer_name) - doc_jea_credit.user_remark = remark - - doc_je.append('accounts', doc_jea_debit) - doc_je.append('accounts', doc_jea_credit) - - doc_je.save() - doc_je.submit() - -def create_je_for_inn_restaurant_finished_order(): - order_list = frappe.get_all('Inn Restaurant Finished Order', - filters={ - 'transfer_charges_folio': ('=', ''), - 'is_journaled': 0, - }, fields=['*']) - for order in order_list: - restaurant_food = 0 - restaurant_beverage = 0 - restaurant_other = 0 - - # 1. ORDER ITEM IN RESTAURANT FINISHED ORDER - order_item_list = frappe.get_all('Inn Restaurant Order Item', filters={'parent': order.name}, fields=['*']) - - print("order item list of " + order.name + " is " + str(len(order_item_list))) - if order_item_list is not None and len(order_item_list) > 0: - print("masuk if order list exist") - # Calculate Total Amount of Food, Beverages and Other Charges in Restaurant Order - for item in order_item_list: - print('item now = ' + item.name) - menu_type = frappe.db.get_value('Inn Restaurant Menu Item', item.item, 'item_type') - print('menu type = ' + menu_type) - if menu_type == 'Food': - restaurant_food += float(item.rate) - print('restaurant_food now = ' + str(restaurant_food)) - elif menu_type == 'Beverage': - restaurant_beverage += float(item.rate) - print('restaurant_beverage now = ' + str(restaurant_beverage)) - elif menu_type == 'Other': - restaurant_other += float(item.rate) - print('restaurant_other now = ' + str(restaurant_other)) - else: - print("order list not exist") - # Create Journal Entry for Total Amount of Orders for Food, Beverages, and Other Restaurant charges - if restaurant_food > 0: - print("entry restaurant food") - food_title = 'Restaurant Food of ' + order.name - food_remark = 'Restaurant Food Charges from Restaurant Order: ' + order.name - food_debit_account = frappe.get_doc('Inn Folio Transaction Type', 'Restaurant Food').debit_account - food_credit_account = frappe.get_doc('Inn Folio Transaction Type', 'Restaurant Food').credit_account - create_journal_entry(food_title, food_remark, food_debit_account, food_credit_account, restaurant_food) - - if restaurant_beverage > 0: - print("entry restaurant beverage") - bev_title = 'Restaurant Beverages of ' + order.name - bev_remark = 'Restaurant Beverage Charges from Restaurant Order: ' + order.name - bev_debit_account = frappe.get_doc('Inn Folio Transaction Type', 'Restaurant Beverages').debit_account - bev_credit_account = frappe.get_doc('Inn Folio Transaction Type', 'Restaurant Beverages').credit_account - create_journal_entry(bev_title, bev_remark, bev_debit_account, bev_credit_account, restaurant_beverage) - if restaurant_other > 0: - print("entry Other Restaurant") - other_title = 'Other Restaurant of ' + order.name - other_remark = 'Other Restaurant Charges from Restaurant Order: ' + order.name - other_debit_account = frappe.get_doc('Inn Folio Transaction Type', 'Other Restaurant').debit_account - other_credit_account = frappe.get_doc('Inn Folio Transaction Type', 'Other Restaurant').credit_account - create_journal_entry(other_title, other_remark, other_debit_account, other_credit_account, restaurant_other) - - # Create Journal Entry for Round Off Charges - if float(order.rounding_amount) > 0: - ro_title = 'Round Off of ' + order.name - ro_remark = 'Rounding off Amount of Restaurant Charges from Restaurant Order: ' + order.name - ro_debit_account = frappe.get_doc('Inn Folio Transaction Type', 'Round Off').debit_account - ro_credit_account = frappe.get_doc('Inn Folio Transaction Type', 'Round Off').credit_account - create_journal_entry(ro_title, ro_remark, ro_debit_account, ro_credit_account, order.rounding_amount) - - # Create Journal Entry for Service - service_title = 'FBS -- Service 10 % of ' + order.name - service_remark = 'Service of Restaurant Charges from Restaurant Order: ' + order.name - srv_debit_account = frappe.get_doc('Inn Folio Transaction Type', 'FBS -- Service 10 %').debit_account - srv_credit_account = frappe.get_doc('Inn Folio Transaction Type', 'FBS -- Service 10 %').credit_account - create_journal_entry(service_title, service_remark, srv_debit_account, srv_credit_account, order.service_amount) - # Create Journal Entry for Tax - tax_title = 'FBS -- Tax 11 %' + order.name - tax_remark = 'Tax of Restaurant Charges from Restaurant Order: ' + order.name - tax_debit_account = frappe.get_doc('Inn Folio Transaction Type', 'FBS -- Tax 11 %').debit_account - tax_credit_account = frappe.get_doc('Inn Folio Transaction Type', 'FBS -- Tax 11 %').credit_account - create_journal_entry(tax_title, tax_remark, tax_debit_account, tax_credit_account, order.tax_amount) - - # 2. ORDER PAYMENT IN RESTAURANT FINISHED ORDER - order_payment_list = order.get('order_payment') - if order_payment_list is not None and len(order_payment_list) > 0: - for payment in order_payment_list: - payment_title = payment.mode_of_payment + ' Payment for ' + order.name - payment_remark = 'Payment with ' + payment.mode_of_payment + 'from Restaurant Order: ' + order.name - payment_debit_account = frappe.db.get_value('Mode of Payment Account', - {'parent': payment.mode_of_payment, 'company': frappe.get_doc( - "Global Defaults").default_company}, "default_account") - payment_credit_account = frappe.db.get_list('Account', filters={'account_number': '2110.005'})[0].name - create_journal_entry(payment_title, payment_remark, payment_debit_account, payment_credit_account, payment.amount) - - # 3. SET VALUE IS_JOURNALED IN FINISHED ORDER TO TRUE, MARKING THAT THE ORDER ALREADY PAIRED WITH JOURNAL ENTRIES - frappe.db.set_value('Inn Restaurant Finished Order', order.name, 'is_journaled', 1) \ No newline at end of file + print("Journal Entry Title: " + title) + customer_name = "Customer Restaurant" + doc_je = frappe.new_doc("Journal Entry") + doc_je.title = title + doc_je.voucher_type = "Journal Entry" + doc_je.naming_series = "ACC-JV-.YYYY.-" + doc_je.posting_date = get_last_audit_date() + doc_je.company = frappe.get_doc("Global Defaults").default_company + doc_je.total_amount_currency = frappe.get_doc("Global Defaults").default_currency + doc_je.remark = remark + doc_je.user_remark = remark + + doc_jea_debit = frappe.new_doc("Journal Entry Account") + doc_jea_debit.account = debit_account + doc_jea_debit.debit = amount + doc_jea_debit.debit_in_account_currency = amount + doc_jea_debit.party_type, doc_jea_debit.party = _fill_party_account( + doc_jea_debit.account, customer_name + ) + doc_jea_debit.user_remark = remark + + doc_jea_credit = frappe.new_doc("Journal Entry Account") + doc_jea_credit.account = credit_account + doc_jea_credit.credit = amount + doc_jea_credit.credit_in_account_currency = amount + doc_jea_credit.party_type, doc_jea_credit.party = _fill_party_account( + doc_jea_credit.account, customer_name + ) + doc_jea_credit.user_remark = remark + + doc_je.append("accounts", doc_jea_debit) + doc_je.append("accounts", doc_jea_credit) + + doc_je.save() + doc_je.submit() + + +def create_je_for_inn_restaurant_finished_order(transaction_types): + order_list = frappe.get_all( + "Inn Restaurant Finished Order", + filters={ + "transfer_charges_folio": ("=", ""), + "is_journaled": 0, + }, + fields=["*"], + ) + for order in order_list: + restaurant_food = 0 + restaurant_beverage = 0 + restaurant_other = 0 + + # 1. ORDER ITEM IN RESTAURANT FINISHED ORDER + order_item_list = frappe.get_all( + "Inn Restaurant Order Item", filters={"parent": order.name}, fields=["*"] + ) + + print("order item list of " + order.name + " is " + str(len(order_item_list))) + if order_item_list is not None and len(order_item_list) > 0: + print("masuk if order list exist") + # Calculate Total Amount of Food, Beverages and Other Charges in Restaurant Order + for item in order_item_list: + print("item now = " + item.name) + menu_type = frappe.db.get_value( + "Inn Restaurant Menu Item", item.item, "item_type" + ) + print("menu type = " + menu_type) + if menu_type == "Food": + restaurant_food += float(item.rate) + print("restaurant_food now = " + str(restaurant_food)) + elif menu_type == "Beverage": + restaurant_beverage += float(item.rate) + print("restaurant_beverage now = " + str(restaurant_beverage)) + elif menu_type == "Other": + restaurant_other += float(item.rate) + print("restaurant_other now = " + str(restaurant_other)) + else: + print("order list not exist") + # Create Journal Entry for Total Amount of Orders for Food, Beverages, and Other Restaurant charges + if restaurant_food > 0: + print("entry restaurant food") + food_title = "Restaurant Food of " + order.name + food_remark = "Restaurant Food Charges from Restaurant Order: " + order.name + food_debit_account = frappe.get_doc( + "Inn Folio Transaction Type", transaction_types["restaurant_food"] + ).debit_account + food_credit_account = frappe.get_doc( + "Inn Folio Transaction Type", transaction_types["restaurant_food"] + ).credit_account + create_journal_entry( + food_title, + food_remark, + food_debit_account, + food_credit_account, + restaurant_food, + ) + + if restaurant_beverage > 0: + print("entry restaurant beverage") + bev_title = "Restaurant Beverages of " + order.name + bev_remark = ( + "Restaurant Beverage Charges from Restaurant Order: " + order.name + ) + bev_debit_account = frappe.get_doc( + "Inn Folio Transaction Type", transaction_types["restaurant_beverages"] + ).debit_account + bev_credit_account = frappe.get_doc( + "Inn Folio Transaction Type", transaction_types["restaurant_beverages"] + ).credit_account + create_journal_entry( + bev_title, + bev_remark, + bev_debit_account, + bev_credit_account, + restaurant_beverage, + ) + if restaurant_other > 0: + print("entry Other Restaurant") + other_title = "Other Restaurant of " + order.name + other_remark = ( + "Other Restaurant Charges from Restaurant Order: " + order.name + ) + other_debit_account = frappe.get_doc( + "Inn Folio Transaction Type", transaction_types["restaurant_other"] + ).debit_account + other_credit_account = frappe.get_doc( + "Inn Folio Transaction Type", transaction_types["restaurant_other"] + ).credit_account + create_journal_entry( + other_title, + other_remark, + other_debit_account, + other_credit_account, + restaurant_other, + ) + + # Create Journal Entry for Round Off Charges + if float(order.rounding_amount) > 0: + ro_title = "Round Off of " + order.name + ro_remark = ( + "Rounding off Amount of Restaurant Charges from Restaurant Order: " + + order.name + ) + ro_debit_account = frappe.get_doc( + "Inn Folio Transaction Type", transaction_types["round_off"] + ).debit_account + ro_credit_account = frappe.get_doc( + "Inn Folio Transaction Type", transaction_types["round_off"] + ).credit_account + create_journal_entry( + ro_title, + ro_remark, + ro_debit_account, + ro_credit_account, + order.rounding_amount, + ) + + # Create Journal Entry for Service + service_title = "FBS -- Service 10 % of " + order.name + service_remark = ( + "Service of Restaurant Charges from Restaurant Order: " + order.name + ) + srv_debit_account = frappe.get_doc( + "Inn Folio Transaction Type", transaction_types["fbs_service_10"] + ).debit_account + srv_credit_account = frappe.get_doc( + "Inn Folio Transaction Type", transaction_types["fbs_service_10"] + ).credit_account + create_journal_entry( + service_title, + service_remark, + srv_debit_account, + srv_credit_account, + order.service_amount, + ) + # Create Journal Entry for Tax + tax_title = "FBS -- Tax 11 %" + order.name + tax_remark = "Tax of Restaurant Charges from Restaurant Order: " + order.name + tax_debit_account = frappe.get_doc( + "Inn Folio Transaction Type", transaction_types["fbs_tax_11"] + ).debit_account + tax_credit_account = frappe.get_doc( + "Inn Folio Transaction Type", transaction_types["fbs_tax_11"] + ).credit_account + create_journal_entry( + tax_title, + tax_remark, + tax_debit_account, + tax_credit_account, + order.tax_amount, + ) + + # 2. ORDER PAYMENT IN RESTAURANT FINISHED ORDER + order_payment_list = order.get("order_payment") + if order_payment_list is not None and len(order_payment_list) > 0: + for payment in order_payment_list: + payment_title = payment.mode_of_payment + " Payment for " + order.name + payment_remark = ( + "Payment with " + + payment.mode_of_payment + + "from Restaurant Order: " + + order.name + ) + payment_debit_account = frappe.db.get_value( + "Mode of Payment Account", + { + "parent": payment.mode_of_payment, + "company": frappe.get_doc("Global Defaults").default_company, + }, + "default_account", + ) + payment_credit_account = frappe.db.get_list( + "Account", filters={"account_number": "2110.005"} + )[0].name + create_journal_entry( + payment_title, + payment_remark, + payment_debit_account, + payment_credit_account, + payment.amount, + ) + + # 3. SET VALUE IS_JOURNALED IN FINISHED ORDER TO TRUE, MARKING THAT THE ORDER ALREADY PAIRED WITH JOURNAL ENTRIES + frappe.db.set_value( + "Inn Restaurant Finished Order", order.name, "is_journaled", 1 + ) diff --git a/inn/inn_hotels/doctype/inn_floor_plan/inn_floor_plan.js b/inn/inn_hotels/doctype/inn_floor_plan/inn_floor_plan.js index 9ce46102..a1c788b9 100644 --- a/inn/inn_hotels/doctype/inn_floor_plan/inn_floor_plan.js +++ b/inn/inn_hotels/doctype/inn_floor_plan/inn_floor_plan.js @@ -1,12 +1,13 @@ // Copyright (c) 2020, Core Initiative and contributors // For license information, please see license.txt -frappe.ui.form.on('Inn Floor Plan', { - refresh: function(frm) { - var wrapper = frm.get_field("html").$wrapper; +frappe.ui.form.on("Inn Floor Plan", { + refresh: function (frm) { + var wrapper = frm.get_field("html").$wrapper; - var head = '' + - '\ + var head = + "" + + '\ \ \ '; - var body = '' + - '\ + var body = + "" + + '\ \ +
\ +
\ +
\ +
\
\ '; - var script = '' + - '' + var script = + "" + + ``; - var svg = ''; + // function changeFloor(id) { + // console.log(id); + // var all = document.getElementsByTagName("svg"); + // console.log(all); + // for (var i = 0; i < all.length; i++) { + // all[i].style.display = "none"; + // } + // console.log(all); + // document.getElementById(id).style.display = "block"; + // document.getElementById("information").style.display = "block"; + // } + wrapper.html(head + body + script); - var floor_files = []; - var rawFile = new XMLHttpRequest(); - rawFile.open('GET', '/files/floor_files.json', false); - rawFile.onreadystatechange = function () { - if (rawFile.readyState === 4) { - if (rawFile.status === 200 || rawFile.status == 0) { - floor_files = JSON.parse(rawFile.responseText); - } - } - } - rawFile.send(); + var svg = ""; - floor_files.forEach(element => { - var z = document.createElement('a'); - z.setAttribute('onclick', 'changeFloor("'+element.id+'")'); - var t = document.createTextNode(element.name); - z.appendChild(t); - document.getElementById('dropdown-content-floor-plan').appendChild(z); + var floor_files = []; + var rawFile = new XMLHttpRequest(); + rawFile.open("GET", "/files/floor_files.json", false); + rawFile.onreadystatechange = function () { + console.log(rawFile, rawFile.responseText); + if (rawFile.readyState === 4) { + if (rawFile.status === 200 || rawFile.status == 0) { + floor_files = JSON.parse(rawFile.responseText); + } + } + }; + rawFile.send(); - rawFile.open('GET', '/files/' + element.file, false); - rawFile.onreadystatechange = function () { - if (rawFile.readyState === 4) { - if (rawFile.status === 200 || rawFile.status == 0) { - svg = svg + rawFile.responseText; - } - } - } - rawFile.send(); - }); + floor_files.forEach((element) => { + console.log(element, "here"); + var z = document.createElement("a"); + console.log(element); + z.setAttribute("onclick", 'changeFloor("' + element.id + '")'); + var t = document.createTextNode(element.name); + z.appendChild(t); + document.getElementById("dropdown-content-floor-plan").appendChild(z); - var div = document.getElementById('floor-plan-content'); - div.insertAdjacentHTML('afterbegin', svg); + rawFile.open("GET", "/files/" + element.file, false); + rawFile.onreadystatechange = function () { + if (rawFile.readyState === 4) { + if (rawFile.status === 200 || rawFile.status == 0) { + svg = svg + rawFile.responseText; + console.log(svg); + } + } + }; + rawFile.send(); + }); - var all = document.getElementsByTagName("svg"); - for (var i = 0; i < all.length; i++) { - all[i].style.display = "none"; - } - - frappe.call({ - method: 'inn.inn_hotels.doctype.inn_room_booking.inn_room_booking.get_all_room_with_room_booking_status', - callback: (resp) => { - resp.forEach(element => { - if (element.status == 'AV') { - document.getElementById('room-' + element.name).setAttribute('style', 'fill:#33a02c;'); - } else if (element.status == 'RS') { - document.getElementById('room-' + element.name).setAttribute('style', 'fill:#1f78b4;'); - } else if (element.status == 'RC') { - document.getElementById('room-' + element.name).setAttribute('style', 'fill:#a6cee3;'); - } else if (element.status == 'OU') { - document.getElementById('room-' + element.name).setAttribute('style', 'fill:#ff7f00;'); - } else if (element.status == 'HU') { - document.getElementById('room-' + element.name).setAttribute('style', 'fill:#fdbf6f;'); - } else if (element.status == 'OO') { - document.getElementById('room-' + element.name).setAttribute('style', 'fill:#e31a1c;'); - } else if (element.status == 'UC') { - document.getElementById('room-' + element.name).setAttribute('style', 'fill:#fb9a99;'); - } - }); - } - }); + var div = document.getElementById("floor-plan-content"); + div.insertAdjacentHTML("afterbegin", svg); + + var all = document.getElementsByTagName("svg"); + for (var i = 0; i < all.length; i++) { + all[i].style.display = "none"; + } + + frappe.call({ + method: + "inn.inn_hotels.doctype.inn_room_booking.inn_room_booking.get_all_room_with_room_booking_status", + callback: (resp) => { + console.log(resp, "resr"); + resp.message.forEach((element) => { + if (element.status == "AV") { + document + .getElementById("room-" + element.name) + .setAttribute("style", "fill:#33a02c;"); + } else if (element.status == "RS") { + console.log( + document.getElementById("room-" + element.name), + element.name + ); + document + .getElementById("room-" + element.name) + .setAttribute("style", "fill:#1f78b4;"); + } else if (element.status == "RC") { + document + .getElementById("room-" + element.name) + .setAttribute("style", "fill:#a6cee3;"); + } else if (element.status == "OU") { + document + .getElementById("room-" + element.name) + .setAttribute("style", "fill:#ff7f00;"); + } else if (element.status == "HU") { + document + .getElementById("room-" + element.name) + .setAttribute("style", "fill:#fdbf6f;"); + } else if (element.status == "OO") { + document + .getElementById("room-" + element.name) + .setAttribute("style", "fill:#e31a1c;"); + } else if (element.status == "UC") { + document + .getElementById("room-" + element.name) + .setAttribute("style", "fill:#fb9a99;"); + } + }); + }, + }); - var information = '' + - '\ + var information = + "" + + '\

Information

\ \ \ @@ -152,8 +210,8 @@ frappe.ui.form.on('Inn Floor Plan', { \ UC (Under Construction)\ \ - ' + '; - wrapper.html(wrapper.html() + information); - } + wrapper.html(wrapper.html() + information); + }, }); diff --git a/inn/inn_hotels/doctype/inn_folio/inn_folio.js b/inn/inn_hotels/doctype/inn_folio/inn_folio.js index 4f33bf27..7d6fe947 100644 --- a/inn/inn_hotels/doctype/inn_folio/inn_folio.js +++ b/inn/inn_hotels/doctype/inn_folio/inn_folio.js @@ -1,793 +1,1091 @@ // Copyright (c) 2020, Core Initiative and contributors // For license information, please see license.txt -var is_check_in = getUrlVars()['is_check_in']; +var is_check_in = getUrlVars()["is_check_in"]; var void_shown = false; var folio_transaction = null; -frappe.ui.form.on('Inn Folio', { - before_save: function(frm) { - make_mandatory(frm); - }, - onload: function(frm) { - frm.get_field("folio_transaction").grid.only_sortable(); - make_read_only(frm); - }, - transfer_to_another_folio: function(frm) { - if (frm.doc.__islocal !== 1) { - let trx_selected = frm.get_field("folio_transaction").grid.get_selected(); - if (trx_selected.length === 0) { - frappe.msgprint('Please select at least one transaction to be transfered'); - } - else { - transfer_to_another_folio(frm, trx_selected); - } - } - }, - add_package: function(frm) { - add_package(frm); - }, - add_charge: function (frm) { - add_charge(frm); - }, - add_payment: function (frm) { - add_payment(frm); - }, - add_refund: function (frm) { - add_refund(frm); - }, - toggle_void_transaction: function(frm, cdt, cdn) { - folio_transaction = frm.get_doc(cdt, cdn).folio_transaction; - if (void_shown == false) { - folio_transaction.forEach(show_void); - void_shown = true; - frm.fields_dict['toggle_void_transaction'].label = 'Hide Void Transaction'; - frm.refresh_field('toggle_void_transaction'); - } - else { - folio_transaction.forEach(hide_void); - void_shown = false; - frm.fields_dict['toggle_void_transaction'].label = 'Show Void Transaction'; - frm.refresh_field('toggle_void_transaction'); - } - }, - refresh: function (frm, cdt, cdn) { - make_read_only(frm); - if (frm.doc.__islocal !== 1) { - var x = frappe.get_doc(cdt, cdn).folio_transaction; - if (x) { - x.forEach(hide_void); - } - if (frm.doc.status === 'Open') { - toggle_visibility_buttons(frm, 0); - // Auto update balance if needed - frappe.call({ - method: 'inn.inn_hotels.doctype.inn_folio.inn_folio.need_to_update_balance', - args: { - folio_id: frm.doc.name - }, - callback: (r) => { - if (r.message === 1) { - // update needed - frappe.call({ - method: 'inn.inn_hotels.doctype.inn_folio.inn_folio.update_balance', - args: { - folio_id: frm.doc.name - }, - callback: (r) => { - if (r.message) { - frm.doc.total_debit = r.message[0]; - frm.doc.total_credit = r.message[1]; - frm.doc.balance = r.message[2]; - frm.refresh_field('total_debit'); - frm.refresh_field('total_credit'); - frm.refresh_field('balance'); - } - } - }); - } - } - }); - // Close folio manually for Folio type master or desk - if (frm.doc.type !== 'Guest') { - frm.page.add_menu_item(__('Close Folio'), function () { - if (frm.doc.balance !== 0) { - frappe.msgprint("Balance is not 0. There are still transactions needed to be resolved."); - } - else { - close_folio(frm); - } - }); - } - } - toggle_guest_in_type(frm, 0); - // Show Reservation Button - if (frm.doc.reservation_id !== undefined) { - frm.add_custom_button(__('Show Reservation'), function () { - let url = frappe.urllib.get_full_url('/app/inn-reservation/' + frm.doc.reservation_id); - if (is_check_in === 'true') { - url = url + '?is_check_in=true' - } - var w = window.open(url, "_self"); - }); - } - // Update Balance Button - if (frm.doc.status !== 'Cancel') { - frm.add_custom_button(__('Update Balance'), function () { - frappe.call({ - method: 'inn.inn_hotels.doctype.inn_folio.inn_folio.need_to_update_balance', - args: { - folio_id: frm.doc.name - }, - callback: (r) => { - if (r.message === 1) { - frappe.call({ - method: 'inn.inn_hotels.doctype.inn_folio.inn_folio.update_balance', - args: { - folio_id: frm.doc.name - }, - callback: (r) => { - if (r.message) { - frappe.show_alert("Balance updated."); - frm.doc.total_debit = r.message[0]; - frm.doc.total_credit = r.message[1]; - frm.doc.balance = r.message[2]; - frm.refresh_field('total_debit'); - frm.refresh_field('total_credit'); - frm.refresh_field('balance'); - } - } - }); - } - else { - frappe.show_alert('Balance already updated.'); - } - } - }); - }); - } - } - else { - toggle_visibility_buttons(frm, 1); - toggle_guest_in_type(frm, 1); - } - }, - close: function (frm) { - if (frm.doc.type != 'Desk') { - if (frm.doc.close < frm.doc.open) { - frm.set_value('close', null); - frappe.msgprint('Close Date must be greater than Open Date'); - } - } - } +frappe.ui.form.on("Inn Folio", { + before_save: function (frm) { + make_mandatory(frm); +}, +onload: function (frm) { + frm.get_field("folio_transaction").grid.only_sortable(); + make_read_only(frm); + make_fields_filtered(frm); + + // Fetch the exchange rate and currency symbol from the linked Inn Reservation + frappe.call({ + method: "inn.inn_hotels.doctype.inn_folio_transaction_type.inn_folio_transaction_type.get_exchange_rate", + args: { + reservation_id: frm.doc.reservation_id, + }, + callback: (response) => { + if (response.message) { + const { exchange_rate, currency_symbol } = response.message; + + // Proceed only if exchange_rate is available and valid + if (exchange_rate) { + const total_debit = frm.doc.total_debit || 0; + const total_credit = frm.doc.total_credit || 0; + const balance = frm.doc.balance || 0; + + // Calculate amounts in the base currency + const total_debit_by_currency = total_debit / exchange_rate; + const total_credit_by_currency = total_credit / exchange_rate; + const balance_by_currency = balance / exchange_rate; + + // Format the values with the currency symbol for display + const formatted_total_debit_by_currency = format_currency(total_debit_by_currency, currency_symbol); + const formatted_total_credit_by_currency = format_currency(total_credit_by_currency, currency_symbol); + const formatted_balance_by_currency = format_currency(balance_by_currency, currency_symbol); + + // Set the calculated values back to the form fields + frm.set_value("total_debit_by_currency", formatted_total_debit_by_currency); + frm.set_value("total_credit_by_currency", formatted_total_credit_by_currency); + frm.set_value("balance_by_currency", formatted_balance_by_currency); + + // Refresh the fields to reflect the updated values + frm.refresh_field("total_debit_by_currency"); + frm.refresh_field("total_credit_by_currency"); + frm.refresh_field("balance_by_currency"); + + // Optionally, update the UI display if the fields exist + if (frm.fields_dict.total_debit_by_currency && frm.fields_dict.total_debit_by_currency.$wrapper) { + frm.fields_dict.total_debit_by_currency.$wrapper.find(".control-value").text(formatted_total_debit_by_currency); + } + if (frm.fields_dict.total_credit_by_currency && frm.fields_dict.total_credit_by_currency.$wrapper) { + frm.fields_dict.total_credit_by_currency.$wrapper.find(".control-value").text(formatted_total_credit_by_currency); + } + if (frm.fields_dict.balance_by_currency && frm.fields_dict.balance_by_currency.$wrapper) { + frm.fields_dict.balance_by_currency.$wrapper.find(".control-value").text(formatted_balance_by_currency); + } + } else { + // Hide the currency fields if exchange_rate is not available + frm.toggle_display("total_debit_by_currency", false); + frm.toggle_display("total_credit_by_currency", false); + frm.toggle_display("balance_by_currency", false); + // frappe.msgprint(__("Exchange rate not found for the linked reservation.")); + } + } + }, + }); +}, + + transfer_to_another_folio: function (frm) { + if (frm.doc.__islocal !== 1) { + let trx_selected = frm.get_field("folio_transaction").grid.get_selected(); + if (trx_selected.length === 0) { + frappe.msgprint( + "Please select at least one transaction to be transfered" + ); + } else { + transfer_to_another_folio(frm, trx_selected); + } + } + }, + add_package: function (frm) { + add_package(frm); + }, + add_charge: function (frm) { + add_charge(frm); + }, + add_payment: function (frm) { + add_payment(frm); + }, + add_refund: function (frm) { + add_refund(frm); + }, + toggle_void_transaction: function (frm, cdt, cdn) { + folio_transaction = frm.get_doc(cdt, cdn).folio_transaction; + if (void_shown == false) { + folio_transaction.forEach(show_void); + void_shown = true; + frm.fields_dict["toggle_void_transaction"].label = + "Hide Void Transaction"; + frm.refresh_field("toggle_void_transaction"); + } else { + folio_transaction.forEach(hide_void); + void_shown = false; + frm.fields_dict["toggle_void_transaction"].label = + "Show Void Transaction"; + frm.refresh_field("toggle_void_transaction"); + } + }, + refresh: function (frm, cdt, cdn) { + make_read_only(frm); + if (frm.doc.__islocal !== 1) { + var x = frappe.get_doc(cdt, cdn).folio_transaction; + if (x) { + x.forEach(hide_void); + } + if (frm.doc.status === "Open") { + toggle_visibility_buttons(frm, 0); + // Auto update balance if needed + frappe.call({ + method: + "inn.inn_hotels.doctype.inn_folio.inn_folio.need_to_update_balance", + args: { + folio_id: frm.doc.name, + }, + callback: (r) => { + if (r.message === 1) { + // update needed + frappe.call({ + method: + "inn.inn_hotels.doctype.inn_folio.inn_folio.update_balance", + args: { + folio_id: frm.doc.name, + }, + callback: (r) => { + if (r.message) { + frm.doc.total_debit = r.message[0]; + frm.doc.total_credit = r.message[1]; + frm.doc.balance = r.message[2]; + frm.refresh_field("total_debit"); + frm.refresh_field("total_credit"); + frm.refresh_field("balance"); + } + }, + }); + } + }, + }); + // Close folio manually for Folio type master or desk + if (frm.doc.type !== "Guest") { + frm.page.add_menu_item(__("Close Folio"), function () { + if (frm.doc.balance !== 0) { + frappe.msgprint( + "Balance is not 0. There are still transactions needed to be resolved." + ); + } else { + close_folio(frm); + } + }); + } + } + toggle_guest_in_type(frm, 0); + // Show Reservation Button + if (frm.doc.reservation_id !== undefined) { + frm.add_custom_button(__("Show Reservation"), function () { + let url = frappe.urllib.get_full_url( + "/app/inn-reservation/" + frm.doc.reservation_id + ); + if (is_check_in === "true") { + url = url + "?is_check_in=true"; + } + var w = window.open(url, "_self"); + }); + } + // Update Balance Button + if (frm.doc.status !== "Cancel") { + frm.add_custom_button(__("Update Balance"), function () { + frappe.call({ + method: + "inn.inn_hotels.doctype.inn_folio.inn_folio.need_to_update_balance", + args: { + folio_id: frm.doc.name, + }, + callback: (r) => { + if (r.message === 1) { + frappe.call({ + method: + "inn.inn_hotels.doctype.inn_folio.inn_folio.update_balance", + args: { + folio_id: frm.doc.name, + }, + callback: (r) => { + if (r.message) { + frappe.show_alert("Balance updated."); + frm.doc.total_debit = r.message[0]; + frm.doc.total_credit = r.message[1]; + frm.doc.balance = r.message[2]; + frm.refresh_field("total_debit"); + frm.refresh_field("total_credit"); + frm.refresh_field("balance"); + } + }, + }); + } else { + frappe.show_alert("Balance already updated."); + } + }, + }); + }); + } + } else { + toggle_visibility_buttons(frm, 1); + toggle_guest_in_type(frm, 1); + } + }, + close: function (frm) { + if (frm.doc.type != "Desk") { + if (frm.doc.close < frm.doc.open) { + frm.set_value("close", null); + frappe.msgprint("Close Date must be greater than Open Date"); + } + } + }, }); -frappe.ui.form.on('Inn Folio Transaction', { - void_transaction: function (frm, cdt, cdn) { - let child = locals[cdt][cdn]; - void_transaction(child); - } +frappe.ui.form.on("Inn Folio Transaction", { + void_transaction: function (frm, cdt, cdn) { + let child = locals[cdt][cdn]; + void_transaction(child); + }, }); // Function to extract variable's value passed on URL function getUrlVars() { - var vars = {}; - var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m,key,value) { - vars[key] = value; - }); - return vars; + var vars = {}; + var parts = window.location.href.replace( + /[?&]+([^=&]+)=([^&]*)/gi, + function (m, key, value) { + vars[key] = value; + } + ); + return vars; } // Function to make form disabled if status cancel function make_read_only(frm) { - let active_flag = 0; - if (frm.doc.status !== 'Open') { - active_flag = 1; - } - else { - active_flag = 0; - } - - frm.set_df_property('sb4', 'hidden', active_flag); - frm.set_df_property('transfer_to_another_folio', 'hidden', active_flag); - frm.set_df_property('open', 'read_only', active_flag); - frm.set_df_property('close', 'read_only', active_flag); - frm.set_df_property('reservation_id', 'read_only', active_flag); - frm.set_df_property('customer_id', 'read_only', active_flag); - frm.set_df_property('type', 'read_only', active_flag); - frm.set_df_property('group_id', 'read_only', active_flag); - - frappe.meta.get_docfield('Inn Folio Transaction', 'void_transaction', frm.doc.name).hidden = active_flag; - frappe.meta.get_docfield('Inn Folio Transaction', 'flag', frm.doc.name).read_only = active_flag; - frappe.meta.get_docfield('Inn Folio Transaction', 'transaction_type', frm.doc.name).read_only = active_flag; - frappe.meta.get_docfield('Inn Folio Transaction', 'amount', frm.doc.name).read_only = active_flag; - frappe.meta.get_docfield('Inn Folio Transaction', 'debit_account', frm.doc.name).read_only = active_flag; - frappe.meta.get_docfield('Inn Folio Transaction', 'credit_account', frm.doc.name).read_only = active_flag; - frappe.meta.get_docfield('Inn Folio Transaction', 'remark', frm.doc.name).read_only = active_flag; + let active_flag = 0; + if (frm.doc.status !== "Open") { + active_flag = 1; + } else { + active_flag = 0; + } + + frm.set_df_property("sb4", "hidden", active_flag); + frm.set_df_property("transfer_to_another_folio", "hidden", active_flag); + frm.set_df_property("open", "read_only", active_flag); + frm.set_df_property("close", "read_only", active_flag); + frm.set_df_property("reservation_id", "read_only", active_flag); + frm.set_df_property("customer_id", "read_only", active_flag); + frm.set_df_property("type", "read_only", active_flag); + frm.set_df_property("group_id", "read_only", active_flag); + frappe.meta.get_docfield( + "Inn Folio Transaction", + "void_transaction", + frm.doc.name + ).hidden = active_flag; + frappe.meta.get_docfield( + "Inn Folio Transaction", + "flag", + frm.doc.name + ).read_only = active_flag; + frappe.meta.get_docfield( + "Inn Folio Transaction", + "transaction_type", + frm.doc.name + ).read_only = active_flag; + frappe.meta.get_docfield( + "Inn Folio Transaction", + "amount", + frm.doc.name + ).read_only = active_flag; + frappe.meta.get_docfield( + "Inn Folio Transaction", + "debit_account", + frm.doc.name + ).read_only = active_flag; + frappe.meta.get_docfield( + "Inn Folio Transaction", + "credit_account", + frm.doc.name + ).read_only = active_flag; + frappe.meta.get_docfield( + "Inn Folio Transaction", + "remark", + frm.doc.name + ).read_only = active_flag; } // Function to toggle visibility of buttons when necessary function toggle_visibility_buttons(frm, active_flag) { - frm.set_df_property('sb4', 'hidden', active_flag); - frm.set_df_property('transfer_to_another_folio', 'hidden', active_flag); + frm.set_df_property("sb4", "hidden", active_flag); + frm.set_df_property("transfer_to_another_folio", "hidden", active_flag); } // Function to toggle visibility of Guest options in Type function toggle_guest_in_type(frm, is_new) { - if (is_new === 1) { - frm.set_df_property('type', 'options', ['Master', 'Desk']); - frm.set_value('type', 'Master'); - } - else { - if (frm.doc.type === 'Guest') { - frm.set_df_property('type', 'read_only', 1); - frm.set_df_property('type', 'options', ['Guest', 'Master', 'Desk']); - } - else { - frm.set_df_property('type', 'options', ['Master', 'Desk']); - } - } - frm.refresh_field('type'); + if (is_new === 1) { + frm.set_df_property("type", "options", ["Master", "Desk"]); + frm.set_value("type", "Master"); + } else { + if (frm.doc.type === "Guest") { + frm.set_df_property("type", "read_only", 1); + frm.set_df_property("type", "options", ["Guest", "Master", "Desk"]); + } else { + frm.set_df_property("type", "options", ["Master", "Desk"]); + } + } + frm.refresh_field("type"); } // Function to show pop up Dialof for adding new package to the folio function add_package(frm) { - frappe.call({ - method: 'inn.inn_hotels.doctype.inn_package.inn_package.get_package_list', - args: { - active_flag: 1 - }, - callback: (r) => { - let fields = [ - { - 'label': __('Package Name'), - 'fieldname': 'package_name', - 'fieldtype': 'Select', - 'options': r.message, - 'reqd': 1 - }, - { - 'label': __('Sub Folio'), - 'fieldname': 'sub_folio', - 'fieldtype': 'Select', - 'options': [ - {'label': __('A'), 'value': 'A'}, {'label': __('B'), 'value': 'B'}, - {'label': __('C'), 'value': 'C'}, {'label': __('D'), 'value': 'D'} - ], - 'default': 'A', - 'reqd':1 - }, - { - 'label': 'Remark', - 'fieldname': 'remark', - 'fieldtype': 'Small Text', - }, - ]; - var d = new frappe.ui.Dialog({ - title: __('Add New Package for Folio ' + frm.doc.name), - fields: fields, - }); - d.set_primary_action(__('Save'), () => { - let remark_to_save = d.get_values().package_name + '.\n'; - if (d.get_values().remark !== undefined || d.get_values().remark != null) { - remark_to_save += d.get_values().remark; - } - frappe.call({ - method: 'inn.inn_hotels.doctype.inn_folio_transaction.inn_folio_transaction.add_package_charge', - args: { - package_name: d.get_values().package_name, - sub_folio: d.get_values().sub_folio, - remark: remark_to_save, - parent: frm.doc.name - }, - callback: (r) => { - if (r.message) { - frappe.msgprint('Charge with ID ' + r.message + " successfully added"); - frm.reload_doc(); - } - } - }); - d.hide(); - }); - d.show(); - } - }); + frappe.call({ + method: "inn.inn_hotels.doctype.inn_package.inn_package.get_package_list", + args: { + active_flag: 1, + }, + callback: (r) => { + let fields = [ + { + label: __("Package Name"), + fieldname: "package_name", + fieldtype: "Select", + options: r.message, + reqd: 1, + }, + { + label: __("Sub Folio"), + fieldname: "sub_folio", + fieldtype: "Select", + options: [ + { label: __("A"), value: "A" }, + { label: __("B"), value: "B" }, + { label: __("C"), value: "C" }, + { label: __("D"), value: "D" }, + ], + default: "A", + reqd: 1, + }, + { + label: "Remark", + fieldname: "remark", + fieldtype: "Small Text", + }, + ]; + var d = new frappe.ui.Dialog({ + title: __("Add New Package for Folio " + frm.doc.name), + fields: fields, + }); + d.set_primary_action(__("Save"), () => { + let remark_to_save = d.get_values().package_name + ".\n"; + if ( + d.get_values().remark !== undefined || + d.get_values().remark != null + ) { + remark_to_save += d.get_values().remark; + } + frappe.call({ + method: + "inn.inn_hotels.doctype.inn_folio_transaction.inn_folio_transaction.add_package_charge", + args: { + package_name: d.get_values().package_name, + sub_folio: d.get_values().sub_folio, + remark: remark_to_save, + parent: frm.doc.name, + }, + callback: (r) => { + if (r.message) { + frappe.msgprint( + "Charge with ID " + r.message + " successfully added" + ); + frm.reload_doc(); + } + }, + }); + d.hide(); + }); + d.show(); + }, + }); } // Function to show pop up Dialog for adding new charge to the folio function add_charge(frm) { - frappe.call({ - method: 'inn.inn_hotels.doctype.inn_folio_transaction_type.inn_folio_transaction_type.get_transaction_type', - args: { - type: 'Debit' - }, - callback: (r)=> { - let fields = [ - { - 'label': __('Transaction Type'), - 'fieldname': 'transaction_type', - 'fieldtype': 'Select', - 'options': r.message, - 'reqd': 1 - }, - { - 'fieldname': 'accb0', - 'fieldtype': 'Column Break' - }, - { - 'label': __('Amount'), - 'fieldname': 'amount', - 'fieldtype': 'Currency', - 'columns': 2, - 'reqd': 1 - }, - { - 'fieldname': 'accb1', - 'fieldtype': 'Column Break' - }, - { - 'label': __('Sub Folio'), - 'fieldname': 'sub_folio', - 'fieldtype': 'Select', - 'options': [ - {'label': __('A'), 'value': 'A'}, {'label': __('B'), 'value': 'B'}, - {'label': __('C'), 'value': 'C'}, {'label': __('D'), 'value': 'D'} - ], - 'default': 'A', - 'reqd':1 - }, - { - 'fieldname': 'acsb0', - 'fieldtype': 'Section Break' - }, - { - 'label': 'Remark', - 'fieldname': 'remark', - 'fieldtype': 'Small Text', - }, - ]; - var d = new frappe.ui.Dialog({ - title: __('Add New Charge for Folio ' + frm.doc.name), - fields: fields, - }); - d.set_primary_action(__('Save'), () => { - let remark_to_save = ''; - let values = d.get_values() - if (values.amount == 0) { - frappe.msgprint({ - title: __("Validation Error"), - indicator: "red", - message: __("Amount cannot be zero") - }) - return - } - - if (d.get_values().remark !== undefined || d.get_values().remark != null) { - remark_to_save = d.get_values().remark; - } - frappe.call({ - method: 'inn.inn_hotels.doctype.inn_folio_transaction.inn_folio_transaction.add_charge', - args: { - transaction_type: d.get_values().transaction_type, - amount: d.get_values().amount, - sub_folio: d.get_values().sub_folio, - remark: remark_to_save, - parent: frm.doc.name - }, - callback: (r) => { - if (r.message) { - frappe.msgprint('Charge with ID ' + r.message + " successfully added"); - frm.reload_doc(); - } - } - }); - d.hide(); - }); - d.show(); - } - }); + frappe.call({ + method: + "inn.inn_hotels.doctype.inn_folio_transaction_type.inn_folio_transaction_type.get_transaction_type", + args: { + type: "Debit", + }, + callback: (r) => { + let fields = [ + { + label: __("Transaction Type"), + fieldname: "transaction_type", + fieldtype: "Select", + options: r.message, + reqd: 1, + }, + { + fieldname: "accb0", + fieldtype: "Column Break", + }, + { + label: __("Amount"), + fieldname: "amount", + fieldtype: "Currency", + columns: 2, + reqd: 1, + }, + { + label: __("Base Room Rate By Currency"), + fieldname: "base_room_rate_by_currency", + fieldtype: "Currency", + columns: 2, + reqd: 1, + hidden: 1, + }, + { + fieldname: "accb1", + fieldtype: "Column Break", + }, + { + label: __("Sub Folio"), + fieldname: "sub_folio", + fieldtype: "Select", + options: [ + { label: __("A"), value: "A" }, + { label: __("B"), value: "B" }, + { label: __("C"), value: "C" }, + { label: __("D"), value: "D" }, + ], + default: "A", + reqd: 1, + }, + { + fieldname: "acsb0", + fieldtype: "Section Break", + }, + { + label: "Remark", + fieldname: "remark", + fieldtype: "Small Text", + }, + ]; + var d = new frappe.ui.Dialog({ + title: __("Add New Charge for Folio " + frm.doc.name), + fields: fields, + }); + + // Fetch exchange rate and currency symbol + frappe.call({ + method: "inn.inn_hotels.doctype.inn_folio_transaction_type.inn_folio_transaction_type.get_exchange_rate", + args: { reservation_id: frm.doc.reservation_id }, + callback: (exchange_rate_response) => { + const { exchange_rate, currency_symbol } = exchange_rate_response.message; + + // Show or hide the Base Room Rate By Currency field based on exchange_rate + if (exchange_rate && exchange_rate !== 0) { + d.fields_dict.base_room_rate_by_currency.df.hidden = 0; + d.fields_dict.base_room_rate_by_currency.df.label = + __("Base Room Rate By Currency") + ` (${currency_symbol})`; + } else { + d.fields_dict.base_room_rate_by_currency.df.hidden = 1; + } + + // Add currency symbol to labels for clarity + d.fields_dict.amount.df.label = __("Amount") + ` (${frappe.sys_defaults.currency})`; + d.refresh(); + + // Add event listeners for dynamic calculations + d.fields_dict.amount.$input.on("input", () => { + const amount = d.get_value("amount"); + if (amount && exchange_rate && exchange_rate !== 0) { + d.set_value("base_room_rate_by_currency", amount / exchange_rate); + } + }); + + d.fields_dict.base_room_rate_by_currency.$input.on("input", () => { + const base_room_rate_by_currency = d.get_value("base_room_rate_by_currency"); + if (base_room_rate_by_currency && exchange_rate && exchange_rate !== 0) { + d.set_value("amount", base_room_rate_by_currency * exchange_rate); + } + }); + }, + }); + + d.set_primary_action(__("Save"), () => { + let remark_to_save = ""; + let values = d.get_values(); + if (values.amount == 0) { + frappe.msgprint({ + title: __("Validation Error"), + indicator: "red", + message: __("Amount cannot be zero"), + }); + return; + } + + if ( + d.get_values().remark !== undefined || + d.get_values().remark != null + ) { + remark_to_save = d.get_values().remark; + } + frappe.call({ + method: + "inn.inn_hotels.doctype.inn_folio_transaction.inn_folio_transaction.add_charge", + args: { + transaction_type: d.get_values().transaction_type, + amount: d.get_values().amount, + sub_folio: d.get_values().sub_folio, + remark: remark_to_save, + parent: frm.doc.name, + }, + callback: (r) => { + if (r.message) { + frappe.msgprint( + "Charge with ID " + r.message + " successfully added" + ); + frm.reload_doc(); + } + }, + }); + d.hide(); + }); + d.show(); + }, + }); } // Function to show pop up Dialog for adding new payment to the folio function add_payment(frm) { - frappe.call({ - method: 'inn.inn_hotels.doctype.inn_folio_transaction_type.inn_folio_transaction_type.get_transaction_type', - args: { - type: 'Credit' - }, - callback: (r)=> { - let fields = [ - { - 'label': __('Transaction Type'), - 'fieldname': 'transaction_type', - 'fieldtype': 'Select', - 'options': r.message, - 'reqd': 1 - }, - { - 'fieldname': 'accb0', - 'fieldtype': 'Column Break' - }, - { - 'label': __('Amount'), - 'fieldname': 'amount', - 'fieldtype': 'Currency', - 'columns': 2, - 'reqd': 1 - }, - { - 'fieldname': 'acsb0', - 'fieldtype': 'Section Break' - }, - { - 'label': __('Mode of Payment'), - 'fieldname': 'mode_of_payment', - 'fieldtype': 'Link', - 'options': 'Mode of Payment', - 'reqd': 1 - }, - { - 'fieldname': 'accb1', - 'fieldtype': 'Column Break' - }, - { - 'label': __('Sub Folio'), - 'fieldname': 'sub_folio', - 'fieldtype': 'Select', - 'options': [ - {'label': __('A'), 'value': 'A'}, {'label': __('B'), 'value': 'B'}, - {'label': __('C'), 'value': 'C'}, {'label': __('D'), 'value': 'D'} - ], - 'default': 'A', - 'reqd':1 - }, - { - 'fieldname': 'acsb1', - 'fieldtype': 'Section Break' - }, - { - 'label': 'Remark', - 'fieldname': 'remark', - 'fieldtype': 'Small Text', - }, - ]; - var d = new frappe.ui.Dialog({ - title: __('Add New Payment for Folio ' + frm.doc.name), - fields: fields, - }); - d.set_primary_action(__('Save'), () => { - let remark_to_save = ''; - - let values = d.get_values() - if (values.amount == 0) { - frappe.msgprint({ - title: __("Validation Error"), - indicator: "red", - message: __("Amount cannot be zero") - }) - return - } - - if (d.get_values.remark !== undefined) { - remark_to_save = d.get_values.remark; - } - frappe.call({ - method: 'inn.inn_hotels.doctype.inn_folio_transaction.inn_folio_transaction.add_payment', - args: { - transaction_type: d.get_values().transaction_type, - amount: d.get_values().amount, - mode_of_payment: d.get_values().mode_of_payment, - sub_folio: d.get_values().sub_folio, - remark: remark_to_save, - parent: frm.doc.name - }, - callback: (r) => { - if (r.message) { - frappe.msgprint('Payment with ID ' + r.message + " successfully added"); - frm.reload_doc(); - } - } - }); - d.hide(); - }); - d.show(); - } - }); + frappe.call({ + method: + "inn.inn_hotels.doctype.inn_folio_transaction_type.inn_folio_transaction_type.get_transaction_type", + args: { + type: "Credit" + }, + callback: (r) => { + const fields = [ + { + label: __("Transaction Type"), + fieldname: "transaction_type", + fieldtype: "Select", + options: r.message, + reqd: 1, + }, + { + fieldname: "accb0", + fieldtype: "Column Break" + }, + { + label: __("Amount"), + fieldname: "amount", + fieldtype: "Currency", + columns: 2, + reqd: 1, + }, + { + label: __("Base Room Rate By Currency"), + fieldname: "base_room_rate_by_currency", + fieldtype: "Currency", + columns: 2, + reqd: 1, + hidden: 1, + }, + { fieldname: "acsb0", + fieldtype: "Section Break" + }, + { + label: __("Mode of Payment"), + fieldname: "mode_of_payment", + fieldtype: "Link", + options: "Mode of Payment", + reqd: 1, + }, + { fieldname: "accb1", + fieldtype: "Column Break" + }, + { + label: __("Sub Folio"), + fieldname: "sub_folio", + fieldtype: "Select", + options: [ + { label: __("A"), value: "A" }, + { label: __("B"), value: "B" }, + { label: __("C"), value: "C" }, + { label: __("D"), value: "D" }, + ], + default: "A", + reqd: 1, + }, + { + fieldname: "acsb1", + fieldtype: "Section Break" + }, + { + label: __("Remark"), + fieldname: "remark", + fieldtype: "Small Text", + }, + ]; + + const d = new frappe.ui.Dialog({ + title: __("Add New Payment for Folio " + frm.doc.name), + fields: fields, + }); + + // Fetch exchange rate and currency symbol + frappe.call({ + method: "inn.inn_hotels.doctype.inn_folio_transaction_type.inn_folio_transaction_type.get_exchange_rate", + args: { reservation_id: frm.doc.reservation_id }, + callback: (exchange_rate_response) => { + const { exchange_rate, currency_symbol } = exchange_rate_response.message; + + // Show or hide the Base Room Rate By Currency field based on exchange_rate + if (exchange_rate && exchange_rate !== 0) { + d.fields_dict.base_room_rate_by_currency.df.hidden = 0; + d.fields_dict.base_room_rate_by_currency.df.label = + __("Base Room Rate By Currency") + ` (${currency_symbol})`; + } else { + d.fields_dict.base_room_rate_by_currency.df.hidden = 1; + } + + // Add currency symbol to labels for clarity + d.fields_dict.amount.df.label = __("Amount") + ` (${frappe.sys_defaults.currency})`; + d.refresh(); + + // Add event listeners for dynamic calculations + d.fields_dict.amount.$input.on("input", () => { + const amount = d.get_value("amount"); + if (amount && exchange_rate && exchange_rate !== 0) { + d.set_value("base_room_rate_by_currency", amount / exchange_rate); + } + }); + + d.fields_dict.base_room_rate_by_currency.$input.on("input", () => { + const base_room_rate_by_currency = d.get_value("base_room_rate_by_currency"); + if (base_room_rate_by_currency && exchange_rate && exchange_rate !== 0) { + d.set_value("amount", base_room_rate_by_currency * exchange_rate); + } + }); + }, + }); + + d.set_primary_action(__("Save"), () => { + let remark_to_save = ""; + + let values = d.get_values(); + if (values.amount == 0) { + frappe.msgprint({ + title: __("Validation Error"), + indicator: "red", + message: __("Amount cannot be zero"), + }); + return; + } + + if (d.get_values().remark !== undefined) { + remark_to_save = d.get_values().remark; + } + frappe.call({ + method: + "inn.inn_hotels.doctype.inn_folio_transaction.inn_folio_transaction.add_payment", + args: { + transaction_type: d.get_values().transaction_type, + amount: d.get_values().amount, + mode_of_payment: d.get_values().mode_of_payment, + sub_folio: d.get_values().sub_folio, + remark: remark_to_save, + parent: frm.doc.name, + }, + callback: (r) => { + if (r.message) { + frappe.msgprint( + "Payment with ID " + r.message + " successfully added" + ); + frm.reload_doc(); + } + }, + }); + d.hide(); + }); + d.show(); + }, + }); } // Function to show pop up Dialog for Adding Refund to folio function add_refund(frm) { - var d = new frappe.ui.Dialog({ - title: __('Add New Refund to Folio ' + frm.doc.name), - fields: [ - { - 'label': __('Amount'), - 'fieldname': 'amount', - 'fieldtype': 'Currency', - 'columns': 2, - 'reqd': 1 - }, - { - 'fieldname': 'arcb0', - 'fieldtype': 'Column Break' - }, - { - 'label': __('Sub Folio'), - 'fieldname': 'sub_folio', - 'fieldtype': 'Select', - 'options': [ - {'label': __('A'), 'value': 'A'}, {'label': __('B'), 'value': 'B'}, - {'label': __('C'), 'value': 'C'}, {'label': __('D'), 'value': 'D'} - ], - 'default': 'A', - 'reqd':1 - }, - { - 'fieldname': 'arsb0', - 'fieldtype': 'Section Break' - }, - { - 'label': 'Remark', - 'fieldname': 'remark', - 'fieldtype': 'Small Text', - }, - ] - }); - if (frm.doc.balance > 0) { - d.set_value('amount', frm.doc.balance); - } - d.set_primary_action(__('Save'), () => { - let remark_to_save = ''; - - let values = d.get_values() - if (values.amount == 0) { - frappe.msgprint({ - title: __("Validation Error"), - indicator: "red", - message: __("Amount cannot be zero") - }) - return - } - if (d.get_values.remark !== undefined) { - remark_to_save = d.get_values.remark; - } - frappe.call({ - method: 'inn.inn_hotels.doctype.inn_folio_transaction.inn_folio_transaction.add_charge', - args: { - transaction_type: 'Refund', - amount: d.get_values().amount, - sub_folio: d.get_values().sub_folio, - remark: remark_to_save, - parent: frm.doc.name - }, - callback: (r) => { - if (r.message) { - frappe.msgprint('Refund with ID ' + r.message + " successfully added"); - frm.reload_doc(); - } - } - }); - d.hide(); - }); - d.show(); + var d = new frappe.ui.Dialog({ + title: __("Add New Refund to Folio " + frm.doc.name), + fields: [ + { + label: __("Amount"), + fieldname: "amount", + fieldtype: "Currency", + columns: 2, + reqd: 1, + }, + { + label: __("Base Room Rate By Currency"), + fieldname: "base_room_rate_by_currency", + fieldtype: "Currency", + columns: 2, + reqd: 1, + hidden: 1, + }, + { + fieldname: "arcb0", + fieldtype: "Column Break", + }, + { + label: __("Sub Folio"), + fieldname: "sub_folio", + fieldtype: "Select", + options: [ + { label: __("A"), value: "A" }, + { label: __("B"), value: "B" }, + { label: __("C"), value: "C" }, + { label: __("D"), value: "D" }, + ], + default: "A", + reqd: 1, + }, + { + fieldname: "arsb0", + fieldtype: "Section Break", + }, + { + label: "Remark", + fieldname: "remark", + fieldtype: "Small Text", + }, + ], + }); + + // Set default amount to the current balance if balance is positive + if (frm.doc.balance > 0) { + d.set_value("amount", frm.doc.balance); + } + + // Fetch exchange rate and currency symbol + frappe.call({ + method: "inn.inn_hotels.doctype.inn_folio_transaction_type.inn_folio_transaction_type.get_exchange_rate", + args: { reservation_id: frm.doc.reservation_id }, + callback: (exchange_rate_response) => { + const { exchange_rate, currency_symbol } = exchange_rate_response.message; + + // Show or hide the Base Room Rate By Currency field based on exchange_rate + if (exchange_rate && exchange_rate !== 0) { + d.fields_dict.base_room_rate_by_currency.df.hidden = 0; + d.fields_dict.base_room_rate_by_currency.df.label = + __("Base Room Rate By Currency") + ` (${currency_symbol})`; + } else { + d.fields_dict.base_room_rate_by_currency.df.hidden = 1; + } + + // Add currency symbol to labels for clarity + d.fields_dict.amount.df.label = __("Amount") + ` (${frappe.sys_defaults.currency})`; + d.refresh(); + + // Add event listeners for dynamic calculations + d.fields_dict.amount.$input.on("input", () => { + const amount = d.get_value("amount"); + if (amount && exchange_rate && exchange_rate !== 0) { + d.set_value("base_room_rate_by_currency", amount / exchange_rate); + } + }); + + d.fields_dict.base_room_rate_by_currency.$input.on("input", () => { + const base_room_rate_by_currency = d.get_value("base_room_rate_by_currency"); + if (base_room_rate_by_currency && exchange_rate && exchange_rate !== 0) { + d.set_value("amount", base_room_rate_by_currency * exchange_rate); + } + }); + }, + }); + + d.set_primary_action(__("Save"), () => { + let remark_to_save = ""; + + let values = d.get_values(); + if (values.amount == 0) { + frappe.msgprint({ + title: __("Validation Error"), + indicator: "red", + message: __("Amount cannot be zero"), + }); + return; + } + + if (d.get_values().remark !== undefined) { + remark_to_save = d.get_values().remark; + } + + frappe.call({ + method: + "inn.inn_hotels.doctype.inn_folio_transaction.inn_folio_transaction.add_charge", + args: { + transaction_type: "Refund", + amount: d.get_values().amount, + sub_folio: d.get_values().sub_folio, + remark: remark_to_save, + parent: frm.doc.name, + }, + callback: (r) => { + if (r.message) { + frappe.msgprint( + "Refund with ID " + r.message + " successfully added" + ); + frm.reload_doc(); + } + }, + }); + d.hide(); + }); + + d.show(); } // Function to show pop up Dialog for transferring transaction selected to another folio function transfer_to_another_folio(frm, trx_selected) { - var d = new frappe.ui.Dialog({ - title: __('Transfer Transactions to Another Folio'), - fields: [ - { - 'label': 'Transfer to Folio: ', - 'fieldname': 'receiving_folio', - 'fieldtype': 'Link', - 'options': 'Inn Folio', - 'get_query': function () { - return { - filters: [ - ['Inn Folio', 'name', '!=', frm.doc.name], - ['Inn Folio', 'status', '=', 'Open'], - ] - } - }, - reqd: 1 - }, - ] - }); - d.set_primary_action(__('Transfer'), () => { - frappe.call({ - method: 'inn.inn_hotels.doctype.inn_folio.inn_folio.transfer_to_another_folio', - args: { - trx_list: trx_selected, - old_parent: frm.doc.name, - new_parent: d.get_values().receiving_folio, - }, - callback: (r) => { - if (r.message === 0) { - frappe.msgprint('Transactions transfered to Folio ' + d.get_values().receiving_folio + ' successfully'); - frm.reload_doc(); - } - } - }); - d.hide(); - }); - d.show(); + var d = new frappe.ui.Dialog({ + title: __("Transfer Transactions to Another Folio"), + fields: [ + { + label: "Transfer to Folio: ", + fieldname: "receiving_folio", + fieldtype: "Link", + options: "Inn Folio", + get_query: function () { + return { + filters: [ + ["Inn Folio", "name", "!=", frm.doc.name], + ["Inn Folio", "status", "=", "Open"], + ], + }; + }, + reqd: 1, + }, + ], + }); + d.set_primary_action(__("Transfer"), () => { + frappe.call({ + method: + "inn.inn_hotels.doctype.inn_folio.inn_folio.transfer_to_another_folio", + args: { + trx_list: trx_selected, + old_parent: frm.doc.name, + new_parent: d.get_values().receiving_folio, + }, + callback: (r) => { + if (r.message === 0) { + frappe.msgprint( + "Transactions transfered to Folio " + + d.get_values().receiving_folio + + " successfully" + ); + frm.reload_doc(); + } + }, + }); + d.hide(); + }); + d.show(); } // Function to void single folio transaction function void_transaction(child) { - // frappe.confirm(__("You are about to void this transaction. Are you sure?"), function () { - // if (child.is_void === 0) { - // child.is_void = 1; - // cur_frm.save(); - // frappe.show_alert('Transaction with ID ' + child.name + ' voided successfully.'); - // } - // else { - // frappe.msgprint("This transaction already voided."); - // } - // }); - if (child.is_void === 0) { - if (!child.journal_entry_id) { - if (child.void_id) { - frappe.call({ - method: 'inn.inn_hotels.doctype.inn_void_folio_transaction.inn_void_folio_transaction.request_status', - args: { - id: child.void_id - }, - callback: (r) => { - if (r.message == 'Requested') { - frappe.msgprint("This transaction already requested to be voided. Please wait for supervisor approval."); - } - else if (r.message == 'Denied') { - frappe.call({ - method: 'inn.inn_hotels.doctype.inn_folio_transaction_bundle.inn_folio_transaction_bundle.get_trx_list', - args: { - trx_id: child.name, - len_only: true - }, - callback: (resp) => { - if (resp.message) { - void_window(child, resp.message); - } - } - }); - } - } - }); - } - else { - frappe.call({ - method: 'inn.inn_hotels.doctype.inn_folio_transaction_bundle.inn_folio_transaction_bundle.get_trx_list', - args: { - trx_id: child.name, - len_only: true - }, - callback: (resp) => { - if (resp.message) { - void_window(child, resp.message); - } - } - }); - } - } else { - frappe.msgprint("Cannot void this transaction anymore, because this transaction has been inputted to Journal."); - } - - - } - else { - frappe.msgprint("This transaction already voided."); - } + // frappe.confirm(__("You are about to void this transaction. Are you sure?"), function () { + // if (child.is_void === 0) { + // child.is_void = 1; + // cur_frm.save(); + // frappe.show_alert('Transaction with ID ' + child.name + ' voided successfully.'); + // } + // else { + // frappe.msgprint("This transaction already voided."); + // } + // }); + if (child.is_void === 0) { + if (!child.journal_entry_id) { + if (child.void_id) { + frappe.call({ + method: + "inn.inn_hotels.doctype.inn_void_folio_transaction.inn_void_folio_transaction.request_status", + args: { + id: child.void_id, + }, + callback: (r) => { + if (r.message == "Requested") { + frappe.msgprint( + "This transaction already requested to be voided. Please wait for supervisor approval." + ); + } else if (r.message == "Denied") { + frappe.call({ + method: + "inn.inn_hotels.doctype.inn_folio_transaction_bundle.inn_folio_transaction_bundle.get_trx_list", + args: { + trx_id: child.name, + len_only: true, + }, + callback: (resp) => { + if (resp.message) { + void_window(child, resp.message); + } + }, + }); + } + }, + }); + } else { + frappe.call({ + method: + "inn.inn_hotels.doctype.inn_folio_transaction_bundle.inn_folio_transaction_bundle.get_trx_list", + args: { + trx_id: child.name, + len_only: true, + }, + callback: (resp) => { + if (resp.message) { + void_window(child, resp.message); + } + }, + }); + } + } else { + frappe.msgprint( + "Cannot void this transaction anymore, because this transaction has been inputted to Journal." + ); + } + } else { + frappe.msgprint("This transaction already voided."); + } } function void_window(child, bundle_len) { - let fields = []; - let default_fields= [ - { - 'label': 'Use Passcode', - 'fieldname': 'use_passcode', - 'fieldtype': 'Check', - "description": "Check if you have Supervisor Passcode to bypass the Approval Void Request by Supervisor Process", - }, - { - 'label': 'Supervisor Passcode', - 'fieldname': 'supervisor_passcode', - 'fieldtype': 'Data', - 'depends_on': 'eval:doc.use_passcode==1' - }, - { - 'label': 'Void Reason', - 'fieldname': 'applicant_reason', - 'fieldtype': 'Small Text', - 'reqd': 1 - }, - ]; - let info_field = { - 'label': 'Info:', - 'fieldname': 'Info', - 'fieldtype': 'Small Text', - 'default': 'This transaction is a part of a bundle of transaction that consist of multiple transactions.
' + - 'By voiding this transaction, the other transactions in the bundle will also be voided for data integrity purposes.', - 'read_only': 1 - }; - if (parseInt(bundle_len) > 1) { - fields = [info_field].concat(default_fields); - } - else { - fields = default_fields; - } - var d = new frappe.ui.Dialog({ - title: __('Request Void Transaction ' + child.name), - fields: fields - }); - d.set_primary_action(__('Request Void'), () => { - frappe.call({ - method: 'inn.inn_hotels.doctype.inn_folio_transaction.inn_folio_transaction.void_transaction', - args: { - trx_id: child.name, - use_passcode: d.get_values().use_passcode, - applicant_reason: d.get_values().applicant_reason, - requester: frappe.session.user, - bundle_len: bundle_len, - supervisor_passcode: d.get_values().supervisor_passcode - }, - callback: (r) => { - if (r.message == 0) { - d.hide(); - frappe.msgprint('Transaction with ID ' + child.name + ' voided successfully. Please Reload the Page'); - } - else if (r.message == 2) { - d.hide(); - frappe.msgprint('Request to Void Transaction with ID ' + child.name + ' successfully submitted. ' + - 'Wait for Supervisor approval to finish void process. Please Reload the Page'); - } - } - }); - }); - d.show(); + let fields = []; + let default_fields = [ + { + label: "Use Passcode", + fieldname: "use_passcode", + fieldtype: "Check", + description: + "Check if you have Supervisor Passcode to bypass the Approval Void Request by Supervisor Process", + }, + { + label: "Supervisor Passcode", + fieldname: "supervisor_passcode", + fieldtype: "Data", + depends_on: "eval:doc.use_passcode==1", + }, + { + label: "Void Reason", + fieldname: "applicant_reason", + fieldtype: "Small Text", + reqd: 1, + }, + ]; + let info_field = { + label: "Info:", + fieldname: "Info", + fieldtype: "Small Text", + default: + "This transaction is a part of a bundle of transaction that consist of multiple transactions.
" + + "By voiding this transaction, the other transactions in the bundle will also be voided for data integrity purposes.", + read_only: 1, + }; + if (parseInt(bundle_len) > 1) { + fields = [info_field].concat(default_fields); + } else { + fields = default_fields; + } + var d = new frappe.ui.Dialog({ + title: __("Request Void Transaction " + child.name), + fields: fields, + }); + d.set_primary_action(__("Request Void"), () => { + frappe.call({ + method: + "inn.inn_hotels.doctype.inn_folio_transaction.inn_folio_transaction.void_transaction", + args: { + trx_id: child.name, + use_passcode: d.get_values().use_passcode, + applicant_reason: d.get_values().applicant_reason, + requester: frappe.session.user, + bundle_len: bundle_len, + supervisor_passcode: d.get_values().supervisor_passcode, + }, + callback: (r) => { + if (r.message == 0) { + d.hide(); + frappe.msgprint( + "Transaction with ID " + + child.name + + " voided successfully. Please Reload the Page" + ); + } else if (r.message == 2) { + d.hide(); + frappe.msgprint( + "Request to Void Transaction with ID " + + child.name + + " successfully submitted. " + + "Wait for Supervisor approval to finish void process. Please Reload the Page" + ); + } + }, + }); + }); + d.show(); } // Function to manually close folio function close_folio(frm) { - frappe.confirm(__("You are about to Close this Folio. Are you sure?"), function () { - frappe.call({ - method:'inn.inn_hotels.doctype.inn_folio.inn_folio.close_folio', - args: { - folio_id: frm.doc.name - }, - callback: (r) => { - if (r.message === 'Closed') { - frappe.show_alert("Folio Closed successfully"); - } - else { - frappe.msgprint(r.message); - } - } - }); - }); + frappe.confirm( + __("You are about to Close this Folio. Are you sure?"), + function () { + frappe.call({ + method: "inn.inn_hotels.doctype.inn_folio.inn_folio.close_folio", + args: { + folio_id: frm.doc.name, + }, + callback: (r) => { + if (r.message === "Closed") { + frappe.show_alert("Folio Closed successfully"); + } else { + frappe.msgprint(r.message); + } + }, + }); + } + ); } // Function to showing voided transaction function show_void(item, index) { - if(item.is_void === 1) { - $('[data-name='+item.name+']').show(); - } + if (item.is_void === 1) { + $("[data-name=" + item.name + "]").show(); + } } // Function to hiding voided transaction function hide_void(item, index) { - if(item.is_void === 1) { - $('[data-name='+item.name+']').hide(); - } + if (item.is_void === 1) { + $("[data-name=" + item.name + "]").hide(); + } } // Function to make mandatory certain number of fields function make_mandatory(frm) { - if (frm.doc.type != 'Guest') { - if (frm.doc.group_id == undefined || frm.doc.group_id == null) { - console.log("masuk sini"); - frappe.validated = false; - frappe.msgprint("The Group field cannot be empty if folio type is " + frm.doc.type); - } - } -} \ No newline at end of file + if (frm.doc.type != "Guest") { + if (frm.doc.group_id == undefined || frm.doc.group_id == null) { + console.log("masuk sini"); + frappe.validated = false; + frappe.msgprint( + "The Group field cannot be empty if folio type is " + frm.doc.type + ); + } + } +} + +// Function to make the fields filtered +function make_fields_filtered(frm) { + const field_filters = { + debit_account: { is_group: 0 }, + credit_account: { is_group: 0 }, + }; + + // Loop through fields and apply filters + for (const [fieldname, filters] of Object.entries(field_filters)) { + frm.set_query(fieldname, "folio_transaction", function () { + return { filters: filters }; + }); + } +} diff --git a/inn/inn_hotels/doctype/inn_folio/inn_folio.json b/inn/inn_hotels/doctype/inn_folio/inn_folio.json index 7ae8502f..a80c8830 100644 --- a/inn/inn_hotels/doctype/inn_folio/inn_folio.json +++ b/inn/inn_hotels/doctype/inn_folio/inn_folio.json @@ -30,10 +30,13 @@ "channel", "sb3", "total_debit", + "total_debit_by_currency", "cb4", "total_credit", + "total_credit_by_currency", "cb5", "balance", + "balance_by_currency", "sb0", "toggle_void_transaction", "folio_transaction", @@ -236,10 +239,28 @@ "label": "Channel", "options": "Inn Channel", "read_only": 1 + }, + { + "fieldname": "total_debit_by_currency", + "fieldtype": "Data", + "label": "Total Debit By Currency", + "read_only": 1 + }, + { + "fieldname": "total_credit_by_currency", + "fieldtype": "Data", + "label": "Total Credit By Currency", + "read_only": 1 + }, + { + "fieldname": "balance_by_currency", + "fieldtype": "Data", + "label": "Balance By Currency", + "read_only": 1 } ], "links": [], - "modified": "2024-07-30 16:33:41.091293", + "modified": "2025-03-05 02:40:17.921502", "modified_by": "Administrator", "module": "Inn Hotels", "name": "Inn Folio", diff --git a/inn/inn_hotels/doctype/inn_folio/inn_folio.py b/inn/inn_hotels/doctype/inn_folio/inn_folio.py index d9271385..b646f071 100644 --- a/inn/inn_hotels/doctype/inn_folio/inn_folio.py +++ b/inn/inn_hotels/doctype/inn_folio/inn_folio.py @@ -1,4 +1,3 @@ - # -*- coding: utf-8 -*- # Copyright (c) 2020, Core Initiative and contributors # For license information, please see license.txt @@ -10,159 +9,199 @@ import datetime from frappe.model.document import Document + class InnFolio(Document): - pass + pass + @frappe.whitelist() def create_folio(reservation_id): - if not frappe.db.exists('Inn Folio', {'reservation_id': reservation_id}): - reservation = frappe.get_doc('Inn Reservation', reservation_id) + if not frappe.db.exists("Inn Folio", {"reservation_id": reservation_id}): + reservation = frappe.get_doc("Inn Reservation", reservation_id) + + doc = frappe.new_doc("Inn Folio") + doc.type = "Guest" + doc.reservation_id = reservation_id + doc.customer_id = reservation.customer_id + doc.open = reservation.expected_arrival + doc.close = reservation.expected_departure + doc.insert() - doc = frappe.new_doc('Inn Folio') - doc.type = 'Guest' - doc.reservation_id = reservation_id - doc.customer_id = reservation.customer_id - doc.open = reservation.expected_arrival - doc.close = reservation.expected_departure - doc.insert() def update_close_by_reservation(reservation_id): - folio_list = frappe.get_all('Inn Folio', filters={'reservation_id': reservation_id}) - reservation = frappe.get_doc('Inn Reservation', reservation_id) - # Update except status finish, because when Checking Out, Close is handled by close_folio function - if reservation.status != 'Finish': - for item in folio_list: - doc_folio = frappe.get_doc('Inn Folio', item.name) - doc_folio.close = reservation.departure.strftime('%Y-%m-%d') - doc_folio.save() + folio_list = frappe.get_all("Inn Folio", filters={"reservation_id": reservation_id}) + reservation = frappe.get_doc("Inn Reservation", reservation_id) + # Update except status finish, because when Checking Out, Close is handled by close_folio function + if reservation.status != "Finish": + for item in folio_list: + doc_folio = frappe.get_doc("Inn Folio", item.name) + doc_folio.close = reservation.departure.strftime("%Y-%m-%d") + doc_folio.save() + @frappe.whitelist() def get_reservation_id(folio_id): - doc = frappe.get_doc('Inn Folio', folio_id) - return doc.reservation_id + doc = frappe.get_doc("Inn Folio", folio_id) + return doc.reservation_id + @frappe.whitelist() def update_balance(folio_id): - doc = frappe.get_doc('Inn Folio', folio_id) - trx_list = doc.get('folio_transaction') - total_debit = 0.0 - total_credit = 0.0 - for trx in trx_list: - if trx.flag == 'Debit' and trx.is_void == 0: - total_debit += float(trx.amount) - elif trx.flag == 'Credit' and trx.is_void == 0: - total_credit += float(trx.amount) - balance = total_credit - math.ceil(total_debit) - - if balance != doc.balance: - frappe.db.set_value('Inn Folio', doc.name, 'total_debit', math.ceil(total_debit)) - frappe.db.set_value('Inn Folio', doc.name, 'total_credit', total_credit) - frappe.db.set_value('Inn Folio', doc.name, 'balance', int(balance)) - - return total_debit, total_credit, balance + doc = frappe.get_doc("Inn Folio", folio_id) + trx_list = doc.get("folio_transaction") + total_debit = 0.0 + total_credit = 0.0 + for trx in trx_list: + if trx.flag == "Debit" and trx.is_void == 0: + total_debit += float(trx.amount) + elif trx.flag == "Credit" and trx.is_void == 0: + total_credit += float(trx.amount) + balance = total_credit - math.ceil(total_debit) + + if balance != doc.balance: + frappe.db.set_value( + "Inn Folio", doc.name, "total_debit", math.ceil(total_debit) + ) + frappe.db.set_value("Inn Folio", doc.name, "total_credit", total_credit) + frappe.db.set_value("Inn Folio", doc.name, "balance", int(balance)) + + return total_debit, total_credit, balance + @frappe.whitelist() def need_to_update_balance(folio_id): - doc = frappe.get_doc('Inn Folio', folio_id) - trx_list = doc.get('folio_transaction') + doc = frappe.get_doc("Inn Folio", folio_id) + trx_list = doc.get("folio_transaction") - total_debit = 0.0 - total_credit = 0.0 - for trx in trx_list: - if trx.flag == 'Debit' and trx.is_void == 0: - total_debit += float(trx.amount) - elif trx.flag == 'Credit' and trx.is_void == 0: - total_credit += float(trx.amount) - balance = total_credit - total_debit + total_debit = 0.0 + total_credit = 0.0 + for trx in trx_list: + if trx.flag == "Debit" and trx.is_void == 0: + total_debit += float(trx.amount) + elif trx.flag == "Credit" and trx.is_void == 0: + total_credit += float(trx.amount) + balance = total_credit - total_debit - if balance != doc.balance: - return 1 - else: - return 0 + if balance != doc.balance: + return 1 + else: + return 0 @frappe.whitelist() def get_balance(folio_id): - update_balance(folio_id) - return frappe.db.get_value('Inn Folio', folio_id, ['balance']) + update_balance(folio_id) + return frappe.db.get_value("Inn Folio", folio_id, ["balance"]) + @frappe.whitelist() def get_balance_by_reservation(reservation_id): - folio = frappe.get_doc('Inn Folio', {'reservation_id': reservation_id}) - balance = get_balance(folio.name) - return balance + folio = frappe.get_doc("Inn Folio", {"reservation_id": reservation_id}) + balance = get_balance(folio.name) + return balance + @frappe.whitelist() def transfer_to_another_folio(trx_list, old_parent, new_parent): - exist_error = 0 - list_json = json.loads(trx_list) - for trx in list_json: - trx_doc = frappe.get_doc('Inn Folio Transaction', trx) - if trx_doc.parent == old_parent: - trx_doc.parent = new_parent - trx_doc.remark = 'Transferred from ' + old_parent + ' to Folio ' + new_parent - trx_doc.save() - else: - exist_error = 1 - - return exist_error + exist_error = 0 + list_json = json.loads(trx_list) + for trx in list_json: + trx_doc = frappe.get_doc("Inn Folio Transaction", trx) + if trx_doc.parent == old_parent: + trx_doc.parent = new_parent + trx_doc.remark = ( + "Transferred from " + old_parent + " to Folio " + new_parent + ) + trx_doc.save() + else: + exist_error = 1 + + return exist_error + def is_using_city_ledger(folio_id): - is_using = False - doc = frappe.get_doc('Inn Folio', folio_id) - trx_list = doc.get('folio_transaction') - for trx in trx_list: - if trx.is_void == 0 and trx.flag == 'Credit' and trx.mode_of_payment == 'City Ledger': - is_using = True - return is_using + is_using = False + doc = frappe.get_doc("Inn Folio", folio_id) + trx_list = doc.get("folio_transaction") + hotel_settings = frappe.get_doc("Inn Hotels Setting") + + for trx in trx_list: + if ( + trx.is_void == 0 + and trx.flag == "Credit" + and trx.mode_of_payment == hotel_settings.city_ledger_mode_of_payment + ): + is_using = True + return is_using + @frappe.whitelist() def close_folio(folio_id): - list = check_void_request(folio_id) - if len(list) == 0: - folio = frappe.get_doc('Inn Folio', folio_id) - folio.status = 'Closed' - folio.close = datetime.date.today() - folio.save() - - # Create AR City Ledger if There are payment using City Ledger as mode_of_payment - if is_using_city_ledger(folio_id): - total_amount = 0.0 - trx_list = frappe.get_all('Inn Folio Transaction', filters={'parent': folio_id, 'is_void': 0, 'flag': 'Credit', - 'mode_of_payment': 'City Ledger'}, fields=['amount']) - for trx in trx_list: - total_amount += trx.amount - - ar_city_ledger = frappe.new_doc('AR City Ledger') - ar_city_ledger.naming_series = 'AR-CL-.YYYY.-' - ar_city_ledger.is_paid = 0 - ar_city_ledger.customer_id = folio.customer_id - if folio.type == 'Guest': - ar_city_ledger.inn_channel_id = frappe.db.get_value('Inn Reservation', folio.reservation_id, 'channel') - else: - ar_city_ledger.inn_group_id = folio.group_id - ar_city_ledger.total_amount = total_amount - ar_city_ledger.folio_id = folio_id - ar_city_ledger.folio_type = folio.type - ar_city_ledger.folio_status = folio.status - ar_city_ledger.folio_open = folio.open - ar_city_ledger.folio_close = folio.close - ar_city_ledger.insert() - - return frappe.db.get_value('Inn Folio', folio_id, 'status') - else: - return "There are void transaction request that still not responded. " \ - "Please consult the supervisor to resolve this before closing Folio." + \ - "
Transaction need to be resolved:
" + list + hotel_settings = frappe.get_doc("Inn Hotels Setting") + + list = check_void_request(folio_id) + if len(list) == 0: + folio = frappe.get_doc("Inn Folio", folio_id) + folio.status = "Closed" + folio.close = datetime.date.today() + folio.save() + + # Create AR City Ledger if There are payment using City Ledger as mode_of_payment + if is_using_city_ledger(folio_id): + total_amount = 0.0 + trx_list = frappe.get_all( + "Inn Folio Transaction", + filters={ + "parent": folio_id, + "is_void": 0, + "flag": "Credit", + "mode_of_payment": hotel_settings.city_ledger_mode_of_payment, + }, + fields=["amount"], + ) + for trx in trx_list: + total_amount += trx.amount + + ar_city_ledger = frappe.new_doc("AR City Ledger") + ar_city_ledger.naming_series = "AR-CL-.YYYY.-" + ar_city_ledger.is_paid = 0 + ar_city_ledger.customer_id = folio.customer_id + if folio.type == "Guest": + ar_city_ledger.inn_channel_id = frappe.db.get_value( + "Inn Reservation", folio.reservation_id, "channel" + ) + else: + ar_city_ledger.inn_group_id = folio.group_id + ar_city_ledger.total_amount = total_amount + ar_city_ledger.folio_id = folio_id + ar_city_ledger.folio_type = folio.type + ar_city_ledger.folio_status = folio.status + ar_city_ledger.folio_open = folio.open + ar_city_ledger.folio_close = folio.close + ar_city_ledger.insert() + + return frappe.db.get_value("Inn Folio", folio_id, "status") + else: + return ( + "There are void transaction request that still not responded. " + "Please consult the supervisor to resolve this before closing Folio." + + "
Transaction need to be resolved:
" + + list + ) @frappe.whitelist() def check_void_request(folio_id): - need_resolve = [] - folio = frappe.get_doc('Inn Folio', folio_id) - trx_list = folio.get('folio_transaction') - for item in trx_list: - if item.is_void == 0 and item.void_id is not None: - if frappe.db.get_value('Inn Void Folio Transaction', {'name': item.void_id }, 'status') == 'Requested': - need_resolve.append(item.name) - return need_resolve + need_resolve = [] + folio = frappe.get_doc("Inn Folio", folio_id) + trx_list = folio.get("folio_transaction") + for item in trx_list: + if item.is_void == 0 and item.void_id is not None: + if ( + frappe.db.get_value( + "Inn Void Folio Transaction", {"name": item.void_id}, "status" + ) + == "Requested" + ): + need_resolve.append(item.name) + return need_resolve diff --git a/inn/inn_hotels/doctype/inn_folio_transaction_type/inn_folio_transaction_type.py b/inn/inn_hotels/doctype/inn_folio_transaction_type/inn_folio_transaction_type.py index 09a0b1c2..79cd8a38 100644 --- a/inn/inn_hotels/doctype/inn_folio_transaction_type/inn_folio_transaction_type.py +++ b/inn/inn_hotels/doctype/inn_folio_transaction_type/inn_folio_transaction_type.py @@ -5,6 +5,8 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document +from frappe import cache +import json class InnFolioTransactionType(Document): pass @@ -26,9 +28,47 @@ def get_accounts_from_id(id): def get_transaction_type(type): return_list = [] type_list = frappe.get_all('Inn Folio Transaction Type', - filters=[['type', '=', type], ['is_included', '=', 1]], - fields=['name']) + filters=[['type', '=', type], ['is_included', '=', 1]], + fields=['name']) for item in type_list: option_item = {'label': item.name, 'value': item.name} return_list.append(option_item) return return_list + + + + +@frappe.whitelist() +def get_exchange_rate(reservation_id): + try: + reservation = frappe.get_doc("Inn Reservation", reservation_id) + except frappe.DoesNotExistError: + return { + "exchange_rate": 0, + "currency_symbol": "" + } + + exchange_rate = reservation.exchange_rate + # Instead of throwing an error, return exchange_rate as 0 if missing or invalid + if not exchange_rate or exchange_rate <= 0: + result = { + "exchange_rate": 0, + "currency_symbol": reservation.currency + } + cache_key = f"exchange_rate:{reservation_id}" + frappe.cache().setex(cache_key, 3600, json.dumps(result)) + return result + + currency_symbol = frappe.db.get_value("Currency", reservation.currency, "symbol") + result = { + "exchange_rate": exchange_rate, + "currency_symbol": currency_symbol or reservation.currency + } + cache_key = f"exchange_rate:{reservation_id}" + frappe.cache().setex(cache_key, 3600, json.dumps(result)) + return result +# @frappe.whitelist() +# def get_exchange_rate(reservation_id): +# reservation = frappe.get_doc("Inn Reservation", reservation_id) +# print("3333333333333333333333333333333333333333333333",reservation) +# return reservation.exchange_rate \ No newline at end of file diff --git a/inn/inn_hotels/doctype/inn_folio_transaction_type_setting/__init__.py b/inn/inn_hotels/doctype/inn_folio_transaction_type_setting/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/inn/inn_hotels/doctype/inn_folio_transaction_type_setting/inn_folio_transaction_type_setting.json b/inn/inn_hotels/doctype/inn_folio_transaction_type_setting/inn_folio_transaction_type_setting.json new file mode 100644 index 00000000..67dd1e0d --- /dev/null +++ b/inn/inn_hotels/doctype/inn_folio_transaction_type_setting/inn_folio_transaction_type_setting.json @@ -0,0 +1,67 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2025-01-19 21:48:27.587825", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "name1", + "type", + "module", + "included_in_dropdown", + "debit_account", + "credit_account" + ], + "fields": [ + { + "fieldname": "name1", + "fieldtype": "Data", + "label": "Name" + }, + { + "fieldname": "type", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Type", + "options": "Debit\nCredit", + "reqd": 1 + }, + { + "fieldname": "module", + "fieldtype": "Select", + "label": "Module ", + "options": "0\n1\n2\n3" + }, + { + "default": "0", + "fieldname": "included_in_dropdown", + "fieldtype": "Check", + "label": "Included in Dropdown?" + }, + { + "fieldname": "debit_account", + "fieldtype": "Link", + "label": "Debit Account", + "options": "Account" + }, + { + "fieldname": "credit_account", + "fieldtype": "Link", + "label": "Credit Account", + "options": "Account" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2025-01-20 07:50:13.955619", + "modified_by": "Administrator", + "module": "Inn Hotels", + "name": "Inn Folio Transaction Type Setting", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/inn/inn_hotels/doctype/inn_folio_transaction_type_setting/inn_folio_transaction_type_setting.py b/inn/inn_hotels/doctype/inn_folio_transaction_type_setting/inn_folio_transaction_type_setting.py new file mode 100644 index 00000000..dea33aa3 --- /dev/null +++ b/inn/inn_hotels/doctype/inn_folio_transaction_type_setting/inn_folio_transaction_type_setting.py @@ -0,0 +1,9 @@ +# Copyright (c) 2025, Core Initiative and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class InnFolioTransactionTypeSetting(Document): + pass diff --git a/inn/inn_hotels/doctype/inn_hotels_setting/inn_hotels_setting.js b/inn/inn_hotels/doctype/inn_hotels_setting/inn_hotels_setting.js index c2ff6017..abe38ff2 100644 --- a/inn/inn_hotels/doctype/inn_hotels_setting/inn_hotels_setting.js +++ b/inn/inn_hotels/doctype/inn_hotels_setting/inn_hotels_setting.js @@ -2,47 +2,118 @@ // For license information, please see license.txt frappe.ui.form.on('Inn Hotels Setting', { - refresh: function (frm) { - if (frappe.user.has_role('Hotel Manager') || - frappe.user.has_role('Hotel Reservation User') || - frappe.user.has_role('Administrator')) { - frm.add_custom_button(__('Show Supervisor Passcode'), function () { - frappe.call({ - method: 'inn.inn_hotels.doctype.inn_hotels_setting.inn_hotels_setting.show_supervisor_passcode', - }); - }); - } - }, - folio_transaction_type_generator: function (frm) { + refresh: function (frm) { + if (frappe.user.has_role('Hotel Manager') || + frappe.user.has_role('Hotel Reservation User') || + frappe.user.has_role('Administrator')) { + frm.add_custom_button(__('Show Supervisor Passcode'), function () { + frappe.call({ + method: 'inn.inn_hotels.doctype.inn_hotels_setting.inn_hotels_setting.show_supervisor_passcode', + }); + }); + } +}, + folio_transaction_type_generator: function (frm) { frappe.call({ method: 'inn.inn_hotels.doctype.inn_hotels_setting.inn_hotels_setting.generate_folio_transaction_type', }); }, - bed_type_generator: function (frm) { - frappe.call({ - method: 'inn.inn_hotels.doctype.inn_hotels_setting.inn_hotels_setting.generate_bed_type', - }); - }, - room_type_generator: function (frm) { - frappe.call({ - method: 'inn.inn_hotels.doctype.inn_hotels_setting.inn_hotels_setting.generate_room_type', - }); - }, - inn_hotels_account_generator: function (frm) { - frappe.confirm(__("This may take a while. Please don't refresh or change the page before the Success or Error Message popped up. Click Yes to continue"), function () { - frappe.call({ - method: 'inn.inn_hotels.doctype.inn_hotels_setting.inn_hotels_setting.generate_hotel_account', - }); - }); - }, - test: function () { - frappe.call({ - method: 'inn.inn_hotels.doctype.inn_hotels_setting.inn_hotels_setting.generate_supervisor_passcode' - }); - }, - role_generator: function (frm) { - frappe.call({ - method: 'inn.inn_hotels.doctype.inn_hotels_setting.inn_hotels_setting.insert_role' - }); - } -}); + + // bed_type_generator: function (frm) { + // frappe.call({ + // method: 'inn.inn_hotels.doctype.inn_hotels_setting.inn_hotels_setting.generate_bed_type', + // }); + // }, + + + bed_type_generator: function (frm) { + frappe.call({ + method: 'inn.inn_hotels.doctype.inn_hotels_setting.inn_hotels_setting.generate_bed_type', + args: { + bed_type: frm.doc.bed_type // Pass the child table data + }, + callback: function (r) { + if (r.message) { + frappe.msgprint(r.message); + frm.refresh(); // Refresh the form to reflect changes + } + } + }); + }, + + // room_type_generator: function (frm) { + // frappe.call({ + // method: 'inn.inn_hotels.doctype.inn_hotels_setting.inn_hotels_setting.generate_room_type', + // }); + // }, + + room_type_generator: function (frm) { + // Ensure bed_type is passed + frappe.call({ + method: 'inn.inn_hotels.doctype.inn_hotels_setting.inn_hotels_setting.generate_room_type', + args: { + room_type: frm.doc.room_type // Pass the child table data + }, + callback: function (r) { + if (r.message) { + frappe.msgprint(r.message); + frm.refresh(); // Refresh the form to reflect changes + } + } + }); + }, + // inn_hotels_account_generator: function (frm) { + // frappe.confirm(__("This may take a while. Please don't refresh or change the page before the Success or Error Message popped up. Click Yes to continue"), function () { + // frappe.call({ + // method: 'inn.inn_hotels.doctype.inn_hotels_setting.inn_hotels_setting.generate_hotel_account', + // }); + // }); + // }, + + + inn_hotels_account_generator: function (frm) { + frappe.call({ + method: 'inn.inn_hotels.doctype.inn_hotels_setting.inn_hotels_setting.generate_hotel_account', + args: { + inn_account_setting: frm.doc.inn_account_setting + }, + callback: function (r) { + if (r.message) { + frappe.msgprint(r.message); + frm.refresh(); + } + } + }); + }, + + + + test: function () { + frappe.call({ + method: 'inn.inn_hotels.doctype.inn_hotels_setting.inn_hotels_setting.generate_supervisor_passcode' + }); + }, + // role_generator: function (frm) { + // frappe.call({ + // method: 'inn.inn_hotels.doctype.inn_hotels_setting.inn_hotels_setting.insert_role' + // }); + // } + + + role_generator: function (frm) { + // Ensure bed_type is passed + frappe.call({ + method: 'inn.inn_hotels.doctype.inn_hotels_setting.inn_hotels_setting.insert_role', + args: { + role: frm.doc.role // Pass the child table data + }, + callback: function (r) { + if (r.message) { + frappe.msgprint(r.message); + frm.refresh(); // Refresh the form to reflect changes + } + } + }); + } + +}); \ No newline at end of file diff --git a/inn/inn_hotels/doctype/inn_hotels_setting/inn_hotels_setting.json b/inn/inn_hotels/doctype/inn_hotels_setting/inn_hotels_setting.json index 81a21450..d963afe9 100644 --- a/inn/inn_hotels/doctype/inn_hotels_setting/inn_hotels_setting.json +++ b/inn/inn_hotels/doctype/inn_hotels_setting/inn_hotels_setting.json @@ -47,6 +47,8 @@ "inn_channel_exclude_tax", "maximum_payment_exclude", "inn_tax_exclude_option", + "column_break_mssu", + "include_tax", "profit_sharing_reservation_section", "guest_account_receiveable", "profit_sharing_account", @@ -62,12 +64,60 @@ "pos_receipt_remarks", "master_data_generator_sb", "bed_type_generator", + "bed_type", "room_type_generator", + "room_type", "role_generator", + "role", "cb4", "inn_hotels_account_generator", + "inn_account_setting", "folio_transaction_type_generator", - "supervisor_passcode" + "folio_transaction_type", + "supervisor_passcode", + "folio_default_tab", + "folio_transaction_type_section", + "package_tax", + "package", + "down_payment", + "additional_charge", + "round_off", + "cancellation_fee", + "late_checkout", + "early_checkin", + "column_break_mtit", + "room_charge_tax_service", + "room_charge", + "room_payment", + "credit_card_administration_fee", + "deposit", + "payment", + "laundry", + "fbs_tax_11", + "fbs_service_10", + "column_break_vbod", + "breakfast_charge_tax_service", + "breakfast_charge", + "dp_kamar", + "refund", + "restaurant_food", + "room_service_food", + "room_service_beverage", + "restaurant_other", + "restaurant_beverages", + "section_break_kkcy", + "ar_city_ledger_invoice_payment_account", + "column_break_wphq", + "mode_of_payment_section", + "city_ledger_mode_of_payment", + "default_roles_section", + "housekeeping_supervisor", + "housekeeping", + "housekeeping_assistant", + "column_break_dynj", + "booking_approver", + "restaurant_user", + "restaurant_supervisor" ], "fields": [ { @@ -434,11 +484,302 @@ "fieldname": "maximum_payment_exclude", "fieldtype": "Currency", "label": "Maximum Payment Exclude" + }, + { + "fieldname": "bed_type", + "fieldtype": "Table", + "hidden": 1, + "label": "Bed Type", + "options": "Inn Setting Tools" + }, + { + "fieldname": "room_type", + "fieldtype": "Table", + "hidden": 1, + "label": "Room Type", + "options": "Inn Setting Tools" + }, + { + "fieldname": "role", + "fieldtype": "Table", + "hidden": 1, + "label": "Role", + "options": "Inn Role" + }, + { + "fieldname": "folio_transaction_type", + "fieldtype": "Table", + "hidden": 1, + "label": "Folio Transaction Type", + "options": "Inn Folio Transaction Type Setting" + }, + { + "fieldname": "inn_account_setting", + "fieldtype": "Table", + "hidden": 1, + "label": "Inn Account Setting", + "options": "Inn Account Setting" + }, + { + "fieldname": "folio_default_tab", + "fieldtype": "Tab Break", + "label": "Hotel Default" + }, + { + "fieldname": "credit_card_administration_fee", + "fieldtype": "Link", + "label": "Credit Card Administration Fee", + "options": "Inn Folio Transaction Type" + }, + { + "fieldname": "package", + "fieldtype": "Link", + "label": "Package", + "options": "Inn Folio Transaction Type" + }, + { + "fieldname": "room_charge", + "fieldtype": "Link", + "label": "Room Charge", + "options": "Inn Folio Transaction Type" + }, + { + "fieldname": "breakfast_charge", + "fieldtype": "Link", + "label": "Breakfast Charge", + "options": "Inn Folio Transaction Type" + }, + { + "fieldname": "refund", + "fieldtype": "Link", + "label": "Refund", + "options": "Inn Folio Transaction Type" + }, + { + "fieldname": "dp_kamar", + "fieldtype": "Link", + "label": "DP Kamar", + "options": "Inn Folio Transaction Type" + }, + { + "fieldname": "room_payment", + "fieldtype": "Link", + "label": "Room Payment", + "options": "Inn Folio Transaction Type" + }, + { + "fieldname": "deposit", + "fieldtype": "Link", + "label": "Deposit", + "options": "Inn Folio Transaction Type" + }, + { + "fieldname": "down_payment", + "fieldtype": "Link", + "label": "Down Payment", + "options": "Inn Folio Transaction Type" + }, + { + "fieldname": "payment", + "fieldtype": "Link", + "label": "Payment", + "options": "Inn Folio Transaction Type" + }, + { + "fieldname": "additional_charge", + "fieldtype": "Link", + "label": "Additional Charge", + "options": "Inn Folio Transaction Type" + }, + { + "fieldname": "restaurant_food", + "fieldtype": "Link", + "label": "Restaurant Food", + "options": "Inn Folio Transaction Type" + }, + { + "fieldname": "restaurant_beverages", + "fieldtype": "Link", + "label": "Restaurant Beverages", + "options": "Inn Folio Transaction Type" + }, + { + "fieldname": "restaurant_other", + "fieldtype": "Link", + "label": "Restaurant Other", + "options": "Inn Folio Transaction Type" + }, + { + "fieldname": "room_service_food", + "fieldtype": "Link", + "label": "Room Service Food", + "options": "Inn Folio Transaction Type" + }, + { + "fieldname": "room_service_beverage", + "fieldtype": "Link", + "label": "Room Service Beverage", + "options": "Inn Folio Transaction Type" + }, + { + "fieldname": "round_off", + "fieldtype": "Link", + "label": "Round Off", + "options": "Inn Folio Transaction Type" + }, + { + "fieldname": "laundry", + "fieldtype": "Link", + "label": "Laundry", + "options": "Inn Folio Transaction Type" + }, + { + "fieldname": "cancellation_fee", + "fieldtype": "Link", + "label": "Cancellation Fee", + "options": "Inn Folio Transaction Type" + }, + { + "fieldname": "late_checkout", + "fieldtype": "Link", + "label": "Late Checkout", + "options": "Inn Folio Transaction Type" + }, + { + "fieldname": "early_checkin", + "fieldtype": "Link", + "label": "Early Checkin", + "options": "Inn Folio Transaction Type" + }, + { + "fieldname": "package_tax", + "fieldtype": "Link", + "label": "Package Tax", + "options": "Inn Folio Transaction Type" + }, + { + "fieldname": "room_charge_tax_service", + "fieldtype": "Link", + "label": "Room Charge Tax/Service", + "options": "Inn Folio Transaction Type" + }, + { + "fieldname": "breakfast_charge_tax_service", + "fieldtype": "Link", + "label": "Breakfast Charge Tax/Service", + "options": "Inn Folio Transaction Type" + }, + { + "fieldname": "fbs_tax_11", + "fieldtype": "Link", + "label": "FBS Tax 11 %", + "options": "Inn Folio Transaction Type" + }, + { + "fieldname": "fbs_service_10", + "fieldtype": "Link", + "label": "FBS Service 10 %", + "options": "Inn Folio Transaction Type" + }, + { + "fieldname": "folio_transaction_type_section", + "fieldtype": "Section Break", + "label": "Folio Transaction Type" + }, + { + "fieldname": "column_break_mtit", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_vbod", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_mssu", + "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "include_tax", + "fieldtype": "Check", + "label": "Include Tax" + }, + { + "fieldname": "section_break_kkcy", + "fieldtype": "Section Break", + "label": "Default Accounts" + }, + { + "fieldname": "ar_city_ledger_invoice_payment_account", + "fieldtype": "Link", + "label": "AR City Ledger Invoice Payment Account", + "options": "Account" + }, + { + "fieldname": "column_break_wphq", + "fieldtype": "Column Break" + }, + { + "fieldname": "housekeeping", + "fieldtype": "Link", + "label": "Housekeeping", + "options": "Role" + }, + { + "fieldname": "housekeeping_assistant", + "fieldtype": "Link", + "label": "Housekeeping Assistant", + "options": "Role" + }, + { + "fieldname": "restaurant_user", + "fieldtype": "Link", + "label": "Restaurant User", + "options": "Role" + }, + { + "fieldname": "restaurant_supervisor", + "fieldtype": "Link", + "label": "Restaurant Supervisor", + "options": "Role" + }, + { + "fieldname": "housekeeping_supervisor", + "fieldtype": "Link", + "label": "Housekeeping Supervisor", + "options": "Role" + }, + { + "fieldname": "booking_approver", + "fieldtype": "Link", + "label": "Booking Approver", + "options": "Role" + }, + { + "fieldname": "default_roles_section", + "fieldtype": "Section Break", + "label": "Default Roles" + }, + { + "fieldname": "column_break_dynj", + "fieldtype": "Column Break" + }, + { + "fieldname": "mode_of_payment_section", + "fieldtype": "Section Break", + "label": "Mode Of Payment" + }, + { + "fieldname": "city_ledger_mode_of_payment", + "fieldtype": "Link", + "label": "City Ledger Mode of Payment", + "options": "Mode of Payment", + "reqd": 1 } ], "issingle": 1, "links": [], - "modified": "2024-08-13 14:06:36.577463", + "modified": "2025-02-03 15:07:07.007333", "modified_by": "Administrator", "module": "Inn Hotels", "name": "Inn Hotels Setting", diff --git a/inn/inn_hotels/doctype/inn_hotels_setting/inn_hotels_setting.py b/inn/inn_hotels/doctype/inn_hotels_setting/inn_hotels_setting.py index e0c3c827..fc7434e9 100644 --- a/inn/inn_hotels/doctype/inn_hotels_setting/inn_hotels_setting.py +++ b/inn/inn_hotels/doctype/inn_hotels_setting/inn_hotels_setting.py @@ -15,7 +15,7 @@ class InnHotelsSetting(Document): - pass + pass @frappe.whitelist() def generate_folio_transaction_type(): @@ -298,110 +298,200 @@ def generate_folio_transaction_type(): make_records(folio_transaction_type_records) frappe.msgprint("Generating Default Folio Transaction Type Success") +# @frappe.whitelist() +# def generate_bed_type(): +# bed_records = [] +# if not frappe.db.exists('Inn Bed Type', {'name': 'Single'}): +# bed_records += [{ +# 'doctype': 'Inn Bed Type', +# 'name': _('Single'), +# 'description': _('Single Bed') +# }] +# if not frappe.db.exists('Inn Bed Type', {'name': 'Double'}): +# bed_records += [{ +# 'doctype': 'Inn Bed Type', +# 'name': _('Double'), +# 'description': _('Double Bed') +# }] +# if not frappe.db.exists('Inn Bed Type', {'name': 'Twin'}): +# bed_records += [{ +# 'doctype': 'Inn Bed Type', +# 'name': _('Twin'), +# 'description': _('Twin Bed') +# }] +# make_records(bed_records) +# frappe.msgprint("Generating Default Bed Type Success") + @frappe.whitelist() -def generate_bed_type(): - bed_records = [] - if not frappe.db.exists('Inn Bed Type', {'name': 'Single'}): - bed_records += [{ - 'doctype': 'Inn Bed Type', - 'name': _('Single'), - 'description': _('Single Bed') - }] - if not frappe.db.exists('Inn Bed Type', {'name': 'Double'}): - bed_records += [{ - 'doctype': 'Inn Bed Type', - 'name': _('Double'), - 'description': _('Double Bed') - }] - if not frappe.db.exists('Inn Bed Type', {'name': 'Twin'}): - bed_records += [{ - 'doctype': 'Inn Bed Type', - 'name': _('Twin'), - 'description': _('Twin Bed') - }] - make_records(bed_records) - frappe.msgprint("Generating Default Bed Type Success") +def generate_bed_type(bed_type): + + if not bed_type: + frappe.throw(_("No bed type data provided.")) + + bed_type = frappe.parse_json(bed_type) + + for bed in bed_type: + # Perform validation for required fields + if "name1" not in bed or "description" not in bed: + frappe.throw(_("Each bed type must have a name and description.")) + + # Check if the bed type already exists + if not frappe.db.exists("Inn Bed Type", bed.get("name1")): + new_bed = frappe.get_doc({ + "doctype": "Inn Bed Type", + "name": bed.get("name1"), + "description": bed.get("description") + }) + new_bed.insert() + else: + frappe.msgprint(_("Bed Type '{0}' already exists. Skipping.").format(bed.get("name1"))) + + return _("Bed types generated successfully.") + + +# @frappe.whitelist() +# def generate_room_type(): +# room_type_records = [] +# if not frappe.db.exists('Inn Room Type', {'name': 'Studio'}): +# room_type_records += [{ +# 'doctype': 'Inn Room Type', +# 'name': _('Studio'), +# 'description': _('Studio Room') +# }] +# if not frappe.db.exists('Inn Room Type', {'name': 'Superior'}): +# room_type_records += [{ +# 'doctype': 'Inn Room Type', +# 'name': _('Superior'), +# 'description': _('Superior Room') +# }] +# if not frappe.db.exists('Inn Room Type', {'name': 'Deluxe'}): +# room_type_records += [{ +# 'doctype': 'Inn Room Type', +# 'name': _('Deluxe'), +# 'description': _('Deluxe Room') +# }] +# if not frappe.db.exists('Inn Room Type', {'name': 'Executive'}): +# room_type_records += [{ +# 'doctype': 'Inn Room Type', +# 'name': _('Executive'), +# 'description': _('Executive Room') +# }] +# if not frappe.db.exists('Inn Room Type', {'name': 'Suite'}): +# room_type_records += [{ +# 'doctype': 'Inn Room Type', +# 'name': _('Suite'), +# 'description': _('Suite Room') +# }] +# make_records(room_type_records) +# frappe.msgprint("Generating Default Room Type Success") + + + + + + @frappe.whitelist() -def generate_room_type(): - room_type_records = [] - if not frappe.db.exists('Inn Room Type', {'name': 'Studio'}): - room_type_records += [{ - 'doctype': 'Inn Room Type', - 'name': _('Studio'), - 'description': _('Studio Room') - }] - if not frappe.db.exists('Inn Room Type', {'name': 'Superior'}): - room_type_records += [{ - 'doctype': 'Inn Room Type', - 'name': _('Superior'), - 'description': _('Superior Room') - }] - if not frappe.db.exists('Inn Room Type', {'name': 'Deluxe'}): - room_type_records += [{ - 'doctype': 'Inn Room Type', - 'name': _('Deluxe'), - 'description': _('Deluxe Room') - }] - if not frappe.db.exists('Inn Room Type', {'name': 'Executive'}): - room_type_records += [{ - 'doctype': 'Inn Room Type', - 'name': _('Executive'), - 'description': _('Executive Room') - }] - if not frappe.db.exists('Inn Room Type', {'name': 'Suite'}): - room_type_records += [{ - 'doctype': 'Inn Room Type', - 'name': _('Suite'), - 'description': _('Suite Room') - }] - make_records(room_type_records) - frappe.msgprint("Generating Default Room Type Success") +def generate_room_type(room_type): + + if not room_type: + frappe.throw(_("No bed type data provided.")) + + room_type = frappe.parse_json(room_type) + + for bed in room_type: + if "name1" not in bed or "description" not in bed: + frappe.throw(_("Each bed type must have a name and description.")) + + # Check if the Room type already exists + if not frappe.db.exists("Inn Room Type", bed.get("name1")): + new_bed = frappe.get_doc({ + "doctype": "Inn Room Type", + "name": bed.get("name1"), + "description": bed.get("description") + }) + new_bed.insert() + else: + frappe.msgprint(_("Room Type '{0}' already exists. Skipping.").format(bed.get("name1"))) + + return _("Room types generated successfully.") + + -def create_account(account_name, parent_number, account_number, is_group, account_currency, account_type, root_type=None, company_name=None): - print('Create account start') - if company_name is None: - company_name = frappe.get_doc("Global Defaults").default_company - - if frappe.db.exists('Account', {'account_number': account_number}): - update_account_number(frappe.db.get_list('Account', filters={'account_number': account_number})[0].name, account_name, account_number) - print('account '+ account_number +' name updated') - this_account = frappe.get_doc('Account', {'account_number': account_number}) - print('this_account group = '+ str(this_account.is_group)) - if int(is_group) == 1 and int(this_account.is_group) != int(is_group): - print("This non-group account need to changed to group") - this_account.account_type = '' - this_account.is_group = 1 - this_account.save() - print("Account changed to group") - else: - new_account = frappe.new_doc("Account") - new_account.account_name = account_name - new_account.company = company_name - if parent_number == '': - new_account.flags.ignore_mandatory = True - new_account.parent_account = None - else: - new_account.parent_account = frappe.db.get_list('Account', filters={'account_number': parent_number})[0].name - new_account.account_number = account_number - new_account.is_group = is_group - new_account.account_currency = account_currency - new_account.account_type = account_type - if root_type is not None: - new_account.root_type = root_type - new_account.insert() - print('new account inserted') - print('Create account end') - print('===================') - print('===================') @frappe.whitelist() -def generate_hotel_account(): - accounts = get_account() - for item in accounts: - create_account(item['account_name'], item['parent_number'], item['account_number'], item['is_group'], - item['account_currency'], item['account_type'], item['root_type']) - frappe.msgprint("Generating Account Success") +def insert_role(role): + + if not role: + frappe.throw(_("No Role data provided.")) + + role = frappe.parse_json(role) + + for bed in role: + if "name1" not in bed: + frappe.throw(_("Each Rolemust have a name ")) + + if not frappe.db.exists("Role", bed.get("name1")): + new_bed = frappe.get_doc({ + "doctype": "Role", + "role_name": bed.get("name1"), + }) + new_bed.insert() + else: + frappe.msgprint(_("Role '{0}' already exists. Skipping.").format(bed.get("name1"))) + + return _("Role generated successfully.") + + + +def create_account(account_name, parent_number, account_number, is_group, account_currency, account_type, root_type=None, company_name=None): + print('Create account start') + if company_name is None: + company_name = frappe.get_doc("Global Defaults").default_company + + # Check if the account already exists + existing_account = frappe.db.get_list('Account', filters={'account_number': account_number}, fields=['name']) + if existing_account: + account_name_in_db = existing_account[0].name + update_account_number(account_name_in_db, account_name, account_number) + print('Account ' + account_number + ' name updated') + this_account = frappe.get_doc('Account', account_name_in_db) + print('this_account group = ' + str(this_account.is_group)) + if int(is_group) == 1 and int(this_account.is_group) != int(is_group): + print("This non-group account needs to be changed to group") + this_account.account_type = '' + this_account.is_group = 1 + this_account.save() + print("Account changed to group") + else: + # If parent_number is provided, ensure the parent account exists + parent_account = None + if parent_number: + parent_account_query = frappe.db.get_list('Account', filters={'account_number': parent_number}, fields=['name']) + if not parent_account_query: + frappe.throw(_("Parent account with number {0} not found.").format(parent_number)) + parent_account = parent_account_query[0].name + + new_account = frappe.new_doc("Account") + new_account.account_name = account_name + new_account.company = company_name + new_account.parent_account = parent_account + new_account.account_number = account_number + new_account.is_group = is_group + new_account.account_currency = account_currency + new_account.account_type = account_type + new_account.root_type = root_type + new_account.insert() + # print('New account inserted') + # print('Create account end') + # print('===================') +# @frappe.whitelist() +# def generate_hotel_account(): +# accounts = get_account() +# for item in accounts: +# create_account(item['account_name'], item['parent_number'], item['account_number'], item['is_group'], +# item['account_currency'], item['account_type'], item['root_type']) +# frappe.msgprint("Generating Account Success") # create_account('Payroll', '', '6000.000', 1, 'IDR', '', frappe.get_doc("Global Defaults").default_company, 'Expense') # if frappe.db.exists('Account', {'account_number': '6000.000'}) and frappe.db.exists('Account', {'account_number': '7000.000'}): # accounts = get_account() @@ -411,6 +501,44 @@ def generate_hotel_account(): # else: # frappe.msgprint("Please Create account 6000.0000 and 7000.000 in the Chart of Account First") + + + +@frappe.whitelist() +def generate_hotel_account(inn_account_setting): + if not inn_account_setting: + frappe.throw(_("No inn_account_setting data provided.")) + + try: + inn_account_setting = frappe.parse_json(inn_account_setting) + except Exception as e: + frappe.throw(_("Invalid JSON format: {0}").format(str(e))) + + if not isinstance(inn_account_setting, list): + frappe.throw(_("Input data must be a list of account settings.")) + + for item in inn_account_setting: + try: + required_keys = ['account_name', 'parent_number', 'account_number', 'is_group', 'account_currency', 'account_type', 'root_type'] + for key in required_keys: + if key not in item: + frappe.throw(_("Missing required key: {0} in account setting").format(key)) + + create_account( + account_name=item['account_name'], + parent_number=item['parent_number'], + account_number=item['account_number'], + is_group=item['is_group'], + account_currency=item['account_currency'], + account_type=item['account_type'], + root_type=item['root_type'] + ) + except Exception as e: + frappe.msgprint(_("Failed to create account: {0}. Error: {1}").format(item.get('account_name', 'Unknown'), str(e))) + + frappe.msgprint(_("Account generation process completed.")) + + @frappe.whitelist() def generate_supervisor_passcode(): digits = string.digits @@ -425,6 +553,6 @@ def show_supervisor_passcode(): generate_supervisor_passcode() show_supervisor_passcode() -@frappe.whitelist() -def insert_role(): - role.insert_role() \ No newline at end of file +# @frappe.whitelist() +# def insert_role(): +# role.insert_role() \ No newline at end of file diff --git a/inn/inn_hotels/doctype/inn_reservation/inn_reservation.js b/inn/inn_hotels/doctype/inn_reservation/inn_reservation.js index a4eb5399..aeac3c96 100644 --- a/inn/inn_hotels/doctype/inn_reservation/inn_reservation.js +++ b/inn/inn_hotels/doctype/inn_reservation/inn_reservation.js @@ -1,1408 +1,1716 @@ // Copyright (c) 2020, Core Initiative and contributors // For license information, please see license.txt -let is_check_in = 'false'; +let is_check_in = "false"; let is_form_not_good_to_go = false; let is_error = false; -let error_message = ''; +let error_message = ""; let room_max_active_card = 5; -frappe.ui.form.on('Inn Reservation', { - onload: function (frm) { - get_room_max_active_card(); - make_read_only(frm); +frappe.ui.form.on("Inn Reservation", { + onload: function (frm) { + get_room_max_active_card(); + make_read_only(frm); - if (frm.doc.__islocal != 1) { - handle_filter_on_start_if_filled(frm) - check_is_room_booking_already_created(frm) - } - }, - refresh: function (frm) { - is_check_in = getUrlVars()['is_check_in']; - get_room_max_active_card(); - make_read_only(frm); - console.log("is error = " + is_error); - // Hide some variables that not needed to be filled first time Reservation Created - if (frm.doc.__islocal === 1) { - frm.add_custom_button(__('Check Membership Card'), function () { - check_membership_cards(); - }); - console.log("notsaved"); - frm.set_value('status', 'Reserved'); - frm.set_df_property('init_actual_room_rate', 'hidden', 0); - frm.set_df_property('arrival', 'hidden', 1); - frm.set_df_property('departure', 'hidden', 1); - frm.set_df_property('actual_room_rate', 'hidden', 1); - frm.set_df_property('actual_room_id', 'hidden', 1); - frm.set_df_property('sb1', 'hidden', 1); // Actual Room Rate Breakdown Section - frm.set_df_property('sb3', 'hidden', 1); // Issue Card Table Section - frm.set_df_property('sb4', 'hidden', 1); // Issue Card Buttons Section - toggle_room_detail(frm) - } - // Show Folio Button - if (frm.doc.__islocal !== 1) { - frm.add_custom_button(__('Show Folio'), function () { - frappe.call({ - method: 'inn.inn_hotels.doctype.inn_reservation.inn_reservation.get_folio_url', - args: { - reservation_id: frm.doc.name, - }, - callback: (r) => { - if (r.message) { - let url = r.message; - if (is_check_in == 'true') { - url = r.message + '?is_check_in=true'; - } - var w = window.open(url, "_self"); - if (!w) { - frappe.msgprint(__("Please enable pop-ups")); return; - } - } - } - }); - }); - } - // Reservation is Saved, and status Reserved - if (frm.doc.__islocal !== 1 && frm.doc.status === 'Reserved') { - console.log("is saved"); - // Set all variables that hidden to be shown again - frm.set_df_property('arrival', 'hidden', 0); - frm.set_df_property('departure', 'hidden', 0); - frm.set_df_property('actual_room_id', 'hidden', 0); - frm.set_df_property('wifi_password', 'hidden', 0); - // Still hide actual room rate - frm.set_df_property('actual_room_rate', 'hidden', 1); - frm.set_df_property('init_actual_room_rate', 'hidden', 0); - console.log("is_check_in = " + is_check_in); - // Show Start Check In Process button if is_check_in flag undefined - if (is_check_in === undefined) { - console.log("is_check_in undefined"); - frm.add_custom_button(__("Start Check In Process"), function () { - is_check_in = "true"; - frappe.call({ - method: "inn.inn_hotels.doctype.inn_reservation.inn_reservation.start_check_in", - args: { - source: 'check_in_button', - reservation: frm.doc.name - }, - callback: (r) => { - if (r.message) { - window.open(r.message, "_self"); - frm.reload_doc(); - } - } - }); - }); - } - // Show Info that Check In is In Progress. Meaning button Check In clicked, either from Reservation List or - // from Start Check In Process Button - if (is_check_in === "true") { - frm.set_intro(__("In Progress Checking In Guest")); - // Assign some variables from "Reservation Detail" to "Room Stay" - autofill(frm); - is_form_not_good_to_go = is_form_good_to_in_house(frm); - console.log("is_form_not_good_to_go = " + is_form_not_good_to_go); - console.log("error_message = " + error_message); - if (is_form_not_good_to_go === true && (error_message !== '' || error_message !== 'Please fill these fields before Finishing Check In process:
"; - if (is_error === true) { - error_message = 'Please fill these fields before Finishing Check In process: