cash_outs_controller.rb 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  1. class CashOutsController < ApplicationController
  2. ##--- Abilities
  3. load_and_authorize_resource
  4. ##--- Breadcrum_rails
  5. add_breadcrumb I18n.t("breadcrumbs." + controller_name), :cash_outs_path
  6. add_breadcrumb "Nueva Corte de caja ", :new_cash_out_path, only: :new
  7. add_breadcrumb "Detalle del corte de caja ", :cash_out_path, only: :show
  8. add_breadcrumb "Cajas abiertas ", :opened_cash_registers_path, only: :opened_cash_registers
  9. before_action :set_cash_out, only: [:show]
  10. before_action :set_data, only: [:new, :create]
  11. before_action :get_filters, only: [:index, :show, :new]
  12. # GET /cash_outs.json
  13. def index
  14. case current_user.usertype
  15. when "A", "SS"
  16. @cash_outs = CashOut.all.includes(:open_cash_register, :user, :received_by).order('cash_outs.created_at desc')
  17. when "G"
  18. @cash_outs = Pointsale.find(current_user.pointsale_id).cash_outs.includes(:open_cash_register, :user, :received_by).order('cash_outs.created_at desc')
  19. when "C"
  20. @cash_outs = Pointsale.find(current_user.pointsale_id).cash_outs.includes(:open_cash_register, :user, :received_by).where("cash_outs.user_id = ?", current_user.id).order('cash_outs.created_at desc')
  21. end
  22. end
  23. # GET /cash_outs/1
  24. # GET /cash_outs/1.json
  25. def show
  26. @incomings = CashRegistersMove.includes(:open_cash_register, :credit_payment, :payment_method).where(open_cash_register_id: @cash_out.open_cash_register.id, move_type: 1, status: 1).order('created_at')
  27. @outgoings = CashRegistersMove.includes(:payment_method).where(open_cash_register_id: @cash_out.open_cash_register.id, move_type: 0, status: 1).order('created_at')
  28. @sales_total = @incomings.sum(:quantity)
  29. @expenses_total = @outgoings.sum(:quantity)
  30. end
  31. # GET /cash_outs/new
  32. def new
  33. @cash_out = CashOut.new
  34. @cash_out.cash_out_details.new
  35. @cash_out.received_cash = nil
  36. @cash_out.physical_cash = nil
  37. @cash_out.cash_fund = nil
  38. end
  39. # GET /cash_outs/1/edit
  40. def edit; end
  41. # POST /cash_outs
  42. # POST /cash_outs.json
  43. def create
  44. final_cash = 0
  45. @cash_out = CashOut.new(cash_out_params)
  46. open_cash_register = OpenCashRegister.find(params[:open_cash_register_id])
  47. @cash_out.amount_in = 0
  48. @cash_out.amount_out = 0
  49. @cash_out.open_cash_register_id = open_cash_register.id
  50. @cash_out.user_id = current_user.id
  51. @cash_out.cash_out_details.each do |detail|
  52. @cash_out.amount_in = @cash_out.amount_in + detail.incoming
  53. @cash_out.amount_out = @cash_out.amount_out + detail.outgoing
  54. if detail.payment_method_id == @cash_payment_method_id
  55. final_cash = detail.adjustment
  56. end
  57. end
  58. open_cash_register.expenses.update_all(status: 2) # registered
  59. respond_to do |format|
  60. message = "Corte de caja de #{@cash_out.open_cash_register.cash_register.name} realizado correctamente"
  61. @cash_out.audit_comment = message
  62. if @cash_out.save
  63. # eliminar el id del open cash de la sesion
  64. session.delete(:open_cash_register_id)
  65. open_cash_register.update_attributes(status: 'closed', final_cash: final_cash)
  66. format.js { flash[:success] = message }
  67. else
  68. format.js
  69. format.json { render json: @cash_out.errors, status: :unprocessable_entity }
  70. end
  71. end
  72. end
  73. def get_open_cash_registers
  74. respond_to do |format|
  75. format.js
  76. end
  77. end
  78. def opened_cash_registers
  79. @opened_cash_registers = OpenCashRegister.abiertas
  80. end
  81. def select_open_cash_to_close
  82. @open_cash_register_id = params[:open_cash_register_id]
  83. respond_to do |format|
  84. format.js
  85. end
  86. end
  87. def find_cash_outs_by_date
  88. if params[:begin_date] != "null" && params[:end_date] != "null"
  89. start_date = params[:begin_date].in_time_zone(Time.zone).beginning_of_day + 1.days
  90. end_date = params[:end_date].in_time_zone(Time.zone).end_of_day + 1.days
  91. else
  92. start_date = DateTime.now
  93. end_date = DateTime.now
  94. end
  95. @cash_outs = params[:pointsale_id] != "null" ? Pointsale.find(params[:pointsale_id]).cash_outs.includes(:open_cash_register, :user, :received_by).where(created_at: start_date..end_date).order(" id DESC ") : CashOut.all.includes(:open_cash_register, :user, :received_by).where(created_at: start_date..end_date).order('cash_outs.created_at desc')
  96. respond_to do |format|
  97. format.js
  98. end
  99. end
  100. def print_receipt
  101. # ticket para la venta
  102. respond_to do |format|
  103. @cash_out = CashOut.find(params[:cash_out_id])
  104. products_ids = @cash_out.open_cash_register.sales_details.joins(:sale).where("sales.status != 1").pluck(:product_id).join(",")
  105. @pointsale = OpenCashRegister.get_pointsale(@cash_out.open_cash_register_id, "open_cash_register")
  106. @initial_cash = @cash_out.open_cash_register.initial_cash
  107. @details = @cash_out.open_cash_register.products.present? ? ActiveRecord::Base.connection.exec_query("SELECT SUM(sales_details.quantity) as quantity, SUM(sales_details.total) as total, sales_details.product_id, sub.name as product_name, sub.category, sub.parent FROM sales_details INNER JOIN sales ON sales_details.sale_id = sales.id INNER JOIN (SELECT products. ID as product_id, products.category_id, categories.category, products. NAME, categories.parent_id, parents.category as parent FROM products INNER JOIN categories_products ON categories_products.product_id = products. ID INNER JOIN categories ON categories. ID = categories_products.category_id LEFT JOIN categories as parents ON parents.ID = categories.parent_id WHERE (products. ID IN (#{products_ids}))) sub ON (sales_details.product_id = sub.product_id) WHERE sales.open_cash_register_id = #{@cash_out.open_cash_register_id} and sales.status != 1 GROUP BY sales_details.product_id, sub.category, sub.name, sub.parent ORDER BY sub.category, sub.parent") : Array.new
  108. all_sales = Sale.where("open_cash_register_id = (?) and status != 1", @cash_out.open_cash_register_id)
  109. @cash_sales = CashRegistersMove.where("open_cash_register_id = (?) and move_type = '1' and status = 1 and sale_id IN (?)", @cash_out.open_cash_register_id, all_sales.where(saletype: 1).pluck(:id)).sum(:quantity)
  110. @credit_sales = CashRegistersMove.where("open_cash_register_id = (?) and move_type = '1' and status = 1 and concept = 3", @cash_out.open_cash_register_id).sum(:quantity)
  111. @reserved_sales = CashRegistersMove.where("open_cash_register_id = (?) and move_type = '1' and status = 1 and sale_id IN (?)", @cash_out.open_cash_register_id, all_sales.where(saletype: 2).pluck(:id)).sum(:quantity)
  112. @details.each do |detail|
  113. detail["price_sale"] = Product.find(detail['product_id']).get_price_sale(@pointsale.id)
  114. detail["total_without_discount"] = detail["price_sale"] * detail['quantity'].to_f
  115. detail["discount"] = detail["total_without_discount"] - detail["total"].to_f
  116. end
  117. @expenses = Expense.where(open_cash_register_id: @cash_out.open_cash_register_id).activos
  118. format.pdf do
  119. render pdf: "ticket_corte_#{@cash_out.id}", template: "cash_outs/receipt.pdf.erb", layout: 'receipt.html.erb', locals: { cash_out: @cash_out, details: @details, pointsale: @pointsale, expenses: @expenses, initial_cash: @initial_cash, sales_total: @cash_sales, credit_sales: @credit_sales, reserved_sales: @reserved_sales }, show_as_html: params.key?('debug'), page_width: '80mm', page_height: '300mm'
  120. end
  121. end
  122. end
  123. def general_public_invoice
  124. @cash_out = CashOut.find(params[:cash_out_id])
  125. crms = CashRegistersMove.where(open_cash_register_id: @cash_out.open_cash_register_id, move_type: 1, status: 1).map(&:sale_id).uniq
  126. @sales = Sale.where(id: crms, saletype: 1, require_invoice: false).order(id: :asc)
  127. @sales_amount = @sales.sum(:amount)
  128. @sales_total = @sales.sum(:total)
  129. @sales_tax = @sales.sum(:tax)
  130. respond_to do |format|
  131. format.js
  132. end
  133. end
  134. def generate_gpi
  135. cash_out = CashOut.find(params[:cash_out_id])
  136. subtotal = params[:amount_for_invoice].to_f
  137. total_tax = params[:tax_for_invoice].to_f
  138. total = subtotal + total_tax
  139. sales = CashRegistersMove.where(open_cash_register_id: cash_out.open_cash_register_id, move_type: 1, status: 1).map(&:sale_id).uniq
  140. result = invoice_cash_out(cash_out, subtotal, total_tax, total.round(2), sales)
  141. respond_to do |format|
  142. flash[:notice] = result["invoice"]
  143. format.js
  144. end
  145. end
  146. def invoice_cash_out(cash_out, subtotal, total_tax, total, sales)
  147. invoice = ''
  148. response = Array.new
  149. concepts = []
  150. sales = Sale.where(id: sales, saletype: 1, require_invoice: false).order(id: :asc)
  151. sales.each do |sale|
  152. concepts << {
  153. "Quantity" => format('%0.2f', 1).to_s,
  154. "UnitKey" => "ACT",
  155. "IdNumber" => sale.sale_code,
  156. "ProductKey" => "01010101",
  157. "Description" => "Venta Publico General - #{sale.sale_code}",
  158. "Price" => format('%0.2f', sale.amount).to_s,
  159. "Amount" => format('%0.2f', sale.amount).to_s,
  160. "Tax" => format('%0.2f', sale.tax).to_s
  161. }
  162. end
  163. save_path = false
  164. save_path = create_invoice(cash_out, subtotal, total_tax, total, concepts)
  165. xml = true
  166. stamp = true
  167. if save_path.blank? || save_path == false
  168. invoice = "No se pudo generar el XML para la factura electrónica"
  169. xml = false
  170. end
  171. if check_stamper_status.to_i != 200
  172. stamp = false
  173. end
  174. if xml == true && stamp == true
  175. response = stamper(save_path, cash_out, concepts)
  176. end
  177. if xml == true && stamp == false
  178. response = {
  179. "code" => 1,
  180. "message" => "Servicio de timbrado no esta disponible"
  181. }
  182. end
  183. if xml == false && stamp == true
  184. response = {
  185. "code" => 2,
  186. "message" => "XML no se pudo generar"
  187. }
  188. end
  189. if xml == false && stamp == false
  190. response = {
  191. "code" => 3,
  192. "message" => "XML no se pudo generar y servicio de timbrado no disponible"
  193. }
  194. end
  195. pdf_path = nil
  196. if response["code"].to_i.zero?
  197. pdf_path = response["message"]
  198. invoice = " Factura electrónica generada."
  199. else
  200. invoice = "#{response['code']} : #{response['message']}"
  201. end
  202. result = { "invoice" => invoice, "pdf_path" => pdf_path }
  203. result
  204. end
  205. def create_invoice(cash_out, subtotal, total_tax, total, concepts)
  206. pointsale = current_user.pointsale
  207. save_path = Rails.public_path.join('invoice/xml_origin', "cfdi_gpi_#{cash_out.id}.xml")
  208. builder = Nokogiri::XML::Builder.new do |xml|
  209. xml.Comprobante(get_namespaces("invoice").merge(get_gpi_attributes(cash_out, subtotal, total, pointsale.postal_code))) do
  210. cfdi = xml.doc.root.add_namespace_definition('cfdi', 'http://www.sat.gob.mx/cfd/3')
  211. xml.doc.root.namespace = cfdi
  212. xml.Emisor(Rfc: pointsale.federal_taxpayer_registration.to_s, Nombre: pointsale.business_name.to_s, RegimenFiscal: pointsale.tax_regime.to_s)
  213. xml.Receptor(Rfc: "XAXX010101000", UsoCFDI: "P01")
  214. xml.Conceptos do
  215. concepts.each do |concept|
  216. xml.Concepto(ClaveProdServ: concept['ProductKey'].to_s, ClaveUnidad: concept['UnitKey'].to_s, Cantidad: concept['Quantity'].to_s, NoIdentificacion: concept['IdNumber'].to_s, Descripcion: concept['Description'].to_s, ValorUnitario: concept['Price'].to_s, Importe: concept['Amount'].to_s) do
  217. if concept['Tax'].to_f > 0.00
  218. xml.Impuestos { xml.Traslados { xml.Traslado(TipoFactor: "Tasa", TasaOCuota: "#{@pos_config.tax_percent / 100}0000", Impuesto: "002", Base: concept['Amount'].to_s, Importe: format('%0.2f', concept['Tax']).to_s) } }
  219. end
  220. end
  221. end
  222. end
  223. if total_tax > 0.00
  224. xml.Impuestos(TotalImpuestosTrasladados: format('%0.2f', total_tax).to_s) { xml.Traslados { xml.Traslado(TipoFactor: "Tasa", TasaOCuota: "#{@pos_config.tax_percent / 100}0000", Impuesto: Rails.application.config.issuing['Impuesto'].to_s, Importe: format('%0.2f', total_tax).to_s) } }
  225. end
  226. end
  227. end
  228. File.open(save_path, 'w') do |file|
  229. file << builder.to_xml
  230. end
  231. save_path
  232. rescue
  233. return false
  234. end
  235. def get_gpi_attributes(cash_out, subtotal, total, postal_code)
  236. attributes = {
  237. "Version" => Rails.application.config.issuing['Version'].to_s,
  238. "Serie" => Rails.application.config.issuing['SerieContado'].to_s,
  239. # "Folio" => "",
  240. "Fecha" => cash_out.created_at.strftime("%FT%T").to_s,
  241. "FormaPago" => "01",
  242. "SubTotal" => format('%0.2f', subtotal).to_s,
  243. "Moneda" => "MXN",
  244. "Total" => format('%0.2f', total).to_s,
  245. "TipoDeComprobante" => Rails.application.config.issuing['CveIngreso'].to_s,
  246. "MetodoPago" => Rails.application.config.issuing['MetodoPago'].to_s,
  247. "LugarExpedicion" => postal_code.to_s
  248. }
  249. end
  250. def stamper(save_path, cash_out, concepts)
  251. response = Array.new
  252. @stamper = Timbradocfdi::Generator.new(Rails.application.config.issuing['Password'].to_s)
  253. t = Time.now
  254. check_directorys(t, "ingreso")
  255. result = @stamper.timbraCFDI(save_path, cash_out.id).to_json
  256. result_json = JSON.parse(result)
  257. if result_json['code'].to_i.zero?
  258. cash_out_id = cash_out.id
  259. @doc = Nokogiri::XML(result_json['xml'])
  260. uuid = @doc.xpath("//@UUID")
  261. create_xml_stamped(t, "#{uuid}_gpi_#{cash_out_id}.xml", result_json['xml'], "ingreso")
  262. create_invoice_qr(t, "qr_#{uuid}_gpi_#{cash_out_id}.png", result_json['qr'], "ingreso")
  263. remove_xml_origin(save_path)
  264. cash_out.update(invoice_num: uuid)
  265. cfdi_type = @doc.xpath("//cfdi:Comprobante//@TipoDeComprobante")
  266. invoice_reason = "Venta Publico General"
  267. rfc_receptor = @doc.xpath("//cfdi:Receptor//@Rfc")
  268. generating_date = @doc.xpath("//@Fecha").to_s
  269. stamping_date = @doc.xpath("//@FechaTimbrado").to_s
  270. storing_url = "invoice/cfdi/#{t.strftime('%Y')}/#{t.strftime('%m')}/"
  271. InvoiceDetail.create(uuid: uuid, cfdi_type: cfdi_type, invoice_reason: invoice_reason, rfc_receptor: rfc_receptor, generating_date: generating_date, stamping_date: stamping_date, storing_url: storing_url)
  272. qr_path = "invoice/cfdi/#{t.strftime('%Y')}/#{t.strftime('%m')}/qr/ingreso/qr_#{uuid}_gpi_#{cash_out_id}.png"
  273. create_pdf_invoice(t, uuid, cash_out_id, concepts, @doc, result_json['details'], qr_path)
  274. pdf_path = "invoice/cfdi/#{t.strftime('%Y')}/#{t.strftime('%m')}/pdf/ingreso/#{uuid}_gpi_#{cash_out_id}.pdf"
  275. response = {
  276. "code" => result_json['code'].to_i,
  277. "message" => pdf_path
  278. }
  279. else
  280. response = {
  281. "code" => result_json['code'].to_i,
  282. "message" => result_json['message']
  283. }
  284. end
  285. rescue
  286. response = {
  287. "code" => -1,
  288. "message" => "Ha ocurrido un error inesperado durante el timbrado de la factura"
  289. }
  290. end
  291. def print_invoice
  292. uuid = CashOut.find(params[:cash_out_id]).invoice_num
  293. storing_url = InvoiceDetail.find_by(uuid: uuid).storing_url
  294. respond_to do |format|
  295. unless storing_url.blank?
  296. pdf_path = "#{storing_url}pdf/ingreso/#{uuid}_gpi_#{params[:cash_out_id]}.pdf"
  297. format.js { render action: "print_invoice", locals: { pdf_path: pdf_path } }
  298. end
  299. format.json { head :no_content }
  300. end
  301. end
  302. private
  303. def set_data
  304. @cash_out.cash_out_details.destroy_all
  305. @cash_payment_method_id = PaymentMethod.find_by(isCash: 1).id
  306. @opened_cash_register = (params[:open_cash_register_id].blank? ? OpenCashRegister.find(session[:open_cash_register_id]) : OpenCashRegister.find(params[:open_cash_register_id]))
  307. if @opened_cash_register.present?
  308. @initial_cash = @opened_cash_register.initial_cash
  309. @incomings = CashRegistersMove.incomings(@opened_cash_register.id).order('cash_registers_moves.created_at')
  310. @outgoings = CashRegistersMove.outgoings(@opened_cash_register.id).order('cash_registers_moves.created_at')
  311. # all_sales = Sale.where("open_cash_register_id = (?) and status != 1", @opened_cash_register.id)
  312. sales_ids = CashRegistersMove.where("open_cash_register_id = (?) and move_type = '1' and status = 1", @opened_cash_register.id).pluck("DISTINCT sale_id")
  313. all_sales = Sale.activas.where("id IN (?)", sales_ids)
  314. @cash_sales = CashRegistersMove.where("open_cash_register_id = (?) and move_type = '1' and status = 1 and sale_id IN (?)", @opened_cash_register.id, all_sales.where(saletype: 1).pluck(:id)).sum(:quantity)
  315. @credit_sales = CashRegistersMove.where("open_cash_register_id = (?) and move_type = '1' and status = 1 and concept = 3", @opened_cash_register.id).sum(:quantity)
  316. @reserved_sales = CashRegistersMove.where("open_cash_register_id = (?) and move_type = '1' and status = 1 and sale_id IN (?)", @opened_cash_register.id, all_sales.where(saletype: 2).pluck(:id)).sum(:quantity)
  317. @expenses_total = @outgoings.sum(:quantity)
  318. end
  319. @payments = ActiveRecord::Base.connection.exec_query("
  320. SELECT DISTINCT(crm.payment_method_id),
  321. pm.method,
  322. i.total as incoming,
  323. o.total as outgoing,
  324. SUM(crm.quantity) AS total
  325. from cash_registers_moves as crm
  326. LEFT JOIN (
  327. SELECT DISTINCT(payment_method_id),
  328. SUM(quantity) as total
  329. FROM cash_registers_moves
  330. WHERE move_type = '1' and status = 1 and open_cash_register_id = #{@opened_cash_register.id}
  331. group by payment_method_id
  332. ) as i ON (i.payment_method_id = crm.payment_method_id)
  333. LEFT JOIN (
  334. SELECT DISTINCT(payment_method_id),
  335. SUM(quantity) as total
  336. FROM cash_registers_moves
  337. WHERE move_type = '0' and status = 1 and open_cash_register_id = #{@opened_cash_register.id}
  338. group by payment_method_id
  339. ) as o ON (o.payment_method_id=crm.payment_method_id),
  340. payment_methods as pm
  341. WHERE pm.id = crm.payment_method_id and crm.open_cash_register_id = #{@opened_cash_register.id}
  342. group by crm.payment_method_id, pm.method, i.total, o.total")
  343. @payments.each do |payment|
  344. payment["incoming"] = "0" if payment["incoming"].nil?
  345. payment["outgoing"] = "0" if payment["outgoing"].nil?
  346. payment["total"] = payment["incoming"].to_f - payment["outgoing"].to_f
  347. # si es el efectivo sumarle el fondo
  348. if payment["payment_method_id"].to_i == @cash_payment_method_id
  349. payment["total"] += @initial_cash
  350. end
  351. @cash_out.cash_out_details.build
  352. end
  353. end
  354. def create_pdf_invoice(t, uuid, cash_out_id, concepts, doc, details, qr_path)
  355. unless File.exist?(Rails.public_path.join("invoice/cfdi/#{t.strftime('%Y')}/#{t.strftime('%m')}/pdf/ingreso", "#{uuid}_gpi_#{cash_out_id}.pdf"))
  356. pdf = render_to_string pdf: "#{uuid}_gpi_#{cash_out_id}", template: "cash_outs/cfdi_gpi.pdf.erb", layout: 'cfdi.html.erb', locals: { doc: doc, uuid: uuid, details: details, qr_path: qr_path, user: current_user, concepts: concepts, pos_config: @pos_config }, show_as_html: params.key?('debug'), page_width: '216mm', page_height: '279mm'
  357. save_path = Rails.public_path.join("invoice/cfdi/#{t.strftime('%Y')}/#{t.strftime('%m')}/pdf/ingreso", "#{uuid}_gpi_#{cash_out_id}.pdf")
  358. File.open(save_path, 'wb') do |file|
  359. file << pdf
  360. end
  361. end
  362. end
  363. # Use callbacks to share common setup or constraints between actions.
  364. def set_cash_out
  365. @cash_out = CashOut.includes(:cash_out_details, cash_out_details: :payment_method).find(params[:id])
  366. end
  367. def get_filters
  368. @current_page = params[:current_page].blank? ? 1 : params[:current_page]
  369. @filter = params[:filter]
  370. end
  371. # Never trust parameters from the scary internet, only allow the white list through.
  372. def cash_out_params
  373. params.require(:cash_out).permit(:received_by_id, :received_cash, :cash_fund, :physical_cash, :observations, cash_out_details_attributes: [:payment_method_id, :observations, :incoming, :outgoing, :total, :adjustment])
  374. end
  375. end