appraisermetrics_report_service 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1192 @@
1
+ class EvalReport < Prawn::Document
2
+ require 'prawn'
3
+ require 'prawn/table'
4
+ require 'date'
5
+
6
+ include ReportUtils # utility methods used across reports
7
+
8
+ def initialize()
9
+ super()
10
+ end
11
+
12
+ def write_content(subject, comparables, images, logo) # main method to populate pdf
13
+ # variables used across instance methods
14
+ @logo = logo
15
+ @sub = subject
16
+ @comps = comparables
17
+ @images = images
18
+ @text_blocks = static_strings # Hash of text blocks loaded from .yml
19
+ @recon_primary_per_acre = [] # used for reconciliation for comparables
20
+ @recon_secondary_per_acre = [] # ""
21
+
22
+ # font defaults to Helvetica
23
+ font_size 11
24
+
25
+ office_logo
26
+ cover_page
27
+ letter_of_transmittal # table/text
28
+ subject_images #images with caption # 2 pages
29
+ table_of_contents #text
30
+ introduction #text and checkboxes
31
+ property_identification
32
+ regional_maps
33
+ topo_maps
34
+ property_description
35
+ property_features
36
+ value_intro
37
+ comp_methodology
38
+ property_comparison # landscape
39
+ discussion_of_sales # landscape
40
+ ag_sales_map
41
+ val_method_and_recon
42
+ assumptions_and_conditions
43
+ addenda_contents
44
+ addendum_a
45
+ addendum_b
46
+ end
47
+
48
+ def office_logo
49
+ move_down 550
50
+ image "#{@logo}", width: 200
51
+ end
52
+
53
+ def cover_page
54
+ bounding_box([220, 700], width: 320, height: 700) do
55
+ text 'EVALUATION OF REAL PROPERTY', size: 14
56
+ move_down 5
57
+ text "#{@sub[:primary_ag_use]}"
58
+
59
+ if @sub[:property_inclusions] && @sub[:property_inclusions].any?
60
+ move_down 3
61
+ text "#{@sub[:property_inclusions].join(', ')}"
62
+ end
63
+
64
+ move_down 10
65
+
66
+ text "#{@sub[:property_name]}"
67
+ move_down 3
68
+ text "#{@sub[:county_state]}".slice(0..-3)
69
+ move_down 3
70
+ text "#{@sub[:property_address_number]} " << "#{@sub[:street_or_road_name]}"
71
+ move_down 3
72
+ add_string = ""
73
+ add_string << "#{@sub[:city]}, "
74
+
75
+ if @sub[:county_state]
76
+ add_string << "#{@sub[:county_state].slice(-2..-1)} "
77
+ end
78
+
79
+ add_string << "#{@sub[:zip_code]}"
80
+ text add_string
81
+ move_down 25
82
+
83
+ text 'AGRICULTURAL REAL ESTATE EVALUATION', size: 14
84
+ move_down 10
85
+ text 'As of ' << "#{datemaker(@sub[:effvaluedateasis])}"
86
+ move_down 3
87
+ text 'Report Date: ' << "#{datemaker(@sub[:valreportdate])}"
88
+ move_down 15
89
+
90
+ text 'Prepared For:'
91
+ text "#{@sub[:clientid]}"
92
+ text "#{@sub[:clientaddress]}"
93
+ text "#{@sub[:clientcontact]}"
94
+ text "#{@sub[:clientrefnum]}"
95
+ move_down 10
96
+ text 'Prepared By:'
97
+ text "#{@sub[:record_created_by]}"
98
+ # "#{@sub[:tenant_name]}" # not found currently
99
+ # "#{@sub[:tenant_contact]}" # not found currently
100
+ end
101
+
102
+ stroke_bounds
103
+ start_new_page
104
+ end
105
+
106
+ def letter_of_transmittal
107
+ header("LETTER OF TRANSMITTAL")
108
+ move_down 10
109
+ text "#{datemaker(@sub[:valreportdate])}"
110
+ text "#{@sub[:clientid]}"
111
+ text "#{@sub[:clientaddress]}"
112
+ text "#{@sub[:clientcontact]}"
113
+
114
+ move_down 25
115
+ float do
116
+ text "Re:"
117
+ end
118
+
119
+ indent(30) do
120
+ taxes = @sub[:taxes]
121
+ if taxes && taxes.any?
122
+ t_s = taxes.inject("") {|memo_s, t| memo_s << "#{t[:tax_parcel_no]}, "; memo_s }
123
+ else
124
+ t_s = ""
125
+ end
126
+
127
+ parallel_text("Evaluation of Real Property", " ", 150)
128
+ parallel_text("Property Type:", "#{@sub[:primary_ag_use]}", 150)
129
+ parallel_text("Property Name:", "#{@sub[:property_name]}", 150)
130
+ parallel_text("Tax Parcel ID No.:", t_s , 150)
131
+ parallel_text("County:","#{@sub[:county_state]}".slice(0..-3), 150)
132
+ parallel_text("Street Address:", "#{@sub[:property_address_number]}" << "#{@sub[:street_or_road_name]}", 150)
133
+ parallel_text("City, State, Zip Code:", "#{@sub[:city]}" << "#{@sub[:county_state]}".chars.last(2).join(''), 150)
134
+ parallel_text("Client Assignment Ref. No.:", "#{@sub[:clientrefnum]}", 150)
135
+ parallel_text("Valuator's Internal File No.:", "#{@sub[:_id]}", 150)
136
+ end
137
+
138
+ move_cursor_to 450
139
+ text "Dear " << "#{@sub[:clientid]},"
140
+ text @text_blocks[:transmittal]
141
+ start_new_page
142
+ end
143
+
144
+ def subject_images
145
+ header("SUBJECT PHOTOGRAPHS")
146
+ move_down 10
147
+ if @images && @images[:subject_photos]
148
+ if @images[:subject_photos][0]
149
+ image_cap("")
150
+ image "#{@images[:subject_photos][0]}", width: 300, position: :center
151
+ move_down 2
152
+ text "Subject Property - Representative Image", align: :center
153
+ move_down 25
154
+ else
155
+ text "No Image", align: :center
156
+ move_cursor_to 350
157
+ end
158
+
159
+ if @images[:subject_photos][1]
160
+ image_cap("")
161
+ image "#{@images[:subject_photos][1]}", width: 300, position: :center
162
+ move_down 2
163
+ text "Subject Property - Representative Image", align: :center
164
+ move_down 25
165
+ else
166
+ text "No Image", align: :center
167
+ move_cursor_to 350
168
+ end
169
+
170
+
171
+ start_new_page
172
+ header("SUBJECT PHOTOGRAPHS")
173
+ move_down 10
174
+ if @images[:subject_photos][2]
175
+ image_cap("")
176
+ image "#{@images[:subject_photos][2]}", width: 300, position: :center
177
+ move_down 2
178
+ text "Subject Property - Representative Image", align: :center
179
+ move_down 25
180
+ else
181
+ text "No Image", align: :center
182
+ move_cursor_to 350
183
+ end
184
+
185
+ if @images[:subject_photos][3]
186
+ image_cap("")
187
+ image "#{@images[:subject_photos][3]}", width: 300, position: :center
188
+ move_down 2
189
+ text "Subject Property - Representative Image", align: :center
190
+ move_down 25
191
+ else
192
+ text "No Image", align: :center
193
+ move_cursor_to 350
194
+ end
195
+ else
196
+ text "No Image", align: :center
197
+ move_cursor_to 350
198
+ text "No Image", align: :center
199
+ move_cursor_to 350
200
+ start_new_page
201
+ text "No Image", align: :center
202
+ move_cursor_to 350
203
+ text "No Image", align: :center
204
+ move_cursor_to 350
205
+ end
206
+
207
+ start_new_page
208
+ end
209
+
210
+ def table_of_contents
211
+ header("TABLE OF CONTENTS")
212
+ move_down 25
213
+ text "Table of Contents", size: 14
214
+ move_down 25
215
+
216
+ contents = ["INTRODUCTION", "SCOPE OF WORK", "REGIONAL MAPS", "MAPS",
217
+ "PROPERTY DESCRIPTION, SURROUNDING LAND USES, REGIONAL ECONOMIC AND \nDEMOGRAPHIC OVERVIEW", "HIGHEST AND BEST USE",
218
+ "SALES COMPARISON APPROACH", "METHODOLOGY", "DISCUSSION OF SALES USED", "GLOSSARY OF TERMS & DEFINITIONS", "CASH EQUIVALENCE",
219
+ "ELLWOOD FORMULA", "EXPOSURE TIME","FEE SIMPLE ESTATE", "LEASED FEE INTEREST", "LEASEHOLD INTEREST", "MARKET RENT", "MARKET VALUE",
220
+ "ADDENDA CONTENTS"
221
+ ]
222
+
223
+ contents.each do |c|
224
+ text "#{c}"
225
+ stroke_horizontal_rule
226
+ move_down 5
227
+ end
228
+ start_new_page
229
+ end
230
+
231
+ def introduction
232
+ header("INTRODUCTION")
233
+ move_down 25
234
+ text "INTRODUCTION", size: 14, style: :bold
235
+ move_down 10
236
+ text "SCOPE OF WORK", size: 14, style: :bold
237
+ text @text_blocks[:scope_of_work1]
238
+ move_down 10
239
+
240
+ if @sub[:research] && @sub[:research].any?
241
+ @sub[:research].each do |r|
242
+ float do # building a checkbox because there is no support for them in base 14 fonts
243
+ float do
244
+ rectangle [0, cursor], 12, 12
245
+ stroke
246
+ end
247
+ text 'X', size: 15
248
+ end
249
+
250
+ indent(50) do
251
+ text r
252
+ move_down 10
253
+ end
254
+ end
255
+ end
256
+
257
+ move_down 5
258
+ text @text_blocks[:scope_of_work3]
259
+ start_new_page
260
+ end
261
+
262
+ def property_identification
263
+ header("PROPERTY IDENTIFICATION")
264
+ move_down 25
265
+
266
+ parallel_text("Effective Date of Value", "#{datemaker(@sub[:effvaluedateasis])}", 150)
267
+ move_down 30
268
+ parallel_text("Report Date:", "#{datemaker(@sub[:valreportdate])}", 150)
269
+ move_down 30
270
+ parallel_text("Estate Valued:", "#{@sub[:property_rights]}", 150)
271
+ move_down 30
272
+ parallel_text("Intended Use:", no_nil_array(@sub[:intendeduse]).join(", "), 150)
273
+ move_down 30
274
+ parallel_text("Intended User:", "#{@sub[:intendeduser]}", 150)
275
+ move_down 30
276
+ parallel_text("Problem being solved:","#{@sub[:appproblem]}", 150)
277
+ move_down 30
278
+
279
+ text "CONFORMITY AND COMPLIANCE IDENTIFICATION FOR THIS EVALUATION ANALYSIS", size: 14, style: :bold
280
+ move_down 20
281
+ text "#{no_nil_array(@sub[:complianceid]).join(', ')}"
282
+ move_down 30
283
+
284
+ text "DEFINITION OF VALUE USED IN THIS EVALUATION:", size: 14, style: :bold
285
+ move_down 20
286
+ text "#{no_nil_array(@sub[:valuationvalues]).join(', ')}"
287
+ move_down 30
288
+
289
+ text "ASSUMPTIONS:", size: 14, style: :bold
290
+ move_down 20
291
+ text "#{(@sub[:extrassumptions])}"
292
+ move_down 30
293
+
294
+ text "EXPOSURE TIME:", size: 14, style: :bold
295
+ move_down 20
296
+ text "#{@sub[:exptimereasoning]}"
297
+ move_down 30
298
+
299
+ text "MARKETING TIME:", size: 14, style: :bold
300
+ move_down 20
301
+ text "#{@sub[:marketingtimereasoning]}"
302
+ move_down 30
303
+
304
+ start_new_page
305
+ end
306
+
307
+ def regional_maps
308
+ header("REGIONAL MAPS")
309
+ move_down 25
310
+ text "REGIONAL MAPS", size: 14, align: :center
311
+ if @images && @images[:regional_maps]
312
+ if @images[:regional_maps][0]
313
+ image_cap("STATE MAP")
314
+ image "#{@images[:regional_maps][0]}", width: 300, position: :center
315
+ move_down 25
316
+ else
317
+ text 'No Image', align: :center
318
+ move_cursor_to 350
319
+ end
320
+
321
+ if @images[:regional_maps][1]
322
+ image_cap("COUNTY MAP")
323
+ image "#{@images[:regional_maps][1]}", width: 300, position: :center
324
+ else
325
+ text 'No Image', align: :center
326
+ move_cursor_to 350
327
+ end
328
+ else
329
+ text 'No Image', align: :center
330
+ move_cursor_to 350
331
+ text 'No Image', align: :center
332
+ end
333
+
334
+ start_new_page
335
+ end
336
+
337
+ def topo_maps
338
+ header("LOCAL AREA MAP")
339
+ move_down 25
340
+ text "MAPS", size: 14, align: :center
341
+
342
+ if @images && @images[:topo_maps]
343
+ if @images[:topo_maps][0]
344
+ image_cap("TOPOGRAPHICAL MAPS")
345
+ image "#{@images[:topo_maps][0]}", width: 300, position: :center
346
+ move_down 25
347
+ else
348
+ text 'No Image', align: :center
349
+ move_cursor_to 350
350
+ end
351
+
352
+
353
+ if @images[:topo_maps][1]
354
+ image_cap("AERIAL FSA MAP")
355
+ image "#{@images[:topo_maps][1]}", width: 300, position: :center
356
+ else
357
+ text 'No Image', align: :center
358
+ end
359
+ else
360
+ text 'No Image', align: :center
361
+ move_cursor_to 350
362
+ text 'No Image', align: :center
363
+ end
364
+ start_new_page
365
+ end
366
+
367
+ def property_description
368
+ header("PROPERTY DESCRIPTION")
369
+ move_down 25
370
+ text "PROPERTY DESCRIPTION, SURROUNDING LAND USES, REGIONAL ECONOMIC AND DEMOGRAPHIC OVERVIEW", size: 14, style: :bold
371
+ font_size 6
372
+ bounding_box([0, 645], height: 300, width: 340) do
373
+ if @sub[:landclassifications] && @sub[:landclassifications].any?
374
+ @number_acres = @sub[:landclassifications].inject(0) {|memo, l| memo += no_nil_number(l[:numacres]); memo }
375
+ else
376
+ @number_acres = 0
377
+ end
378
+
379
+ general_data = [
380
+ [{content: "General Information", colspan:2}],
381
+ ["Effective Date of Analysis:", @sub[:effvaluedateasis]],
382
+ ["Property Rights Being Analyzed:", @sub[:property_rights]],
383
+ ["Total No. Acres:", @number_acres]
384
+ ]
385
+
386
+ table(general_data) do
387
+ columns(0..1).width = 170
388
+ cells.style(border_width: 0)
389
+ cells.padding = [1, 2.5]
390
+ column(0).style(font_style: :bold, align: :right)
391
+ row(0).style(align: :center, border_bottom_width: 0.5)
392
+ end
393
+
394
+ # construction of combo strings
395
+ street_address = "#{@sub[:property_address_number]} " << "#{@sub[:street_or_road_name]}"
396
+ lat_long_string = ""
397
+ lat_long_string << "#{@sub[:position][0]}/#{@sub[:position][1]}" unless @sub[:positioin] == nil
398
+ meridian_town_range = "#{@sub[:meridian]}, " << "#{@sub[:township]} " << "#{@sub[:range]}"
399
+
400
+ taxes = @sub[:taxes]
401
+ if taxes
402
+ parcel_nos = taxes.inject("") {|memo, t| memo << "#{t[:tax_parcel_no]}; "; memo}
403
+ else
404
+ parcel_nos = ""
405
+ end
406
+
407
+ re_tax_string = "#{@sub[:total_RET]} "
408
+ if @number_acres == 0
409
+ re_tax_string << "/ 0"
410
+ else
411
+ re_tax_string << "/ #{@sub[:total_RET].to_f / @number_acres}"
412
+ end
413
+
414
+ if @sub[:county_state]
415
+ state_string = "#{@sub[:county_state].slice(-2..-1)}"
416
+ else
417
+ state_string = ""
418
+ end
419
+
420
+ identification_data = [
421
+ [{content: "Property Identification", colspan: 2}],
422
+ ["Property Owner:", @sub[:curr_owner]],
423
+ ["Property Name:", @sub[:property_name]],
424
+ ["Street Address:", street_address],
425
+ ["City:", @sub[:city]],
426
+ ["State:", state_string],
427
+ ["Zip Code:", @sub[:zip_code]],
428
+ ["Latitude/Longitude:", lat_long_string],
429
+ ["Meridian/Township/Range:", meridian_town_range],
430
+ ["Brief Legal:", @sub[:legal_description]],
431
+ ["Tax Parcel ID(s):", parcel_nos],
432
+ ["RE Taxes/ $/Acre:", re_tax_string],
433
+ ["Assessed Value of Land:", @sub[:total_assess_value]],
434
+ ["Current Real Estate Taxes", @sub[:total_RET]],
435
+ ["Total Number of Acres:", @number_acres],
436
+ ["Land Use Zone and Allowed Uses:", "#{@sub[:land_use_zone]}, #{@sub[:allowed_uses_and_limitations]}"],
437
+ ["Does property conform to zoning?:", @sub[:conf_type]],
438
+ ["Is Property located in Flood Zone/Wetland?:", "#{@sub[:flood_zone]} #{@sub[:wetlands]}"],
439
+ ["Is Property currently listed, under contract? If property has sold in prior 3 years see Transaction History table below.", "#{@sub[:under_purchase_agr]}, #{@sub[:listed_for_sale]}"],
440
+ ["Does Property have Legal Access? How?:", "#{@sub[:has_legal_access]} / #{@sub[:legal_access]}"],
441
+ ["Does the property have Physical Access? How?:", "#{@sub[:has_physical_access]} / #{@sub[:physical_access]}"],
442
+ ["Easements, ROW or Encroachments:", @sub[:easements_description]]
443
+ ]
444
+
445
+ table(identification_data) do
446
+ columns(0..1).width = 170
447
+ cells.style(border_width: 0)
448
+ cells.padding = [1, 2.5]
449
+ row(0).style(align: :center, font_style: :bold, border_bottom_width: 0.5)
450
+ column(0).style(align: :right)
451
+ end
452
+
453
+ stroke_bounds
454
+ end
455
+
456
+ bounding_box([340, 645], height: 300, width: 200) do
457
+ neighborhood_data = [
458
+ [{content: "Neighborhood Information", colspan: 2}],
459
+ ["Land Use North:", @sub[:north_land]],
460
+ ["Land Use South:", @sub[:south_land]],
461
+ ["Land Use East:", @sub[:east_land]],
462
+ ["Land Use West:", @sub[:west_land]],
463
+ [{content: "Comments - Distance to Services, Farm to Market, etc.:", colspan: 2}],
464
+ [{content: "#{@sub[:SWOT_analysis1]}", colspan: 2}]
465
+ ]
466
+
467
+ table(neighborhood_data) do
468
+ cells.style(border_width: 0)
469
+ cells.padding = [1, 2.5]
470
+ row(0).style(align: :center, font_style: :bold, border_bottom_width: 0.5)
471
+ column(0).style(border_right_width: 0.5)
472
+ column(0).width = 80
473
+ column(1).width = 120
474
+ end
475
+
476
+ stroke_horizontal_rule
477
+ text "Economics and Demographics", align: :center, style: :bold
478
+ stroke_horizontal_rule
479
+ move_down 3
480
+ text "Pertinent Local Economic Factors including a description of current supply and
481
+ demand of similar property types and commodities produced. Included influences on value."
482
+ stroke_horizontal_rule
483
+ move_down 3
484
+ text "#{@sub[:SWOT_analysis2]}"
485
+
486
+ stroke_bounds
487
+ end
488
+
489
+ # Property Description Table Header
490
+ move_down 4
491
+ text 'Property Description', align: :center, style: :bold
492
+
493
+
494
+ # bounding box for left table under property description section
495
+ bounding_box([0, 330], height: 230, width: 270) do
496
+ inclusions = @sub[:inclusions]
497
+ if inclusions
498
+ incl_string = inclusions.join(", ")
499
+ else
500
+ incl_string = ""
501
+ end
502
+
503
+ # getting primary and secondary land classes by index
504
+ if @sub[:landclassifications] && @sub[:landclassifications].any?
505
+ num_primary = no_nil_number(@sub[:landclassifications][0][:numacres])
506
+
507
+ if @sub[:landclassifications][1]
508
+ num_secondary = no_nil_number(@sub[:landclassifications][1][:numacres])
509
+ secondary_land_type = @sub[:landclassifications][1][:landclass]
510
+ else
511
+ num_secondary = 0
512
+ end
513
+ else
514
+ num_primary = 0
515
+ num_secondary = 0
516
+ end
517
+
518
+ # getting primary and secondary commodities by index
519
+ if @sub[:estimatedproductivity] && @sub[:estimatedproductivity].any?
520
+ primary_commodity = "#{@sub[:estimatedproductivity][0][:commodity]}"
521
+ commodity_num_primary = no_nil_number(@sub[:estimatedproductivity][0][:estmtdyield])
522
+
523
+ if @sub[:estimatedproductivity][1]
524
+ secondary_commodity = "#{@sub[:estimatedproductivity][1][:commodity]}"
525
+ commodity_num_secondary = no_nil_number(@sub[:estimatedproductivity][1][:estmtdyield])
526
+ else
527
+ commodity_num_secondary = 0
528
+ secondarycommodity = "None"
529
+ end
530
+
531
+ else
532
+ commodity_num_primary = 0
533
+ commodity_num_secondary = 0
534
+ secondary_commodity = "None"
535
+ primary_commodity = "None"
536
+ end
537
+
538
+ # conditional strings
539
+ is_encumbered = @sub[:occupancy] == "leased" || @sub[:occupancy] = "partially leased" ? "Yes" : "No"
540
+ end_of_lease = @sub[:lease_data] && @sub[:lease_data].any? ? @sub[:lease_data][0][:endLeaseDate] : "NA"
541
+
542
+ if @sub[:inclusions]
543
+ has_improvs = (@sub[:inclusions] & ["Improved with non-Ag Use Improvements",
544
+ "Improved with Ag Use Improvements",
545
+ "Improved with residence"]).present?
546
+ improvs_string = has_improvs ? "See Improvements Table" : "No"
547
+ else
548
+ improvs_string = "No"
549
+ end
550
+
551
+ # calculated strings
552
+ income = Money.new(no_nil_number(@sub[:eff_gross_income]) * 100, 'USD')
553
+ expense = Money.new(no_nil_number(@sub[:total_expenses]) * 100, 'USD')
554
+ noi = income - expense
555
+ noi_string = noi.format(no_cents: :true)
556
+ expense_ratio = "#{((expense / income) * 100).round(2)}" + "%"
557
+
558
+ description_data2 = [
559
+ ["Effective Unit Size/Acres:", "#{@sub[:num_of_units]}"],
560
+ ["Current Land Use", "See General Comments"],
561
+ ["Historic Land Use:", "See General Comments"],
562
+ ["Intended Future use by Borrower:", "#{@sub[:change_of_use]}"],
563
+ ["Occupancy:", "#{@sub[:occupancy]}"],
564
+ ["What does this property include?:", incl_string],
565
+ ["Primary Agricultural Land Use:", "#{@sub[:primary_ag_use]}"],
566
+ ["Primary Agricultural Land Use Acres:", num_primary],
567
+ ["Primary Commodity:", primary_commodity],
568
+ ["Primary Ag Commodity Est. Productivity Yield:", commodity_num_primary],
569
+ ["Secondary Agricultural Land Class:", secondary_land_type],
570
+ ["Secondary Agricultural Land Use Acres:", num_secondary],
571
+ ["Secondary Commodity:", secondary_commodity],
572
+ ["Secondary Ag Commodity Est. Productivity Yield:", commodity_num_secondary],
573
+ ["Is subject encumbered by a current lease?:", is_encumbered],
574
+ ["Lease Structure:", "#{@sub[:type_of_lease]}"],
575
+ ["Lease Term:", end_of_lease],
576
+ ["NOI Projected Income from Rent:", noi_string],
577
+ ["Expense Ratio:", expense_ratio],
578
+ ["Does the subject have Building Improvements?:", improvs_string]
579
+ ]
580
+
581
+ table(description_data2) do
582
+ cells.style(border_width: 0)
583
+ cells.padding = [1, 2.5]
584
+ column(0).style(border_right_width: 0.5, align: :right)
585
+ column(0..1).width = 135
586
+ end
587
+
588
+ stroke_bounds
589
+ end
590
+
591
+ # bounding box for right table under property description section
592
+ bounding_box([270, 330], height: 230, width: 270) do
593
+
594
+ if @sub[:perm_plantings] && @sub[:perm_plantings].any?
595
+ has_perm_plants = "Yes"
596
+ else
597
+ has_perm_plants = "No"
598
+ end
599
+
600
+ if @sub[:crops] && @sub[:crops].any?
601
+ has_crops = "Yes"
602
+ else
603
+ has_crops = "No"
604
+ end
605
+
606
+ description_data = [
607
+ ["Infrastructure-Farming Practice:", "#{@sub[:farming_practices]}"],
608
+ ["Predominant Soils Types:", "See Comments Below"],
609
+ ["Dominant Soils Capability Class:", "#{@sub[:soils]}"],
610
+ ["Corn Suitability Rating (NRCS):", "#{@sub[:CSR2]}"],
611
+ ["Average Annual Precipitation:", "#{@sub[:avg_precipitation]}"],
612
+ ["Average Growing Season:", "#{@sub[:growing_season]}"],
613
+ ["Elevation:", "#{@sub[:elevation]}"],
614
+ ["Predominant Slope:", "#{@sub[:topography]}"],
615
+ ["Does property have water rights?:", "#{@sub[:any_water_rights]}"],
616
+ ["Annual Water Cost/Irrigated Acre:", "#{moneymaker(@sub[:irrig_cost_per_acre], false)}"],
617
+ ["Does the property have a water distribution system?", "stubbed"],
618
+ ["Annual Pumping Cost/Irrigated Acre:", "#{moneymaker(@sub[:pumping_cost_per_acre], false)}"],
619
+ ["Does the property have Permanent Plantings?", has_perm_plants],
620
+ ["Is a Crop Insurance Yield Report Available?", has_crops],
621
+ [{content: "Soils, Water, Distribution System, Permanent Planting Comments", colspan: 2}],
622
+ [{content: "#{@sub[:soils]} #{@sub[:accessibility]} #{@sub[:mineral_rights]} #{@sub[:water_rights_comment]} #{@sub[:water_distribution_comment]}", colspan: 2}]
623
+ ]
624
+
625
+ table(description_data) do
626
+ cells.style(border_width: 0)
627
+ cells.padding = [1, 2.5]
628
+ column(0).style(border_right_width: 0.5, align: :right)
629
+ row(-2).style(align: :center, border_bottom_width: 0.5, border_top_width: 0.5)
630
+ row(-1).style(align: :right)
631
+
632
+ end
633
+
634
+ stroke_bounds
635
+ end
636
+ move_down 3
637
+ text "General Comments - Subject Property", align: :center, style: :bold
638
+ stroke_horizontal_rule
639
+ move_down 3
640
+
641
+ comment_proc = Proc.new do |k|
642
+ if k
643
+ text "#{k}"
644
+ end
645
+ end
646
+
647
+ comment_proc.call(@sub[:directions_or_ownership_comments])
648
+ comment_proc.call(@sub[:development_potential])
649
+ comment_proc.call(@sub[:general_comments])
650
+ comment_proc.call(@sub[:listing_comments])
651
+ comment_proc.call(@sub[:lease_comments])
652
+ comment_proc.call(@sub[:foo])
653
+ comment_proc.call(@sub[:foo])
654
+ comment_proc.call(@sub[:income_comments])
655
+ comment_proc.call(@sub[:problem_descr])
656
+ comment_proc.call(@sub[:hazard_descr])
657
+ comment_proc.call(@sub[:env_problem_descr])
658
+ comment_proc.call(@sub[:windgen_comment])
659
+
660
+ start_new_page
661
+ font_size 11 # change fontsize back to default
662
+ end
663
+
664
+ def property_features # container method to create various tables
665
+ font_size 6
666
+ transaction_history
667
+ utilities
668
+ improvements
669
+ water_rights
670
+ water_distribution
671
+
672
+ start_new_page
673
+
674
+ crop_yield
675
+ perm_plantings
676
+ externalities
677
+
678
+ start_new_page
679
+ font_size 11
680
+ end
681
+
682
+
683
+ def value_intro
684
+ header("VALUATION PROCESS")
685
+ move_down 25
686
+ text "VALUATION PROCESS", size: 14, style: :bold
687
+ move_down 20
688
+ text "#{@text_blocks[:valuation_process]}"
689
+ start_new_page
690
+ end
691
+
692
+ def comp_methodology
693
+ header("DIRECT COMPARISON METHODOLOGY")
694
+ move_down 25
695
+ text "ESTIMATE OF VALUE BY DIRECT COMPARISON METHODOLOGY", size: 14, style: :bold
696
+ move_down 20
697
+ text "#{@text_blocks[:comp_method]}"
698
+ start_new_page
699
+ end
700
+
701
+ def property_comparison
702
+ header("DIRECT COMPARISON ANALYSIS")
703
+ move_down 25
704
+ font_size 6
705
+
706
+ table(flip_comp_array) do
707
+ column(0).width = 85
708
+ columns(1..7).width = 65
709
+ cells.padding = [1, 2.5]
710
+ columns(1..7).style(align: :center)
711
+ end
712
+ start_new_page
713
+ font_size 11
714
+ end
715
+
716
+ def discussion_of_sales
717
+ header("DIRECT COMPARISON ANALYSIS")
718
+
719
+ discussion_data = [
720
+ ["Subject", "Sale 1", "Sale 2", "Sale 3", "Sale 4", "Sale 5", "Sale 6"]
721
+ ]
722
+
723
+ sale_discussions = ["N/A"]
724
+ @comps.each do |c|
725
+ c_string = "#{c[:directions_or_ownership_comments]}\n\n" << "#{c[:allowed_uses_and_limitations]}\n\n"
726
+ c_string << "#{c[:development_potential]}\n\n" << "#{c[:comments_on_floodways]}\n\n" << "#{c[:sale_adjustment_comments]}\n\n"
727
+ c_string << "#{c[:general_comments]}\n\n" << "#{c[:atmarket_comments]}\n\n" << "#{c[:listing_comments]}\n\n"
728
+ c_string << "#{c[:lease_comments]}\n\n" << "#{c[:change_of_use]}\n\n" << "#{c[:water_rights_comment]}\n\n"
729
+ c_string << "#{c[:water_distribution_comment]}\n\n" << "#{c[:accessibility]}\n\n" << "#{c[:easements_description]}\n\n"
730
+ c_string << "#{c[:elevation]}\n\n" << "#{c[:property_improvement_comments]}\n\n" << "#{c[:income_comments]}"
731
+
732
+ # build on sub-array
733
+ sale_discussions << c_string
734
+ end
735
+
736
+ # push subarray to main table array
737
+ discussion_data << sale_discussions
738
+
739
+ table(discussion_data) do
740
+ cells.padding = [1, 2.5]
741
+ columns(0..6).width = 77
742
+ row(0).style(align: :center, background_color: 'd7d7d7')
743
+ cells.style(border_width: 0)
744
+ columns(0..6).style(border_right_width: 0.5)
745
+ end
746
+
747
+ start_new_page
748
+ end
749
+
750
+ def ag_sales_map
751
+ header("AGRICULTURE SALES LOCATION MAP")
752
+ move_down 50
753
+ if @images && @images[:ag_sales_map]
754
+ image "#{@images[:ag_sales_map]}", width: 500, align: :center
755
+ else
756
+ text 'No Image'
757
+ end
758
+
759
+ start_new_page
760
+ end
761
+
762
+ def val_method_and_recon
763
+ header("FINAL VALUE INDICATION")
764
+ move_down 25
765
+ text "Valuation Methodology Review and Reconciliation", size: 14, style: :bold
766
+ move_down 20
767
+ text @text_blocks[:val_method]
768
+ move_down 10
769
+ text "The previous analysis indicated the following:"
770
+ move_down 10
771
+
772
+ parallel_text("Average Primary Land Value/Unit", "#{average_primary_land_val(@recon_primary_per_acre).format(no_cents: true)}", 200)
773
+ move_down 20
774
+ parallel_text("Median Primary Land Value/Unit", "#{median_primary_land_val(@recon_primary_per_acre).format(no_cents: true)}", 200)
775
+ move_down 20
776
+ parallel_text("Maximum Primary Land Value/Unit", "#{maximum_primary_land_val(@recon_primary_per_acre).format(no_cents: true)}", 200)
777
+ move_down 20
778
+ parallel_text("Minimum Primary Land Value/Unit", "#{minimum_primary_land_val(@recon_primary_per_acre).format(no_cents: true)}", 200)
779
+ move_down 20
780
+ parallel_text("Unit of Comparison", "$/Acre", 200)
781
+ move_down 20
782
+ parallel_text("Indicated Value per Unit", "stubbed", 200)
783
+ move_down 20
784
+ parallel_text("Indicated Primary Land Value/Unit", "stubbed", 200)
785
+ move_down 20
786
+ parallel_text("Indicated Secondary Land Value/Unit", "stubbed", 200)
787
+ move_down 20
788
+ parallel_text("Improvement Value Allocation", "stubbed", 200)
789
+ move_down 20
790
+ parallel_text("Overall Indicated Value", "stubbed", 200)
791
+ move_down 20
792
+ parallel_text("SAY", "stubbed", 200)
793
+ start_new_page
794
+ end
795
+
796
+ def assumptions_and_conditions
797
+ header("ASSUMPTIONS AND LIMITING CONDITIONS")
798
+ move_down 25
799
+ text "ASSUMPTIONS AND LIMITING CONDITIONS", size: 14, style: :bold
800
+ move_down 20
801
+ text "#{@text_blocks[:assumptions_conditions]}", style: :bold
802
+ move_down 20
803
+ text "Evaluators Associated with this Evaluation Report are identified as follows:"
804
+ text "#{@sub[:validentification]}"
805
+ start_new_page
806
+ end
807
+
808
+ def addenda_contents
809
+ header("ADDENDA CONTENTS")
810
+ move_down 25
811
+ text "ADDENDA CONTENTS", size: 14, style: :bold
812
+ start_new_page
813
+ end
814
+
815
+ def addendum_a
816
+ header("ENGAGEMENT LETTER")
817
+ move_down 25
818
+ text "ADDENDUM A", size: 14, style: :bold
819
+ start_new_page
820
+ end
821
+
822
+ def addendum_b
823
+ header("LEGAL DESCRIPTION")
824
+ move_down 25
825
+ text "ADDENDUM B", size: 14, style: :bold
826
+ # end of document
827
+ end
828
+
829
+ def header(page_title)
830
+ text "#{page_title} " << "#{page_count - 1}", align: :right
831
+ stroke_horizontal_rule
832
+ end
833
+
834
+ def flip_comp_array # necessary to produce 2D array of comps side by side
835
+ props = [@sub, @comps].compact.flatten
836
+ final_a = [
837
+ [{content: 'Valuation Analysis - Sales Comparison Approach', colspan: 8}], # [0]
838
+ ['Characteristic', 'Subject', 'Sale 1', 'Sale 2', 'Sale 3', 'Sale 4', 'Sale 5', 'Sale 6'], # [1]
839
+ ['Record No.'], ['Tax Parcel #(s)'], ['Address'], ['City'], ['State'], ['Zip Code'], ['Sale Date'], ['Sale Price'], # [2] -> [9]
840
+ ['Conditions of Sale'], ['Adjusted Sale Price'], ['Property Rights Conveyed'], ['Unit of Comparison'], # [10 -> 13]
841
+ ['Price/Unit'], ['Total Acres'], ['Primary Land Use'], ['No. Units - Acres'], ['$/Unit - Primary Land'], # [14 -> 18]
842
+ ['Secondary Land Use'], ['No. Units - Acres'], ['$/Unit - Secondary Land'], # [19 -> 21]
843
+ ['$/Unit Allocated to Improvements'], ['Physical Access'], ['Legal Access'], # [22 -> 24]
844
+ ['Topography'], ['Rainfal'], ['Primary Crop'], ['Yield'], ['Cap Rate'], [''], # [25 -> 30]
845
+ ['% Primary Land'], [''], ['Deed Instrument No.'] # [31 -> 33]
846
+ ]
847
+
848
+ props.each do |p|
849
+ final_a[2].push(p[:sequence]) # record number
850
+
851
+ taxes = p[:taxes]
852
+ if taxes && taxes.any?
853
+ t_s = taxes.inject("") {|memo_s, t| memo_s << "#{t[:tax_parcel_no]}, "; memo_s }
854
+ else
855
+ t_s = ""
856
+ end
857
+
858
+ final_a[3].push(t_s) # tax parcels
859
+ final_a[4].push("#{p[:property_address_number]} " << "#{p[:street_or_road_name]}") # address
860
+ final_a[5].push(p[:city]) # city
861
+ final_a[6].push("#{p[:county_state]}".slice(-2..-1)) # state
862
+ final_a[7].push(p[:zip_code]) # zip
863
+ final_a[8].push(datemaker(p[:sale_date])) # sale date
864
+ final_a[9].push(moneymaker(p[:sale_price],false)) # sale price
865
+ final_a[10].push(moneymaker(p[:sale_adjustment],false)) # conditions of sale
866
+ final_a[11].push(moneymaker(p[:cesaleprice],false)) # adjusted sale price
867
+ final_a[12].push(p[:property_rights]) # property rights conveyed
868
+ final_a[13].push(p[:unit]) # unit of comparison
869
+ final_a[14].push(moneymaker(p[:cesaleunitprice],false)) # price/unit
870
+ final_a[15].push(p[:num_of_units]) # total acres
871
+ final_a[16].push(p[:primary_ag_use]) # primary land use
872
+
873
+ if p[:landclassifications] && p[:landclassifications].any?
874
+ units_primary = p[:landclassifications][0][:numacres]
875
+ units_secondary = p[:landclassifications][1][:numacres]
876
+
877
+ primary_per_acre = moneymaker(p[:landclassifications][0][:priceperacre], false)
878
+ if p[:landclassifications][1]
879
+ secondary_per_acre = moneymaker(p[:landclassifications][1][:priceperacre], false)
880
+ else
881
+ secondary_per_acre = 0
882
+ end
883
+ else
884
+ units_primary = 0
885
+ units_secondary = 0
886
+ end
887
+
888
+ @recon_primary_per_acre << primary_per_acre.to_f unless primary_per_acre == 0
889
+
890
+ percent_primary = p[:num_of_units] ? ((units_primary * 100) / p[:num_of_units]) : 0
891
+
892
+ final_a[17].push(units_primary) # no. units
893
+ final_a[18].push(primary_per_acre) # $/unit - primary
894
+ final_a[19].push(p[:secondary_ag_use]) # secondary land use
895
+ final_a[20].push(units_secondary) # no. units - acres
896
+ final_a[21].push(secondary_per_acre) # $/unit - secondary
897
+
898
+
899
+ expenses_per_unit = p[:num_of_units] ? (Money.new(no_nil_number(p[:total_expenses]) * 100, 'USD') / p[:num_of_units]).format(no_cents: true) : 0
900
+
901
+ final_a[22].push(expenses_per_unit) # $/unit allocated to improvements
902
+ final_a[23].push("#{p[:has_physical_access]} " << "#{p[:physical_access]}") # physical access
903
+ final_a[24].push("#{p[:has_legal_access]} " << "#{p[:legal_access]}") # legal access
904
+ final_a[25].push(p[:topography]) # topography
905
+ final_a[26].push(p[:avg_precipitation]) # rainfall
906
+
907
+ if p[:estimatedproductivity] && p[:estimatedproductivity].any?
908
+ primary_commodity = "#{p[:estimatedproductivity][0][:commodity]}"
909
+ commodity_num_primary = no_nil_number(p[:estimatedproductivity][0][:estmtdyield])
910
+
911
+ else
912
+ commodity_num_primary = 0
913
+ primary_commodity = "None"
914
+ end
915
+
916
+ final_a[27].push(primary_commodity) # primary crop
917
+ final_a[28].push(commodity_num_primary) # yield
918
+ final_a[29].push("#{p[:overall_capitalization_rate]}%") # cap rate
919
+ # [30] is a blank line
920
+ final_a[31].push("#{percent_primary}%") # % primary land
921
+ # [32] is a blank line
922
+ final_a[33].push(p[:public_rec_ref_number]) # deed instrument no.
923
+ end
924
+
925
+ return final_a
926
+
927
+ end
928
+
929
+ # table methods for the property_features_method
930
+ def transaction_history
931
+ bounding_box([0, 700], height: 135, width: 540) do
932
+ transaction_history_data = [
933
+ [{content: "Transaction History", colspan: 4}],
934
+ ["Historic Transaction Type", "Transaction Description", "Date", "Sale Price, List Price, or List Price"]
935
+ ]
936
+
937
+ transactions = @sub[:historyrecords]
938
+
939
+ if transactions # node could be nil
940
+ transactions.each do |t|
941
+ if t # array could be nil
942
+ transaction_history_data.push(
943
+ [t[:transType], t[:transDescr], datemaker(t[:transDate]), moneymaker(t[:price], true)]
944
+ )
945
+ end
946
+ end
947
+ end
948
+
949
+ table(transaction_history_data) do
950
+ columns(0..3).width = 135
951
+ columns(0..3).style(align: :right, font_style: :bold)
952
+ cells.padding = [1, 2.5]
953
+ cells.style(border_width: 0)
954
+ row(0).style(align: :center, background_color: 'd7d7d7', font_size: 8, font_style: :bold)
955
+ row(1).style(border_bottom_width: 0.5, font_style: nil)
956
+ end
957
+ stroke_bounds
958
+ end
959
+ end
960
+
961
+ def utilities
962
+ bounding_box([0, 565], height: 120, width: 540) do
963
+ utilities_data = [
964
+ [{content: "Description of Utilities", colspan: 4}],
965
+ ["Utility Description", "Service Availability", "Service Provider", "Comments"]
966
+ ]
967
+
968
+ utilities = @sub[:utilities]
969
+ if utilities # node could be nil
970
+ utilities.each do |u|
971
+ if u # array could be empty
972
+ utilities_data.push(
973
+ [u[:description], u[:availbility], u[:provider], u[:comments]]
974
+ )
975
+ end
976
+ end
977
+ end
978
+
979
+ table(utilities_data) do
980
+ columns(0..3).width = 135
981
+ columns(0..3).style(align: :right, font_style: :bold)
982
+ cells.padding = [1, 2.5]
983
+ cells.style(border_width: 0)
984
+ row(0).style(align: :center, background_color: 'd7d7d7', font_size: 8, font_style: :bold)
985
+ row(1).style(border_bottom_width: 0.5, font_style: nil)
986
+ end
987
+ stroke_bounds
988
+ end
989
+ # no new page
990
+ end
991
+
992
+ def improvements
993
+ bounding_box([0, 445], height: 220, width: 540) do
994
+
995
+ improvements_array = @sub[:improvements]
996
+
997
+ sorted_array = flip_improvements_array(improvements_array)
998
+
999
+ table(sorted_array) do
1000
+ columns(0..7).width = 67.5
1001
+ columns(1..7).style(font_style: :bold)
1002
+ row(0).style(align: :center, background_color: 'd7d7d7', font_size: 8, font_style: :bold)
1003
+ cells.padding = [1, 2.5]
1004
+ cells.style(border_width: 0, font_size: 6)
1005
+ row(1).style(border_bottom_width: 0.5, font_style: nil)
1006
+ end
1007
+ stroke_bounds
1008
+ end
1009
+ # no new page
1010
+ end
1011
+
1012
+ def water_rights
1013
+ bounding_box([0, 225], height: 115, width: 540) do
1014
+
1015
+ water_rights_data = [
1016
+ [{content: "Water Rights", colspan: 8}],
1017
+ ["Water Right No.", "Water Right", "Water Source", "Priority Date", "Beneficial Use", "No. Acres Irrigated", "Annual Volume Ac-Ft", "Period of Use"]
1018
+ ]
1019
+
1020
+ rights = @sub[:waterrights]
1021
+ if rights # node could be nil
1022
+ rights.each do |r|
1023
+ if r # array could be empty
1024
+ water_rights_data.push(
1025
+ [r[:waterrightNum], r[:waterRight], r[:waterSrc], datemaker(r[:priorityDate]), r[:purpose], r[:numIrrAcres], r[:annVolume]]
1026
+ )
1027
+ end
1028
+ end
1029
+ end
1030
+
1031
+ table(water_rights_data) do
1032
+ columns(0..7).width = 67.5
1033
+ columns(0..7).style(font_style: :bold)
1034
+ row(0).style(align: :center, background_color: 'd7d7d7', font_size: 8, font_style: :bold)
1035
+ cells.padding = [1, 2.5]
1036
+ cells.style(border_width: 0)
1037
+ row(1).style(border_bottom_width: 0.5, font_style: nil)
1038
+ end
1039
+
1040
+ stroke_bounds
1041
+ end
1042
+ # no new page
1043
+ end
1044
+
1045
+ def water_distribution
1046
+ bounding_box([0, 110], height: 110, width: 540) do
1047
+
1048
+ distribution_data = [
1049
+ [{content: "Water Distribution System", colspan: 8}],
1050
+ ["Water Distrib. Equip.", "Manuf.", "Make/Brand", "Type", "Description", "Yr. Manufactured", "Remaining Ec. Life", "No. Acres Irrigated"]
1051
+ ]
1052
+
1053
+ distributions = @sub[:waterdistributions]
1054
+
1055
+ if distributions # node could be nil
1056
+ distributions.each do |d|
1057
+ if d # array could be empty
1058
+ distribution_data.push(
1059
+ [d[:waterdistrEq], d[:manufacturer], d[:brand], d[:eqType], d[:descr], d[:yearManuf], d[:remainingEcLife], d[:irrAcres]]
1060
+ )
1061
+ end
1062
+ end
1063
+ end
1064
+
1065
+ table(distribution_data) do
1066
+ columns(0..7).width = 67.5
1067
+ columns(0..7).style(font_style: :bold)
1068
+ row(0).style(align: :center, background_color: 'd7d7d7', font_size: 8, font_style: :bold)
1069
+ cells.padding = [1, 2.5]
1070
+ cells.style(border_width: 0)
1071
+ row(1).style(border_bottom_width: 0.5, font_style: nil)
1072
+ end
1073
+
1074
+ stroke_bounds
1075
+ end
1076
+ end
1077
+
1078
+ def crop_yield
1079
+ bounding_box([0, 700], height: 100, width: 540) do
1080
+
1081
+ crops = @sub[:crops]
1082
+
1083
+ crop_data = [
1084
+ [{content: "Crop Yield Summary", colspan: 7}],
1085
+ ["Crop Year", "Commodity Identified", "Unit of Measure", "Average Yield", " ", " ", ", "]
1086
+ ]
1087
+
1088
+ if crops # node could be nil
1089
+ crops.each do |c|
1090
+ if c # array could be empty
1091
+ crop_data.push(
1092
+ [c[:year], c[:commodity], c[:unit], c[:avgyield]]
1093
+ )
1094
+ end
1095
+ end
1096
+ end
1097
+
1098
+ table(crop_data) do
1099
+ column(0).width = 135
1100
+ columns(1..6).width = 67.5
1101
+ columns(0..6).style(font_style: :bold)
1102
+ row(0).style(align: :center, background_color: 'd7d7d7', font_size: 8, font_style: :bold)
1103
+ cells.padding = [1, 2.5]
1104
+ cells.style(border_width: 0)
1105
+ row(1).style(border_bottom_width: 0.5, font_style: nil)
1106
+ end
1107
+ stroke_bounds
1108
+ end
1109
+ end
1110
+
1111
+ def perm_plantings
1112
+ bounding_box([0, 600], height: 150, width: 540) do
1113
+ planting_data = [
1114
+ [{content: "Permanent Plantings", colspan: 8}],
1115
+ ["Planting", "Variety", "Ac. Type", "No. of Acres", "Average Age", "Plants/Acre", "Unit Description", "Average Yield"]
1116
+ ]
1117
+
1118
+ plantings = @sub[:plantings]
1119
+
1120
+ if plantings # node could be nil
1121
+ plantings.each do |p|
1122
+ if p # array could be empty
1123
+ planting_data.push(
1124
+ [p[:planting], p[:variety], p[:acres], p[:numacres], p[:avgage], p[:plantsacre], p[:unitdescr1], p[:avgproduction]]
1125
+ )
1126
+ end
1127
+ end
1128
+ end
1129
+
1130
+ table(planting_data) do
1131
+ columns(0..7).width = 67.5
1132
+ columns(0..7).style(font_style: :bold)
1133
+ row(0).style(align: :center, background_color: 'd7d7d7', font_size: 8, font_style: :bold)
1134
+ cells.padding = [1, 2.5]
1135
+ cells.style(border_width: 0)
1136
+ row(1).style(border_bottom_width: 0.5, font_style: nil)
1137
+ end
1138
+
1139
+ stroke_bounds
1140
+ end
1141
+ end
1142
+
1143
+ def externalities
1144
+ bounding_box([0, 450], height: 350, width: 540) do
1145
+
1146
+ extern_data = ReportUtils.conditional_externs_array(@sub) # instance method to populate dynamic array
1147
+ table(extern_data) do
1148
+ columns(0..3).width = 135
1149
+ columns([1, 3]).style(font_style: :bold)
1150
+ row(0).style(align: :center, background_color: 'd7d7d7', font_size: 8, font_style: :bold)
1151
+ cells.padding = [1, 2.5]
1152
+ cells.style(border_width: 0)
1153
+ end
1154
+
1155
+ stroke_bounds
1156
+ end
1157
+ end
1158
+
1159
+ def image_cap(title)
1160
+ float do
1161
+ fill_color '24478F'
1162
+ fill_rectangle [120, cursor], 300, 20
1163
+ fill_color '000000'
1164
+ end
1165
+ move_down 4
1166
+ text "#{title}", align: :center, color: 'ffffff'
1167
+ end
1168
+
1169
+ def indicated_value_per_unit
1170
+ # stubbed
1171
+ end
1172
+
1173
+ def indicated_primary_land_per_unit
1174
+ # stubbed
1175
+ end
1176
+
1177
+ def indicated_secondary_land_per_unit
1178
+ # stubbed
1179
+ end
1180
+
1181
+ def improvement_val_alloc
1182
+ # stubbed
1183
+ end
1184
+
1185
+ def overall_val
1186
+ # stubbed
1187
+ end
1188
+
1189
+ def say_value
1190
+ # stubbed
1191
+ end
1192
+ end