diff --git a/Gemfile b/Gemfile index 957e9968..ba568b94 100644 --- a/Gemfile +++ b/Gemfile @@ -44,9 +44,6 @@ gem 'rack-cors' # Multi-tenancy for shops gem 'acts_as_tenant' -# Activerecord-Import is a library for bulk inserting data using ActiveRecord. -gem 'activerecord-import' - # image upload gem 'carrierwave', '~> 1.0' gem 'mini_magick' diff --git a/Gemfile.lock b/Gemfile.lock index 76ce8948..bbcc1085 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -41,8 +41,6 @@ GEM activemodel (= 5.1.7) activesupport (= 5.1.7) arel (~> 8.0) - activerecord-import (1.0.3) - activerecord (>= 3.2) activesupport (5.1.7) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 0.7, < 2) @@ -322,7 +320,6 @@ PLATFORMS ruby DEPENDENCIES - activerecord-import acts_as_tenant aescrypt axlsx (= 2.0.1) diff --git a/app/controllers/api/bill_controller.rb b/app/controllers/api/bill_controller.rb index 77fc9204..9ef0df15 100755 --- a/app/controllers/api/bill_controller.rb +++ b/app/controllers/api/bill_controller.rb @@ -9,7 +9,7 @@ class Api::BillController < Api::ApiController if !ShiftSale.current_shift.nil? #create Bill by Booking ID table = 0 - if (params[:booking_id]) + if params[:booking_id].present? booking = Booking.find(params[:booking_id]) if booking.booking_orders.count > 0 if booking.checkin_at.utc.strftime("%Y-%m-%d %H:%M") > Time.now.utc.strftime("%Y-%m-%d %H:%M") && booking.checkout_at.nil? @@ -51,13 +51,6 @@ class Api::BillController < Api::ApiController @status = false @error_message = "There is no order for '#{params[:booking_id]}'" end - # elsif (params[:order_id]) - # order = Order.find(params[:order_id]) - # @status, @sale_id = Sale.generate_invoice_from_order(params[:order_id], current_login_employee, get_cashier, order.source) - # - # # for Job - # booking = Booking.find_by_sale_id(@sale_id) - # table = DiningFacility.find(booking.dining_facility_id) end # Bind shift sale id to sale diff --git a/app/controllers/api/orders_controller.rb b/app/controllers/api/orders_controller.rb index a1e9ad9e..3002d996 100755 --- a/app/controllers/api/orders_controller.rb +++ b/app/controllers/api/orders_controller.rb @@ -67,15 +67,10 @@ class Api::OrdersController < Api::ApiController if checkin_checkout_time(params[:booking_id]) - if params[:booking_id].present? - booking = Booking.find(params[:booking_id]) - end - if params[:table_id].present? - if booking.nil? || booking.dining_facility_id.to_i != params[:table_id].to_i - table = DiningFacility.find(params[:table_id]) - booking = table.get_current_booking - end - end + table = DiningFacility.find(params[:table_id]) if params[:table_id].present? + + booking = table.current_checkin_booking if table + booking ||= Booking.find(params[:booking_id]) if params[:booking_id].present? #for extratime is_extra_time = false @@ -118,6 +113,7 @@ class Api::OrdersController < Api::ApiController end @status, @booking = @order.generate + if @status && @booking Order.process_order_queue(@order.order_id,@order.table_id,@order.source) end @@ -195,7 +191,7 @@ class Api::OrdersController < Api::ApiController #checked checkin and checkout time def checkin_checkout_time(booking_id) status = true - if !booking_id.nil? + if booking_id.present? if booking = Booking.find(booking_id) if booking.checkout_at.present? if booking.checkout_at.utc <= Time.now.utc diff --git a/app/controllers/api/restaurant/zones_controller.rb b/app/controllers/api/restaurant/zones_controller.rb index 3e2d34ab..d9b5149a 100755 --- a/app/controllers/api/restaurant/zones_controller.rb +++ b/app/controllers/api/restaurant/zones_controller.rb @@ -2,8 +2,8 @@ class Api::Restaurant::ZonesController < Api::ApiController def index if (params[:filter] && params[:filter] = "all" ) - @all_tables = Table.active - @all_rooms = Room.active + @all_tables = Table.includes(:zone, :current_checkin_booking, :current_checkout_booking, :current_reserved_booking).active + @all_rooms = Room.includes(:zone, :current_checkin_booking, :current_checkout_booking, :current_reserved_booking).active else @zones = Zone.includes([:tables, :rooms]).where("is_active = true") end diff --git a/app/controllers/concerns/number_formattable.rb b/app/controllers/concerns/number_formattable.rb index 2507b10a..60f6c4c0 100644 --- a/app/controllers/concerns/number_formattable.rb +++ b/app/controllers/concerns/number_formattable.rb @@ -24,7 +24,7 @@ module NumberFormattable @delimiter = @number_formats.find { |f| f.name.parameterize.underscore == 'delimiter'}.value.gsub(/\\u(\h{4})/) { |m| [$1].pack("H*").unpack("n*").pack("U*") } rescue nil end if @delimiter.nil? - @print_settings = PrintSetting.get_precision_delimiter if !defined? @number_formats + @print_settings = PrintSetting.get_precision_delimiter if !defined? @print_settings if @print_settings && @print_settings.delimiter @delimiter = "," else @@ -42,7 +42,7 @@ module NumberFormattable end def number_format(number, options = {}) - options[:precision] = options[:precision] || precision + options[:precision] = options[:precision] || precision # options[:delimiter] = options[:delimiter] || delimiter options[:strip_insignificant_zeros] = options[:strip_insignificant_zeros] || strip_insignificant_zeros diff --git a/app/controllers/origami/addorders_controller.rb b/app/controllers/origami/addorders_controller.rb index 90bc9183..d1c7bcb3 100755 --- a/app/controllers/origami/addorders_controller.rb +++ b/app/controllers/origami/addorders_controller.rb @@ -186,7 +186,6 @@ class Origami::AddordersController < BaseOrigamiController result = {:status=> @status, :data => 0 } render :json => result.to_json end - end # render json for http status code diff --git a/app/controllers/origami/home_controller.rb b/app/controllers/origami/home_controller.rb index 5393dc96..ad135e03 100755 --- a/app/controllers/origami/home_controller.rb +++ b/app/controllers/origami/home_controller.rb @@ -4,8 +4,8 @@ class Origami::HomeController < BaseOrigamiController def index @webview = check_mobile - @tables = Table.unscope(:order).includes(:zone, :current_checkin_booking, :current_checkout_booking, :current_reserved_booking).all.active.order('status desc') - @rooms = Room.unscope(:order).includes(:zone, :current_checkin_booking, :current_checkout_booking, :current_reserved_booking).all.active.order('status desc') + @tables = Table.unscope(:order).includes(:zone, :current_checkin_booking, :current_checkout_booking, :current_reserved_booking).active.order('status desc') + @rooms = Room.unscope(:order).includes(:zone, :current_checkin_booking, :current_checkout_booking, :current_reserved_booking).active.order('status desc') @complete = Sale.completed_sale("cashier") @orders = Order.includes("sale_orders").where("DATE_FORMAT(date,'%Y-%m-%d') = ? and status != 'billed' and source != 'quick_service'",DateTime.now.strftime('%Y-%m-%d')).order('date desc') diff --git a/app/controllers/origami/request_bills_controller.rb b/app/controllers/origami/request_bills_controller.rb index 69a316f5..ad872a38 100755 --- a/app/controllers/origami/request_bills_controller.rb +++ b/app/controllers/origami/request_bills_controller.rb @@ -18,96 +18,71 @@ class Origami::RequestBillsController < ApplicationController else table = DiningFacility.find_by(id: booking.dining_facility_id) - if sale_data = booking.sale - @status = true - elsif sale_data = Sale.generate_invoice_from_booking(booking, current_login_employee, current_user, order.source, params[:current_checkin_induties_count]) - @status = true - # in-duty update - in_duties = InDuty.where("booking_id=?", booking.id) - if !in_duties.empty? - in_duties.each do |in_duty| - induty = InDuty.find(in_duty.id) - induty.sale_id = sale_data.sale_id - induty.out_time = Time.now.utc - induty.save - end - end - end - - # Bind shift sale id to sale - # @sale_data.shift_sale_id = shift.id - # @sale_data.save - - action_by = current_user.name - type = "REQUEST_BILL" - - remark = "Request bill Receipt No #{sale_data.receipt_no}" - sale_audit = SaleAudit.record_audit_sale(sale_data.sale_id,remark,action_by,type ) - - # Promotion Activation - Promotion.promo_activate(sale_data) - - #bill channel - if ENV["SERVER_MODE"] == 'cloud' - from = request.subdomain + "." + request.domain - else - from = "" - end - - if order.source == "cashier" || order.source == "quick_service" - ActionCable.server.broadcast "bill_channel",table: table, from: from - end - if order.source == "quick_service" || order.source == "food_court" - result = {:status=> @status, :data => sale_data.sale_id } - render :json => result.to_json - else - #check checkInOut pdf print - checkout_time = Lookup.collection_of('checkout_time') - if !booking.dining_facility_id.nil? - terminal = DiningFacility.find_by_id(booking.dining_facility_id) - cashier_terminal = CashierTerminal.find_by_id(terminal.zone_id) - - if (!checkout_time.empty?) && (ENV["SERVER_MODE"] != "cloud") #no print in cloud server - unique_code = "CheckInOutPdf" - printer = PrintSetting.find_by_unique_code(unique_code) - - # print when complete click - order_queue_printer = Printer::OrderQueuePrinter.new(printer) - - if !printer.nil? - order_queue_printer.print_check_in_out(printer, cashier_terminal, booking, table) + if booking.sale.nil? + if sale_data = Sale.generate_invoice_from_booking(booking, current_login_employee, current_user, order.source, params[:current_checkin_induties_count]) + # in-duty update + in_duties = InDuty.where("booking_id=?", booking.id) + if !in_duties.empty? + in_duties.each do |in_duty| + induty = InDuty.find(in_duty.id) + induty.sale_id = sale_data.sale_id + induty.out_time = Time.now.utc + induty.save end end + + action_by = current_user.name + type = "REQUEST_BILL" + + remark = "Request bill Receipt No #{sale_data.receipt_no}" + sale_audit = SaleAudit.record_audit_sale(sale_data.sale_id,remark,action_by,type ) + + # Promotion Activation + Promotion.promo_activate(sale_data) + + #bill channel + if ENV["SERVER_MODE"] == 'cloud' + from = request.subdomain + "." + request.domain + else + from = "" + end + + if order.source == "cashier" || order.source == "quick_service" + ActionCable.server.broadcast "bill_channel",table: table, from: from + end + if order.source == "quick_service" || order.source == "food_court" + result = {:status=> @status, :data => sale_data.sale_id } + render :json => result.to_json + else + #check checkInOut pdf print + checkout_time = Lookup.collection_of('checkout_time') + if !booking.dining_facility_id.nil? + terminal = DiningFacility.find_by_id(booking.dining_facility_id) + cashier_terminal = CashierTerminal.find_by_id(terminal.zone_id) + + if (!checkout_time.empty?) && (ENV["SERVER_MODE"] != "cloud") #no print in cloud server + unique_code = "CheckInOutPdf" + printer = PrintSetting.find_by_unique_code(unique_code) + + # print when complete click + order_queue_printer = Printer::OrderQueuePrinter.new(printer) + + if !printer.nil? + order_queue_printer.print_check_in_out(printer, cashier_terminal, booking, table) + end + end + end + end + @status = true + else + @status = false end end end - @status = true else @status = false @error_message = "No Current Open Shift for This Employee" end - -# Not Use for these printed bill cannot give customer - # unique_code = "ReceiptBillPdf" - # #shop detail - # shop_details = Shop.current_shop - # # customer= Customer.where('customer_id=' +.customer_id) - # customer= Customer.find(@sale_data.customer_id) - # # get member information - # member_info = Customer.get_member_account(customer) - # # get printer info - # print_settings=PrintSetting.find_by_unique_code(unique_code) - - # # find order id by sale id - # # sale_order = SaleOrder.find_by_sale_id(@sale_data.sale_id) - - # # Calculate price_by_accounts - # item_price_by_accounts = SaleItem.calculate_price_by_accounts(@sale_items) - - # printer = Printer::ReceiptPrinter.new(print_settings) - - - # printer.print_receipt_bill(print_settings, false, nil,@sale_items,@sale_data,customer.name, item_price_by_accounts,member_info,shop_details) end end diff --git a/app/controllers/origami/sales_controller.rb b/app/controllers/origami/sales_controller.rb index 98bbd288..05a49a39 100755 --- a/app/controllers/origami/sales_controller.rb +++ b/app/controllers/origami/sales_controller.rb @@ -23,55 +23,36 @@ class Origami::SalesController < BaseOrigamiController end def add_to_existing_invoice - dining = params[:dining_id] - sale_id = params[:sale_id] - tax_type = params[:tax_type] - sale_data = [] - table = DiningFacility.find(dining) - existing_booking = Booking.find_by_sale_id(sale_id) - table.bookings.active.where("DATE_FORMAT(created_at,'%Y-%m-%d') = '#{DateTime.now.strftime('%Y-%m-%d')}' OR DATE_FORMAT(created_at,'%Y-%m-%d') = '#{Date.today.prev_day}' ").each do |booking| - if booking.sale_id.nil? - order_array = [] - booking.booking_orders.each do |booking_order| + Sale.transaction do + dining = params[:dining_id] + sale_id = params[:sale_id] + tax_type = params[:tax_type] - booking.booking_status = 'moved' - order = Order.find(booking_order.order_id) - order.status = 'billed' - order.order_items.each do |item| - item.order_item_status = 'billed' - end - # create sale item - saleobj = Sale.find(sale_id) - order.order_items.each do |orer_item| - saleobj.add_item (orer_item) - if !orer_item.set_menu_items.nil? - saleobj.add_sub_item(orer_item.set_menu_items) - end - sale_data.push(orer_item) - end + table = DiningFacility.find(dining) + booking = table.current_checkin_booking - # Re-compute for add - saleobj.compute(order.source,tax_type) - saleobj.save - order.save - booking.save + sale = Sale.find(sale_id) + existing = sale.booking - order_array.push(order.order_id) - end + sale.sale_items << booking.order_items.to_sale_items + sale.orders << booking.orders - receipt_no = Sale.find(sale_id).receipt_no - action_by = current_user.name - type = "ADD_TO_EXISTING" + sale.compute(booking.orders[0].source, tax_type) - remark = "#{action_by} add to existing order #{order_array} to Receipt No=>#{receipt_no} in #{table.name}" - sale_audit = SaleAudit.record_audit_sale(sale_id,remark,action_by,type ) + type = "ADD_TO_EXISTING" + receipt_no = sale.receipt_no + action_by = current_user.name + order_ids = booking.orders.map(&:order_id) - booking_order = BookingOrder.where('booking_id=?',booking) - booking_order.each do |bo| - bo.booking_id = existing_booking.booking_id - bo.save - end - end + remark = "#{action_by} add to existing order #{order_ids.to_s} to Receipt No=>#{receipt_no} in #{table.name}" + sale_audit = SaleAudit.record_audit_sale(sale_id, remark, action_by, type) + + booking.orders.update_all(status: "billed") + booking.order_items.update_all(order_item_status: "billed") + BookingOrder.where(booking_id: booking.booking_id).update_all(booking_id: existing) + + booking.booking_status = "moved" + booking.save end end diff --git a/app/models/booking.rb b/app/models/booking.rb index f99df507..71833943 100755 --- a/app/models/booking.rb +++ b/app/models/booking.rb @@ -8,7 +8,53 @@ class Booking < ApplicationRecord belongs_to :sale, :optional => true has_many :booking_orders has_many :orders, :through => :booking_orders - has_many :order_items, :through => :orders + has_many :order_items, :through => :orders do + def to_sale_items + sale_items = [] + proxy_association.load_target.select(&:order_items_id).each do |order_item| + menu_category = order_item.menu_category || OpenStruct.new(name: 'Product', code: '') #get menu category for menu items + + sale_items << SaleItem.new({ + menu_category_name: menu_category.name, + menu_category_code: menu_category.code, + product_name: order_item.item_name, + product_code: order_item.item_code, + product_alt_name: order_item.alt_name, + account_id: order_item.account_id, + is_taxable: order_item.taxable, + item_instance_code: order_item.item_instance_code, + qty: order_item.qty, + unit_price: order_item.price, + price: order_item.qty * order_item.price, + taxable_price: order_item.qty * order_item.price, + status: order_item.remark + }) + + if order_item.set_menu_items + JSON.parse(order_item.set_menu_items).each do |item| + instance = MenuItemInstance.find_by_item_instance_code(item["item_instance_code"]) + menu_item = instance.menu_item + menu_category = menu_item.menu_category #get menu category for menu items + sale_items << SaleItem.new({ + menu_category_name: menu_category.name, + menu_category_code: menu_category.code, + product_name: instance.item_instance_name, + product_code: menu_item.item_code, + product_alt_name: menu_item.alt_name, + account_id: menu_item.account_id, + is_taxable: menu_item.taxable, + item_instance_code: item["item_instance_code"], + qty: item["quantity"], + unit_price: item["price"], + price: item["quantity"].to_f * item["price"].to_f, + taxable_price: item["quantity"].to_f * item["price"].to_f + }) + end + end + end + sale_items + end + end scope :active, -> {where("booking_status != 'moved'")} scope :today, -> {where("created_at >= #{Time.now.utc}")} diff --git a/app/models/cashier_terminal.rb b/app/models/cashier_terminal.rb index d8e38c5f..ca34ac1e 100755 --- a/app/models/cashier_terminal.rb +++ b/app/models/cashier_terminal.rb @@ -1,9 +1,10 @@ class CashierTerminal < ApplicationRecord - has_many :cashier_terminal_by_zones - has_many :zones, through: :cashier_terminal_by_zones + has_many :cashier_terminal_by_zones + has_many :zones, through: :cashier_terminal_by_zones + has_one :current_shift, -> { where.not(shift_started_at: nil).where(shift_closed_at: nil) }, class_name: "ShiftSale" - scope :available, -> {where(is_currently_login: false)} - scope :is_active, -> {where(is_active: true)} + scope :available, -> {where(is_currently_login: false)} + scope :is_active, -> {where(is_active: true)} # validations validates_presence_of :name, :printer_name diff --git a/app/models/dining_charge.rb b/app/models/dining_charge.rb index 6f931bcf..bfca726e 100755 --- a/app/models/dining_charge.rb +++ b/app/models/dining_charge.rb @@ -4,7 +4,7 @@ class DiningCharge < ApplicationRecord def self.amount_calculate(dining_charges_obj, checkin , checkout) # note :: the first Charge Block will cost all, the Time rounding block will included in 2nd Charge Block - if !checkin.nil? && !checkout.nil? && !dining_charges_obj.nil? + if !checkin.nil? && !checkout.nil? && !dining_charges_obj.nil? block_count = 0 price = 0 minutes = DiningCharge.time_diff(checkout, checkin) @@ -13,18 +13,17 @@ class DiningCharge < ApplicationRecord if dining_minutes <= free_time price = 0 else - charge_type = dining_charges_obj.charge_type + charge_type = dining_charges_obj.charge_type if charge_type == 'hr' block_count, price = DiningCharge.charges(dining_charges_obj, dining_minutes, 'hr') elsif charge_type == 'day' block_count, price = DiningCharge.charges(dining_charges_obj, dining_minutes, 'day') end end - return block_count, price + return block_count, price else - puts "<<<<<<<< NO" + return 0, 0 end - end # dining charges calculate @@ -37,7 +36,7 @@ class DiningCharge < ApplicationRecord rounding_time = DiningCharge.convert_to_minutes(chargesObj.time_rounding_block.utc.strftime('%H:%M')) if result.to_i < 1 # for dining minute is under charge_block - if dining_minutes > rounding_time + if dining_minutes > rounding_time rounding_block = dining_minutes / rounding_time solid_price = rounding_block * chargesObj.time_rounding_block_price @@ -46,7 +45,7 @@ class DiningCharge < ApplicationRecord else return 1, result.to_i,chargesObj.unit_price end - elsif result.to_i >= 1 + elsif result.to_i >= 1 solid_price = result * chargesObj.unit_price @@ -64,7 +63,7 @@ class DiningCharge < ApplicationRecord else solid_price += (roundingblock * chargesObj.time_rounding_block_price) - return result.to_i, DiningCharge.check_rounding(chargesObj, solid_price, extra_minutes) + return result.to_i, DiningCharge.check_rounding(chargesObj, solid_price, extra_minutes) end end end @@ -74,7 +73,7 @@ class DiningCharge < ApplicationRecord # rounding_block_remain = roundingblock / 1 free_time = DiningCharge.convert_to_minutes(chargesObj.minimum_free_time.utc.strftime('%H:%M')) rounding_block_remain = extra_minutes - free_time - + if chargesObj.time_rounding == "down" return solid_price else diff --git a/app/models/dining_facility.rb b/app/models/dining_facility.rb index 052c4a69..527cb287 100755 --- a/app/models/dining_facility.rb +++ b/app/models/dining_facility.rb @@ -1,12 +1,14 @@ class DiningFacility < ApplicationRecord belongs_to :zone - has_many :dining_charges has_many :in_juties + has_one :dining_charge + has_one :cashier_terminal_by_zone, foreign_key: "zone_id", primary_key: "zone_id" + has_one :cashier_terminal, through: :cashier_terminal_by_zone has_many :bookings has_many :current_bookings, -> { left_joins(:sale).assign.within_time_limit.merge(Booking.where(checkout_at: nil).or(Booking.merge(Sale.where(sale_status: ['new', nil])))) }, class_name: "Booking" - has_one :current_checkin_booking, -> { assign.within_time_limit.where(checkout_at: nil) }, class_name: "Booking" - has_one :current_checkout_booking, -> { left_joins(:sale).assign.within_time_limit.where.not(checkout_at: nil).merge(Sale.where(sale_status: ['new', nil])) }, class_name: "Booking" + has_one :current_checkin_booking, -> { left_joins(:sale).assign.within_time_limit.merge(Sale.where(sale_status: nil)) }, class_name: "Booking" + has_one :current_checkout_booking, -> { left_joins(:sale).assign.within_time_limit.where.not(checkout_at: nil).merge(Sale.where(sale_status: 'new')) }, class_name: "Booking" has_one :current_reserved_booking, -> { left_joins(:sale).assign.within_time_limit.where.not(reserved_at: nil).merge(Sale.where(sale_status: nil)) }, class_name: "Booking" TABLE_TYPE = "Table" diff --git a/app/models/employee.rb b/app/models/employee.rb index f27adf29..6247da6a 100755 --- a/app/models/employee.rb +++ b/app/models/employee.rb @@ -2,6 +2,7 @@ class Employee < ApplicationRecord has_secure_password has_many :commissioners has_many :shit_sales + has_one :current_shift, -> { where.not(shift_started_at: nil).where(shift_closed_at: nil) },class_name: "ShiftSale" belongs_to :order_queue_station validates_presence_of :name, :role diff --git a/app/models/menu_item.rb b/app/models/menu_item.rb index 4735e6d8..9df5783b 100755 --- a/app/models/menu_item.rb +++ b/app/models/menu_item.rb @@ -40,8 +40,8 @@ class MenuItem < ApplicationRecord account_id: account_id, item_code: item_code, item_instance_code: item_instance_code, - name: item_name.to_s + (' - ' + item_instance_name.to_s) || '', - alt_name: item_alt_name || '', + name: "#{item_name}#{' - ' + item_instance_name if item_instance_name.present?}", + alt_name: "#{item_alt_name}", price: price, promotion_price: promotion_price, is_on_promotion: is_on_promotion, diff --git a/app/models/order.rb b/app/models/order.rb index 09caef45..970be522 100755 --- a/app/models/order.rb +++ b/app/models/order.rb @@ -145,7 +145,6 @@ class Order < ApplicationRecord end sub_order_items = sub_order_items.to_json new_order_items << OrderItem.new({ - order_id: id, item_code: menu_item[:item_code], item_instance_code: order_item[:item_instance_code], item_name: menu_item[:name], @@ -160,17 +159,7 @@ class Order < ApplicationRecord }) end - ids = OrderItem.generate_ids(new_order_items.length) - new_order_items.each_with_index do |order_item, index| - order_item.order_items_id = ids[index] - end - - OrderItem.import new_order_items, validate: false - - new_order_items.each do |order_item| - order_item.run_callbacks(:save) { true } - end - + self.order_items << new_order_items self.item_count = self.order_items.count self.save! diff --git a/app/models/order_item.rb b/app/models/order_item.rb index e696bf5a..4ff895af 100755 --- a/app/models/order_item.rb +++ b/app/models/order_item.rb @@ -6,6 +6,8 @@ class OrderItem < ApplicationRecord #Associations belongs_to :order, autosave: true + has_one :menu_item, foreign_key: "item_code", primary_key: "item_code" + has_one :menu_category, through: :menu_item # belongs_to :order, counter_cache: true #Validation diff --git a/app/models/order_queue_station.rb b/app/models/order_queue_station.rb index da7be3c2..e1ebc529 100755 --- a/app/models/order_queue_station.rb +++ b/app/models/order_queue_station.rb @@ -15,7 +15,6 @@ class OrderQueueStation < ApplicationRecord validates_presence_of :station_name, :printer_name def process_order(order, table_id, order_source = nil, pdf_status = nil ,change_to=nil, current_user=nil) - oqs_stations = OrderQueueStation.active order_items = order.order_items @@ -47,14 +46,7 @@ class OrderQueueStation < ApplicationRecord end end - if pdf_status.nil? - ids = AssignedOrderItem.generate_ids(assigned_order_items.length) - assigned_order_items.each_with_index do |assigned_order_item, index| - assigned_order_item.assigned_order_item_id = ids[index] - end - - AssignedOrderItem.import assigned_order_items, validate: false - end + assigned_order_items.each(&:save) end def pay_process_order_queue (order_id, table_id) diff --git a/app/models/product.rb b/app/models/product.rb index 5d1a5fe6..69037e2a 100755 --- a/app/models/product.rb +++ b/app/models/product.rb @@ -14,8 +14,8 @@ class Product < ApplicationRecord account_id: account_id, item_code: item_code, item_instance_code: item_code, - name: item_name || '', - alt_name: item_name || '', + name: "#{item_name}", + alt_name: "#{item_name}", price: price, promotion_price: 0, is_on_promotion: 0, diff --git a/app/models/sale.rb b/app/models/sale.rb index d9fa7db4..eab262ce 100644 --- a/app/models/sale.rb +++ b/app/models/sale.rb @@ -5,10 +5,11 @@ class Sale < ApplicationRecord #primary key - need to be unique generated for multiple shops before_create :generate_custom_id before_create :generate_receipt_no + belongs_to :cashier, foreign_key: "cashier_id", class_name: "Employee" belongs_to :customer, :optional => true belongs_to :shift_sale - has_one :survey, foreign_key: "receipt_no" + has_one :survey, foreign_key: "receipt_no" has_many :sale_audits has_many :sale_items has_many :sale_discount_items @@ -20,6 +21,7 @@ class Sale < ApplicationRecord has_many :orders, through: :sale_orders has_many :order_items, through: :sale_orders has_many :bookings + has_one :booking has_many :product_commissions before_validation :round_to_precision @@ -79,188 +81,109 @@ class Sale < ApplicationRecord Rails.logger.debug '........ Sale data sync completed .......' end end - def self.generate_invoice_from_booking(booking, requested_by, cashier, order_source = nil, current_checkin_induties_count) - if booking - Rails.logger.debug "Booking -> " + booking.id.to_s - Rails.logger.debug "Booking -> Booking Order Count -> " + booking.booking_orders.count.to_s - #get all order attached to this booking and combine into 1 invoice - unless sale = booking.sale - sale = booking.build_sale( + def self.generate_invoice_from_booking(booking, requested_by, cashier, order_source = nil, in_duties_count = 0) + Sale.transaction do + if booking + current = Time.now + #get all order attached to this booking and combine into 1 invoice + sale = booking.sale || booking.build_sale( { tax_type: "execulive" # Default Tax - Values } ) - end - booking.booking_orders.each do |order| - sale.generate_invoice_from_order(order.order_id, booking, requested_by, cashier, order_source) - # saleObj = Sale.find(sale_id) - # order = booking.booking_orders.take.order - # link_order_sale(order.order_id) - end - # InventoryJob.perform_now(self.id) - # InventoryDefinition.calculate_product_count(saleObj) - - # dining charges - charges = DiningCharge.where('dining_facility_id=?', booking.dining_facility_id).take - if !charges.nil? - block_count, diningprice = DiningCharge.amount_calculate(charges, booking.checkin_at, booking.checkout_at) - if charges.charge_type =='hr' - dining_time = booking.checkin_at.strftime('%H:%M %p').to_s + " - " + booking.checkout_at.strftime('%H:%M %p').to_s + if cashier.role == 'cashier' + sale.cashier = cashier + sale.shift_sale = cashier.current_shift + elsif booking.dining_facility_id + sale.shift_sale = booking.dining_facility.cashier_terminal.current_shift + sale.cashier = booking.dining_facility.cashier_terminal.current_shift.employee else - dining_time = booking.checkin_at.strftime('%B %d, %H:%M %p').to_s + " - " + booking.checkout_at.strftime('%B %d, %H:%M %p').to_s + sale.cashier = Employee.where(role: 'cashier').where.not(token_session: [nil, '']).first + sale.shift_sale = sale.cashier.current_shift end - later_time = booking.checkout_at - early_time = booking.checkin_at - distance_in_minutes = ((later_time - early_time)/60.0).round - basic_pay_amount = 0 - name = "" - if current_checkin_induties_count != "0" - basic_pay = Commission.where('commission_type=?','Basic Pay') - basic_pay.each do |pay| - basic_pay_amount = pay.amount - name = pay.name - end - induties_pay_amount = (current_checkin_induties_count.to_i * (distance_in_minutes / 60.0).to_f * basic_pay_amount).to_i - sale.create_saleitem_indutycharges(charges, current_checkin_induties_count.to_i, induties_pay_amount, booking.dining_facility.name, dining_time, order_source, basic_pay_amount) - end - sale.create_saleitem_diningcharges(charges, block_count, diningprice, booking.dining_facility.name, dining_time, order_source) - end - return sale - end - end + sale.cashier_name = sale.cashier.name + sale.requested_by = requested_by.name + sale.requested_at = current - def generate_invoice_from_order(order_id, booking, requested_by, cashier = nil, order_source = nil) - taxable = true + sale.sale_items << booking.order_items.to_sale_items - order = Order.find(order_id) + if dining_charge = booking.dining_facility.dining_charge + block_count, dining_price = DiningCharge.amount_calculate(dining_charge, booking.checkin_at, booking.checkout_at) + format = "%I:%M %p" if dining_charge.charge_type == "hr" + format ||= "%B %d, %I:%M %p" + dining_time = "#{booking.checkin_at.strftime(format)} - #{booking.checkout_at.strftime(format)}" - # current cashier login - open_cashier = Employee.where("role = 'cashier' AND token_session <> ''") - current_shift = ShiftSale.current_shift - # shift with terminal zone + sale.sale_items.build({ + menu_category_name: "Dining Charge", + menu_category_code: "DiningCharge", + product_name: "#{booking.dining_facility.name} ( #{dining_time} )", + product_code: dining_charge.item_code, + product_alt_name: '-', + account_id: 0, + is_taxable: dining_charge.taxable, + qty: block_count, + unit_price: dining_charge.unit_price, + price: dining_price, + taxable_price: dining_price, + }) - # set cashier - if order_source.present? && order_source.downcase == "emenu" - if !booking.dining_facility_id.nil? - table = DiningFacility.find(booking.dining_facility_id) - cashier_zone = CashierTerminalByZone.find_by_zone_id(table.zone_id) - shift = ShiftSale.where("shift_started_at is not null and shift_closed_at is null and cashier_terminal_id = #{cashier_zone.cashier_terminal_id}").first - #for multiple zone with terminal - if shift.nil? - multiple_zone = CashierTerminalByZone.where("zone_id = #{table.zone_id}") - multiple_zone.each do |zone| - shift = ShiftSale.where("shift_started_at is not null and shift_closed_at is null and cashier_terminal_id = #{zone.cashier_terminal_id}").first - if !shift.nil? then - break - end + if in_duties_count.to_i > 0 + basic_pay = Commission.find_by(commission_type: 'Basic Pay') + in_duties_pay_amount = (in_duties_count.to_i * ((booking.checkout_at - booking.checkin_at) / 1.hours) * basic_pay_amount).to_i + + sale.sale_items.build({ + menu_category_name: "Induty Charge", + menu_category_code: "IndutyCharge", + product_name: "#{basic_pay.name} ( #{dining_time} )", + product_code: "", + product_alt_name: '-', + account_id: 0, + is_taxable: dining_charge.taxable, + qty: in_duties_count, + unit_price: basic_pay.amount, + price: in_duties_pay_amount, + taxable_price: in_duties_pay_amount, + }) end end - end - else - shift = ShiftSale.current_open_shift(cashier.id) - end - # set cashier - if shift != nil #if current login employee open shift - self.cashier_id = cashier.id - self.cashier_name = cashier.name - self.shift_sale_id = shift.id - else - if open_cashier.count>0 # if we have two open cashier - # table and terminal in multiple shift - self.cashier_id = open_cashier[0].id - self.cashier_name = open_cashier[0].name - shift_id = ShiftSale.current_open_shift(open_cashier[0].id) - if shift_id - self.shift_sale_id = shift_id.id - else - self.shift_sale_id = current_shift.id - end - else - self.cashier_id = current_shift.employee_id - self.cashier_name = Employee.find(current_shift.employee_id).name - self.shift_sale_id = current_shift.id + + sale.orders << booking.orders + sale.customer_id = booking.orders[0].customer_id + + sale.compute(booking.orders[0].source) + + booking.orders.update_all(status: "billed") + booking.order_items.update_all(order_item_status: "billed") + + booking.checkout_at = current unless booking.checkout_at && booking.checkout_at > current + booking.checkout_by = requested_by.name + + booking.save + return sale end end - - - # set waiter - self.requested_by = requested_by.name - - self.requested_at = DateTime.now.utc.getlocal - - Rails.logger.debug "Order -> #{order.id} | order_status -> #{order.status}" - if order - self.customer_id = order.customer_id - Rails.logger.debug "Order -> #{order.id} | Items Count -> #{order.order_items.count}" - - order.order_items.each do |item| - self.add_item(item) - if !item.set_menu_items.nil? - self.add_sub_item(item.set_menu_items) - end - end - self.orders << order - end - - #compute sales summary - if order_source.nil? - order_source = order.source - end - self.compute(order_source) - - #Update the order items that is billed - order.update_items_status_to_billed(nil) - order.status = "billed" - order.save - - if !booking.checkout_at.nil? - if booking.checkout_at.utc < Time.now.utc - booking.checkout_at = Time.now.utc.getlocal - end - else - booking.checkout_at = Time.now.utc.getlocal - end - - booking.checkout_by = requested_by.name - booking.save - - # InventoryJob.perform_now(self.id) - # saleObj = Sale.find(self.id) - # InventoryDefinition.calculate_product_count(saleObj) - - return self end #fOR Quick Service pay and create - def self.request_bill(order,current_user,current_login_employee) + def self.request_bill(order, current_user, current_login_employee) if !ShiftSale.current_shift.nil? order_id = order.order_id # order_id - bk_order = BookingOrder.find_by_order_id(order_id) - check_booking = Booking.find_by_booking_id(bk_order.booking_id) + booking = order.booking - if @sale_data = check_booking.sale - # Create Sale if it doesn't exist - # puts "current_login_employee" - # puts current_login_employee.name - @sale_items = SaleItem.where("sale_id=?",@sale_id) - elsif @sale_data = Sale.generate_invoice_from_booking(check_booking,current_login_employee,current_user,order.source) - @sale_items = SaleItem.where("sale_id=?",@sale_data.sale_id) - end + sale_data = Sale.generate_invoice_from_booking(check_booking, current_login_employee, current_user, order.source) # Bind shift sale id to sale # @sale_data.shift_sale_id = shift.id # @sale_data.save # Promotion Activation - Promotion.promo_activate(@sale_data) - @status = true - return @status, @sale_data + Promotion.promo_activate(sale_data) + return true, sale_data else - @status = false - @message = "No Current Open Shift for This Employee" + return false, "No Current Open Shift for This Employee" end end #This is when spilt bill is request - then we cannot link order to invoice diff --git a/app/models/sale_item.rb b/app/models/sale_item.rb index 3510b7c4..f686ca09 100755 --- a/app/models/sale_item.rb +++ b/app/models/sale_item.rb @@ -280,6 +280,11 @@ class SaleItem < ApplicationRecord # Loader Service SFTP End + protected + def self.generate_ids(count = 1) + SeedGenerator.generate_ids(self.name, "SLI", count) + end + private def generate_custom_id if self.sale_item_id.nil? diff --git a/app/views/api/restaurant/menu/_menu_item.json.jbuilder b/app/views/api/restaurant/menu/_menu_item.json.jbuilder index 4f283e2c..1ac16ebc 100755 --- a/app/views/api/restaurant/menu/_menu_item.json.jbuilder +++ b/app/views/api/restaurant/menu/_menu_item.json.jbuilder @@ -6,21 +6,21 @@ if item.is_available attr_format = [] # Format for attributes json if item.item_attributes.count > 0 - item_attributes = @item_attributes.select{ |x| item.item_attributes.include?(x.id.to_s) } - attr_format = item_attributes.group_by {|att| att.attribute_type }.map { |type, values| {type: type, values: values.map(&:name)} } + item_attributes = item.item_attributes.map(&:to_s) + attr_format = @item_attributes.select { |x| item_attributes.include?(x.id.to_s) }.group_by {|att| att.attribute_type.strip }.map { |type, values| {type: type, values: values.map { |x| x.name.strip } } } end # Format for option json opt_format = [] # Format for attributes json if item.item_options.count > 0 - item_options = @item_options.select{ |x| item.item_options.include?(x.id.to_s) } - opt_format = item_options.group_by {|opt| opt.option_type }.map { |type, values| {type: type, values: values.map(&:name)} } + item_options = item.item_options.map(&:to_s) + opt_format = @item_options.select { |x| item_options.include?(x.id.to_s) }.group_by {|opt| opt.option_type.strip }.map { |type, values| {type: type, values: values.map { |x| x.name.strip } } } end #Menu Item Information json.id item.id - json.code item.item_code + json.code item.item_code json.name item.name json.alt_name item.alt_name if !request_url.nil? && request_url != '' && !item.image_path.nil? @@ -44,7 +44,7 @@ if item.is_available json.alt_name its.alt_name json.min_selectable_qty its.min_selectable_qty json.max_selectable_qty its.max_selectable_qty - json.instances its.menu_item_instances.map { |i| {id: i.id} } + json.instances its.menu_item_instances.map { |i| {id: i.id} } end json.attributes attr_format @@ -64,7 +64,8 @@ if item.is_available json.instances item.menu_item_instances do |is| if is.is_available # Convert id to name for attributes - instance_attr = @item_attributes.select{ |x| item.item_attributes.include?(x.id) }.pluck(:name) + item_attributes = is.item_attributes.map(&:to_s) + instance_attr = @item_attributes.select{ |x| item_attributes.include?(x.id.to_s) }.pluck(:name) json.id is.id json.code is.item_instance_code diff --git a/app/views/origami/addorders/_menu_item.json.jbuilder b/app/views/origami/addorders/_menu_item.json.jbuilder index 2768fa9c..d6b72535 100755 --- a/app/views/origami/addorders/_menu_item.json.jbuilder +++ b/app/views/origami/addorders/_menu_item.json.jbuilder @@ -31,15 +31,13 @@ json.is_sub_item item.is_sub_item json.unit item.unit # Item Sets of Menu Item -json.item_sets item.item_sets.includes(:menu_item_instances) do |its| +json.item_sets item.item_sets do |its| json.id its.id json.name its.name json.alt_name its.alt_name json.min_selectable_qty its.min_selectable_qty json.max_selectable_qty its.max_selectable_qty - json.instances its.menu_item_instances do |i| - json.id i.id - end + json.instances its.menu_item_instances.map { |i| {id: i.id} } end json.attributes attr_format diff --git a/app/views/origami/dashboard/_menu.json.jbuilder b/app/views/origami/dashboard/_menu.json.jbuilder index 372d521c..6539ffee 100644 --- a/app/views/origami/dashboard/_menu.json.jbuilder +++ b/app/views/origami/dashboard/_menu.json.jbuilder @@ -41,21 +41,21 @@ if (menu.menu_categories) attr_format = [] # Format for attributes json if item.item_attributes.count > 0 - item_attributes = @item_attributes.select{ |x| item.item_attributes.include?(x.id.to_s) } - attr_format = item_attributes.group_by {|att| att.attribute_type }.map { |type, values| {type: type, values: values.map(&:name)} } + item_attributes = item.item_attributes.map(&:to_s) + attr_format = @item_attributes.select { |x| item_attributes.include?(x.id.to_s) }.group_by {|att| att.attribute_type.strip }.map { |type, values| {type: type, values: values.map { |x| x.name.strip } } } end # Format for option json opt_format = [] # Format for attributes json if item.item_options.count > 0 - item_options = @item_options.select{ |x| item.item_options.include?(x.id.to_s) } - opt_format = item_options.group_by {|opt| opt.option_type }.map { |type, values| {type: type, values: values.map(&:name)} } + item_options = item.item_options.map(&:to_s) + opt_format = @item_options.select { |x| item_options.include?(x.id.to_s) }.group_by {|opt| opt.option_type.strip }.map { |type, values| {type: type, values: values.map { |x| x.name.strip } } } end #Menu Item Information json.id item.id - json.code item.item_code + json.code item.item_code json.name item.name json.alt_name item.alt_name json.image item.image_path.url @@ -69,15 +69,14 @@ if (menu.menu_categories) json.unit item.unit # Item Sets of Menu Item - json.item_sets item.item_sets.map { |its| - { id: its.id, - name: its.name, - alt_name: its.alt_name, - min_selectable_qty: its.min_selectable_qty, - max_selectable_qty: its.max_selectable_qty, - instances: its.menu_item_instances.map { |i| {id: i.id} } - } - } + json.item_sets item.item_sets do |its| + json.id its.id + json.name its.name + json.alt_name its.alt_name + json.min_selectable_qty its.min_selectable_qty + json.max_selectable_qty its.max_selectable_qty + json.instances its.menu_item_instances.map { |i| {id: i.id} } + end json.attributes attr_format json.options opt_format @@ -85,7 +84,8 @@ if (menu.menu_categories) json.instances item.menu_item_instances do |is| if is.is_available # Convert id to name for attributes - instance_attr = @item_attributes.select{ |x| item.item_attributes.include?(x.id) }.pluck(:name) + item_attributes = is.item_attributes.map(&:to_s) + instance_attr = @item_attributes.select{ |x| item_attributes.include?(x.id.to_s) }.pluck(:name) json.id is.id json.code is.item_instance_code diff --git a/app/views/settings/rooms/_form.html.erb b/app/views/settings/rooms/_form.html.erb index 27ec89a9..db6f9121 100755 --- a/app/views/settings/rooms/_form.html.erb +++ b/app/views/settings/rooms/_form.html.erb @@ -11,7 +11,7 @@ <%= f.input :seater %> <%= f.input :order_by %> <%= f.input :is_active %> - <% if @settings_room.dining_charges.length == 0 %> + <% if @settings_room.dining_charge.nil? %> <% if @settings_room.id != nil %>
|
- Dining Charge |
- ||||||
| Item code : | -<%= dc.item_code %> | -Unit price : | -<%= dc.unit_price %> | -Charge type : | -<%= dc.charge_type %> | -- <%= link_to 'Edit Charges', edit_settings_zone_room_dining_charge_path(@zone,@settings_room,dc),:class => 'btn bg-deep-purple' %> - - - | -
|
+ Dining Charge |
+ ||||||
| Item code : | +<%= dc.item_code %> | +Unit price : | +<%= dc.unit_price %> | +Charge type : | +<%= dc.charge_type %> | ++ <%= link_to 'Edit Charges', edit_settings_zone_room_dining_charge_path(@zone,@settings_room,dc),:class => 'btn bg-deep-purple' %> + + + | +
- 1) <%= t("views.right_panel.detail.name") %> - <%= t("views.right_panel.detail.write_txt") %> <%= t("views.right_panel.detail.room_txt") %> <%= t("views.right_panel.detail.name_txt2") %>
+ 1) <%= t("views.right_panel.detail.name") %> - <%= t("views.right_panel.detail.write_txt") %> <%= t("views.right_panel.detail.room_txt") %> <%= t("views.right_panel.detail.name_txt2") %>
2) <%= t("views.right_panel.detail.status") %> - <%= t("views.right_panel.detail.write_txt") %> <%= t("views.right_panel.detail.status_txt") %>
3) <%= t("views.right_panel.detail.seater") %> - <%= t("views.right_panel.detail.write_txt") %> <%= t("views.right_panel.detail.seat_txt") %> <%= t("views.right_panel.detail.for") %> <%= t("views.right_panel.detail.room_txt") %>
4) <%= t("views.right_panel.detail.order_by") %> - <%= t("views.right_panel.detail.write_txt") %> <%= t("views.right_panel.detail.order_txt") %> <%= t("views.right_panel.detail.for") %> <%= t("views.right_panel.detail.room_txt") %>
@@ -78,7 +76,6 @@
2) <%= t("views.right_panel.button.back") %> - <%= t("views.right_panel.detail.back_txt") %> <%= t("views.right_panel.detail.zone_txt") %> <%= t("views.right_panel.detail.detail_txt") %>
- 1) <%= t("views.right_panel.detail.name") %> - <%= t("views.right_panel.detail.write_txt") %> <%= t("views.right_panel.detail.table_txt") %> <%= t("views.right_panel.detail.name_txt2") %>
+ 1) <%= t("views.right_panel.detail.name") %> - <%= t("views.right_panel.detail.write_txt") %> <%= t("views.right_panel.detail.table_txt") %> <%= t("views.right_panel.detail.name_txt2") %>
2) <%= t("views.right_panel.detail.status") %> - <%= t("views.right_panel.detail.write_txt") %> <%= t("views.right_panel.detail.status_txt") %>
3) <%= t("views.right_panel.detail.seater") %> - <%= t("views.right_panel.detail.write_txt") %> <%= t("views.right_panel.detail.seat_txt") %> <%= t("views.right_panel.detail.for") %> <%= t("views.right_panel.detail.table_txt") %>
4) <%= t("views.right_panel.detail.order_by") %> - <%= t("views.right_panel.detail.write_txt") %> <%= t("views.right_panel.detail.order_txt") %> <%= t("views.right_panel.detail.for") %> <%= t("views.right_panel.detail.table_txt") %>
@@ -81,8 +80,6 @@
2) <%= t("views.right_panel.button.back") %> - <%= t("views.right_panel.detail.back_txt") %> <%= t("views.right_panel.detail.zone_txt") %>
| Item Code | -Unit Price | -Charge Type | -Free Time | -Charges Block | -Time Rounding | -Actions | - - -
|---|---|---|---|---|---|---|
| <%= dc.item_code %> | -<%= dc.unit_price %> | -<%= dc.charge_type %> | -<%= (dc.minimum_free_time).utc.strftime("%H:%M") %> | -<%= (dc.charge_block).utc.strftime("%H:%M") %> | -<%= dc.time_rounding %> | -<%= link_to 'Edit Charges', edit_settings_zone_table_dining_charge_path(@zone,@settings_table,dc),:class => 'btn bg-deep-purple' %> - - | -
| Item Code | +Unit Price | +Charge Type | +Free Time | +Charges Block | +Time Rounding | +Actions | + + +
|---|---|---|---|---|---|---|
| <%= dc.item_code %> | +<%= dc.unit_price %> | +<%= dc.charge_type %> | +<%= (dc.minimum_free_time).utc.strftime("%H:%M") %> | +<%= (dc.charge_block).utc.strftime("%H:%M") %> | +<%= dc.time_rounding %> | +<%= link_to 'Edit Charges', edit_settings_zone_table_dining_charge_path(@zone,@settings_table,dc),:class => 'btn bg-deep-purple' %> + + | +
- 1) <%= t("views.right_panel.detail.name") %> - <%= t("views.right_panel.detail.table_txt") %> <%= t("views.right_panel.detail.name_txt") %> <%= t("views.right_panel.detail.table_txt") %>
+ 1) <%= t("views.right_panel.detail.name") %> - <%= t("views.right_panel.detail.table_txt") %> <%= t("views.right_panel.detail.name_txt") %> <%= t("views.right_panel.detail.table_txt") %>
2) <%= t("views.right_panel.detail.status") %> - <%= t("views.right_panel.detail.table_txt") %> <%= t("views.right_panel.detail.status_txt") %>
3) <%= t("views.right_panel.detail.seater") %> - <%= t("views.right_panel.detail.seat_txt") %> <%= t("views.right_panel.detail.for") %> <%= t("views.right_panel.detail.table_txt") %>
4) <%= t("views.right_panel.detail.order_by") %> - <%= t("views.right_panel.detail.order_txt") %> <%= t("views.right_panel.detail.for") %> <%= t("views.right_panel.detail.table_txt") %>
@@ -118,8 +117,6 @@
2) <%= t("views.right_panel.button.back") %> - <%= t("views.right_panel.detail.back_txt") %> <%= t("views.right_panel.detail.zone_txt") %>