sales_controller.rb 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  1. class SalesController < ApplicationController
  2. ##--- Abilities
  3. load_and_authorize_resource
  4. ##--- Breadcrum_rails
  5. add_breadcrumb I18n.t("breadcrumbs." + controller_name), :sales_path
  6. add_breadcrumb "Nueva " + I18n.t("breadcrumbs." + controller_name).singularize, :new_sale_path, only: :new
  7. add_breadcrumb "Detalle de la " + I18n.t("breadcrumbs." + controller_name).singularize, :sale_path, only: :show
  8. add_breadcrumb "Editar " + I18n.t("breadcrumbs." + controller_name).singularize, :edit_sale_path, only: :edit
  9. before_action :set_sale, only: [:show, :edit, :update, :destroy]
  10. before_action :get_filters, only: [:index, :show, :edit, :new, :sales_reserved]
  11. # GET /sales
  12. # GET /sales.json
  13. def index
  14. today = Date.current
  15. thirty_day_ago = today - 30
  16. @sales = current_user.usertype == 'A' ? Sale.includes(:customer, :user, :seller).where(date_sale: thirty_day_ago..today).where('saletype != 2').order(" created_at DESC ") : Pointsale.find(current_user.pointsale_id).sales.includes(:customer, :user, :seller).where(date_sale: thirty_day_ago..today).where('saletype != 2').order(" created_at DESC ")
  17. end
  18. def sales_reserved
  19. beg_of_month = Date.current.beginning_of_month
  20. end_of_month = Date.current.end_of_month
  21. @sales = current_user.usertype == 'A' ? Sale.where(saletype: 2).order(" created_at DESC ") : Pointsale.find(current_user.pointsale_id).sales.where(saletype: 2).order(" created_at DESC")
  22. end
  23. # GET /sales/1
  24. # GET /sales/1.json
  25. def show
  26. @payments = CashRegistersMove.where(sale_id: @sale.id, status: 1)
  27. # saca la devolucion si es que hay.
  28. @products_return = ProductsReturn.find_by(sale_id: @sale.id)
  29. @returned_prods_ids = Array.new
  30. @returned_prods_ids = @products_return.products_return_ins.pluck(:product_id) if @products_return.present?
  31. end
  32. # GET /sales/new
  33. def new
  34. @sale = Sale.new
  35. @sale.sales_details.new
  36. @general_public_id = Customer.find_by(is_public: 1).id
  37. @pre_sales = PreSale.where(user_id: current_user.id)
  38. @sale.saletype = 'cash'
  39. # se desactivan cuando tienen pre sales, para mantener la congruencia de los datos.
  40. @disabled_select = false
  41. @disabled_button = true
  42. @enable_radios = true
  43. @opened_cash_registers = Pointsale.find(current_user.pointsale_id).open_cash_registers.abiertas
  44. @sale.open_cash_register_id = session[:open_cash_register_id] || nil
  45. if @pre_sales.present?
  46. @sale.saletype = @pre_sales[0].sale_type
  47. @sale.customer_id = @pre_sales[0].customer_id
  48. @sale.open_cash_register_id = @pre_sales[0].open_cash_register_id
  49. @disabled_select = true
  50. @disabled_button = false
  51. @enable_radios = false
  52. end
  53. end
  54. # GET /sales/1/edit
  55. def edit; end
  56. # POST /sales
  57. # POST /sales.json
  58. # rubocop:disable Metrics/BlockLength
  59. def create
  60. respond_to do |format|
  61. @sale = Sale.new(sale_params)
  62. @pre_sales = PreSale.where(user_id: current_user.id)
  63. @sale.user_id = current_user.id
  64. @sale.open_cash_register_id = session[:open_cash_register_id]
  65. @sale.status = :notpaid
  66. @sale.expiration_date = Date.today + @pos_config.days_cancel_reserved if @sale.reserved?
  67. @sale.audit_comment = "Venta #{@sale.sale_code} por #{@sale.total} creada."
  68. if @sale.save
  69. # agregar detalles de la venta
  70. @pre_sales.each do |pre_sale|
  71. detail = SalesDetail.new
  72. detail.product_id = pre_sale.product_id
  73. detail.unit_price = pre_sale.unit_price
  74. detail.quantity = pre_sale.quantity
  75. detail.amount = pre_sale.amount
  76. detail.tax = pre_sale.tax
  77. detail.discount = pre_sale.discount
  78. detail.total = pre_sale.total
  79. detail.special_price_id = pre_sale.special_price_id
  80. detail.status = :active
  81. @sale.sales_details << detail
  82. pre_sale.destroy
  83. # actualizar stock del producto
  84. stock_product = AvailableProduct.find_by(product_id: detail.product_id, pointsale_id: @sale.get_pointsale.id)
  85. next if stock_product.blank?
  86. if stock_product.stock.present?
  87. stock_product.stock = stock_product.stock - detail.quantity
  88. stock_product.save
  89. else
  90. errors.add(:base, "No se tiene registrado el stock de alguno de los productos, es necesario configurarlo antes de generar una venta")
  91. format.json { render json: @sale.errors.values, status: :unprocessable_entity }
  92. end
  93. # guardar en bitacora de inventario
  94. move = InventoriesMove.new
  95. move.product_id = detail.product_id
  96. move.sale_id = @sale.id
  97. move.quantity = detail.quantity
  98. move.move_type = "outgoing"
  99. move.reason = "sale"
  100. move.save
  101. end
  102. # dependiendo el tipo de venta: contado/credito determina que hacer
  103. if @sale.cash?
  104. format.js { redirect_to new_cash_registers_move_path(sale: @sale.id) }
  105. elsif @sale.credit?
  106. credit = Credit.new
  107. credit.customer_id = @sale.customer_id
  108. credit.pointsale_id = current_user.pointsale_id
  109. credit.sale_id = @sale.id
  110. credit.total = @sale.total
  111. credit.rest = @sale.total
  112. credit.status = "active"
  113. credit.credit_note = @sale.credit_note if @sale.credit_note.present?
  114. credit.save
  115. flash[:success] = "Venta a credito registrada al cliente: #{@sale.customer.nick_name} por $ #{@sale.total}"
  116. format.js { render 'create_credit_sale' }
  117. elsif @sale.reserved?
  118. format.js { redirect_to new_cash_registers_move_path(sale: @sale.id) }
  119. end
  120. else
  121. format.js
  122. format.json { render json: @sale.errors, status: :unprocessable_entity }
  123. end
  124. end
  125. end
  126. # rubocop:enable Metrics/BlockLength
  127. # PATCH/PUT /sales/1
  128. # PATCH/PUT /sales/1.json
  129. def update
  130. respond_to do |format|
  131. if @sale.update(sale_params)
  132. format.html { redirect_to @sale, notice: 'Venta modificada.' }
  133. format.json { render :show, status: :ok, location: @sale }
  134. else
  135. format.html { render :edit }
  136. format.json { render json: @sale.errors, status: :unprocessable_entity }
  137. end
  138. end
  139. end
  140. # DELETE /sales/1
  141. # DELETE /sales/1.json
  142. # rubocop:disable Metrics/BlockLength
  143. def destroy
  144. respond_to do |format|
  145. @sale.audit_comment = "Venta #{@sale.sale_code} cancelada."
  146. if @sale.update_attributes(status: :cancelled)
  147. # rubocop:disable Metrics/BlockNesting
  148. if @sale.reserved?
  149. return_cash = params[:return_cash]
  150. if return_cash == 'true'
  151. moves = CashRegistersMove.where(sale_id: @sale.id, move_type: 1)
  152. if moves.present?
  153. if moves[0].open_cash_register.closed?
  154. quantity_to_return = moves.sum(:quantity)
  155. new_cash_move = CashRegistersMove.new
  156. new_cash_move.skip_received_validation = true
  157. new_cash_move.open_cash_register_id = session[:open_cash_register_id]
  158. new_cash_move.payment_method_id = PaymentMethod.find_by(isCash: 1).id
  159. new_cash_move.quantity = quantity_to_return
  160. new_cash_move.move_type = :egreso
  161. new_cash_move.sale_id = @sale.id
  162. new_cash_move.concept = :sale
  163. new_cash_move.status = :active
  164. new_cash_move.save
  165. else
  166. moves.destroy_all
  167. end
  168. end
  169. end
  170. elsif @sale.cash?
  171. # checa si hay pagos de esta venta
  172. moves = CashRegistersMove.where(sale_id: @sale.id)
  173. # si la caja sigue abierta, solo elimina los moves, si ya corto, genera un egreso por esa cantidad
  174. if moves.present?
  175. if moves[0].open_cash_register.closed?
  176. new_cash_move = CashRegistersMove.new
  177. new_cash_move.skip_received_validation = true
  178. new_cash_move.open_cash_register_id = session[:open_cash_register_id]
  179. new_cash_move.payment_method_id = PaymentMethod.find_by(isCash: true).id
  180. new_cash_move.quantity = moves.sum(:quantity)
  181. new_cash_move.move_type = :egreso
  182. new_cash_move.sale_id = moves[0].sale_id
  183. new_cash_move.concept = :sale
  184. new_cash_move.ticket = moves[0].sale.sale_code
  185. new_cash_move.status = :active
  186. new_cash_move.save
  187. else
  188. moves.destroy_all
  189. end
  190. end
  191. elsif @sale.credit?
  192. credit = Credit.find_by(sale_id: @sale.id, customer_id: @sale.customer_id)
  193. credit.update_attributes(status: :cancelled)
  194. end
  195. # rubocop:enable Metrics/BlockNesting
  196. @sale.sales_details.each do |detail|
  197. detail.update_attributes(status: :inactive)
  198. stock_product = AvailableProduct.find_by(product_id: detail.product_id, pointsale_id: @sale.get_pointsale.id)
  199. next if stock_product.blank?
  200. # sumarle al stock del producto
  201. stock = stock_product.stock + detail.quantity
  202. stock_product.update_attributes(stock: stock)
  203. # guardar en bitacora de inventario
  204. move = InventoriesMove.new
  205. move.product_id = detail.product_id
  206. move.sale_id = @sale.id
  207. move.quantity = detail.quantity
  208. move.move_type = "incoming"
  209. move.reason = @sale.reserved? ? "sale_reserved_cancelled" : "sale_cancel"
  210. move.save
  211. end
  212. format.html { redirect_to (@sale.reserved? ? sales_reserved_path : sales_url), warning: (@sale.reserved? ? "Apartado con folio #{@sale.sale_code} cancelado." : "Venta con folio #{@sale.sale_code} cancelado.") }
  213. format.json { head :no_content }
  214. end
  215. end
  216. end
  217. # rubocop:enable Metrics/BlockLength
  218. def find_sales_by_date
  219. respond_to do |format|
  220. start_date = DateTime.parse(params[:begin_date])
  221. end_date = DateTime.parse(params[:end_date])
  222. @sales = current_user.usertype == 'A' ? Sale.where(date_sale: start_date..end_date).where('saletype != 2').order(" created_at DESC ") : Pointsale.find(current_user.pointsale_id).sales.where(date_sale: start_date..end_date).where('saletype != 2').order(" created_at DESC ")
  223. format.js
  224. end
  225. end
  226. def find_customer_sales_by_date
  227. respond_to do |format|
  228. start_date = DateTime.parse(params[:begin_date])
  229. end_date = DateTime.parse(params[:end_date])
  230. cliente = params[:cliente]
  231. @sales = Sale.where(date_sale: start_date..end_date, customer_id: cliente).where('saletype != 2').order(" id DESC ")
  232. format.js { render action: "find_sales_by_date" }
  233. end
  234. end
  235. def find_reserved_sales_by_date
  236. respond_to do |format|
  237. start_date = DateTime.parse(params[:begin_date])
  238. end_date = DateTime.parse(params[:end_date])
  239. @sales = current_user.usertype == 'A' ? Sale.where(date_sale: start_date..end_date, saletype: 2).order(" created_at DESC ") : Pointsale.find(current_user.pointsale_id).sales.where(date_sale: start_date..end_date).where('saletype = 2').order(" created_at DESC ")
  240. format.js
  241. end
  242. end
  243. def return_expired
  244. @sale = Sale.find(params[:sale_id])
  245. respond_to do |format|
  246. if @sale.update_attributes(status: :cancelled_by_expiration)
  247. @sale.sales_details.each do |detail|
  248. detail.update_attributes(status: :inactive)
  249. stock_product = AvailableProduct.find_by(product_id: detail.product_id, pointsale_id: @sale.get_pointsale.id)
  250. next if stock_product.blank?
  251. # sumarle al stock del producto
  252. stock = stock_product.stock + detail.quantity
  253. stock_product.update_attributes(stock: stock)
  254. # guardar en bitacora de inventario
  255. move = InventoriesMove.new
  256. move.product_id = detail.product_id
  257. move.sale_id = @sale.id
  258. move.quantity = detail.quantity
  259. move.move_type = "incoming"
  260. move.reason = "sale_expired"
  261. move.save
  262. end
  263. format.html { redirect_to sales_reserved_url, warning: "Productos reingresados al inventario del apartado #{@sale.sale_code}." }
  264. format.json { head :no_content }
  265. end
  266. end
  267. end
  268. def liquidate_reserve
  269. respond_to do |format|
  270. @sale = Sale.find(params[:sale_id])
  271. @payments = CashRegistersMove.where(sale_id: @sale.id, status: 1)
  272. format.js { redirect_to new_cash_registers_move_path(sale: @sale.id) }
  273. end
  274. end
  275. def cancel_reserved_sale
  276. @sale = Sale.find(params[:sale_id])
  277. end
  278. def print_receipt
  279. respond_to do |format|
  280. @sale = Sale.find(params[:sale_id])
  281. unless File.exist?(Rails.public_path.join('pdfs', "ticket_venta_#{@sale.id}.pdf"))
  282. pdf = render_to_string pdf: "ticket_venta_#{@sale.id}", template: "sales/receipt.pdf.erb", layout: 'receipt.html.erb', locals: { sale: @sale }, show_as_html: params.key?('debug'), page_width: '80mm', page_height: '300mm'
  283. save_path = Rails.public_path.join('pdfs', "ticket_venta_#{@sale.id}.pdf")
  284. File.open(save_path, 'wb') do |file|
  285. file << pdf
  286. end
  287. end
  288. format.js
  289. end
  290. end
  291. def print_reserve_receipt
  292. # ticket para apartado
  293. respond_to do |format|
  294. sale = Sale.find(params[:sale_id])
  295. # debt = sale.cash_registers_moves.first.quantity
  296. debt = sale.cash_registers_moves.sum(:quantity)
  297. format.pdf do
  298. render pdf: "ticket_apartado_#{sale.id}", template: "sales/receipt_reserve.pdf.erb", layout: 'receipt.html.erb', locals: { sale: sale, debt: debt }, show_as_html: params.key?('debug'), page_width: '80mm', page_height: '300mm'
  299. end
  300. end
  301. end
  302. def print_credit_receipt
  303. # ticket para credito
  304. respond_to do |format|
  305. sale = Sale.find(params[:sale_id])
  306. debt = 0
  307. sale.customer.credits.activos.each do |credit|
  308. debt += credit.rest
  309. end
  310. format.pdf do
  311. render pdf: "ticket_credito_#{sale.id}", template: "sales/receipt_credit.pdf.erb", layout: 'receipt.html.erb', locals: { sale: sale, debt: debt }, show_as_html: params.key?('debug'), page_width: '80mm', page_height: '300mm'
  312. end
  313. end
  314. end
  315. def print_partial_payment_receipt
  316. # ticket para abonos a apartado
  317. respond_to do |format|
  318. sale = Sale.find(params[:sale_id])
  319. new_moves = CashRegistersMove.where(id: params[:new_moves_array])
  320. quantity = new_moves.sum(:quantity)
  321. format.pdf do
  322. render pdf: "ticket_abono_apartado_#{sale.id}", template: "sales/receipt_partial_payment.pdf.erb", layout: 'receipt.html.erb', locals: { sale: sale, new_payments_quantity: quantity, moves: new_moves }, show_as_html: params.key?('debug'), page_width: '80mm', page_height: '300mm'
  323. end
  324. end
  325. end
  326. def print_credit_payment_receipt
  327. # ticket para abonos a credito
  328. respond_to do |format|
  329. sale = Sale.find(params[:sale_id])
  330. deuda = params[:debts]
  331. cash_move = CashRegistersMove.where(sale_id: sale.id).last
  332. quantity = cash_move.quantity
  333. debt = Credit.where(customer_id: sale.customer_id).sum(:rest)
  334. format.pdf do
  335. render pdf: "ticket_abono_credito_#{sale.id}", template: "sales/receipt_credit_payment.pdf.erb", layout: 'receipt.html.erb', locals: { sale: sale, new_payment: quantity, debt: debt, deuda: deuda, move: cash_move }, show_as_html: params.key?('debug'), page_width: '80mm', page_height: '300mm'
  336. end
  337. end
  338. end
  339. def add_haggle
  340. @pre_sale = PreSale.find(params[:pre_sale])
  341. @suggested_haggle = (@pos_config.haggle_in_sale_percent.to_f / 100) * @pre_sale.unit_price
  342. end
  343. def create_haggle
  344. respond_to do |format|
  345. @pre_sale = PreSale.find(params[:pre_sale])
  346. @haggle_percent = params[:haggle_percent].present? ? params[:haggle_percent].to_f : nil
  347. @haggle_quantity = params[:haggle_quantity].present? ? params[:haggle_quantity].to_f : nil
  348. if @haggle_percent.present?
  349. @pre_sale.haggle = (@haggle_percent / 100) * @pre_sale.total
  350. elsif @haggle_quantity.present?
  351. @pre_sale.haggle = @haggle_quantity
  352. end
  353. @pre_sale.get_totals
  354. if @pre_sale.save
  355. format.js
  356. end
  357. end
  358. end
  359. def find_sales_by_dates_or_code
  360. respond_to do |format|
  361. if params[:sale_code].present?
  362. @sales = Pointsale.find(current_user.pointsale_id).sales.activas.where(sale_code: params[:sale_code], saletype: 1).order("id DESC")
  363. else
  364. start_date = DateTime.parse(params[:start_date])
  365. end_date = DateTime.parse(params[:end_date])
  366. product_id = params[:product_id]
  367. @sales = product_id.present? ? Pointsale.find(current_user.pointsale_id).sales.activas.where(date_sale: start_date..end_date, saletype: 1).joins(:sales_details).where('sales_details.product_id = (?)', product_id).order("id DESC") : Pointsale.find(current_user.pointsale_id).sales.activas.where(date_sale: start_date..end_date, saletype: 1).joins(:sales_details).order("id DESC")
  368. end
  369. format.js
  370. end
  371. end
  372. def sales_per_month_report
  373. @pointsales = Pointsale.vigentes
  374. end
  375. def sales_per_month
  376. respond_to do |format|
  377. start_date = DateTime.parse(params[:start_date])
  378. end_date = DateTime.parse(params[:end_date])
  379. @cash_sales_total = 0
  380. @reserved_sales_total = 0
  381. @credit_sales_total = 0
  382. if params[:pointsale_id].present?
  383. @pointsale = Pointsale.find(params[:pointsale_id])
  384. @sales = @pointsale.sales.activas.where(date_sale: start_date..end_date).includes(:sales_details).order("id DESC")
  385. else
  386. @sales = Sale.activas.where(date_sale: start_date..end_date).includes(:sales_details).order("id DESC")
  387. end
  388. @sales_total = CashRegistersMove.activos.where("sale_id IN (?)", @sales.pluck(:id)).sum(:quantity)
  389. @sales_quantity = @sales.size
  390. @prods_total = SalesDetail.where("sale_id IN (?)", @sales.pluck(:id)).sum(:quantity).round
  391. @cash_sales_total = CashRegistersMove.activos.where("sale_id IN (?)", @sales.where(saletype: 1).pluck(:id)).sum(:quantity)
  392. @reserved_sales_total = CashRegistersMove.activos.where("sale_id IN (?)", @sales.where(saletype: 2).pluck(:id)).sum(:quantity)
  393. @credit_sales_total = CashRegistersMove.activos.where("sale_id IN (?)", @sales.where(saletype: 0).pluck(:id)).sum(:quantity)
  394. format.js
  395. end
  396. end
  397. private
  398. # Use callbacks to share common setup or constraints between actions.
  399. def set_sale
  400. @sale = Sale.find(params[:id])
  401. end
  402. def get_filters
  403. @current_page = params[:current_page].blank? ? 1 : params[:filter]
  404. @filter = params[:filter]
  405. end
  406. # Never trust parameters from the scary internet, only allow the white list through.
  407. def sale_params
  408. params.require(:sale).permit(:customer_id, :saletype, :amount, :tax, :discount, :total, :date_sale, :user_id, :seller_id, :sale_code, :credit_note)
  409. end
  410. end