diff --git a/app/controllers/foodcourt/qrpay_controller.rb b/app/controllers/foodcourt/qrpay_controller.rb index c4c084eb..eaf87b93 100644 --- a/app/controllers/foodcourt/qrpay_controller.rb +++ b/app/controllers/foodcourt/qrpay_controller.rb @@ -1,9 +1,10 @@ class Foodcourt::QrpayController < BaseFoodcourtController + class PaymentProcessingError < StandardError; end + require 'rqrcode' skip_before_action :authenticate, only: [:create] skip_before_action :verify_authenticity_token, only: [:create] - def init @cash_exist = PaymentMethodSetting.cash_exist @credit_exist = PaymentMethodSetting.credit_exist @@ -31,185 +32,199 @@ class Foodcourt::QrpayController < BaseFoodcourtController @membership_rebate_balance=0 if Sale.exists?(sale_id) - @cash = 0.0 - @kbz_pay_amount = 0.0 - @other = 0.0 - @ppamount = 0.0 - @visacount= 0.0 - @jcbcount= 0.0 - @mastercount = 0.0 - @unionpaycount = 0.0 - @alipaycount = 0.0 - @junctionpaycount = 0.0 - @credit = 0.0 - @paymalcount = 0.0 - @dingacount = 0.0 - @giftvouchercount = 0.0 - @sale_data = Sale.find_by_sale_id(sale_id) - @balance = 0 - @accountable_type = '' - @table_no = '' - @dining = '' - @other_payment = 0.0 - @pdf_view = nil - @lookup_pdf = Lookup.find_by_lookup_type("ReceiptPdfView") - if !@lookup_pdf.nil? - @pdf_view = @lookup_pdf.value - end + begin + @cash = 0.0 + @kbz_pay_amount = 0.0 + @other = 0.0 + @ppamount = 0.0 + @visacount= 0.0 + @jcbcount= 0.0 + @mastercount = 0.0 + @unionpaycount = 0.0 + @alipaycount = 0.0 + @junctionpaycount = 0.0 + @credit = 0.0 + @paymalcount = 0.0 + @dingacount = 0.0 + @giftvouchercount = 0.0 + @sale_data = Sale.find_by_sale_id(sale_id) + @balance = 0 + @accountable_type = '' + @table_no = '' + @dining = '' + @other_payment = 0.0 + @pdf_view = nil + @lookup_pdf = Lookup.find_by_lookup_type("ReceiptPdfView") + if !@lookup_pdf.nil? + @pdf_view = @lookup_pdf.value + end - amount = SalePayment.get_kbz_pay_amount(sale_id, current_user) - @kbz_pay_amount += amount.to_f + amount = SalePayment.get_kbz_pay_amount(sale_id, current_user) + @kbz_pay_amount += amount.to_f - # @shop = shop_detail #show shop info + # @shop = shop_detail #show shop info - @customer_lists = Customer.where(name: ["WALK-IN", "TAKEAWAY"]) - saleObj = Sale.find(sale_id) + @customer_lists = Customer.where(name: ["WALK-IN", "TAKEAWAY"]) + saleObj = Sale.find(sale_id) - #total customer with individual total amount - @individual_total = Array.new - if !saleObj.equal_persons.nil? - per_person_amount = saleObj.grand_total.to_f / saleObj.equal_persons.to_i - @individual_total.push({'total_customer' => saleObj.equal_persons.to_i, 'per_person_amount' => per_person_amount.to_f }) - end + #total customer with individual total amount + @individual_total = Array.new + if !saleObj.equal_persons.nil? + per_person_amount = saleObj.grand_total.to_f / saleObj.equal_persons.to_i + @individual_total.push({'total_customer' => saleObj.equal_persons.to_i, 'per_person_amount' => per_person_amount.to_f }) + end - if current_shop.is_rounding_adj - a = saleObj.grand_total % 25 # Modulus - b = saleObj.grand_total / 25 # Division - #not calculate rounding if modulus is 0 and division is even - #calculate rounding if modulus is zero or not zero and division are not even - if (a != 0.0 && b%2 != 0.0) || (a==0.0 && b%2 !=0) - new_total = Sale.get_rounding_adjustment(saleObj.grand_total) - @rounding_adj = new_total-saleObj.grand_total - saleObj.update_attributes(grand_total: new_total,old_grand_total: saleObj.grand_total,rounding_adjustment:@rounding_adj) - @sale_data.grand_total = new_total - @sale_data.old_grand_total = saleObj.grand_total - @sale_data.rounding_adjustment = @rounding_adj + if current_shop.is_rounding_adj + a = saleObj.grand_total % 25 # Modulus + b = saleObj.grand_total / 25 # Division + #not calculate rounding if modulus is 0 and division is even + #calculate rounding if modulus is zero or not zero and division are not even + if (a != 0.0 && b%2 != 0.0) || (a==0.0 && b%2 !=0) + new_total = Sale.get_rounding_adjustment(saleObj.grand_total) + @rounding_adj = new_total-saleObj.grand_total + saleObj.update_attributes(grand_total: new_total,old_grand_total: saleObj.grand_total,rounding_adjustment:@rounding_adj) + @sale_data.grand_total = new_total + @sale_data.old_grand_total = saleObj.grand_total + @sale_data.rounding_adjustment = @rounding_adj + else + @rounding_adj = @sale_data.rounding_adjustment + end else @rounding_adj = @sale_data.rounding_adjustment end - else - @rounding_adj = @sale_data.rounding_adjustment - end #end rounding adjustment # get printer info - @print_settings = PrintSetting.get_precision_delimiter() + @print_settings = PrintSetting.get_precision_delimiter() - #get customer amount - @customer = Customer.find(@sale_data.customer_id) - # accounts = @customer.tax_profiles - accounts = TaxProfile.where("group_type = ?",@cashier_type).order("order_by ASC") - @account_arr =[] - @tax_arr =[] - accounts.each do |acc| - account = TaxProfile.find(acc.id) - # @account_arr.push(account) - @tax_arr.push(account.name) - end - sale_taxes = SaleTax.where("sale_id = ?", saleObj.sale_id) - if !sale_taxes.empty? - sale_taxes.each do |sale_tax| - @account_arr.push(sale_tax) + #get customer amount + @customer = Customer.find(@sale_data.customer_id) + # accounts = @customer.tax_profiles + accounts = TaxProfile.where("group_type = ?",@cashier_type).order("order_by ASC") + @account_arr =[] + @tax_arr =[] + accounts.each do |acc| + account = TaxProfile.find(acc.id) + # @account_arr.push(account) + @tax_arr.push(account.name) end - end - rebate = MembershipSetting.find_by_rebate(1) - # get member information - if @customer.membership_id != nil && rebate - response = Customer.get_member_account(@customer) - if response["status"]==true - response["account_data"].each do |res| - if res["accountable_type"] == "RebateAccount" || res["accountable_type"] == "RebatebonusAccount" - @balance = @balance.to_f + res["balance"].to_f - # @accountable_type = res["accountable_type"] - @accountable_type = "Rebate Balance" + sale_taxes = SaleTax.where("sale_id = ?", saleObj.sale_id) + if !sale_taxes.empty? + sale_taxes.each do |sale_tax| + @account_arr.push(sale_tax) end end + rebate = MembershipSetting.find_by_rebate(1) + # get member information + if @customer.membership_id != nil && rebate + response = Customer.get_member_account(@customer) + if response["status"]==true + response["account_data"].each do |res| + if res["accountable_type"] == "RebateAccount" || res["accountable_type"] == "RebatebonusAccount" + @balance = @balance.to_f + res["balance"].to_f + # @accountable_type = res["accountable_type"] + @accountable_type = "Rebate Balance" + end + end + end end - end - #end customer amount + #end customer amount - #paymal payment + #paymal payment - @sale_data.bookings.each do |sbk| - if sbk.dining_facility_id.to_i >0 - df = DiningFacility.find(sbk.dining_facility_id) - @table_no = df.type + ' ' + df.name - @checkin_time = sbk.checkin_at - @dining = df - break + @sale_data.bookings.each do |sbk| + if sbk.dining_facility_id.to_i >0 + df = DiningFacility.find(sbk.dining_facility_id) + @table_no = df.type + ' ' + df.name + @checkin_time = sbk.checkin_at + @dining = df + break + else + @table_no = nil + @checkin_time = nil + @dining = nil + end + end + + if path.include? ("credit_payment") + @sale_payment_data = SalePayment.get_sale_payment_for_credit(@sale_data) else - @table_no = nil - @checkin_time = nil - @dining = nil + @sale_payment_data = SalePayment.get_sale_payments(@sale_data) end - end + @sale_payment_data.each do |spay| + if spay.payment_method == "cash" + @cash += spay.payment_amount + end + if spay.payment_method !="creditnote" + @other_payment += spay.payment_amount + end - if path.include? ("credit_payment") - @sale_payment_data = SalePayment.get_sale_payment_for_credit(@sale_data) - else - @sale_payment_data = SalePayment.get_sale_payments(@sale_data) - end - @sale_payment_data.each do |spay| - if spay.payment_method == "cash" - @cash += spay.payment_amount - end - if spay.payment_method !="creditnote" - @other_payment += spay.payment_amount + if spay.payment_method == "mpu" + @other += spay.payment_amount + elsif spay.payment_method == "paypar" + @ppamount += spay.payment_amount + elsif spay.payment_method == "visa" + @visacount += spay.payment_amount + elsif spay.payment_method == "jcb" + @jcbcount += spay.payment_amount + elsif spay.payment_method == "master" + @mastercount += spay.payment_amount + elsif spay.payment_method == "unionpay" + @unionpaycount += spay.payment_amount + elsif spay.payment_method == "JunctionPay" + @junctionpaycount += spay.payment_amount + elsif spay.payment_method == "creditnote" + @credit += spay.payment_amount + elsif spay.payment_method == "paymal" + @paymalcount += spay.payment_amount + elsif spay.payment_method == "alipay" + @alipaycount += spay.payment_amount + elsif spay.payment_method == "dinga" + @dingacount += spay.payment_amount + elsif spay.payment_method == "giftvoucher" + @giftvouchercount += spay.payment_amount + end end - if spay.payment_method == "mpu" - @other += spay.payment_amount - elsif spay.payment_method == "paypar" - @ppamount += spay.payment_amount - elsif spay.payment_method == "visa" - @visacount += spay.payment_amount - elsif spay.payment_method == "jcb" - @jcbcount += spay.payment_amount - elsif spay.payment_method == "master" - @mastercount += spay.payment_amount - elsif spay.payment_method == "unionpay" - @unionpaycount += spay.payment_amount - elsif spay.payment_method == "JunctionPay" - @junctionpaycount += spay.payment_amount - elsif spay.payment_method == "creditnote" - @credit += spay.payment_amount - elsif spay.payment_method == "paymal" - @paymalcount += spay.payment_amount - elsif spay.payment_method == "alipay" - @alipaycount += spay.payment_amount - elsif spay.payment_method == "dinga" - @dingacount += spay.payment_amount - elsif spay.payment_method == "giftvoucher" - @giftvouchercount += spay.payment_amount + @paymethod = PaymentMethodSetting.find_by(payment_method: "MMQR") + + if @paymethod.nil? + raise "MMQR payment method not configured" end + + @merchant = KbzMerchant.new(@paymethod) + + @response = @merchant.create_order(amount: @sale_data.grand_total, merch_order_id: @sale_data.receipt_no) + case @response[:status] + when 'success' + @qr_string = @response[:data]["qrCode"] + qrcode = RQRCode::QRCode.new(@qr_string) + + @qr_svg = qrcode.as_svg( + color: "000", + shape_rendering: "crispEdges", + module_size: 3, + standalone: true, + use_path: true + ) + ActionCable.server.broadcast('second_display_view_channel', { data: @qr_string, qr_svg: @qr_svg, grand_total: @sale_data.grand_total, invoice_no: @sale_data.receipt_no }) + + when 'error' + @error = @response[:message] + raise PaymentProcessingError, @response[:message] + when 'failed' + @error = @response[:message] + raise PaymentProcessingError, @response[:message] + end + rescue PaymentProcessingError, StandardError => e + Rails.logger.error("Payment processing error: #{e.message}") + cancel_order_and_sale(sale_id, e.message) + flash[:error] = "Payment failed: #{e.message}" + redirect_to foodcourt_food_court_path + return end - - @paymethod = PaymentMethodSetting.find_by(payment_method: "MMQR") - - - @merchant = KbzMerchant.new(@paymethod) - - @response = @merchant.create_order(amount: @sale_data.grand_total, merch_order_id: @sale_data.receipt_no) - case @response[:status] - when 'success' - @qr_string = @response[:data]["qrCode"] - qrcode = RQRCode::QRCode.new(@qr_string) - - @qr_svg = qrcode.as_svg( - color: "000", - shape_rendering: "crispEdges", - module_size: 3, - standalone: true, - use_path: true - ) - ActionCable.server.broadcast('second_display_view_channel', { data: @qr_string, qr_svg: @qr_svg, grand_total: @sale_data.grand_total, invoice_no: @sale_data.receipt_no }) - - when 'error' - @error = @response[:message] - when 'failed' - @error = @response[:message] - end + end end @@ -333,4 +348,85 @@ class Foodcourt::QrpayController < BaseFoodcourtController format.json { render :json => { status: true, order_id: order.order_id } } end end + + private + + def cancel_order_and_sale(sale_id, error_message = nil) + result = { success: false, error: nil } + + sale = Sale.find_by(sale_id: sale_id) + unless sale + result[:error] = "Sale not found" + return result + end + + order = sale.orders.first + unless order + result[:error] = "Order not found" + return result + end + + booking = order.booking + unless booking + result[:error] = "Booking not found" + return result + end + + begin + Sale.transaction do + order.order_items.update_all(order_item_status: 'cancelled') if order.order_items.present? + + order.update!(status: 'cancelled') + + booking.update!(booking_status: 'cancelled') + + if sale.shift_sale_id + shift = ShiftSale.find(sale.shift_sale_id) + if sale.sale_status == "completed" + shift.calculate(sale_id, "void") + else + shift.update!(total_void: shift.total_void + sale.grand_total) + end + end + + if sale.discount_type == "member_discount" + sale.update!(total_discount: 0) + sale.compute_by_sale_items(0, nil, 'foodcourt') + end + + sale.update!( + rounding_adjustment: 0.0, + payment_status: 'void', + sale_status: 'void' + ) + + if table = booking.dining_facility + table.update!(status: 'available') unless table.current_bookings.exists? + end + + action_by = current_user.role == "cashier" && params[:access_code].present? ? + Employee.find_by(emp_id: params[:access_code])&.name : + current_user.name + + remark = error_message ? "Payment failed: #{error_message}" : "Manually cancelled" + SaleAudit.record_audit_for_edit( + sale.sale_id, + current_user.name, + action_by, + remark, + "SALEVOID" + ) + + SaleOrder.where(sale_id: sale.sale_id).find_each do |sodr| + AssignedOrderItem.where(order_id: sodr.order_id).update_all(delivery_status: 1) + end + + result[:success] = true + end + rescue StandardError => e + result[:error] = "Cancellation failed: #{e.message}" + Rails.logger.error "Cancellation error: #{e.message}\n#{e.backtrace.join("\n")}" + end + result + end end diff --git a/app/views/foodcourt/second_display/_second_display.html.erb b/app/views/foodcourt/second_display/_second_display.html.erb index 2184910e..395e6168 100644 --- a/app/views/foodcourt/second_display/_second_display.html.erb +++ b/app/views/foodcourt/second_display/_second_display.html.erb @@ -61,7 +61,7 @@
- MMQR Payment + MMQR Payment
Scan to Pay

Use your mobile wallet app

@@ -298,16 +298,17 @@ @media(min-width: 600px){ #second_display_order_items{ - margin-top: -3rem; + margin-top: 1rem; } #mmqr_payment{ - margin-top: -3rem; + margin-top: 1rem; + } + + #payment_success{ + margin-top: 3rem; } } - - - @media (max-width: 576px) { .card-header h4 { font-size: 1.1rem; } #order-items-table th, #order-items-table td { diff --git a/app/views/settings/payment_method_settings/_form.html.erb b/app/views/settings/payment_method_settings/_form.html.erb index 66e22fab..a6cc7f1e 100755 --- a/app/views/settings/payment_method_settings/_form.html.erb +++ b/app/views/settings/payment_method_settings/_form.html.erb @@ -1,184 +1,184 @@ -<% breadcrumb_add t("payment_methods"), settings_payment_method_settings_path, settings_payment_method_settings_path, t("views.btn.#{action_name}") %> + <% breadcrumb_add t("payment_methods"), settings_payment_method_settings_path, settings_payment_method_settings_path, t("views.btn.#{action_name}") %> -
-
-
-
- <%= simple_form_for([:settings, @settings_payment_method_setting]) do |f| %> - <%= f.error_notification %> +
+
+
+
+ <%= simple_form_for([:settings, @settings_payment_method_setting]) do |f| %> + <%= f.error_notification %> -
- <%= f.input :payment_method %> - <%= f.input :is_active %> - <%= f.input :gateway_communication_type, collection: [ "Api", "Device", "Manual"] %> - <%= f.input :gateway_url, required: true %> - <%= f.input :auth_token, required: true %> - <%= f.input :merchant_account_id, required: true %> +
+ <%= f.input :payment_method %> + <%= f.input :is_active %> + <%= f.input :gateway_communication_type, collection: [ "Api", "Device", "Manual"] %> + <%= f.input :gateway_url, required: true %> + <%= f.input :auth_token, required: true %> + <%= f.input :merchant_account_id, required: true %> - -
- <%= f.label :additional_parameters, 'Additional Parameters (Key-Value Pairs)' %> -
- <% if @settings_payment_method_setting.additional_parameters.present? %> - <% JSON.parse(@settings_payment_method_setting.additional_parameters).each do |key, value| %> + +
+ <%= f.label :additional_parameters, 'Additional Parameters (Key-Value Pairs)' %> +
+ <% if @settings_payment_method_setting.additional_parameters.present? %> + <% JSON.parse(@settings_payment_method_setting.additional_parameters).each do |key, value| %> +
+
+ +
+
+ +
+
+ +
+
+ <% end %> + <% else %>
-
- -
-
- -
-
+
+ +
+
+ +
+
-
+
- <% end %> - <% else %> -
-
- -
-
- -
-
- -
-
- <% end %> -
+ <% end %> +
- + + + <%= f.hidden_field :additional_parameters, id: 'additional-parameters-json' %> +
- <%= f.hidden_field :additional_parameters, id: 'additional-parameters-json' %>
-
- -
- <%= f.submit "Submit", class: 'btn btn-primary submitBtn btn-lg waves-effect' %> -
- <% end %> +
+ <%= f.submit "Submit", class: 'btn btn-primary submitBtn btn-lg waves-effect' %> +
+ <% end %> +
+
+
+
+
+
+
view_headline <%= t("views.right_panel.header.page_detail") %>
+

+ 1) <%= t("views.right_panel.detail.payment_method") %> - <%= t("views.right_panel.detail.write_txt") %> <%= t("views.right_panel.detail.payment_method_txt") %>
+ 2) <%= t("views.right_panel.detail.is_active") %> - <%= t("views.right_panel.detail.checkbox") %> <%= t("views.right_panel.detail.for") %> <%= t("views.right_panel.detail.is_active_txt") %>
+ 3) <%= t("views.right_panel.detail.gateway") %> <%= t("views.right_panel.detail.comm_type") %> - <%= t("views.right_panel.detail.write_txt") %> <%= t("views.right_panel.detail.gateway_txt") %> <%= t("views.right_panel.detail.comm_type_txt") %>
+ 4) <%= t("views.right_panel.detail.gateway") %> <%= t("views.right_panel.detail.url_txt") %> - <%= t("views.right_panel.detail.write_txt") %> <%= t("views.right_panel.detail.gateway_txt") %> <%= t("views.right_panel.detail.url_txt") %>
+ 5) <%= t("views.right_panel.detail.auth_token") %> - <%= t("views.right_panel.detail.write_txt") %> <%= t("views.right_panel.detail.auth_token_txt") %>
+ 6) <%= t("views.right_panel.detail.merchant_account") %> - <%= t("views.right_panel.detail.write_txt") %> <%= t("views.right_panel.detail.merchant_account_txt") %>
+ 7) <%= t("views.right_panel.detail.additional_parameters") %> - <%= t("views.right_panel.detail.write_txt") %> <%= t("views.right_panel.detail.additional_parameters_txt") %>
+

+
list <%= t("views.right_panel.header.button_lists") %>
+

+ 1) <%= t("views.right_panel.button.submit") %> - <%= t("views.right_panel.detail.submit_btn_txt") %> <%= t("views.right_panel.detail.payment_method_txt") %>
+

+
list <%= t("views.right_panel.header.link_lists") %>
+

+ 1) <%= t("views.right_panel.button.home") %> - <%= t("views.right_panel.detail.home_txt") %>
+ 2) <%= t("views.right_panel.button.back") %> - <%= t("views.right_panel.detail.back_txt") %> <%= t("views.right_panel.detail.payment_method_txt") %>
+

+
-
-
-
-
-
view_headline <%= t("views.right_panel.header.page_detail") %>
-

- 1) <%= t("views.right_panel.detail.payment_method") %> - <%= t("views.right_panel.detail.write_txt") %> <%= t("views.right_panel.detail.payment_method_txt") %>
- 2) <%= t("views.right_panel.detail.is_active") %> - <%= t("views.right_panel.detail.checkbox") %> <%= t("views.right_panel.detail.for") %> <%= t("views.right_panel.detail.is_active_txt") %>
- 3) <%= t("views.right_panel.detail.gateway") %> <%= t("views.right_panel.detail.comm_type") %> - <%= t("views.right_panel.detail.write_txt") %> <%= t("views.right_panel.detail.gateway_txt") %> <%= t("views.right_panel.detail.comm_type_txt") %>
- 4) <%= t("views.right_panel.detail.gateway") %> <%= t("views.right_panel.detail.url_txt") %> - <%= t("views.right_panel.detail.write_txt") %> <%= t("views.right_panel.detail.gateway_txt") %> <%= t("views.right_panel.detail.url_txt") %>
- 5) <%= t("views.right_panel.detail.auth_token") %> - <%= t("views.right_panel.detail.write_txt") %> <%= t("views.right_panel.detail.auth_token_txt") %>
- 6) <%= t("views.right_panel.detail.merchant_account") %> - <%= t("views.right_panel.detail.write_txt") %> <%= t("views.right_panel.detail.merchant_account_txt") %>
- 7) <%= t("views.right_panel.detail.additional_parameters") %> - <%= t("views.right_panel.detail.write_txt") %> <%= t("views.right_panel.detail.additional_parameters_txt") %>
-

-
list <%= t("views.right_panel.header.button_lists") %>
-

- 1) <%= t("views.right_panel.button.submit") %> - <%= t("views.right_panel.detail.submit_btn_txt") %> <%= t("views.right_panel.detail.payment_method_txt") %>
-

-
list <%= t("views.right_panel.header.link_lists") %>
-

- 1) <%= t("views.right_panel.button.home") %> - <%= t("views.right_panel.detail.home_txt") %>
- 2) <%= t("views.right_panel.button.back") %> - <%= t("views.right_panel.detail.back_txt") %> <%= t("views.right_panel.detail.payment_method_txt") %>
-

-
-
-
- - +