• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pulibrary / orangelight / 4c391e0e-519a-40cb-8ad3-354445f4ce03

12 Aug 2025 08:47PM UTC coverage: 85.348% (-10.0%) from 95.335%
4c391e0e-519a-40cb-8ad3-354445f4ce03

push

circleci

web-flow
[#5143] Use access restriction note as Aeon ItemInfo1 if available (#5173)

With this commit, if a user visits a record with an access
restrictions note and presses the Reading Room Request
button, they will get to an Aeon form with the 'Restrictions'
field pre-filled with the restriction note.

If the record does not have an access restrictions note,
the field will be pre-filled with 'Reading Room Access Only',
as it has been previously.

Closes #5143

5493 of 6436 relevant lines covered (85.35%)

251.82 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.97
/app/models/requests/form.rb
1
# frozen_string_literal: true
2
require 'faraday'
1✔
3

4
module Requests
1✔
5
  # Request class is responsible of building a request
6
  # using items and location of the holding
7
  class Form
1✔
8
    attr_reader :system_id
1✔
9
    attr_reader :mfhd
1✔
10
    attr_reader :patron
1✔
11
    attr_reader :doc
1✔
12
    attr_reader :requestable
1✔
13
    attr_reader :requestable_unrouted
1✔
14
    attr_reader :holdings
1✔
15
    attr_reader :location
1✔
16
    attr_reader :location_code
1✔
17
    attr_reader :items
1✔
18
    attr_reader :pick_ups
1✔
19
    alias default_pick_ups pick_ups
1✔
20
    delegate :ctx, to: :@ctx_obj
1✔
21
    delegate :eligible_for_library_services?, to: :patron
1✔
22

23
    include Requests::Bibdata
1✔
24
    include Requests::Scsb
1✔
25

26
    # @option opts [String] :system_id A bib record id or a special collection ID value
27
    # @option opts [Fixnum] :mfhd alma holding id
28
    # @option opts [Patron] :patron current Patron object
29
    def initialize(system_id:, mfhd:, patron: nil)
1✔
30
      @system_id = system_id
266✔
31
      @doc = SolrDocument.new(solr_doc(system_id))
266✔
32
      @holdings = JSON.parse(doc[:holdings_1display] || '{}')
266✔
33
      # scsb items are the only ones that come in without a MFHD parameter from the catalog now
34
      # set it for them, because they only ever have one location
35
      @mfhd = mfhd || @holdings.keys.first
266✔
36
      @patron = patron
266✔
37
      @location_code = @holdings[@mfhd]["location_code"] if @holdings[@mfhd].present?
266✔
38
      @location = load_bibdata_location
266✔
39
      @items = load_items
266✔
40
      @pick_ups = build_pick_ups
266✔
41
      @requestable_unrouted = build_requestable
266✔
42
      @requestable = route_requests(@requestable_unrouted)
266✔
43
      @ctx_obj = Requests::SolrOpenUrlContext.new(solr_doc: doc)
266✔
44
    end
45

46
    delegate :user, to: :patron
1✔
47

48
    def requestable?
1✔
49
      requestable.size.positive?
72✔
50
    end
51

52
    def first_filtered_requestable
1✔
53
      requestable&.first
75✔
54
    end
55

56
    # Does this request object have any available copies?
57
    def any_loanable_copies?
1✔
58
      requestable_unrouted.any? do |requestable|
2,254✔
59
        !(requestable.charged? || (requestable.aeon? || !requestable.circulates? || requestable.partner_holding? || requestable.on_reserve?))
24,737✔
60
      end
61
    end
62

63
    def any_enumerated?
1✔
64
      requestable_unrouted.any?(&:enumerated?)
1✔
65
    end
66

67
    def route_requests(requestable_items)
1✔
68
      routed_requests = []
266✔
69
      return [] if requestable_items.blank?
266✔
70
      requestable_items.each do |requestable|
258✔
71
        router = Requests::Router.new(requestable:, any_loanable: any_loanable_copies?, patron:)
2,250✔
72
        routed_requests << router.routed_request
2,250✔
73
      end
74
      routed_requests
258✔
75
    end
76

77
    def serial?
1✔
78
      doc[:format].present? && doc[:format].include?('Journal')
1✔
79
    end
80

81
    def recap?
1✔
82
      return false if location.blank?
241✔
83
      location[:remote_storage] == "recap_rmt"
233✔
84
    end
85

86
    # returns nil if there are no attached items
87
    # if mfhd set returns only items associated with that mfhd
88
    # if no mfhd returns items sorted by mfhd
89
    def load_items
1✔
90
      return nil if too_many_items?
266✔
91
      mfhd_items = load_items_by_mfhd
266✔
92
      mfhd_items.empty? ? nil : mfhd_items.with_indifferent_access
266✔
93
    end
94

95
    # returns basic metadata for hidden fields on the request form via solr_doc values
96
    # Fields to return all keys are arrays
97
    ## Add more fields here as needed
98
    def hidden_field_metadata
1✔
99
      {
100
        title: doc["title_citation_display"],
72✔
101
        author: doc["author_citation_display"],
102
        isbn: doc["isbn_s"]&.values_at(0),
103
        date: doc["pub_date_display"]
104
      }
105
    end
106

107
    # Calls Requests::BibdataService to get the delivery_locations
108

109
    def ill_eligible?
1✔
110
      requestable.any? { |r| r.services.include? 'ill' }
×
111
    end
112

113
    def other_id
1✔
114
      doc['other_id_s'].first
25✔
115
    end
116

117
    def scsb_location
1✔
118
      doc['location_code_s'].first
94✔
119
    end
120

121
    # holdings: The holdings1_display from the SolrDocument
122
    # holding: The holding of the holding_id(mfhd) from the SolrDocument
123
    # happens on 'click' the 'Request' button
124
    def too_many_items?
1✔
125
      holding = holdings[@mfhd]
270✔
126
      items = holding.try(:[], "items")
270✔
127
      return false if items.blank?
270✔
128

129
      return true if items.count > 500
228✔
130

131
      false
228✔
132
    end
133

134
    private
1✔
135

136
      ### builds a list of possible requestable items
137
      # returns a collection of requestable objects or nil
138
      # @return [Array<Requests::Requestable>] array containing Requests::Requestables
139
      def build_requestable
1✔
140
        return [] if doc._source.blank?
266✔
141
        if doc.scsb_record?
265✔
142
          build_scsb_requestable
24✔
143
        elsif items.present?
241✔
144
          # for single aeon item, ends up in this statement
145
          build_requestable_with_items
241✔
146
        else
147
          # for too many aeon items, ends up in this statement
148
          build_requestable_from_data
×
149
        end
150
      end
151

152
      def availability_data(id)
1✔
153
        @availability_data ||= items_by_id(id, scsb_owning_institution(scsb_location))
94✔
154
      end
155

156
      # @return [Array<Requests::Requestable>] array containing Requests::Requestables
157
      def build_scsb_requestable
1✔
158
        requestable_items = []
24✔
159
        holdings.each do |id, values|
24✔
160
          requestable_items = build_holding_scsb_items(id:, values:, availability_data: availability_data(other_id), requestable_items:)
24✔
161
        end
162
        requestable_items
24✔
163
      end
164

165
      # @return [Array<Requests::Requestable>] array containing Requests::Requestables
166
      def build_holding_scsb_items(id:, values:, availability_data:, requestable_items:)
1✔
167
        values_items = values['items']
24✔
168
        return requestable_items if values_items.blank?
24✔
169
        barcodesort = build_barcode_sort(items: values_items, availability_data:)
24✔
170
        barcodesort.each_value do |item|
24✔
171
          item['location_code'] = location_code
54✔
172
          params = build_requestable_params(item: item.with_indifferent_access, holding: Holding.new(mfhd_id: id.to_sym.to_s, holding_data: holdings[id]),
54✔
173
                                            location:)
174
          requestable_items << Requests::Requestable.new(**params)
54✔
175
        end
176
        requestable_items
24✔
177
      end
178

179
      def build_barcode_sort(items:, availability_data:)
1✔
180
        barcodesort = {}
94✔
181
        items.each do |item|
94✔
182
          item[:status_label] = status_label(item:, availability_data:)
408✔
183
          barcodesort[item['barcode']] = item
408✔
184
        end
185
        availability_data.each do |item|
94✔
186
          barcode_item = barcodesort[item['itemBarcode']]
65✔
187
          next if barcode_item.blank? || barcode_item["status_source"] == "work_order" || item['errorMessage'].present?
65✔
188
          barcode_item['status_label'] = item['itemAvailabilityStatus']
56✔
189
          barcode_item['status'] = nil
56✔
190
        end
191
        barcodesort
94✔
192
      end
193

194
      # :reek:DuplicateMethodCall
195
      def status_label(item:, availability_data:)
1✔
196
        item_object = Item.new item
408✔
197
        if item_object.not_a_work_order? && availability_data.empty?
408✔
198
          "Unavailable"
306✔
199
        elsif item_object.not_a_work_order? && item_object.status_label == 'Item in place' && availability_data.size == 1 && availability_data.first['errorMessage'] == "Bib Id doesn't exist in SCSB database."
102✔
200
          "In Process"
1✔
201
        else
202
          item_object.status_label
101✔
203
        end
204
      end
205

206
      # @return [Array<Requests::Requestable>] array containing Requests::Requestables
207
      def build_requestable_with_items
1✔
208
        requestable_items = []
241✔
209
        barcodesort = {}
241✔
210
        barcodesort = build_barcode_sort(items: items[mfhd], availability_data: availability_data(system_id)) if recap?
241✔
211
        # items from the availability lookup using the Bibdata Service
212
        items.each do |holding_id, mfhd_items|
241✔
213
          next if mfhd != holding_id
241✔
214
          requestable_items = build_requestable_from_mfhd_items(requestable_items:, holding_id:, mfhd_items:, barcodesort:)
241✔
215
        end
216
        requestable_items.compact
241✔
217
      end
218

219
      # @return [Array<Requests::Requestable>] array containing Requests::Requestables or empty array
220
      def build_requestable_from_data
1✔
221
        return if doc[:holdings_1display].blank?
×
222
        return [] if holdings[@mfhd].blank?
×
223

224
        [build_requestable_from_holding(@mfhd, holdings[@mfhd].with_indifferent_access)]
×
225
      end
226

227
      def build_requestable_from_mfhd_items(requestable_items:, holding_id:, mfhd_items:, barcodesort:)
1✔
228
        if !mfhd_items.empty?
241✔
229
          mfhd_items.each do |item|
203✔
230
            requestable_items << build_requestable_mfhd_item(holding_id, item, barcodesort)
2,171✔
231
          end
232
        else
233
          requestable_items << build_requestable_from_holding(holding_id, holdings[holding_id])
38✔
234
        end
235
        requestable_items.compact
241✔
236
      end
237

238
      def holding_data(item, holding_id, item_location_code)
1✔
239
        if item["in_temp_library"] && item["temp_location_code"] != "RES_SHARE$IN_RS_REQ"
2,164✔
240
          holdings[item_location_code]
6✔
241
        else
242
          holdings[holding_id]
2,158✔
243
        end
244
      end
245

246
      # Item we get from the 'load_items' live call to bibdata
247
      def build_requestable_mfhd_item(holding_id, item, barcodesort)
1✔
248
        return if item['on_reserve'] == 'Y'
2,171✔
249
        item['status_label'] = barcodesort[item['barcode']][:status_label] unless barcodesort.empty?
2,164✔
250
        item_current_location = item_current_location(item)
2,164✔
251
        params = build_requestable_params(
2,164✔
252
          item: item.with_indifferent_access,
253
          holding: Holding.new(mfhd_id: holding_id.to_sym.to_s, holding_data: holding_data(item, holding_id, item_location_code)),
254
          location: item_current_location
255
        )
256
        Requests::Requestable.new(**params)
2,164✔
257
      end
258

259
      def get_current_location(item_location_code:)
1✔
260
        if item_location_code != location_code
2,164✔
261
          @temp_locations ||= TempLocationCache.new
7✔
262
          @temp_locations.retrieve(item_location_code)
7✔
263
        else
264
          location
2,157✔
265
        end
266
      end
267

268
      # This method will always return a Requestable object where .item is a NullItem, because we don't pass an item in
269
      def build_requestable_from_holding(holding_id, holding)
1✔
270
        return if holding.blank?
38✔
271
        params = build_requestable_params(holding: Holding.new(mfhd_id: holding_id.to_sym.to_s, holding_data: holding), location:)
32✔
272
        Requests::Requestable.new(**params)
32✔
273
      end
274

275
      def load_bibdata_location
1✔
276
        return if location_code.blank?
266✔
277
        location = get_location_data(location_code)
257✔
278
        location_object = Location.new location
257✔
279
        location[:delivery_locations] = location_object.sort_pick_ups if location_object.delivery_locations.present?
257✔
280
        location
257✔
281
      end
282

283
      def build_requestable_params(params)
1✔
284
        {
285
          bib: doc,
2,250✔
286
          holding: params[:holding],
287
          item: params[:item],
288
          location: build_requestable_location(params),
289
          patron:
290
        }
291
      end
292

293
      def build_requestable_location(params)
1✔
294
        location = params[:location]
2,250✔
295
        location_object = Location.new location
2,250✔
296
        location["delivery_locations"] = location_object.build_delivery_locations if location_object.delivery_locations.present?
2,250✔
297
        location
2,250✔
298
      end
299

300
      ## Loads item availability through the Request Bibdata service using the items_by_mfhd method
301
      # items_by_mfhd makes the availabiliy call:
302
      # bibdata_conn.get "/bibliographic/#{system_id}/holdings/#{mfhd_id}/availability.json"
303
      # rename to: load_items_by_holding_id
304
      def load_items_by_mfhd
1✔
305
        mfhd_items = {}
266✔
306
        mfhd_items[@mfhd] = items_by_mfhd(@system_id, @mfhd)
266✔
307
        mfhd_items
266✔
308
      end
309

310
      def items_to_symbols(items = [])
1✔
311
        items_with_symbols = []
×
312
        items.each do |item|
×
313
          items_with_symbols << item.with_indifferent_access
×
314
        end
315
        items_with_symbols
×
316
      end
317

318
      def item_current_location(item)
1✔
319
        @item_location_code = if item['in_temp_library']
2,164✔
320
                                item['temp_location_code']
7✔
321
                              else
322
                                item['location']
2,157✔
323
                              end
324
        get_current_location(item_location_code:)
2,164✔
325
      end
326
      attr_reader :item_location_code
1✔
327
  end
328
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc