kmaune kmaune - 3 months ago 6
Ruby Question

Undefined Method for Active Record

When creating a new job, I get an error about an undefined method

Employee
. I will post relevant sections of my code; thanks in advance for the help!

Here is the error message:


undefined method `employee' for #
ActiveRecord::Associations::CollectionProxy []


_form.html.erb
(where error is occuring):

<td colspan="4">Client-Job
# <%= text_field_tag 'client_num', @job.opportunities.employee.office.client_num, :size => "4", :readonly => true, :tabindex => -1 %>
-<%= f.text_field :number %></td>


Jobs Controller:

class JobsController < ApplicationController
before_action :set_job, only: [:show, :edit, :update, :destroy]
skip_load_and_authorize_resource
# GET /jobs
# GET /jobs.json
def index
@jobs = Job.all
end

# GET /jobs/1
# GET /jobs/1.json
def show
end

# GET /jobs/new
def new
@job = Job.new
end

# GET /jobs/1/edit
def edit
end

# POST /jobs
# POST /jobs.json
def create
@job = Job.new(job_params)

respond_to do |format|
if @job.save
format.html { redirect_to @job, notice: 'Job was successfully created.' }
format.json { render :show, status: :created, location: @job }
else
format.html { render :new }
format.json { render json: @job.errors, status: :unprocessable_entity }
end
end
end

# PATCH/PUT /jobs/1
# PATCH/PUT /jobs/1.json
def update
respond_to do |format|
if @job.update(job_params)
format.html { redirect_to @job, notice: 'Job was successfully updated.' }
format.json { render :show, status: :ok, location: @job }
else
format.html { render :edit }
format.json { render json: @job.errors, status: :unprocessable_entity }
end
end
end

# DELETE /jobs/1
# DELETE /jobs/1.json
def destroy
@job.destroy
respond_to do |format|
format.html { redirect_to jobs_url, notice: 'Job was successfully deleted.' }
format.json { head :no_content }
end
end

private

# Use callbacks to share common setup or constraints between actions.
def set_job
@job = Job.find(params[:id])
end

# Never trust parameters from the scary internet, only allow the white list through.
def job_params
params.require(:job).permit(:opportunity_id, :number, :name, :flight_date, :flight_sub, :camera, :roll, :map_type, :plan_only, :lab_only, :est_hrs_model, :due_date, :edge_job_id, :custom_trans, :comp_inhouse, :delivered_date, :done, :control_in, :control_status, :at_date, :control_results, :control_check, :scan_staff, :scan_date, :scan_check, :comp_staff, :comp_date, :comp_check, :comp_sub, :comp_sub_due_date, :comp_sub_rec, :img_staff, :img_date, :img_check, :edit_staff, :edit_date, :edit_check, :notes, :file1, :file2, :file3, :file4, :file5, :add_files)
end
end


Employee Controller:

class EmployeesController < ApplicationController
before_action :set_employee, only: :show
skip_load_and_authorize_resource

# GET /employees
# GET /employees.json
def index
@employees = Employee.all
end

# GET /employees/1
# GET /employees/1.json
def show
end

# GET /employees/new
def new
@employee = Employee.new
end

# GET /employees/1/edit
def edit
end

# POST /employees
# POST /employees.json
def create
@employee = Employee.new(employee_params)

respond_to do |format|
if @employee.save
format.html { redirect_to @employee, notice: 'Contact was successfully created.' }
format.json { render :show, status: :created, location: @employee }
else
format.html { render :new }
format.json { render json: @employee.errors, status: :unprocessable_entity }
end
end
end

# PATCH/PUT /employees/1
# PATCH/PUT /employees/1.json
def update
respond_to do |format|
if @employee.update(employee_params)
format.html { redirect_to @employee, notice: 'Contact was successfully updated.' }
format.json { render :show, status: :ok, location: @employee }
else
format.html { render :edit }
format.json { render json: @employee.errors, status: :unprocessable_entity }
end
end
end

# DELETE /employees/1
# DELETE /employees/1.json
def destroy
@employee.destroy
respond_to do |format|
format.html { redirect_to employees_url, notice: 'Contact was successfully destroyed.' }
format.json { head :no_content }
end
end

private

# Use callbacks to share common setup or constraints between actions.
def set_employee
@employee = Employee.find(params[:id])
@opportunities = @employee.opportunities.all
end

# Never trust parameters from the scary internet, only allow the white list through.
def employee_params
params.require(:employee).permit(:office_id, :f_name, :l_name, :suffix, :position, :email, :phone, :ext, :mobile, :per_email, :per_phone, :archived, :replacement)
end
end


Job Model:

class Job < ActiveRecord::Base
mount_uploader :file1, AttachmentUploader
belongs_to :cost_proposal
has_many :opportunities
end


Employee Model:

class Employee < ActiveRecord::Base
belongs_to :office
has_many :opportunities
has_one :user
delegate :company, to: :office
validates :f_name, :l_name, presence: true

def self.emp_id(emp_id)
find_by(id: emp_id)
end

def self.emp_f_name(emp_id)
find_by(id: emp_id).f_name
end

def name_1
[f_name, l_name].compact.join(' ')
end

def full_name
if suffix?
[name_1, suffix].compact.join(', ')
else
name_1
end
end

def self.emp_full_name(emp_id)
find_by(id: emp_id).full_name
end

def full_phone
if ext?
[phone, ext].compact.join(' ext: ')
else
phone
end
end
end


Schema.rb:(relevant tables)

create_table 'employees', force: true do |t|
t.integer 'office_id'
t.string 'f_name'
t.string 'l_name'
t.string 'suffix'
t.string 'email'
t.string 'phone'
t.string 'ext'
t.string 'mobile'
t.string 'per_email'
t.string 'per_phone'
t.integer 'archived'
t.integer 'replacement'
t.datetime 'created_at'
t.datetime 'updated_at'
t.string 'position'
end

create_table 'jobs', force: true do |t|
t.integer 'cost_proposal_id'
t.string 'number'
t.string 'name'
t.date 'flight_date'
t.string 'flight_sub'
t.string 'camera'
t.string 'roll'
t.string 'map_type'
t.integer 'plan_only'
t.integer 'lab_only'
t.integer 'est_hrs_model'
t.date 'due_date'
t.integer 'edge_job_id'
t.integer 'custom_trans'
t.integer 'comp_inhouse'
t.date 'delivered_date'
t.integer 'done'
t.date 'control_in'
t.string 'control_status'
t.date 'at_date'
t.string 'control_results'
t.integer 'control_check'
t.string 'scan_staff'
t.date 'scan_date'
t.integer 'scan_check'
t.string 'comp_staff'
t.date 'comp_date'
t.integer 'comp_check'
t.string 'comp_sub'
t.date 'comp_sub_due_date'
t.integer 'comp_sub_rec'
t.string 'img_staff'
t.date 'img_date'
t.integer 'img_check'
t.string 'edit_staff'
t.date 'edit_date'
t.integer 'edit_check'
t.text 'notes'
t.string 'file1'
t.string 'file2'
t.string 'file3'
t.string 'file4'
t.string 'file5'
t.string 'add_files'
t.datetime 'created_at'
t.datetime 'updated_at'
t.integer 'flown'
t.integer 'cust_trans'
t.integer 'delivered'
t.string 'at_staff'
t.integer 'at_check'
t.integer 'opportunity_id'
end


**Update:**Adding opportunity client model/schema

Opportunity Model:

class Opportunity < ActiveRecord::Base
belongs_to :employee
has_one :user
has_many :film_specs
has_many :digital_specs
has_many :film_quotes
has_many :cost_proposals
has_many :jobs

validates :opp_status_id, presence: true
end


Opportunity Schema:

create_table 'opportunities', force: true do |t|
t.integer 'employee_id'
t.integer 'emp2_id'
t.integer 'emp3_id'
t.string 'name'
t.datetime 'prop_date'
t.integer 'opp_status_id'
t.string 'delay'
t.date 'con_signed'
t.integer 'quote_won_id'
t.float 'total_cost'
t.date 'exp_close'
t.integer 'pri_comp_id'
t.text 'notes'
t.datetime 'created_at'
t.datetime 'updated_at'
t.string 'lost'
t.string 'won'
t.string 'location'
t.integer 'pm_id'
t.integer 'job_id'
end


Client Model:

class Company < ActiveRecord::Base
mount_uploader :logo, LogoUploader
has_many :offices
has_many :employees, through: :offices
has_one :office_type
validates :name, uniqueness: { message: 'That company already exists' }

def self.master_company
find_by(type_id: 1)
end

def self.company_name(comp_id)
find_by(id: comp_id).name
end
end


Client schema:

create_table 'companies', force: true do |t|
t.string 'name'
t.string 'website'
t.string 'logo'
t.datetime 'created_at'
t.datetime 'updated_at'
t.integer 'type_id'
end

Answer

Your issue is that you're calling .employee on an ActiveRecord collection (@job.opportunities). It looks like you're trying to display the client number using @job.opportunities.employee.office.client_num which will never work since you would first need to select a single opportunity record to get its employee. You should revisit how your Job and Office models are associated.