From 6a84a34a9f04ea3d8bf43c067ee01a395ec4fbbc Mon Sep 17 00:00:00 2001 From: Min Zeya Phyo Date: Mon, 17 Apr 2017 13:09:54 +0630 Subject: [PATCH] sale receipt generation and bill api --- app/controllers/api/api_controller.rb | 8 +- app/controllers/api/bill_controller.rb | 15 +- app/models/menu_item.rb | 1 + app/models/order.rb | 16 +- app/models/order_item.rb | 5 +- app/models/sale.rb | 154 ++++++++++++++---- app/models/sale_discount_item.rb | 3 - app/views/api/bill/create.json.jbuilder | 7 + config/routes.rb | 2 +- .../20170331024749_create_menu_items.rb | 1 + .../20170403140820_create_order_items.rb | 2 + db/migrate/20170403160742_create_sales.rb | 4 +- ...170403162844_create_sale_discount_items.rb | 19 --- db/seeds.rb | 6 +- spec/models/sale_discount_item_spec.rb | 5 - 15 files changed, 177 insertions(+), 71 deletions(-) delete mode 100644 app/models/sale_discount_item.rb create mode 100644 app/views/api/bill/create.json.jbuilder delete mode 100644 db/migrate/20170403162844_create_sale_discount_items.rb delete mode 100644 spec/models/sale_discount_item_spec.rb diff --git a/app/controllers/api/api_controller.rb b/app/controllers/api/api_controller.rb index 2c8a1453..c61b5bc2 100644 --- a/app/controllers/api/api_controller.rb +++ b/app/controllers/api/api_controller.rb @@ -1,7 +1,9 @@ class Api::ApiController < ActionController::API include TokenVerification - helper_method :current_token + helper_method :current_token, :current_login_employee + + private #this is base api base controller to need to inherit. #all token authentication must be done here #response format must be set to JSON @@ -10,4 +12,8 @@ class Api::ApiController < ActionController::API return token end end + + def current_login_employee + @employee = Employee.find_by_token_session(current_token) + end end diff --git a/app/controllers/api/bill_controller.rb b/app/controllers/api/bill_controller.rb index 31aa674c..41f6badd 100644 --- a/app/controllers/api/bill_controller.rb +++ b/app/controllers/api/bill_controller.rb @@ -1,10 +1,21 @@ -class Api::Restaurant::BillController < Api::ApiController - +class Api::BillController < Api::ApiController #Create invoice based on booking #Output and invoice def create + @status = false + @error_message = "Order ID or Booking ID is require to request for a bill." + #create Bill by Booking ID + if (params[:booking_id]) + @sale = Sale.new + @sale.generate_invoice_from_booking(params[:booking_id], current_login_employee.name) + + elsif (params[:order_id]) + @sale = Sale.new + @sale.generate_invoice_from_order(params[:order_id], current_login_employee.name) + + end end diff --git a/app/models/menu_item.rb b/app/models/menu_item.rb index f2aab33c..5adf161a 100644 --- a/app/models/menu_item.rb +++ b/app/models/menu_item.rb @@ -20,6 +20,7 @@ class MenuItem < ApplicationRecord menu_item_hash[:promotion_price] = mt_instance.promotion_price menu_item_hash[:is_on_promotion] = mt_instance.is_on_promotion menu_item_hash[:is_available] = mt_instance.is_available + menu_item_hash[:taxable] = menu_item.taxable return menu_item_hash end diff --git a/app/models/order.rb b/app/models/order.rb index 101ba718..3589d5d9 100644 --- a/app/models/order.rb +++ b/app/models/order.rb @@ -70,7 +70,7 @@ class Order < ApplicationRecord if (menu_item) OrderItem.processs_item(menu_item[:item_instance_code], menu_item[:name], - item[:qty], item[:options], set_order_items, self.id, + item[:qty],item[:price], item[:options], set_order_items, self.id, self.employee_name) end end @@ -86,6 +86,20 @@ class Order < ApplicationRecord end + def update_items_status_to_billed(items) + if (items) + ##Update the order status to ensure that reflect the stage + order.order_items.each do |item| + item.order_item_status = "billed" + item.save + end + else + items.each do |item| + item.order_item_status = "billed" + item.save + end + end + end def default_values self.customer = Customer.find(1) if self.customer_id.nil? diff --git a/app/models/order_item.rb b/app/models/order_item.rb index 14eb6287..afba7d7e 100644 --- a/app/models/order_item.rb +++ b/app/models/order_item.rb @@ -15,14 +15,15 @@ class OrderItem < ApplicationRecord # option_values : [], # sub_order_items : [], # } - def self.processs_item (item_code, menu_name, qty, options, set_menu_items, order_id, item_order_by) + def self.processs_item (item_code, menu_name, qty,price, options, set_menu_items, order_id, item_order_by) orderitem = OrderItem.create do |oitem| oitem.order_id = order_id oitem.item_code = item_code oitem.item_name = menu_name oitem.qty = qty - oitem.options = options + oitem.price = price + oitem.options = options oitem.set_menu_items = set_menu_items oitem.item_order_by = item_order_by #person who order this. * If emenu - it will be login user on the app end diff --git a/app/models/sale.rb b/app/models/sale.rb index a0d7037c..f304363d 100644 --- a/app/models/sale.rb +++ b/app/models/sale.rb @@ -1,7 +1,7 @@ class Sale < ApplicationRecord - before_create :generate_receipt_no - belongs_to :cashier - belongs_to :customer + #before_create :generate_receipt_no + belongs_to :cashier, :optional => true + belongs_to :customer, :optional => true has_many :sale_items has_many :sale_discount_items has_many :sale_discounts @@ -9,32 +9,93 @@ class Sale < ApplicationRecord has_many :sale_payments has_many :sale_orders - def generate_invoice_from_order (order_no, sale_id) + def generate_invoice_from_booking(booking_id, requested_by) + booking = Booking.find(booking_id) + status = false + Rails.logger.debug "Booking -> " + booking.id.to_s + if (booking) + 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 + booking.booking_orders.each do |order| + if booking.sale_id + status, sale_id = generate_invoice_from_order(order.order_id, nil, requested_by) + else + status, sale_id = generate_invoice_from_order(order.order_id, booking.sale_id, requested_by) + end + booking.sale_id = sale_id + end + + return status + end + end + + def generate_invoice_from_order (order_no, sale_id, requested_by) + taxable = true #if sale_id is exsit and validate #add order to that invoice if (sale_id) - self = Sale.find(sale_id) + self.find(sale_id) end + Rails.logger.debug "Does it have Existing Sale -> [#{self.id.to_s}] - Status [#{self.sale_status}]" + if self.sale_status == "void" return false, "Invoice is void. Cannot be edited" else + #if this is new sale generate_receipt_no + generate_receipt_no + #Default - Values + self.tax_type = "execlusive" + + self.requested_by = requested_by + self.requested_at = DateTime.now.utc + order = Order.find(order_no) + Rails.logger.debug "Order -> #{order.id} | order_status -> #{order.status}" if order + Rails.logger.debug "Order -> #{order.id} | Items Count -> #{order.order_items.count}" + order.order_items.each do |item| - self.sale_items.add(add_item(item)) + add_item(item) end + end link_order_sale(order.id) - return self.save! + self.save! + #compute summary + compute + #Update the order items that is billed + order.update_items_status_to_billed + + return true, self.id end + return false, nil end - def generate_invoice_by_items (items) + #This is when spilt bill is request - then we cannot link order to invoice + #Cos there will be multiple orders - and items are spilt from there. + #Unless order is spilt by then it is possible. + def generate_invoice_by_items (items, requested_by) + taxable = true + self.requested_by = requested_by + self.requested_at = DateTime.now.utc + + items.each do |item| + add_item(item) + + #this will result in multiple orders belonging in multiple invoices - because of spilt invoices. + link_order_sale(item.order_id, taxable) + end + + #Update item status as billed + order.update_items_status_to_billed(items) + + status = self.save! + return status, self.id end @@ -43,11 +104,20 @@ class Sale < ApplicationRecord #save sale_audit sale_item = SaleItem.new - sale_item.sale = self + + #pull sale_item.product_code = item.item_code sale_item.product_name = item.item_name + sale_item.remark = item.remark + sale_item.qty = item.qty - return sale_item + sale_item.unit_price = item.price + sale_item.taxable_price = item.price + sale_item.is_taxable = item.taxable + + sale_item.price = sale_item.qty * sale_item.unit_price + + self.sale_items << sale_item end @@ -56,7 +126,8 @@ class Sale < ApplicationRecord end - def apply_item_discount (promotion_id, item) + def apply_item_discount (item_code, discount_type, discount_amount) + end def apply_discount (discount_type, discount_code) @@ -64,15 +135,12 @@ class Sale < ApplicationRecord end def accept_payment (payment_method, amount, payment_ref, payment_external_result) + end def void_sales (void_by, reason, approval_code, request_by) #save sale_audit self.sale_status = "void" - self. - - - end #compute - invoice total @@ -80,65 +148,87 @@ class Sale < ApplicationRecord sales_items = self.sale_items #Computation Fields - total_items_price = 0 - total_discounts = 0 + subtotal_price = 0 total_taxable = 0 rounding_adjustment = 0 sales_items.each do |item| #compute each item and added to total + subtotal_price = subtotal_price + item.price + total_taxable = total_taxable + item.taxable_price end - apply_tax + apply_tax (total_taxable) + self.total_amount = subtotal_price + self.total_discount = total_discount + + + self.save! + end - def apply_tax + def apply_tax(total_taxable) + #if tax is not apply create new record - self.sale_taxes.each |existing_tax| + self.sale_taxes.each do |existing_tax| #delete existing and create new existing_tax.delete end + total_tax_amount = 0 #tax_profile - list by order_by tax_profiles = TaxProfile.all.order("order_by asc") - total_amount = self.total_amount #Creat new tax records tax_profiles.each do |tax| sale_tax = SaleTax.new(:sale => self) sale_tax.tax_name = tax.name sale_tax.tax_rate = tax.rate #include or execulive - sale_tax.tax_payable_amount = total_amount * tax.rate + sale_tax.tax_payable_amount = total_taxable * tax.rate #new taxable amount - total_amount = total_amount + sale_tax.tax_payable_amount + total_taxable = total_taxable + sale_tax.tax_payable_amount sale_tax.inclusive = tax.inclusive sale_tax.save + + total_tax_amount = total_tax_amount + sale_tax.tax_payable_amount end - + self.total_tax = total_tax_amount end private - def product_get_unit_price(item.item_code) - menu_instance_code = MenuInstanceCode.find_by_item_instance_code(item.item_code) + def product_get_unit_price(item_code) + menu_item_hash =MenuItem.search_by_item_code(item_code) if (menu_instance_code) - + return menu_ item_hash[:item_instance_code], menu_item_hash[:price] + end + return nil,nil end - def link_order_sale(order.id) - + def link_order_sale(order_id) + #create if it doesn't exist + if (SaleOrder.where("sale_id = #{self.id} and order_id=#{order_id}").nil?) + SaleOrder.create(:sale_id => self.id, :order_id => order_id) + end + #dosomrting here + #puts Time.now.format(":short") end + #Generate new Receipt No when it is not assigned def generate_receipt_no #Date-Shift- - if !self.receipt_no.nil? - prefix = Date.now() - self.receipt_no = prefix.to_s + "/" + self.shit_id.to_s + "/" + SeedGenerator.new_receipt_no().to_s + if self.receipt_no.nil? + prefix = DateTime.now() + #self.receipt_no = prefix.to_s + "/" + self.shit_id.to_s + "/" + SeedGenerator.new_receipt_no().to_s + self.receipt_no = prefix.strftime("%Y%m%d") + "/" + SeedGenerator.new_receipt_no().to_s + self.receipt_date = prefix + Rails.logger.debug "Receipt No #{self.receipt_no} | Date #{ self.receipt_date.to_s}" end end + end diff --git a/app/models/sale_discount_item.rb b/app/models/sale_discount_item.rb deleted file mode 100644 index 5b4df3bd..00000000 --- a/app/models/sale_discount_item.rb +++ /dev/null @@ -1,3 +0,0 @@ -class SaleDiscountItem < ApplicationRecord - belongs_to :sale -end diff --git a/app/views/api/bill/create.json.jbuilder b/app/views/api/bill/create.json.jbuilder new file mode 100644 index 00000000..332a9cec --- /dev/null +++ b/app/views/api/bill/create.json.jbuilder @@ -0,0 +1,7 @@ +if @status == true + #show invoice number and stuff + json.status @status +else + json.status @status + json.error_message @error_message +end diff --git a/config/routes.rb b/config/routes.rb index 391979b0..69d66681 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -31,7 +31,7 @@ Rails.application.routes.draw do end #User request move table or bills post "bill/:booking_id" => "bill#create" - post "move" => "move#update" + post "move" => "move#create" #Order Controller resources :orders, only: [:create, :show, :update] do diff --git a/db/migrate/20170331024749_create_menu_items.rb b/db/migrate/20170331024749_create_menu_items.rb index 979f51af..0685dc34 100644 --- a/db/migrate/20170331024749_create_menu_items.rb +++ b/db/migrate/20170331024749_create_menu_items.rb @@ -8,6 +8,7 @@ class CreateMenuItems < ActiveRecord::Migration[5.0] t.references :menu_category, foreign_key: true t.references :menu_item, foreign_key: true t.integer :min_qty, :null => false, :default => 1 + t.boolean :taxable, :null => false, :default => true t.integer :min_selectable_item, :null => false, :default => 1 t.integer :max_selectable_item, :null => false, :default => 1 t.string :created_by diff --git a/db/migrate/20170403140820_create_order_items.rb b/db/migrate/20170403140820_create_order_items.rb index 24cc81ca..847381a4 100644 --- a/db/migrate/20170403140820_create_order_items.rb +++ b/db/migrate/20170403140820_create_order_items.rb @@ -7,9 +7,11 @@ class CreateOrderItems < ActiveRecord::Migration[5.0] t.string :item_code, :null => false t.string :item_name, :null => false t.decimal :qty, :precision => 10, :scale => 2, :null => false, :default => 0.00 + t.decimal :price, :precision => 10, :scale => 2, :null => false, :default => 0.00 t.string :remark t.string :options t.json :set_menu_items #this parameter is require to route the items correctly + t.boolean :taxable, :null => false, :default => true t.timestamps end end diff --git a/db/migrate/20170403160742_create_sales.rb b/db/migrate/20170403160742_create_sales.rb index ae8fcdaa..02db3361 100644 --- a/db/migrate/20170403160742_create_sales.rb +++ b/db/migrate/20170403160742_create_sales.rb @@ -1,8 +1,8 @@ class CreateSales < ActiveRecord::Migration[5.0] def change create_table :sales do |t| - t.integer :cashier_id, :null => false, :index => true - t.string :cashier_name, :null => false + t.integer :cashier_id, :index => true + t.string :cashier_name t.string :requested_by, :null => false t.datetime :requested_at, :null => false t.string :receipt_no, :null => false diff --git a/db/migrate/20170403162844_create_sale_discount_items.rb b/db/migrate/20170403162844_create_sale_discount_items.rb deleted file mode 100644 index aab04564..00000000 --- a/db/migrate/20170403162844_create_sale_discount_items.rb +++ /dev/null @@ -1,19 +0,0 @@ -class CreateSaleDiscountItems < ActiveRecord::Migration[5.0] - def change - create_table :sale_discount_items do |t| - t.references :sale, foreign_key: true - t.string :product_code, :null => false - t.string :product_name, :null => false - t.decimal :unit_price, :precision => 10, :scale => 2, :null => false, :default => 0.00 - t.decimal :discounted_price, :precision => 10, :scale => 2, :null => false, :default => 0.00 - t.decimal :qty, :precision => 10, :scale => 2, :null => false, :default => 0.00 - t.string :remark - t.decimal :price, :precision => 10, :scale => 2, :null => false, :default => 0.00 - t.string :discount_type, :precision => 10, :scale => 2, :null => false, :default => 0.00 - t.decimal :discounted_value, :precision => 10, :scale => 2, :null => false, :default => 0.00 - t.boolean :is_taxable, :null => false, :default => true - - t.timestamps - end - end -end diff --git a/db/seeds.rb b/db/seeds.rb index ea396e1b..66429e48 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -30,9 +30,9 @@ sales_status = Lookup.create([{lookup_type:'sales_status', name: 'New', value: ' order_status = Lookup.create([{lookup_type:'order_status', name: 'New', value: 'new'}, {lookup_type:'order_status', name: 'Completed', value: 'completed'}]) -order_item_status = Lookup.create([{lookup_type:'order_status', name: 'New', value: 'new'}, - {lookup_type:'order_status', name: 'Processing', value: 'processing'}, - {lookup_type:'order_status', name: 'Served', value: 'served'}]) +order_item_status = Lookup.create([{lookup_type:'order_item_status', name: 'New', value: 'new'}, + {lookup_type:'order_item_status', name: 'Processing', value: 'processing'}, + {lookup_type:'order_item_status', name: 'Served', value: 'served'}, {lookup_type:'order_item_status', name: 'BIlled', value: 'billed'}]) #order_source [tablet, order_station, emenu, api] order_source = Lookup.create([{lookup_type:'order_source', name: 'API', value: 'api'}, diff --git a/spec/models/sale_discount_item_spec.rb b/spec/models/sale_discount_item_spec.rb deleted file mode 100644 index 57919447..00000000 --- a/spec/models/sale_discount_item_spec.rb +++ /dev/null @@ -1,5 +0,0 @@ -require 'rails_helper' - -RSpec.describe SaleDiscountItem, type: :model do - pending "add some examples to (or delete) #{__FILE__}" -end