Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions config/ucpath_fields.yml
Original file line number Diff line number Diff line change
Expand Up @@ -205,3 +205,8 @@ job:
jpath: "$.position.percentOfFullTime"
dbdef: "VARCHAR(16)"
status: OPTIONAL

- name: percent_of_fulltime_job
jpath: "$.percentOfFullTime"
dbdef: "VARCHAR(16)"
status: OPTIONAL
7 changes: 6 additions & 1 deletion lib/ucpath/jobs.rb
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,13 @@ def valid_org_relationship?(j)
end

# 4. The job will be ineligible if the percentage of full time is zero
# Note - percentage of full time can appear in 2 different places (ugh)
def positive_full_time?(j)
j.percent_of_fulltime.to_f.positive?
values = []
values << j.percent_of_fulltime if j.respond_to?(:percent_of_fulltime)
values << j.percent_of_fulltime_job if j.respond_to?(:percent_of_fulltime_job)

values.any? { |v| v.to_f.positive? }
end

def find_priority_jobs(job_list)
Expand Down
5 changes: 3 additions & 2 deletions lib/ucpath/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -144,12 +144,13 @@ def create_user_record
job_code = jobs.job.job_code || nil
priority_job = Config.check_ucpath_code('priority_job_codes', job_code)
percent_of_fulltime = jobs.job.percent_of_fulltime.to_f
percent_of_fulltime_job = jobs.job.percent_of_fulltime_job.to_f

# PERCENT OF FULL TIME CHECK
# AP-559 If an employee is in a position that is 0 FTE,
# their record should should be filtered out from the UCPath files.
if percent_of_fulltime.zero?
logger.info "#{id} - Ineligible: Percentage of Full Time Check: #{percent_of_fulltime}"
if percent_of_fulltime.zero? && percent_of_fulltime_job.zero?
logger.info "#{id} - Ineligible: Percentage of Full Time Check"
return nil
end

Expand Down
144 changes: 144 additions & 0 deletions spec/data/ucpath/10725310_jobs.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
{
"source": "UCB-HR-PATH-DB",
"correlationId": "8c37994b-3132-4f04-98b0-5ecdc96aec79",
"timeStamp": "2026-02-03",
"httpStatus": {
"code": "200",
"description": "OK"
},
"response": [
{
"identifiers": [
{
"type": "hr-employee-id",
"id": "10725310"
},
{
"type": "campus-uid",
"id": "10725310"
}
],
"jobs": [
{
"number": 0,
"sequence": 0,
"type": {
"code": "4",
"description": "Staff: Limited"
},
"classification": {
"code": "1",
"description": "Professional & Support Staff"
},
"position": {
"number": "41195443",
"department": {
"code": "KPADM",
"description": "Library Administration"
},
"description": "LIBRARY AST 2",
"pool": {
},
"percentOfFullTime": 0.000000,
"employeeRelationship": {
"code": "E",
"description": "All Others, Not Confidential"
},
"jobCode": {
"code": {
"code": "006760",
"description": "LIBRARY AST 2"
},
"status": {
"code": "A",
"description": "Active"
},
"family": {
"code": "J092",
"description": "Library Services"
},
"function": {
"code": "442",
"description": "Library"
},
"flsaEligibilty": {
},
"representation": {
"union": {
"code": "CX",
"description": "Clerical & Allied Services"
}
}
},
"reportsTo": {
"position": {
"number": "40145569"
}
},
"status": {
"code": "A",
"description": "Approved"
},
"headCount": {
"active": 1,
"maximum": 1,
"status": {
"code": "F",
"description": "Filled"
}
},
"active": {
"code": "A",
"description": "Active"
},
"fromDate": "2025-08-22"
},
"organizationRelationship": {
"code": "EMP",
"description": "Employee"
},
"flsaEligibilty": {
"code": "N",
"description": "Nonexempt"
},
"percentOfFullTime": 0.000000,
"fullTimeStatus": {
"code": "X",
"description": "Fixed"
},
"permanent": {
"code": "R",
"description": "Not Applicable"
},
"primary": {
"code": "P",
"description": "Primary Job"
},
"department": {
"code": "KPADM",
"description": "Library Administration"
},
"location": {
"code": "10467",
"description": "Bancroft Lib (Doe Anx)-F03-94704"
},
"status": {
"hrStatus": {
"code": "A",
"description": "Active"
},
"employmentStatus": {
"code": "A",
"description": "Active"
},
"benefitsStatus": {
"code": "A",
"description": "Active"
}
},
"fromDate": "2025-10-28"
}
]
}
]
}
51 changes: 51 additions & 0 deletions spec/data/ucpath/10725310_user.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
{
"source": "UCB-HR-PATH-DB",
"correlationId": "23d9a1b2-7f7a-4693-8a88-051a7d3c5caa",
"timeStamp": "2026-02-18",
"httpStatus": {
"code": "200",
"description": "OK"
},
"response": [
{
"identifiers": [
{
"type": "hr-employee-id",
"id": "10725310"
},
{
"type": "campus-uid",
"id": "1926706"
},
{
"type": "calnet-id",
"id": "hptruong"
},
{
"type": "campus-solutions-id",
"id": "3040792017"
}
],
"names": [
{
"type": {
"code": "PRF",
"description": "Preferred"
},
"familyName": "Familyname",
"givenName": "Givenname",
"fromDate": "2024-03-26"
},
{
"type": {
"code": "PRI",
"description": "Primary"
},
"familyName": "Familyname",
"givenName": "Givenname",
"fromDate": "2024-03-26"
}
]
}
]
}
29 changes: 19 additions & 10 deletions spec/lib/ucpath_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -586,6 +586,15 @@
u = UCPath::User.new(id)
expect(u.eligible?).to be(false)
end

it 'skips with zero Percentage of FullTime' do
id = '10725310'
stub_ucpath_user(id)
stub_ucpath_jobs(id)

u = UCPath::User.new(id)
expect(u.eligible?).to be(false)
end
end

describe UCPath::Jobs do
Expand All @@ -603,7 +612,7 @@ def eligible(job)
# Minimal structs to stand in for your “job” objects
let(:choose_job_struct) { Struct.new(:expected_end_date) }
let(:eligible_job_struct) do
Struct.new(:hr_status_code, :expected_end_date, :org_relationship_code, :job_code, :percent_of_fulltime)
Struct.new(:hr_status_code, :expected_end_date, :org_relationship_code, :job_code, :percent_of_fulltime, :percent_of_fulltime_job)
end

describe '#choose_job' do
Expand Down Expand Up @@ -652,55 +661,55 @@ def eligible(job)
end

context "when hr_status_code is not 'A'" do
let(:job) { eligible_job_struct.new('I', '', '', 'ANY', 1.0) }
let(:job) { eligible_job_struct.new('I', '', '', 'ANY', 1.0, 0.0) }

it 'returns false' do
expect(eligible(job)).to be(false)
end
end

context "when hr_status_code is 'A' and expected_end_date is blank" do
let(:job) { eligible_job_struct.new('A', '', '', 'ANY', 1.0) }
let(:job) { eligible_job_struct.new('A', '', '', 'ANY', 1.0, 0.0) }

it 'returns true' do
expect(eligible(job)).to be(true)
end
end

context "when hr_status_code is 'A' and expected_end_date is after today" do
let(:job) { eligible_job_struct.new('A', '2026-03-01', '', 'ANY', 1.0) }
let(:job) { eligible_job_struct.new('A', '2026-03-01', '', 'ANY', 1.0, 0.0) }

it 'returns true' do
expect(eligible(job)).to be(true)
end
end

context "when hr_status_code is 'A' and expected_end_date is today" do
let(:job) { eligible_job_struct.new('A', '2026-02-23', '', 'ANY', 1.0) }
let(:job) { eligible_job_struct.new('A', '2026-02-23', '', 'ANY', 1.0, 0.0) }

it 'returns false (<= today is not eligible per implementation)' do
expect(eligible(job)).to be(false)
end
end

context "when hr_status_code is 'A' and expected_end_date is before today" do
let(:job) { eligible_job_struct.new('A', '2026-02-01', '', 'ANY', 1.0) }
let(:job) { eligible_job_struct.new('A', '2026-02-01', '', 'ANY', 1.0, 0.0) }

it 'returns false' do
expect(eligible(job)).to be(false)
end
end

context 'when org_relationship_code is blank and other conditions pass' do
let(:job) { eligible_job_struct.new('A', '', '', 'ANY', 1.0) }
let(:job) { eligible_job_struct.new('A', '', '', 'ANY', 1.0, 0.0) }

it 'returns true' do
expect(eligible(job)).to be(true)
end
end

context "when org_relationship_code is 'CWR' and job_code is NOT in either allowlist" do
let(:job) { eligible_job_struct.new('A', '', 'CWR', 'NOT_ALLOWED', 1.0) }
let(:job) { eligible_job_struct.new('A', '', 'CWR', 'NOT_ALLOWED', 1.0, 0.0) }

before do
allow(Config).to receive(:check_ucpath_code)
Expand All @@ -718,7 +727,7 @@ def eligible(job)
end

context "when org_relationship_code is 'CWR' and job_code IS in visiting_scholar_job_code" do
let(:job) { eligible_job_struct.new('A', '', 'CWR', 'ALLOWED', 1.0) }
let(:job) { eligible_job_struct.new('A', '', 'CWR', 'ALLOWED', 1.0, 0.0) }

before do
allow(Config).to receive(:check_ucpath_code)
Expand All @@ -736,7 +745,7 @@ def eligible(job)
end

context "when org_relationship_code is 'CWR' and job_code IS in ucb_academic_dept_affiliate_code" do
let(:job) { eligible_job_struct.new('A', '', 'CWR', 'ALLOWED', 1.0) }
let(:job) { eligible_job_struct.new('A', '', 'CWR', 'ALLOWED', 1.0, 0.0) }

before do
allow(Config).to receive(:check_ucpath_code)
Expand Down
Loading