appraisermetrics_report_service 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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