#<ActiveRecord::QueryMethods::WhereChain 的未定义方法“order”

Undefined method `order' for #<ActiveRecord::QueryMethods::WhereChain

我开发一个名为 Ctrlpanlel 的程序已经有一段时间了,我在 Heroku 上安装了它,它正在运行……大部分情况下只有少数例外。对于背景,它曾经在 Ruby 2.2.2 和 Rails 4.2.1 上使用 运行,我已将其升级到 Ruby 2.7.2 和 Rails 6.1。 3.2

此应用程序的功能之一是每周费用报告,我对某些查询逻辑有疑问,因为它最初是使用名为 squeel 的 gem 编写的。我是 Rails 和 Activerecord 的新手,尽管我正在学习我正在尝试在几个地方取消压缩代码。我相信我已经修复了前几个查询(我认为至少)并且它超过了我 运行 宁到错误的前几个部分,我可以 运行 通过 运行ning rails weekly_expense_reports 它毫无问题地进入了开信人,并显示了人员和他们每个人的指控数量,但随后它也对每个人说了以下错误:

ERROR processing Smith John because: undefined method `order' for #ActiveRecord::QueryMethods::WhereChain:0x000000001067ee08

为了清楚起见,我将员工姓名更改为 John Smith。 :)

它对每个人都这样做,所以我知道我认为我正确修复的第一个查询我没有修复,或者在控制器中有另一个问题,其中有 .order 行,这就是它指的是。我了解到,有时在 Rails 中,当它指向一个错误的方法时,有时它是提供问题的方法的数据,而不一定是方法本身。在这种情况下,我不确定它是哪一个。

我改的第一行是:

@users = User.where.not(:expense_reports_to_user_id => nil)

我改的第二行是:

charge_count = Charge.between_times( start_date, end_date, field: :post_date ).
                       where(:cardholder_name => user.cardholder_name).count

这是 scheduler.rake

中的任务
task :weekly_expense_reports => :environment do
  
  if Time.zone.now.wday == 2
    log = []
#    Squeel logic I had to de-squeel v0.0040 on 5/10/2021 Scott Milella   
#    @users = User.where{ expense_reports_to_user_id.not_eq nil }

     @users = User.where.not(:expense_reports_to_user_id => nil)    # <- First Line I changed

    message = "Evaluating #{@users.count} users"
    puts message
    log << message

    start_date = Time.zone.today - 7.days
    end_date   = Time.zone.today - 1.days

    if Charge.between_times( start_date, end_date, field: :post_date ).any?
      date_ranges = []

      if start_date.month == end_date.month
        date_ranges << { start_date: start_date, end_date: end_date }
      else
        date_ranges << { start_date: start_date, end_date: start_date.end_of_month }
        date_ranges << { start_date: end_date.beginning_of_month, end_date: end_date }
      end

      message = "Found charges in date ranges: #{date_ranges.join(" to ")}"
      puts message
      log << message

      @users.each do |user|
        puts "Checking #{user.name} for charges..."

#  Below here is the next query I changed...
#        More squeel code yet again, v0.0040 on 5/10/2021 Scott Milella
#       charge_count = Charge.between_times( start_date, end_date, field: :post_date ).
#                       where{ cardholder_name.eq my{ user.cardholder_name } }.count

        charge_count = Charge.between_times( start_date, end_date, field: :post_date ).
                       where(:cardholder_name => user.cardholder_name).count


        if charge_count > 0
          message = "Found #{charge_count} charges for #{user.name}"
          puts message
          log << message

          begin
            ExpenseMailer.weekly_report(user, date_ranges).deliver_now!
            sleep 1

            message = "Successfully emailed charges for #{user.name}"
            puts message
            log << message
          rescue Exception => error
            message = "ERROR processing #{user.name} because: #{error}"
            puts message
            log << message
          end
        end
      end

      # finished processing emails, send report
      log << "Finished Processing Expense Reports"
      ActionMailer::Base.mail(
        from: "tech@des.direct",
        to: "terrym@des.direct",
        subject: "Expense Report Summary",
        body: log.join("\n\n").html_safe
      ).deliver_now
    else
      # no charges found, send notification
      emails = ["terrym@des.direct", "laurah@des.direct"]
      puts "No charges were found, skipping expense report."
      ActionMailer::Base.mail(
        from: "tech@des.direct",
        to: emails,
        subject: "[Expense Reports] No Charges Found, Cannot Process",
        body: "Error, no charges were uploaded for the period of #{start_date} to #{end_date}, please reprocess manually."
      ).deliver_now
    end
    log = nil
  end
end

以下是模型以防万一:

class Charge < ActiveRecord::Base
  attr_accessible :card_last_four, :cardholder_name, :cardholder_last_name, :cardholder_origin,
    :post_date, :merchant_name, :amount, :trans_type, :gl_code, :mcc_code,
    :mcc_description, :gas_region, :transaction_date

  before_save :add_cardholder_last_name

  def add_cardholder_last_name
    if cardholder_name
      self.cardholder_last_name = cardholder_name.split(" ").last
    end
  end

  def self.allowed_user_ids
    administration_user_ids
  end

  def self.administration_user_ids
    [ 8,
      9,
      36,
      50,
      128 # 
    ] # this includes  (not in order)
  end

  def update_gl_code
    self.gl_code = Charge.mcc_to_gl_table[mcc_code.to_i]
    self.save
  end

  def self.mcc_to_gl_table
    {
    0    => 740,
    }
  end

  def self.cardholder_name_select
    Charge.pluck(:cardholder_name).
    uniq.
    delete_if{ |x| x.in?( Charge.cardholder_reject_list ) }.
    reject(&:blank?).
    sort_by{ |k| k.split(" ").last }
  end

  def self.cardholder_reject_list
    [
      "JOHN DOE",
      "JOHN DOE",
      "JOHN DOE",
      "JOHN DOE",
      
    ]
  end

  def self.cardholder_name_to_details
    {
    "ACCOUNTS PAYABLE"          => { branch: "redding",     region: "TRAVEL" },
  
    }
  end

  def self.import_csv_text(csv_text)
    require 'csv'

    csv = CSV.parse( csv_text, headers: true )

    csv.each do |raw|
      row = raw.to_hash

      merchant_name      = row["Merchant Name"].strip

      if merchant_name == "PAYMENT - THANK YOU"
        card_last_four     = 0
        cardholder_name    = "Payment"
      else
        if row["Originating Account Number"].present?
          card_last_four     = row["Originating Account Number"][-4, 4]
          cardholder_name    = row["Cardholder Name"].strip
        else
          cardholder_name    = "ACCOUNTS PAYABLE"
          card_last_four     = 1234
        end
      end

      transaction_date   = Chronic.parse( row["Tran Date"] ).to_date
      post_date          = Chronic.parse( row["Post Date"] ).to_date
      amount             = row["Amount"].remove('$')
      trans_type         = row["Tran Type"].strip
      mcc_code           = row["MCC Code"].to_i
      mcc_description    = row["MCC Description"].strip
      reference_number   = row["Reference Number"].strip

      gl_code            = Charge.mcc_to_gl_table[ mcc_code ]

      cardholder_details = Charge.cardholder_name_to_details[ cardholder_name ]

      if cardholder_details
        cardholder_origin  = cardholder_details[:branch]
        gas_region         = cardholder_details[:region]
      end

      @charge = Charge.where(reference_number: reference_number, amount: amount).first_or_create

      @charge.card_last_four    = card_last_four
      @charge.transaction_date  = transaction_date
      @charge.post_date         = post_date
      @charge.merchant_name     = merchant_name
      @charge.cardholder_name   = cardholder_name
      @charge.trans_type        = trans_type
      @charge.mcc_code          = mcc_code        unless @charge.mcc_code.present?
      @charge.mcc_description   = mcc_description unless @charge.mcc_description.present?
      @charge.gl_code           = gl_code         unless @charge.gl_code.present?
      @charge.cardholder_origin = cardholder_origin unless @charge.cardholder_origin.present?
      @charge.gas_region        = gas_region      unless @charge.gas_region.present?

      @charge.save

    end
  end
end

以下是 rails 面板中员工姓名已更改的完整错误:

From:
tech@email.domain
Subject:
Expense Report Summary
Date:
May 11, 2021 03:41:12 PM Pacific Daylight Time
To:
Tech@email.domain
Evaluating 38 users

Found charges in date ranges: {:start_date=>Tue, 04 May 2021, :end_date=>Mon, 10 May 2021}

Found 10 charges for John Doe

ERROR processing John Doe because: undefined method `order' for #<ActiveRecord::QueryMethods::WhereChain:0x000000000cc282e0>

Found 3 charges for John Doe

ERROR processing John Doe because: undefined method `order' for #<ActiveRecord::QueryMethods::WhereChain:0x0000000009970410>


Found 1 charges for John Doe

ERROR processing John Doe because: undefined method `order' for #<ActiveRecord::QueryMethods::WhereChain:0x00000000106fc830>

Finished Processing Expense Reports

我感谢任何人提供的任何建议、建议和解决方案!

谢谢,

斯科特

这是来自 rails 面板的副本,这是我所知道的手动触发此脚本的唯一方法。通常它会从 Heroku Scheduler 调用。

scottm@RED-IT-LAP-0001 MINGW64 /d/rails/ctrlpanel (master)
$ rake weekly_expense_reports
Evaluating 38 users
Found charges in date ranges: {:start_date=>Wed, 05 May 2021, :end_date=>Tue, 11 May 2021}
Checking John Doe for charges...
Found 8 charges for John Doe
ERROR processing John Doe because: undefined method `order' for #<ActiveRecord::QueryMethods::WhereChain:0x000000000f06fdb0>
Checking John Doe for charges...
Found 2 charges for John Doe
ERROR processing John Doe because: undefined method `order' for #<ActiveRecord::QueryMethods::WhereChain:0x000000000f3bec78>
Checking John Doe for charges...
Checking John Doe for charges...
Found 5 charges for John Doe
ERROR processing John Doe because: undefined method `order' for #<ActiveRecord::QueryMethods::WhereChain:0x000000000dfc0a50>
Checking John Doe for charges...
Found 1 charges for John Doe
ERROR processing John Doe because: undefined method `order' for #<ActiveRecord::QueryMethods::WhereChain:0x000000000f5e1fa0>
Checking John Doe for charges...
Found 1 charges for John Doe
ERROR processing John Doe  because: undefined method `order' for #<ActiveRecord::QueryMethods::WhereChain:0x000000000f653420>
Checking John Doe for charges...
Found 1 charges for John Doe
ERROR processing John Doe because: undefined method `order' for #<ActiveRecord::QueryMethods::WhereChain:0x000000000dbfbe38>
Found 5 charges for John Doe
ERROR processing John Doe because: undefined method `order' for #<ActiveRecord::QueryMethods::WhereChain:0x000000000f70ac38>
Checking John Doe for charges...
Checking John Doe for charges...
Found 5 charges for John Doe
ERROR processing John Doe because: undefined method `order' for #<ActiveRecord::QueryMethods::WhereChain:0x0000000010614328>
Checking John Doe for charges...
Found 1 charges for John Doe
ERROR processing John Doe because: undefined method `order' for #<ActiveRecord::QueryMethods::WhereChain:0x00000000106cc810>
Checking John Doe for charges...
Found 3 charges for John Doe
ERROR processing John Doe because: undefined method `order' for #<ActiveRecord::QueryMethods::WhereChain:0x000000001072c6c0>
Checking John Doe for charges...
Found 1 charges for John Doe
ERROR processing John Doe because: undefined method `order' for #<ActiveRecord::QueryMethods::WhereChain:0x000000001078d2e0>

scottm@RED-IT-LAP-0001 MINGW64 /d/rails/ctrlpanel (master)
$

正如您在上面看到的那样,它为我提供的信息与电子邮件中的信息几乎相同,但不是很有用。抱歉,我提供了太多代码,我以为我只提供了解决该问题的必要代码,而且人们通常会要求我提供更多代码。

它引用的有问题的行在 charges_controller.rb 但我担心我修改的查询(并且可能不正确)可能是它说该方法未定义的原因,因为控制器逻辑对我来说没问题?但话又说回来,我还是个新手,仍在努力理解 Rails 逻辑。 控件的方法如下::

def index
    if current_user.id.in?( Charge.administration_user_ids ) || current_user.cardholder_name.present?
      params[:q] = {} if params[:q].blank?
      params[:q][:post_date_gteq] = Date.today.beginning_of_month - 1.month if params[:q][:post_date_gteq].blank?

      # pre-set the cardholder name if they've been assigned
      # unless they're an administrator
      if current_user.cardholder_name.present? && !current_user.id.in?( Charge.administration_user_ids )
        params[:q]["cardholder_name_eq"] = current_user.cardholder_name
      end

      @q = Charge.ransack(params[:q])

      # done too quickly, seriously needs rework
      if params[:q][:cardholder_name_eq].present?
        if current_user.id.in?( Charge.administration_user_ids )
          @charges = @q.result.order(:gl_code, :post_date).group_by(&:gl_code)
        else
          @charges = @q.result.order(:cardholder_name, :post_date)  
        end
      else
        @charges = @q.result.order(:cardholder_name, :post_date)
      end
    else
      redirect_to root_path, notice: "Nope."
    end
  end

为了清楚起见,我将所有员工姓名重命名为 John Doe。 :)

如果您还有什么需要请告诉我,再次感谢! 斯科特

我删除了日志文件,因为很明显它以不同的方式提供了几乎相同的信息。

这是app/mailers/expense_mailer.rb

中的文件
class ExpenseMailer < ApplicationMailer
  def weekly_report(user, date_ranges)
    require 'prawn'

    date_ranges.each do | range |
      start_date = range[ :start_date ]
      end_date   = range[ :end_date ]

      @charges = Charge.between_times( start_date, end_date, field: :post_date ).
                        where{cardholder_name.eq my { user.cardholder_name } }.
                        order("post_date asc")

      if @charges.any?
        prawn_output  = ExpenseReport.new(user.cardholder_name, start_date, end_date, @charges).render
        prawn_object  = CombinePDF.parse(prawn_output, page_layout: :landscape)
        combined_file = CombinePDF.new

        template_path  = 'app/assets/pdfs/DES_Expense_Report_W4.pdf'
        template_page  = CombinePDF.load( template_path, page_layout: :landscape ).pages[0] ; true

        prawn_object.pages.each do |page|
          template_page  = CombinePDF.load( template_path, page_layout: :landscape ).pages[0] ; true
          template_page[:Annots].each { |a|
            a[:referenced_object][:T] = 'Combine' + SecureRandom.hex(7) + 'PDF'
          }

          combined_file << template_page
        end

        prawn_object.pages.each_with_index do |page, i|
          combined_file.pages[i] << prawn_object.pages[i]
        end

        page_total = combined_file.pages.count
        combined_file.pages.each_with_index do |page, i|
          page.textbox( "Page #{i + 1}/#{page_total}", x: 680, y: -520, max_font_size: 10 )
        end

        attachments["#{user.cardholder_name} [ #{start_date.strftime("%m/%d/%Y")} - #{end_date.strftime("%m/%d/%Y")} ].pdf"] = combined_file.to_pdf
      end
    end

    to_with_name   = %("#{user.expense_report_manager.name}" <#{user.expense_report_manager.email}>)
    # to_with_name   = %("John" <johnd@mail.domain>)
    from_with_name = %("CtrlPanel" <tech@mail.domain>)

    mail to: to_with_name, bcc: 'tech@mail.domain 
from: from_with_name, subject: "Expense Report for #{user.name} [ #{date_ranges.first[:start_date].strftime("%m/%d/%Y")} - #{date_ranges.last[:end_date].strftime("%m/%d/%Y")} ]"
  end
end

谢谢, 斯科特

答案竟然在Expense_Mailer.rb文件中。 它的逻辑来自一个叫做 squeel 的旧 gem。

这一行有错误的逻辑:

where{cardholder_name.eq my { user.cardholder_name } }.order("post_date asc") 

改为:

where(:cardholder_name => user.cardholder_name).order("post_date asc") 

然后它开始正常工作。 再次感谢@Denny Mueller,如果你没有告诉我它正在调用另一个文件,我就不会发现其他逻辑。仍在努力完全理解 rails 是如何联系在一起的。