class SaleItem < ApplicationRecord include NumberFormattable self.primary_key = "sale_item_id" #primary key - need to be unique generated for multiple shops before_create :generate_custom_id belongs_to :sale has_many :product_commissions #compute items - discount, tax, price_change def compute_item end before_validation :round_to_precision after_update :update_stock_journal after_save :update_stock_journal_set_item # Add Sale Items def self.add_sale_items(sale_items) sale_items.each do|saleitemObj| @newsaleitem = SaleItem.new @newsaleitem = saleitemObj.dup @newsaleitem.qty = saleitemObj.qty * (-1) @newsaleitem.unit_price = saleitemObj.unit_price * (-1) @newsaleitem.taxable_price = (saleitemObj.unit_price * saleitemObj.qty) * (-1) @newsaleitem.price = (saleitemObj.unit_price * saleitemObj.qty) * (-1) @newsaleitem.product_name = saleitemObj.product_name + ' (FOC)' @newsaleitem.save end end def self.sync_sale_item_records(sale_items) if !sale_items.nil? sale_items.each do |si| sale_item = SaleItem.find_by_sale_item_id(si['sale_item_id']) # unless SaleItem.exists?(si['sale_item_id']) if sale_item.nil? sale_item = SaleItem.new end sale_item.sale_item_id = si['sale_item_id'] sale_item.sale_id = si['sale_id'] sale_item.menu_category_code = si['menu_category_code'] sale_item.menu_category_name = si['menu_category_name'] sale_item.product_code = si['product_code'] sale_item.product_name = si['product_name'] sale_item.product_alt_name = si['product_alt_name'] sale_item.item_instance_code = si['item_instance_code'] sale_item.account_id = si['account_id'] sale_item.status = si['status'] sale_item['remark'] = si['remark'] sale_item['qty'] = si['qty'] sale_item['unit_price'] = si['unit_price'] sale_item['taxable_price'] = si['taxable_price'] sale_item['price'] = si['price'] sale_item['is_taxable'] = si['is_taxable'] sale_item.save end Rails.logger.debug '....... Sale Item sync completed ......' end end def self.update_existing_item(qty, item, sale_id, type, item_price, price) # Original Item to add remark item.status = type item.save menu_category = MenuCategory.get_menu_category(item.product_code) #get menu category for menu items # sale_item = SaleItem.new # sale_item.menu_category_code = menu_category.code ? menu_category.code : nil # sale_item.menu_category_name = menu_category.name # sale_item.product_code = item.product_code # sale_item.item_instance_code = item.item_instance_code # sale_item.product_name = item.product_name + " (#{type.upcase})" # sale_item.product_alt_name = item.product_alt_name # sale_item.account_id = item.account_id # sale_item.status = type # sale_item.remark = type # if type == "foc" || type == "promotion" || type == "void" || type == "waste" || type == "spoile" # sale_item.qty = qty * (-1) # else # sale_item.qty = qty # end # sale_item.unit_price = item_price # * (-1) # sale_item.taxable_price = (price) * (-1) # sale_item.price = (price) * (-1) # sale_item.is_taxable = 1 # sale_item.sale_id = sale_id # sale_item.save if type != "foc" sale = Sale.find(sale_id) sale.compute_by_sale_items(sale.total_discount) end end def self.get_order_items_details(sale_id) order_details = SaleItem.select("sales.total_tax as tax_amount, sales.grand_total as grand_total_amount , sales.total_discount as discount_amount,sales.receipt_date as receipt_date, sales.cashier_name,sales.receipt_no,sale_items.product_name as item_name,sale_items.qty,sale_items.price,sale_items.unit_price as total_price") .joins("left join sales on sales.sale_id = sale_items.sale_id") .where("sale_items.sale_id=?",sale_id) # sale_orders = SaleOrder.where("sale_id=?",sale_id) # if sale_orders # sale_orders.each do |sale_order| # order_details = SaleItem.select("sales.total_discount as discount_amount,DATE_FORMAT(sales.receipt_date,'%Y-%m-%d %h:%m') as receipt_date,sales.cashier_name,sales.receipt_no,sale_items.product_name as item_name,sale_items.qty,sale_items.price,sale_items.unit_price as total_price") # .joins("left join sales on sales.id = sale_items.sale_id") # .where("sale_items.sale_id=?",sale_order.sale_id) # return order_details # end # else # return false # end end # Get Prices for each accounts (eg: food, beverage) def self.calculate_price_by_accounts(sale_items) price_accounts = [] Account.all.each do |a| account_price = {:name => a.title, :price => 0} # Check for actual sale items sale_items.each do |si| if si.account_id == a.id account_price[:price] = account_price[:price] + si.price end end price_accounts.push(account_price) end return price_accounts end # Get discount Prices for each accounts (eg: food, beverage) def self.get_discount_price_by_accounts(sale_items) discount_accounts = [] Account.all.each do |a| discount_account = {:name => a.title, :price => 0} # Check for actual sale items sale_items.where("status = 'Discount'").each do |si| if si.account_id == a.id discount_account[:price] = (discount_account[:price].abs + si.price.abs) * (1) end end discount_accounts.push(discount_account) end return discount_accounts end # Calculate rebate_by_account def self.calculate_rebate_by_account(sale_items) rebateacc = Account.where("rebate=?",true) price = 0 rebate_arr = [] rebateacc.each do |a| account_price = {:type => a.title, :amount => 0} # Check for actual sale items sale_items.each do |si| if si.account_id == a.id account_price[:amount] = account_price[:amount] + si.price price = price + si.price end end rebate_arr.push(account_price) end return price,rebate_arr end # get food price or beverage price for item def self.get_rebate_price(sale_item_id,rebateacc) price = 0 type = '' item=SaleItem.select("sale_items.price , menu_items.account_id") .joins("left join menu_items on menu_items.item_code = sale_items.product_code") .where("sale_items.sale_item_id=?", sale_item_id.to_s) rebateacc.each do |i| if item[0].account_id == i.id price = item[0].price type = i.title end end return price,type end # Get Prices for each accounts (eg: food, beverage) def self.calculate_other_charges(sale_items) total = 0 # Check for actual sale items sale_items.each do |si| if si.product_code == "Other Charges" && si.item_instance_code == nil total = total + si.price end end return total end # def self.get_overall_discount(sale_id) # price = 0.0 # item=SaleItem.where("product_code=?", sale_id) # # item.each do|i| # price += i.price # end # # return price # end def self.get_all_sale_items(sale_id) sale_items = SaleItem.select("sale_id,product_code,item_instance_code, product_name,product_alt_name,account_id,status,remark, SUM(qty) as qty, unit_price, taxable_price, SUM(price) as price, is_taxable") .where("sale_id = ?",sale_id) .order("product_name asc") .group("status,product_name,item_instance_code,unit_price") return sale_items end # Loader Service SFTP Start # Detail Sale Data def self.get_detail_sale_data(transaction_date) query = SaleItem.select(" sale_items.sale_item_id as id, sale_items.sale_id as parent_id, s.receipt_no as check_num, s.receipt_date as business_date, s.receipt_date as transaction_date, '' as item_seq, sale_items.menu_category_code as category_code, sale_items.menu_category_name as category_name, '' as sub_category_code, '' as sub_category_name, '' as report_group_code, '' as report_group_name, sale_items.product_code as item_id, sale_items.product_name as item_name, sale_items.qty as qty, CASE WHEN s.sale_status = 'completed' OR s.sale_status = 'void' THEN 'Sales' WHEN s.sale_status = 'waste' THEN 'Waste' WHEN s.sale_status = 'spoile' THEN 'Spoil' END as transaction_type, sale_items.price as gross_sales, '' as discount_code, CASE WHEN i.unit_price IS NOT NULL THEN i.unit_price ELSE 0 END as discount_amt, (sale_items.price - (CASE WHEN i.unit_price IS NOT NULL THEN i.unit_price ELSE 0 END)) as sales, ((sale_items.price - (CASE WHEN i.unit_price IS NOT NULL THEN i.unit_price ELSE 0 END))/21) as tax_amt, '' as service_charges, ((sale_items.price - (CASE WHEN i.unit_price IS NOT NULL THEN i.unit_price ELSE 0 END)) - ((sale_items.price - (CASE WHEN i.unit_price IS NOT NULL THEN i.unit_price ELSE 0 END))/21)) as net_sales, '0' as is_set_item, '0' as is_staff_meal, '0' as is_raw_wastage, '0' as is_semi_wastage, CASE WHEN s.sale_status = 'waste' THEN 1 ELSE 0 END as is_wastage, CASE WHEN s.sale_status = 'spoile' THEN 1 ELSE 0 END as is_spoilage, '0' as is_sampling, '1' as tax_able, CASE WHEN s.sale_status = 'void' THEN 1 ELSE 0 END as is_void ") .joins("LEFT JOIN sales s ON s.sale_id = sale_items.sale_id") .joins("LEFT JOIN sale_items i ON sale_items.sale_id = i.sale_id AND sale_items.item_instance_code = i.item_instance_code AND i.status = 'Discount' AND sale_items.qty = abs(i.qty)") .where("DATE(s.receipt_date) = ? AND s.sale_status != 'void' AND (sale_items.status NOT IN('Discount', 'void','foc') OR sale_items.status IS NULL)", transaction_date) .order("s.receipt_no") end # 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? self.sale_item_id = SeedGenerator.generate_id(self.class.name, "SLI") end end def round_to_precision unit_price_fraction = self.unit_price % 1 price_fraction = self.unit_price % 1 # is_dining_charge = self.menu_category_code == 'DingingCharge' if self.unit_price != self.unit_price_was || self.price != self.price_was if unit_price_fraction > 0 || price_fraction > 0 if ['Discount', 'promotion'].include?(self.status) self.unit_price = self.unit_price.round(precision) self.price = (self.unit_price * self.qty).round(precision) self.taxable_price = self.price end end end end def update_stock_journal unless MenuItemInstance.where("item_instance_name <> ''").pluck(:item_instance_code).include?(self.item_instance_code) is_void = self.status == "void" && self.status_before_last_save != "void" cancel_void = self.status_before_last_save == "void" && self.status.nil? is_edit = self.qty >= 0 && self.qty != self.qty_before_last_save is_foc = self.status == "foc" && self.status_before_last_save != "foc" cancel_foc = self.status_before_last_save == "foc" if is_void or cancel_void or is_edit or is_foc or cancel_foc found, inventory_definition = InventoryDefinition.find_product_in_inventory(self) if found stock = StockJournal.where('item_code=?', self.item_instance_code).order("id DESC").first unless stock.nil? check_item = StockCheckItem.where('item_code=?', self.item_instance_code).order("id DESC").first if is_void or cancel_void or is_edit if is_void qty = -self.qty remark = "void" elsif cancel_void qty = self.qty remark = "cancel void" elsif is_edit qty = self.qty - self.qty_before_last_save remark = "edit" end StockJournal.add_to_journal(self.item_instance_code, qty, stock.balance, remark, inventory_definition, self.id, StockJournal::SALES_TRANS) check_item.different = check_item.different + qty check_item.save else is_foc or cancel_foc qty = StockJournal.where(trans_ref: self.sale_item_id).sum("credit-debit") if order_item_id = self.sale.bookings.first.order_items.where(item_instance_code: self.item_instance_code, qty: self.qty + qty).select(:order_items_id).first.order_items_id if stock_journal = StockJournal.find_by_trans_ref(order_item_id) if is_foc stock_journal.update(remark: "foc") elsif cancel_foc stock_journal.update(remark: "cancel_foc") end end end end end end end end end def update_stock_journal_set_item is_void = self.status == "void" && self.status_before_last_save != "void" && self.qty > 0 cancel_void = self.status_before_last_save == "void" && self.status.nil? is_edit = self.qty >= 0 && self.qty != self.qty_before_last_save is_foc = self.status == "foc" && self.status_before_last_save != "foc" cancel_foc = self.status_before_last_save == "foc" is_waste = self.status == "waste" is_spoile = self.status == "spoile" if MenuItemInstance.where("item_instance_name <> ''").pluck(:item_instance_code).include?(self.item_instance_code) if self.qty == 1 && self.qty != self.qty_before_last_save found, inventory_definition = InventoryDefinition.find_product_in_inventory(self) if found stock = StockJournal.where('item_code=?', self.item_instance_code).order("id DESC").first unless stock.nil? check_item = StockCheckItem.where('item_code=?', self.item_instance_code).order("id DESC").first if self.qty.to_i >= 0 qty = self.qty - self.qty_was if stock.balance.to_i >= qty Rails.logger.info ">> stock is greater than order qty" remark = "ok" else Rails.logger.info " << stock is less than order qty" remark = "out of stock" end end StockJournal.add_to_journal(self.item_instance_code, qty, stock.balance, remark, inventory_definition, self.id, StockJournal::SALES_TRANS) check_item.different = check_item.different - qty check_item.save else StockJournal.add_to_journal(self.item_instance_code, self.qty, 0, "out of stock", inventory_definition, self.id, StockJournal::SALES_TRANS) end end elsif is_void or cancel_void or is_edit if is_void qty = -self.qty remark = "void" elsif cancel_void qty = self.qty remark = "cancel void" elsif is_edit qty = self.qty - self.qty_before_last_save remark = "edit" end found, inventory_definition = InventoryDefinition.find_product_in_inventory(self) if found stock = StockJournal.where('item_code=?', self.item_instance_code).order("id DESC").first unless stock.nil? check_item = StockCheckItem.where('item_code=?', self.item_instance_code).order("id DESC").first StockJournal.add_to_journal(self.item_instance_code, qty, stock.balance, remark, inventory_definition, self.id, StockJournal::SALES_TRANS) check_item.different = check_item.different + qty check_item.save end end elsif is_foc or cancel_foc qty = StockJournal.where(trans_ref: self.sale_item_id).sum("credit-debit") if stock_journal = StockJournal.where(trans_ref: self.sale_item_id, item_code: self.item_instance_code).order(id: :desc).first if is_foc stock_journal.update(remark: "foc") elsif cancel_foc stock_journal.update(remark: "cancel_foc") end end elsif is_waste or is_spoile found, inventory_definition = InventoryDefinition.find_product_in_inventory(self) if found if stock_journal = StockJournal.where(trans_ref: self.sale_item_id, item_code: self.item_instance_code) stock_journal.update(remark: self.status) end end end end end end