im_onix 1.0.2 → 1.1.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.
- checksums.yaml +4 -4
- data/.gitignore +4 -0
- data/.yardopts +1 -0
- data/Gemfile +8 -0
- data/LICENSE.md +7 -0
- data/README.md +3 -3
- data/Rakefile +10 -0
- data/bin/html_codelist_to_yml.rb +14 -15
- data/bin/onix_bench.rb +1 -0
- data/bin/onix_pp.rb +4 -8
- data/bin/onix_serialize.rb +27 -0
- data/doc-src/handlers.rb +154 -0
- data/im_onix.gemspec +32 -0
- data/lib/im_onix.rb +0 -1
- data/lib/onix/addressee.rb +10 -0
- data/lib/onix/code.rb +108 -282
- data/lib/onix/collateral_detail.rb +24 -17
- data/lib/onix/collection.rb +38 -0
- data/lib/onix/collection_sequence.rb +7 -0
- data/lib/onix/contributor.rb +40 -39
- data/lib/onix/date.rb +73 -109
- data/lib/onix/descriptive_detail.rb +90 -417
- data/lib/onix/discount_coded.rb +3 -16
- data/lib/onix/entity.rb +28 -62
- data/lib/onix/epub_usage_constraint.rb +7 -0
- data/lib/onix/epub_usage_limit.rb +6 -0
- data/lib/onix/extent.rb +39 -0
- data/lib/onix/helper.rb +25 -25
- data/lib/onix/identifier.rb +13 -54
- data/lib/onix/language.rb +8 -0
- data/lib/onix/market.rb +5 -0
- data/lib/onix/market_publishing_detail.rb +20 -0
- data/lib/onix/onix21.rb +76 -139
- data/lib/onix/onix_message.rb +87 -100
- data/lib/onix/price.rb +19 -39
- data/lib/onix/product.rb +141 -637
- data/lib/onix/product_form_feature.rb +7 -0
- data/lib/onix/product_part.rb +89 -0
- data/lib/onix/product_supplies_extractor.rb +275 -0
- data/lib/onix/product_supply.rb +17 -58
- data/lib/onix/publishing_detail.rb +16 -32
- data/lib/onix/related_material.rb +4 -3
- data/lib/onix/related_product.rb +9 -29
- data/lib/onix/related_work.rb +3 -17
- data/lib/onix/sales_outlet.rb +2 -10
- data/lib/onix/sales_restriction.rb +8 -21
- data/lib/onix/sales_rights.rb +1 -5
- data/lib/onix/sender.rb +12 -0
- data/lib/onix/serializer.rb +156 -0
- data/lib/onix/subject.rb +9 -30
- data/lib/onix/subset.rb +88 -78
- data/lib/onix/supply_detail.rb +42 -0
- data/lib/onix/supporting_resource.rb +29 -86
- data/lib/onix/tax.rb +9 -18
- data/lib/onix/territory.rb +23 -17
- data/lib/onix/title_detail.rb +22 -0
- data/lib/onix/title_element.rb +32 -0
- data/lib/onix/website.rb +3 -16
- metadata +53 -34
data/lib/onix/onix_message.rb
CHANGED
@@ -1,54 +1,16 @@
|
|
1
1
|
require 'nokogiri'
|
2
|
-
require 'pp'
|
3
2
|
require 'time'
|
4
|
-
require 'benchmark'
|
5
3
|
|
6
4
|
require 'onix/subset'
|
7
|
-
|
8
5
|
require 'onix/helper'
|
9
6
|
require 'onix/code'
|
10
|
-
require 'onix/
|
11
|
-
require 'onix/
|
7
|
+
require 'onix/sender'
|
8
|
+
require 'onix/addressee'
|
12
9
|
require 'onix/product'
|
13
10
|
|
14
11
|
require 'onix/onix21'
|
15
12
|
|
16
13
|
module ONIX
|
17
|
-
|
18
|
-
class Sender < SubsetDSL
|
19
|
-
include GlnMethods
|
20
|
-
|
21
|
-
elements "SenderIdentifier", :subset
|
22
|
-
element "SenderName", :text
|
23
|
-
element "ContactName", :text
|
24
|
-
element "EmailAddress", :text
|
25
|
-
|
26
|
-
# shortcuts
|
27
|
-
def identifiers
|
28
|
-
@sender_identifiers
|
29
|
-
end
|
30
|
-
|
31
|
-
def name
|
32
|
-
@sender_name
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
class Addressee < SubsetDSL
|
37
|
-
include GlnMethods
|
38
|
-
|
39
|
-
elements "AddresseeIdentifier", :subset
|
40
|
-
element "AddresseeName", :text
|
41
|
-
|
42
|
-
# shortcuts
|
43
|
-
def identifiers
|
44
|
-
@addressee_identifiers
|
45
|
-
end
|
46
|
-
|
47
|
-
def name
|
48
|
-
@addressee_name
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
14
|
class ONIXMessage < Subset
|
53
15
|
attr_accessor :sender, :adressee, :sent_date_time,
|
54
16
|
:default_language_of_text, :default_currency_code,
|
@@ -56,8 +18,8 @@ module ONIX
|
|
56
18
|
:release
|
57
19
|
|
58
20
|
def initialize
|
59
|
-
@products=[]
|
60
|
-
@vault={}
|
21
|
+
@products = []
|
22
|
+
@vault = {}
|
61
23
|
end
|
62
24
|
|
63
25
|
def vault
|
@@ -71,8 +33,8 @@ module ONIX
|
|
71
33
|
# merge another message in this one
|
72
34
|
# current object erase other values
|
73
35
|
def merge!(other)
|
74
|
-
@products+=other.products
|
75
|
-
@products
|
36
|
+
@products += other.products
|
37
|
+
@products = @products.uniq { |p| p.ean }
|
76
38
|
init_vault
|
77
39
|
self
|
78
40
|
end
|
@@ -86,10 +48,10 @@ module ONIX
|
|
86
48
|
|
87
49
|
# initialize hash between ID and product object
|
88
50
|
def init_vault
|
89
|
-
@vault={}
|
51
|
+
@vault = {}
|
90
52
|
@products.each do |product|
|
91
53
|
product.identifiers.each do |ident|
|
92
|
-
@vault[ident.uniq_id]=product
|
54
|
+
@vault[ident.uniq_id] = product
|
93
55
|
end
|
94
56
|
end
|
95
57
|
|
@@ -97,7 +59,7 @@ module ONIX
|
|
97
59
|
product.related.each do |rel|
|
98
60
|
rel.identifiers.each do |ident|
|
99
61
|
if @vault[ident.uniq_id]
|
100
|
-
rel.product
|
62
|
+
rel.product = @vault[ident.uniq_id]
|
101
63
|
end
|
102
64
|
end
|
103
65
|
end
|
@@ -105,7 +67,7 @@ module ONIX
|
|
105
67
|
product.parts.each do |prt|
|
106
68
|
prt.identifiers.each do |ident|
|
107
69
|
if @vault[ident.uniq_id]
|
108
|
-
prt.product
|
70
|
+
prt.product = @vault[ident.uniq_id]
|
109
71
|
end
|
110
72
|
end
|
111
73
|
end
|
@@ -113,75 +75,100 @@ module ONIX
|
|
113
75
|
end
|
114
76
|
|
115
77
|
# open with arg detection
|
116
|
-
def open(arg, force_encoding=nil)
|
117
|
-
data=ONIX::Helper.arg_to_data(arg)
|
78
|
+
def open(arg, force_encoding = nil)
|
79
|
+
data = ONIX::Helper.arg_to_data(arg)
|
118
80
|
|
119
|
-
xml=nil
|
81
|
+
xml = nil
|
120
82
|
if force_encoding
|
121
|
-
xml=Nokogiri::XML.parse(data, nil, force_encoding)
|
83
|
+
xml = Nokogiri::XML.parse(data, nil, force_encoding)
|
122
84
|
else
|
123
|
-
xml=Nokogiri::XML.parse(data)
|
85
|
+
xml = Nokogiri::XML.parse(data)
|
124
86
|
end
|
125
87
|
|
126
88
|
xml.remove_namespaces!
|
127
89
|
xml
|
128
90
|
end
|
129
91
|
|
130
|
-
#
|
131
|
-
def
|
92
|
+
# release as an integer eg: 210, 300, 301
|
93
|
+
def version
|
94
|
+
if @release
|
95
|
+
@release.gsub(/\./, "").to_i * 10 ** (3 - @release.scan(".").length - 1)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# detect ONIX version from XML tags
|
100
|
+
def detect_release(element)
|
101
|
+
if element
|
102
|
+
return "3.0" if element.search("//DescriptiveDetail").length > 0
|
103
|
+
return "3.0" if element.search("//CollateralDetail").length > 0
|
104
|
+
return "3.0" if element.search("//ContentDetail").length > 0
|
105
|
+
return "3.0" if element.search("//PublishingDetail").length > 0
|
106
|
+
end
|
107
|
+
"2.1"
|
108
|
+
end
|
132
109
|
|
133
|
-
|
134
|
-
@
|
110
|
+
def set_release_from_xml(node, force_release)
|
111
|
+
@release = node["release"]
|
112
|
+
unless @release
|
113
|
+
@release = detect_release(node)
|
114
|
+
end
|
115
|
+
if force_release
|
116
|
+
@release = force_release.to_s
|
117
|
+
end
|
118
|
+
end
|
135
119
|
|
120
|
+
# parse filename or file
|
121
|
+
def parse(arg, force_encoding = nil, force_release = nil)
|
122
|
+
xml = open(arg, force_encoding)
|
123
|
+
@products = []
|
136
124
|
root = xml.root
|
125
|
+
set_release_from_xml(root, force_release)
|
137
126
|
case root
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
@default_currency_code=t.text
|
159
|
-
else
|
160
|
-
unsupported(t)
|
161
|
-
end
|
162
|
-
end
|
163
|
-
when tag_match("Product")
|
164
|
-
product=nil
|
165
|
-
if @release =~ /^3.0/
|
166
|
-
product=Product.parse(e)
|
167
|
-
else
|
168
|
-
product=ONIX21::Product.parse(e)
|
169
|
-
end
|
170
|
-
product.default_language_of_text=@default_language_of_text
|
171
|
-
product.default_currency_code=@default_currency_code
|
172
|
-
@products << product
|
127
|
+
when tag_match("ONIXMessage")
|
128
|
+
root.elements.each do |e|
|
129
|
+
case e
|
130
|
+
when tag_match("Header")
|
131
|
+
e.elements.each do |t|
|
132
|
+
case t
|
133
|
+
when tag_match("Sender")
|
134
|
+
@sender = Sender.parse(t)
|
135
|
+
when tag_match("Addressee")
|
136
|
+
@addressee = Addressee.parse(t)
|
137
|
+
when tag_match("SentDateTime")
|
138
|
+
tm = t.text
|
139
|
+
@sent_date_time = Time.strptime(tm, "%Y%m%dT%H%M%S") rescue Time.strptime(tm, "%Y%m%dT%H%M") rescue Time.strptime(tm, "%Y%m%d") rescue nil
|
140
|
+
when tag_match("DefaultLanguageOfText")
|
141
|
+
@default_language_of_text = LanguageCode.parse(t)
|
142
|
+
when tag_match("DefaultCurrencyCode")
|
143
|
+
@default_currency_code = t.text
|
144
|
+
else
|
145
|
+
unsupported(t)
|
146
|
+
end
|
173
147
|
end
|
148
|
+
when tag_match("Product")
|
149
|
+
product = nil
|
150
|
+
if self.version >= 300
|
151
|
+
product = Product.parse(e)
|
152
|
+
else
|
153
|
+
product = ONIX21::Product.parse(e)
|
154
|
+
end
|
155
|
+
product.default_language_of_text = @default_language_of_text
|
156
|
+
product.default_currency_code = @default_currency_code
|
157
|
+
@products << product
|
174
158
|
end
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
product.
|
180
|
-
|
159
|
+
end
|
160
|
+
when tag_match("Product")
|
161
|
+
product = nil
|
162
|
+
if self.version >= 300
|
163
|
+
product = Product.parse(root)
|
164
|
+
else
|
165
|
+
product = ONIX21::Product.parse(root)
|
166
|
+
end
|
167
|
+
product.default_language_of_text = @default_language_of_text
|
168
|
+
product.default_currency_code = @default_currency_code
|
169
|
+
@products << product
|
181
170
|
end
|
182
|
-
|
183
171
|
init_vault
|
184
172
|
end
|
185
173
|
end
|
186
|
-
|
187
174
|
end
|
data/lib/onix/price.rb
CHANGED
@@ -4,71 +4,51 @@ require 'onix/date'
|
|
4
4
|
|
5
5
|
module ONIX
|
6
6
|
class Price < SubsetDSL
|
7
|
-
element "PriceType", :subset
|
8
|
-
element "PriceQualifier", :subset
|
9
|
-
element "DiscountCoded", :subset
|
7
|
+
element "PriceType", :subset, :shortcut => :type
|
8
|
+
element "PriceQualifier", :subset, :shortcut => :qualifier
|
9
|
+
element "DiscountCoded", :subset, :shortcut => :discount
|
10
10
|
element "PriceStatus", :subset
|
11
|
-
elements "PriceDate", :subset
|
11
|
+
elements "PriceDate", :subset, :shortcut => :dates
|
12
12
|
element "PriceAmount", :float,
|
13
13
|
{
|
14
|
-
:
|
15
|
-
:
|
14
|
+
:shortcut => :amount,
|
15
|
+
:parse_lambda => lambda { |v| (v * 100).round },
|
16
|
+
:serialize_lambda => lambda { |v| v / 100.0 }
|
16
17
|
}
|
17
18
|
element "Tax", :subset
|
18
|
-
element "CurrencyCode", :text
|
19
|
+
element "CurrencyCode", :text, :shortcut => :currency
|
19
20
|
element "Territory", :subset
|
20
21
|
|
21
|
-
#
|
22
|
-
def dates
|
23
|
-
@price_dates
|
24
|
-
end
|
25
|
-
|
26
|
-
def amount
|
27
|
-
@price_amount
|
28
|
-
end
|
29
|
-
|
30
|
-
def type
|
31
|
-
@price_type
|
32
|
-
end
|
33
|
-
|
34
|
-
def currency
|
35
|
-
@currency_code
|
36
|
-
end
|
37
|
-
|
38
|
-
def qualifier
|
39
|
-
@price_qualifier
|
40
|
-
end
|
41
|
-
|
42
|
-
def discount
|
43
|
-
@discount_coded
|
44
|
-
end
|
22
|
+
# @!group High level
|
45
23
|
|
24
|
+
# price from date
|
25
|
+
# @return [Date]
|
46
26
|
def from_date
|
47
|
-
dt
|
27
|
+
dt = @price_dates.from_date.first
|
48
28
|
if dt
|
49
29
|
dt.date
|
50
|
-
else
|
51
|
-
nil
|
52
30
|
end
|
53
31
|
end
|
54
32
|
|
33
|
+
# price until date
|
34
|
+
# @return [Date]
|
55
35
|
def until_date
|
56
|
-
dt
|
36
|
+
dt = @price_dates.until_date.first
|
57
37
|
if dt
|
58
38
|
dt.date
|
59
|
-
else
|
60
|
-
nil
|
61
39
|
end
|
62
40
|
end
|
63
41
|
|
42
|
+
# does the price include taxes ?
|
43
|
+
# @return [Boolean]
|
64
44
|
def including_tax?
|
65
|
-
if self.type.human
|
45
|
+
if self.type.human =~ /IncludingTax/
|
66
46
|
true
|
67
47
|
else
|
68
48
|
false
|
69
49
|
end
|
70
50
|
end
|
71
51
|
|
52
|
+
# @!endgroup
|
72
53
|
end
|
73
|
-
|
74
54
|
end
|
data/lib/onix/product.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'forwardable'
|
1
2
|
require 'onix/helper'
|
2
3
|
require 'onix/code'
|
3
4
|
require 'onix/entity'
|
@@ -6,676 +7,205 @@ require 'onix/publishing_detail'
|
|
6
7
|
require 'onix/collateral_detail'
|
7
8
|
require 'onix/related_material'
|
8
9
|
require 'onix/supporting_resource'
|
9
|
-
require 'onix/subject'
|
10
|
-
require 'onix/contributor'
|
11
10
|
require 'onix/product_supply'
|
12
11
|
require 'onix/territory'
|
13
12
|
require 'onix/error'
|
13
|
+
require 'onix/product_supplies_extractor'
|
14
14
|
|
15
15
|
module ONIX
|
16
|
-
# flattened supplies extractor
|
17
|
-
module ProductSuppliesExtractor
|
18
|
-
# class must define a product_supplies returning an Array of objects responding to :
|
19
|
-
# - availability_date (Date)
|
20
|
-
# - countries (country code Array)
|
21
|
-
|
22
|
-
# :category: High level
|
23
|
-
# flattened supplies with prices
|
24
|
-
#
|
25
|
-
# supplies is a hash symbol array in the form :
|
26
|
-
# [{:available=>bool,
|
27
|
-
# :availability_date=>date,
|
28
|
-
# :including_tax=>bool,
|
29
|
-
# :currency=>string,
|
30
|
-
# :territory=>string,
|
31
|
-
# :suppliers=>[Supplier,...],
|
32
|
-
# :prices=>[{:amount=>int,
|
33
|
-
# :from_date=>date,
|
34
|
-
# :until_date=>date,
|
35
|
-
# :tax=>{:amount=>int, :rate_percent=>float}}]}]
|
36
|
-
def supplies(keep_all_prices_dates=false)
|
37
|
-
supplies=[]
|
38
|
-
|
39
|
-
# add territories if missing
|
40
|
-
if self.product_supplies
|
41
|
-
self.product_supplies.each do |ps|
|
42
|
-
ps.supply_details.each do |sd|
|
43
|
-
sd.prices.each do |p|
|
44
|
-
supply={}
|
45
|
-
supply[:suppliers]=sd.suppliers
|
46
|
-
supply[:available]=sd.available?
|
47
|
-
supply[:availability_date]=sd.availability_date
|
48
|
-
|
49
|
-
unless supply[:availability_date]
|
50
|
-
if ps.availability_date
|
51
|
-
supply[:availability_date]=ps.market_publishing_detail.availability_date
|
52
|
-
end
|
53
|
-
end
|
54
|
-
supply[:price]=p.amount
|
55
|
-
supply[:qualifier]=p.qualifier.human if p.qualifier
|
56
|
-
supply[:including_tax]=p.including_tax?
|
57
|
-
if !p.territory or p.territory.countries.length==0
|
58
|
-
supply[:territory]=[]
|
59
|
-
supply[:territory]=ps.countries
|
60
|
-
|
61
|
-
if supply[:territory].length==0
|
62
|
-
if @publishing_detail
|
63
|
-
supply[:territory]=self.countries_rights
|
64
|
-
end
|
65
|
-
end
|
66
|
-
else
|
67
|
-
supply[:territory]=p.territory.countries
|
68
|
-
end
|
69
|
-
supply[:from_date]=p.from_date
|
70
|
-
supply[:until_date]=p.until_date
|
71
|
-
supply[:currency]=p.currency
|
72
|
-
supply[:tax]=p.tax
|
73
|
-
|
74
|
-
unless supply[:availability_date]
|
75
|
-
if @publishing_detail
|
76
|
-
supply[:availability_date]=@publishing_detail.publication_date
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
supplies << supply
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
grouped_supplies={}
|
87
|
-
supplies.each do |supply|
|
88
|
-
pr_key="#{supply[:available]}_#{supply[:including_tax]}_#{supply[:currency]}_#{supply[:territory].join('_')}"
|
89
|
-
grouped_supplies[pr_key]||=[]
|
90
|
-
grouped_supplies[pr_key] << supply
|
91
|
-
end
|
92
|
-
|
93
|
-
nb_suppliers = supplies.map{|s| s[:suppliers][0].name}.uniq.length
|
94
|
-
# render prices sequentially with dates
|
95
|
-
grouped_supplies.each do |ksup, supply|
|
96
|
-
if supply.length > 1
|
97
|
-
global_price=supply.select{|p| not p[:from_date] and not p[:until_date]}
|
98
|
-
global_price=global_price.first
|
99
|
-
|
100
|
-
if global_price
|
101
|
-
if nb_suppliers > 1
|
102
|
-
grouped_supplies[ksup] += self.prices_with_periods(supply, global_price)
|
103
|
-
else
|
104
|
-
grouped_supplies[ksup] = self.prices_with_periods(supply, global_price)
|
105
|
-
end
|
106
|
-
grouped_supplies[ksup].uniq!
|
107
|
-
else
|
108
|
-
# remove explicit from date
|
109
|
-
explicit_from=supply.select{|p| p[:from_date] and not supply.select{|sp| sp[:until_date] and sp[:until_date]<=p[:from_date]}.first}.first
|
110
|
-
if explicit_from
|
111
|
-
explicit_from[:from_date]=nil unless keep_all_prices_dates
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
|
116
|
-
else
|
117
|
-
supply.each do |s|
|
118
|
-
if s[:from_date] and s[:availability_date] and s[:from_date] >= s[:availability_date]
|
119
|
-
s[:availability_date]=s[:from_date]
|
120
|
-
end
|
121
|
-
s[:from_date]=nil unless keep_all_prices_dates
|
122
|
-
|
123
|
-
end
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
# merge by territories
|
128
|
-
grouped_territories_supplies={}
|
129
|
-
grouped_supplies.each do |ksup,supply|
|
130
|
-
fsupply=supply.first
|
131
|
-
pr_key="#{fsupply[:available]}_#{fsupply[:including_tax]}_#{fsupply[:currency]}"
|
132
|
-
supply.each do |s|
|
133
|
-
pr_key+="_#{s[:price]}_#{s[:from_date]}_#{s[:until_date]}"
|
134
|
-
end
|
135
|
-
grouped_territories_supplies[pr_key]||=[]
|
136
|
-
grouped_territories_supplies[pr_key] << supply
|
137
|
-
end
|
138
|
-
|
139
|
-
supplies=[]
|
140
|
-
|
141
|
-
grouped_territories_supplies.each do |ksup,supply|
|
142
|
-
fsupply=supply.first.first
|
143
|
-
supplies << {:including_tax=>fsupply[:including_tax],:currency=>fsupply[:currency],
|
144
|
-
:territory=>supply.map{|fs| fs.map{|s| s[:territory]}}.flatten.uniq,
|
145
|
-
:available=>fsupply[:available],
|
146
|
-
:availability_date=>fsupply[:availability_date],
|
147
|
-
:suppliers=>fsupply[:suppliers],
|
148
|
-
:prices=>supply.first.map{|s|
|
149
|
-
|
150
|
-
s[:amount]=s[:price]
|
151
|
-
s.delete(:price)
|
152
|
-
s.delete(:available)
|
153
|
-
s.delete(:currency)
|
154
|
-
s.delete(:availability_date)
|
155
|
-
s.delete(:including_tax)
|
156
|
-
s.delete(:territory)
|
157
|
-
s
|
158
|
-
}}
|
159
|
-
end
|
160
|
-
|
161
|
-
supplies
|
162
|
-
end
|
163
|
-
|
164
|
-
# add missing periods when they can be guessed
|
165
|
-
def prices_with_periods(supplies, global_supply)
|
166
|
-
complete_supplies = supplies.select{ |supply| supply[:from_date] && supply[:until_date] }.sort_by { |supply| supply[:from_date] }
|
167
|
-
missing_start_period_supplies = supplies.select{ |supply| supply[:from_date] && !supply[:until_date] }.sort_by { |supply| supply[:from_date] }
|
168
|
-
missing_end_period_supplies = supplies.select{ |supply| !supply[:from_date] && supply[:until_date] }.sort_by { |supply| supply[:until_date] }
|
169
|
-
|
170
|
-
return [global_supply] if [complete_supplies, missing_start_period_supplies, missing_end_period_supplies].all? {|supply| supply.empty? }
|
171
|
-
|
172
|
-
return self.add_missing_periods(complete_supplies, global_supply) unless complete_supplies.empty?
|
173
|
-
|
174
|
-
without_start = missing_start_period_supplies.length == 1 && complete_supplies.empty? && missing_end_period_supplies.empty?
|
175
|
-
without_end = missing_end_period_supplies.length == 1 && complete_supplies.empty? && missing_start_period_supplies.empty?
|
176
|
-
|
177
|
-
return self.add_starting_period(missing_start_period_supplies.first, global_supply) if without_start
|
178
|
-
return self.add_ending_period(missing_end_period_supplies.first, global_supply) if without_end
|
179
|
-
|
180
|
-
[global_supply]
|
181
|
-
end
|
182
|
-
|
183
|
-
def add_missing_periods(supplies, global_supply)
|
184
|
-
new_supplies = []
|
185
|
-
|
186
|
-
supplies.each.with_index do |supply, index|
|
187
|
-
new_supplies << global_supply.dup.tap{ |start_sup| start_sup[:until_date] = supply[:from_date] - 1 } if index == 0
|
188
|
-
|
189
|
-
if index > 0 && index != supplies.length
|
190
|
-
new_supplies << global_supply.dup.tap do |missing_supply|
|
191
|
-
missing_supply[:from_date] = supplies[index - 1][:until_date] + 1
|
192
|
-
missing_supply[:until_date] = supply[:from_date] - 1
|
193
|
-
end
|
194
|
-
end
|
195
|
-
|
196
|
-
new_supplies << supply
|
197
|
-
|
198
|
-
new_supplies << global_supply.dup.tap{ |end_sup| end_sup[:from_date] = supply[:until_date] + 1 } if index == supplies.length - 1
|
199
|
-
end
|
200
|
-
|
201
|
-
new_supplies
|
202
|
-
end
|
203
|
-
|
204
|
-
def add_starting_period(supply, global_supply)
|
205
|
-
missing_supply = global_supply.dup
|
206
|
-
missing_supply[:until_date] = supply[:from_date] - 1
|
207
|
-
|
208
|
-
[missing_supply, supply]
|
209
|
-
end
|
210
|
-
|
211
|
-
def add_ending_period(supply, global_supply)
|
212
|
-
missing_supply = global_supply.dup
|
213
|
-
missing_supply[:from_date] = supply[:until_date] + 1
|
214
|
-
|
215
|
-
[supply, missing_supply]
|
216
|
-
end
|
217
|
-
|
218
|
-
# :category: High level
|
219
|
-
# flattened supplies only including taxes
|
220
|
-
def supplies_including_tax
|
221
|
-
self.supplies.select{|p| p[:including_tax]}
|
222
|
-
end
|
223
|
-
|
224
|
-
# :category: High level
|
225
|
-
# flattened supplies only excluding taxes
|
226
|
-
def supplies_excluding_tax
|
227
|
-
self.supplies.select{|p| not p[:including_tax]}
|
228
|
-
end
|
229
|
-
|
230
|
-
# :category: High level
|
231
|
-
# flattened supplies with default tax (excluding tax for US and CA, including otherwise)
|
232
|
-
def supplies_with_default_tax
|
233
|
-
self.supplies_including_tax + self.supplies_excluding_tax.select{|s| ["CAD","USD"].include?(s[:currency])}
|
234
|
-
end
|
235
|
-
|
236
|
-
# :category: High level
|
237
|
-
# flattened supplies for country
|
238
|
-
def supplies_for_country(country,currency=nil)
|
239
|
-
country_supplies=self.supplies
|
240
|
-
if currency
|
241
|
-
country_supplies=country_supplies.select{|s| s[:currency]==currency}
|
242
|
-
end
|
243
|
-
country_supplies.select{|s|
|
244
|
-
if s[:territory].include?(country)
|
245
|
-
true
|
246
|
-
else
|
247
|
-
false
|
248
|
-
end
|
249
|
-
}
|
250
|
-
end
|
251
|
-
|
252
|
-
# :category: High level
|
253
|
-
# price amount for given +currency+ and country at time
|
254
|
-
def at_time_price_amount_for(time,currency,country=nil)
|
255
|
-
sups=self.supplies_with_default_tax.select { |p| p[:currency]==currency }
|
256
|
-
if country
|
257
|
-
sups=sups.select{|p| p[:territory].include?(country)}
|
258
|
-
end
|
259
|
-
if sups.length > 0
|
260
|
-
# exclusive
|
261
|
-
sup=sups.first[:prices].select { |p|
|
262
|
-
(!p[:from_date] or p[:from_date].to_date <= time.to_date) and
|
263
|
-
(!p[:until_date] or p[:until_date].to_date > time.to_date)
|
264
|
-
}.first
|
265
|
-
|
266
|
-
if sup
|
267
|
-
sup[:amount]
|
268
|
-
else
|
269
|
-
# or inclusive
|
270
|
-
sup=sups.first[:prices].select { |p|
|
271
|
-
(!p[:from_date] or p[:from_date].to_date <= time.to_date) and
|
272
|
-
(!p[:until_date] or p[:until_date].to_date >= time.to_date)
|
273
|
-
}.first
|
274
|
-
|
275
|
-
if sup
|
276
|
-
sup[:amount]
|
277
|
-
else
|
278
|
-
nil
|
279
|
-
end
|
280
|
-
end
|
281
|
-
|
282
|
-
else
|
283
|
-
nil
|
284
|
-
end
|
285
|
-
end
|
286
|
-
|
287
|
-
# :category: High level
|
288
|
-
# current price amount for given +currency+ and country
|
289
|
-
def current_price_amount_for(currency,country=nil)
|
290
|
-
at_time_price_amount_for(Time.now,currency,country)
|
291
|
-
end
|
292
|
-
end
|
293
|
-
|
294
16
|
class Product < SubsetDSL
|
17
|
+
extend Forwardable
|
295
18
|
include EanMethods
|
296
19
|
include IsbnMethods
|
297
20
|
include ProprietaryIdMethods
|
21
|
+
include ProductSuppliesExtractor
|
298
22
|
|
299
23
|
element "RecordReference", :text
|
300
24
|
element "NotificationType", :subset
|
301
25
|
element "RecordSourceName", :text
|
302
|
-
elements "ProductIdentifier", :subset
|
26
|
+
elements "ProductIdentifier", :subset, :shortcut => :identifiers
|
303
27
|
element "DescriptiveDetail", :subset
|
304
28
|
element "CollateralDetail", :subset
|
305
29
|
element "PublishingDetail", :subset
|
306
30
|
element "RelatedMaterial", :subset
|
307
31
|
elements "ProductSupply", :subset
|
308
32
|
|
309
|
-
# shortcuts
|
310
|
-
def identifiers
|
311
|
-
@product_identifiers
|
312
|
-
end
|
313
|
-
|
314
33
|
# default LanguageCode from ONIXMessage
|
315
34
|
attr_accessor :default_language_of_text
|
316
35
|
# default code from ONIXMessage
|
317
36
|
attr_accessor :default_currency_code
|
318
37
|
|
319
|
-
|
320
|
-
|
321
|
-
# :category: High level
|
322
|
-
# product title string
|
323
|
-
def title
|
324
|
-
@descriptive_detail.title
|
325
|
-
end
|
326
|
-
|
327
|
-
# :category: High level
|
328
|
-
# product subtitle string
|
329
|
-
def subtitle
|
330
|
-
@descriptive_detail.subtitle
|
331
|
-
end
|
332
|
-
|
333
|
-
# :category: High level
|
334
|
-
# product description string including HTML
|
335
|
-
def description
|
336
|
-
if @collateral_detail
|
337
|
-
@collateral_detail.description
|
338
|
-
else
|
339
|
-
nil
|
340
|
-
end
|
341
|
-
end
|
38
|
+
# @!group Shortcuts
|
342
39
|
|
343
|
-
#
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
40
|
+
# @return (see PublishingDetail#publishers)
|
41
|
+
def publishers
|
42
|
+
@publishing_detail ? @publishing_detail.publishers : []
|
43
|
+
end
|
44
|
+
|
45
|
+
# !@endgroup
|
46
|
+
|
47
|
+
# @!group High level
|
48
|
+
|
49
|
+
def_delegator :descriptive_detail, :title
|
50
|
+
def_delegator :descriptive_detail, :subtitle
|
51
|
+
def_delegator :descriptive_detail, :edition_number
|
52
|
+
def_delegator :descriptive_detail, :publisher_collection_title
|
53
|
+
def_delegator :descriptive_detail, :bisac_categories # deprecated
|
54
|
+
def_delegator :descriptive_detail, :clil_categories # deprecated
|
55
|
+
def_delegator :descriptive_detail, :bisac_categories_codes
|
56
|
+
def_delegator :descriptive_detail, :clil_categories_codes
|
57
|
+
def_delegator :descriptive_detail, :keywords
|
58
|
+
def_delegator :descriptive_detail, :protection_type
|
59
|
+
def_delegator :descriptive_detail, :protections
|
60
|
+
def_delegator :descriptive_detail, :drmized?
|
61
|
+
def_delegator :descriptive_detail, :digital?
|
62
|
+
def_delegator :descriptive_detail, :audio?
|
63
|
+
def_delegator :descriptive_detail, :streaming?
|
64
|
+
def_delegator :descriptive_detail, :bundle?
|
65
|
+
def_delegator :descriptive_detail, :parts
|
66
|
+
def_delegator :descriptive_detail, :filesize
|
67
|
+
def_delegator :descriptive_detail, :audio_formats
|
68
|
+
def_delegator :descriptive_detail, :audio_format
|
69
|
+
def_delegator :descriptive_detail, :file_format
|
70
|
+
def_delegator :descriptive_detail, :file_mimetype
|
71
|
+
def_delegator :descriptive_detail, :file_description
|
72
|
+
def_delegator :descriptive_detail, :reflowable?
|
73
|
+
def_delegator :descriptive_detail, :pages
|
74
|
+
def_delegator :descriptive_detail, :subjects
|
75
|
+
def_delegator :descriptive_detail, :form_details
|
76
|
+
def_delegator :descriptive_detail, :contributors
|
77
|
+
|
78
|
+
def_delegator :collateral_detail, :description
|
79
|
+
def_delegator :collateral_detail, :frontcover_url
|
80
|
+
def_delegator :collateral_detail, :frontcover_last_updated
|
81
|
+
def_delegator :collateral_detail, :frontcover_mimetype
|
82
|
+
def_delegator :collateral_detail, :epub_sample_url
|
83
|
+
def_delegator :collateral_detail, :epub_sample_last_updated
|
84
|
+
def_delegator :collateral_detail, :epub_sample_mimetype
|
85
|
+
|
86
|
+
def_delegator :publishing_detail, :publication_date
|
87
|
+
def_delegator :publishing_detail, :embargo_date
|
88
|
+
def_delegator :publishing_detail, :preorder_embargo_date
|
89
|
+
def_delegator :publishing_detail, :public_announcement_date
|
90
|
+
def_delegator :publishing_detail, :sales_restriction
|
91
|
+
def_delegator :publishing_detail, :imprint
|
92
|
+
def_delegator :publishing_detail, :publisher
|
396
93
|
|
397
94
|
# product LanguageCode of text
|
398
95
|
def language_of_text
|
399
96
|
@descriptive_detail.language_of_text || @default_language_of_text
|
400
97
|
end
|
401
98
|
|
402
|
-
# :category: High level
|
403
99
|
# product language code string of text (eg: fre)
|
100
|
+
# @return [String]
|
404
101
|
def language_code_of_text
|
405
102
|
if self.language_of_text
|
406
103
|
self.language_of_text.code
|
407
104
|
end
|
408
105
|
end
|
409
106
|
|
410
|
-
# :category: High level
|
411
107
|
# product language name string of text (eg: French)
|
108
|
+
# @return [String]
|
412
109
|
def language_name_of_text
|
413
110
|
if self.language_of_text
|
414
111
|
self.language_of_text.human
|
415
112
|
end
|
416
113
|
end
|
417
114
|
|
418
|
-
#
|
419
|
-
#
|
420
|
-
def publisher_collection_title
|
421
|
-
@descriptive_detail.publisher_collection_title
|
422
|
-
end
|
423
|
-
|
424
|
-
def subjects
|
425
|
-
@descriptive_detail.subjects
|
426
|
-
end
|
427
|
-
|
428
|
-
# BISAC categories Subject
|
429
|
-
def bisac_categories
|
430
|
-
@descriptive_detail.bisac_categories
|
431
|
-
end
|
432
|
-
|
433
|
-
# :category: High level
|
434
|
-
# BISAC categories identifiers string array (eg: FIC000000)
|
435
|
-
def bisac_categories_codes
|
436
|
-
self.bisac_categories.map{|c| c.code}.uniq
|
437
|
-
end
|
438
|
-
|
439
|
-
# CLIL categories Subject
|
440
|
-
def clil_categories
|
441
|
-
@descriptive_detail.clil_categories
|
442
|
-
end
|
443
|
-
|
444
|
-
# :category: High level
|
445
|
-
# CLIL categories identifier string array
|
446
|
-
def clil_categories_codes
|
447
|
-
self.clil_categories.map{|c| c.code}.uniq
|
448
|
-
end
|
449
|
-
|
450
|
-
# :category: High level
|
451
|
-
# keywords string array
|
452
|
-
def keywords
|
453
|
-
@descriptive_detail.keywords
|
454
|
-
end
|
455
|
-
|
456
|
-
# :category: High level
|
457
|
-
# Protection type string (None, Watermarking, Drm, AdobeDrm)
|
458
|
-
def protection_type
|
459
|
-
@descriptive_detail.protection_type
|
460
|
-
end
|
461
|
-
|
462
|
-
# :category: High level
|
463
|
-
# List of protections type string (None, Watermarking, DRM, AdobeDRM)
|
464
|
-
def protections
|
465
|
-
@descriptive_detail.protections
|
466
|
-
end
|
467
|
-
|
468
|
-
# :category: High level
|
469
|
-
# product has DRM ?
|
470
|
-
def drmized?
|
471
|
-
if @protections.any? {|p| p =~ /Drm/ }
|
472
|
-
true
|
473
|
-
else
|
474
|
-
false
|
475
|
-
end
|
476
|
-
end
|
477
|
-
|
478
|
-
# :category: High level
|
479
|
-
# is product digital ?
|
480
|
-
def digital?
|
481
|
-
@descriptive_detail.digital?
|
482
|
-
end
|
483
|
-
|
484
|
-
# :category: High level
|
485
|
-
# is product audio ?
|
486
|
-
def audio?
|
487
|
-
@descriptive_detail.audio?
|
488
|
-
end
|
489
|
-
|
490
|
-
# :category: High level
|
491
|
-
# is product digital ?
|
492
|
-
def streaming?
|
493
|
-
@descriptive_detail.streaming?
|
494
|
-
end
|
495
|
-
|
496
|
-
# :category: High level
|
497
|
-
# is product a bundle of multiple parts ?
|
498
|
-
def bundle?
|
499
|
-
@descriptive_detail.bundle?
|
500
|
-
end
|
501
|
-
|
115
|
+
# product can be sold separately ?
|
116
|
+
# @return [Boolean]
|
502
117
|
def sold_separately?
|
503
|
-
@product_supplies.map{
|
118
|
+
@product_supplies.map { |product_supply|
|
119
|
+
product_supply.supply_details.map { |supply_detail| supply_detail.sold_separately? }.flatten
|
120
|
+
}.flatten.uniq.first
|
504
121
|
end
|
505
122
|
|
506
|
-
# :category: High level
|
507
|
-
# bundle ProductPart array
|
508
|
-
def parts
|
509
|
-
@descriptive_detail.parts
|
510
|
-
end
|
511
|
-
|
512
|
-
# :category: High level
|
513
|
-
# digital file filesize in bytes
|
514
|
-
def filesize
|
515
|
-
@descriptive_detail.filesize
|
516
|
-
end
|
517
|
-
|
518
|
-
# :category: High level
|
519
|
-
# audio formats array
|
520
|
-
def audio_formats
|
521
|
-
@descriptive_detail.audio_formats
|
522
|
-
end
|
523
|
-
|
524
|
-
# :category: High level
|
525
|
-
# audio format string ()
|
526
|
-
def audio_format
|
527
|
-
@descriptive_detail.audio_format
|
528
|
-
end
|
529
|
-
|
530
|
-
# :category: High level
|
531
|
-
# digital file format string (Epub,Pdf,Mobipocket)
|
532
|
-
def file_format
|
533
|
-
@descriptive_detail.file_format
|
534
|
-
end
|
535
|
-
|
536
|
-
def form_details
|
537
|
-
@descriptive_detail.form_details
|
538
|
-
end
|
539
|
-
|
540
|
-
def reflowable?
|
541
|
-
@descriptive_detail.reflowable?
|
542
|
-
end
|
543
|
-
|
544
|
-
# :category: High level
|
545
|
-
# digital file mimetype (Epub,Pdf,Mobipocket)
|
546
|
-
def file_mimetype
|
547
|
-
@descriptive_detail.file_mimetype
|
548
|
-
end
|
549
|
-
|
550
|
-
# :category: High level
|
551
|
-
# digital file description string
|
552
|
-
def file_description
|
553
|
-
@descriptive_detail.file_description
|
554
|
-
end
|
555
|
-
|
556
|
-
# :category: High level
|
557
123
|
# raw file description string without HTML
|
124
|
+
# @return [String]
|
558
125
|
def raw_file_description
|
559
126
|
if @descriptive_detail.file_description
|
560
|
-
Helper.strip_html(@descriptive_detail.file_description).gsub(/\s+/," ").strip
|
561
|
-
else
|
562
|
-
nil
|
127
|
+
Helper.strip_html(@descriptive_detail.file_description).gsub(/\s+/, " ").strip
|
563
128
|
end
|
564
129
|
end
|
565
130
|
|
566
|
-
# :category: High level
|
567
|
-
# page count
|
568
|
-
def pages
|
569
|
-
@descriptive_detail.pages
|
570
|
-
end
|
571
|
-
|
572
|
-
# :category: High level
|
573
131
|
# raw book description string without HTML
|
132
|
+
# @return [String]
|
574
133
|
def raw_description
|
575
134
|
if self.description
|
576
|
-
Helper.strip_html(self.description).gsub(/\s+/," ").strip
|
577
|
-
else
|
578
|
-
nil
|
579
|
-
end
|
580
|
-
end
|
581
|
-
|
582
|
-
def publishers
|
583
|
-
if @publishing_detail
|
584
|
-
@publishing_detail.publishers
|
585
|
-
else
|
586
|
-
[]
|
135
|
+
Helper.strip_html(self.description).gsub(/\s+/, " ").strip
|
587
136
|
end
|
588
137
|
end
|
589
138
|
|
590
|
-
def publisher
|
591
|
-
if @publishing_detail
|
592
|
-
@publishing_detail.publisher
|
593
|
-
else
|
594
|
-
nil
|
595
|
-
end
|
596
|
-
end
|
597
|
-
|
598
|
-
# :category: High level
|
599
139
|
# publisher name string, if multiple publishers are found, then they are concatenated with " / "
|
140
|
+
# @return [String]
|
600
141
|
def publisher_name
|
601
142
|
if self.publishers.length > 0
|
602
|
-
self.publishers.map{|p| p.name}.join(" / ")
|
143
|
+
self.publishers.map { |p| p.name }.join(" / ")
|
603
144
|
end
|
604
145
|
end
|
605
146
|
|
606
|
-
# :category: High level
|
607
147
|
# publisher GLN string, nil if multiple publishers are found
|
148
|
+
# @return [String]
|
608
149
|
def publisher_gln
|
609
|
-
if self.publishers.length==1
|
150
|
+
if self.publishers.length == 1
|
610
151
|
self.publisher.gln
|
611
152
|
end
|
612
153
|
end
|
613
154
|
|
614
|
-
def imprint
|
615
|
-
if @publishing_detail
|
616
|
-
@publishing_detail.imprint
|
617
|
-
else
|
618
|
-
nil
|
619
|
-
end
|
620
|
-
end
|
621
|
-
|
622
|
-
# :category: High level
|
623
155
|
# imprint name string
|
156
|
+
# @return [String]
|
624
157
|
def imprint_name
|
625
158
|
if self.imprint
|
626
159
|
self.imprint.name
|
627
|
-
else
|
628
|
-
nil
|
629
160
|
end
|
630
161
|
end
|
631
162
|
|
632
|
-
# :category: High level
|
633
163
|
# imprint GLN string
|
164
|
+
# @return [String]
|
634
165
|
def imprint_gln
|
635
166
|
if self.imprint
|
636
167
|
self.imprint.gln
|
637
168
|
end
|
638
169
|
end
|
639
170
|
|
171
|
+
# product distributors names
|
172
|
+
# @return [Array<String>]
|
640
173
|
def distributors
|
641
|
-
@product_supplies.map{|ps| ps.distributors}.flatten.uniq{|d| d.name}
|
174
|
+
@product_supplies.map { |ps| ps.distributors }.flatten.uniq { |d| d.name }
|
642
175
|
end
|
643
176
|
|
644
|
-
# product distributor
|
177
|
+
# product distributor name
|
178
|
+
# @return [String]
|
645
179
|
def distributor
|
646
180
|
if self.distributors.length > 0
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
181
|
+
if self.distributors.length == 1
|
182
|
+
self.distributors.first
|
183
|
+
else
|
184
|
+
raise ExpectsOneButHasSeveral, self.distributors.map(&:name)
|
185
|
+
end
|
652
186
|
else
|
653
187
|
nil
|
654
188
|
end
|
655
189
|
end
|
656
190
|
|
657
|
-
# :category: High level
|
658
191
|
# product distributor name string
|
192
|
+
# @return [String]
|
659
193
|
def distributor_name
|
660
194
|
if self.distributor
|
661
195
|
self.distributor.name
|
662
|
-
else
|
663
|
-
nil
|
664
196
|
end
|
665
197
|
end
|
666
198
|
|
667
|
-
# :category: High level
|
668
199
|
# product distributor GLN string
|
200
|
+
# @return [String]
|
669
201
|
def distributor_gln
|
670
202
|
if self.distributor
|
671
203
|
self.distributor.gln
|
672
|
-
else
|
673
|
-
nil
|
674
204
|
end
|
675
205
|
end
|
676
206
|
|
677
|
-
# :category: High level
|
678
207
|
# return every related subset
|
208
|
+
# @return [Array]
|
679
209
|
def related
|
680
210
|
if @related_material
|
681
211
|
(@related_material.related_products + @related_material.related_works)
|
@@ -684,16 +214,16 @@ module ONIX
|
|
684
214
|
end
|
685
215
|
end
|
686
216
|
|
687
|
-
#
|
688
|
-
#
|
217
|
+
# first part RelatedProduct
|
218
|
+
# @return [RelatedProduct]
|
689
219
|
def part_of_product
|
690
220
|
if @related_material
|
691
221
|
@related_material.part_of_products.first
|
692
222
|
end
|
693
223
|
end
|
694
224
|
|
695
|
-
|
696
|
-
#
|
225
|
+
# first paper linking RelatedProduct
|
226
|
+
# @return [RelatedProduct]
|
697
227
|
def print_product
|
698
228
|
if @related_material
|
699
229
|
@related_material.print_products.first
|
@@ -701,146 +231,120 @@ module ONIX
|
|
701
231
|
end
|
702
232
|
|
703
233
|
# DEPRECATED see print_product instead
|
234
|
+
# @return [RelatedProduct]
|
704
235
|
def paper_linking
|
705
236
|
self.print_product
|
706
237
|
end
|
707
238
|
|
708
|
-
# :category: High level
|
709
|
-
# date of publication
|
710
|
-
def publication_date
|
711
|
-
if @publishing_detail
|
712
|
-
@publishing_detail.publication_date
|
713
|
-
end
|
714
|
-
end
|
715
|
-
|
716
|
-
# date of embargo
|
717
|
-
def embargo_date
|
718
|
-
if @publishing_detail
|
719
|
-
@publishing_detail.embargo_date
|
720
|
-
end
|
721
|
-
end
|
722
|
-
|
723
|
-
def preorder_embargo_date
|
724
|
-
if @publishing_detail
|
725
|
-
@publishing_detail.preorder_embargo_date
|
726
|
-
end
|
727
|
-
end
|
728
|
-
|
729
|
-
def public_announcement_date
|
730
|
-
if @publishing_detail
|
731
|
-
@publishing_detail.public_announcement_date
|
732
|
-
end
|
733
|
-
end
|
734
|
-
|
735
|
-
def sales_restriction
|
736
|
-
if @publishing_detail
|
737
|
-
@publishing_detail.sales_restriction
|
738
|
-
end
|
739
|
-
end
|
740
|
-
|
741
|
-
# :category: High level
|
742
239
|
# product countries rights string array
|
240
|
+
# @return [Array<String>]
|
743
241
|
def countries_rights
|
744
|
-
countries=[]
|
242
|
+
countries = []
|
745
243
|
if @publishing_detail
|
746
|
-
countries
|
244
|
+
countries += @publishing_detail.sales_rights.map { |sale_right| sale_right.territory.countries }.flatten.uniq
|
747
245
|
end
|
748
246
|
|
749
247
|
if @product_supplies
|
750
|
-
countries
|
248
|
+
countries += @product_supplies.map { |product_supply|
|
249
|
+
product_supply.markets.map { |market| market.territory.countries }.flatten
|
250
|
+
}.flatten.uniq
|
751
251
|
end
|
752
252
|
|
753
253
|
countries.uniq
|
754
254
|
end
|
755
255
|
|
756
|
-
#
|
256
|
+
# all images
|
757
257
|
def illustrations
|
758
258
|
return [] unless @collateral_detail && @collateral_detail.supporting_resources
|
759
259
|
|
760
260
|
@collateral_detail.supporting_resources.image.map do |image_resource|
|
761
261
|
{
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
|
262
|
+
:url => image_resource.versions.last.links.first.strip,
|
263
|
+
:type => image_resource.type.human,
|
264
|
+
:width => image_resource.versions.last.image_width,
|
265
|
+
:height => image_resource.versions.last.image_height,
|
266
|
+
:caption => image_resource.caption,
|
267
|
+
:format_code => image_resource.versions.last.file_format,
|
268
|
+
:updated_at => image_resource.versions.last.last_updated_utc
|
769
269
|
}
|
770
270
|
end
|
771
271
|
end
|
772
272
|
|
773
|
-
#
|
273
|
+
# all excerpts
|
774
274
|
def excerpts
|
775
275
|
return [] unless @collateral_detail && @collateral_detail.supporting_resources
|
776
276
|
|
777
|
-
@collateral_detail.supporting_resources.sample_content.human_code_match(:resource_mode,["Text","Multimode"]).map do |resource|
|
277
|
+
@collateral_detail.supporting_resources.sample_content.human_code_match(:resource_mode, ["Text", "Multimode"]).map do |resource|
|
778
278
|
{
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
|
279
|
+
:url => resource.versions.last.links.first.strip,
|
280
|
+
:form => resource.versions.last.form.human,
|
281
|
+
:md5 => resource.versions.last.md5_hash,
|
282
|
+
:format_code => resource.versions.last.file_format,
|
283
|
+
:updated_at => resource.versions.last.last_updated_utc
|
784
284
|
}
|
785
285
|
end
|
786
286
|
end
|
787
287
|
|
288
|
+
# available product supplies
|
289
|
+
# @return [Array<ProductSupply>]
|
788
290
|
def available_product_supplies
|
789
|
-
@product_supplies.select{|
|
291
|
+
@product_supplies.select { |product_supply| product_supply.available? }
|
790
292
|
end
|
791
293
|
|
792
|
-
# :category: High level
|
793
294
|
# is product available ?
|
295
|
+
# @return [Boolean]
|
794
296
|
def available?
|
795
297
|
self.available_product_supplies.length > 0 and not self.delete?
|
796
298
|
end
|
797
299
|
|
798
|
-
# :category: High level
|
799
300
|
# is product available for given +country+ ?
|
301
|
+
# @return [Boolean]
|
800
302
|
def available_for_country?(country)
|
801
|
-
self.supplies_for_country(country).select{|s| s[:available]}.length > 0 and self.available?
|
303
|
+
self.supplies_for_country(country).select { |s| s[:available] }.length > 0 and self.available?
|
802
304
|
end
|
803
305
|
|
804
|
-
# :category: High level
|
805
306
|
# is product price to be announced ?
|
307
|
+
# @return [Boolean]
|
806
308
|
def price_to_be_announced?
|
807
309
|
unless self.product_supplies.empty? || self.product_supplies.first.supply_details.empty?
|
808
310
|
unpriced_item_type = self.product_supplies.first.supply_details.first.unpriced_item_type
|
809
311
|
end
|
810
|
-
unpriced_item_type ? unpriced_item_type.human=="PriceToBeAnnounced" : false
|
312
|
+
unpriced_item_type ? unpriced_item_type.human == "PriceToBeAnnounced" : false
|
811
313
|
end
|
812
314
|
|
813
|
-
#
|
814
|
-
#
|
315
|
+
# is product a deletion notification ?
|
316
|
+
# @return [Boolean]
|
815
317
|
def delete?
|
816
|
-
self.notification_type.human=="Delete"
|
817
|
-
end
|
818
|
-
|
819
|
-
# :category: High level
|
820
|
-
# Contributor array
|
821
|
-
def contributors
|
822
|
-
@descriptive_detail.contributors
|
318
|
+
self.notification_type.human == "Delete"
|
823
319
|
end
|
824
320
|
|
825
|
-
# :category: High level
|
826
321
|
# List of ONIX outlets values
|
322
|
+
# @return [Array<String>]
|
827
323
|
def onix_outlets_values
|
828
324
|
if @publishing_detail
|
829
|
-
@publishing_detail.sales_rights.map { |
|
830
|
-
|
831
|
-
|
832
|
-
|
325
|
+
@publishing_detail.sales_rights.map { |sales_right|
|
326
|
+
sales_right.sales_restrictions.select { |sales_restriction|
|
327
|
+
(!sales_restriction.start_date or sales_restriction.start_date <= Date.today) and
|
328
|
+
(!sales_restriction.end_date or sales_restriction.end_date >= Date.today)
|
329
|
+
}.map { |sale_right|
|
330
|
+
sale_right.sales_outlets.select { |sale_outlet|
|
331
|
+
sale_outlet.identifier and sale_outlet.identifier.type.human == "OnixSalesOutletIdCode" }.map { |sale_outlet|
|
332
|
+
sale_outlet.identifier.value
|
333
|
+
}
|
334
|
+
}
|
335
|
+
}.flatten
|
833
336
|
else
|
834
337
|
[]
|
835
338
|
end
|
836
339
|
end
|
837
340
|
|
341
|
+
# @!endgroup
|
342
|
+
|
838
343
|
def parse(n)
|
839
344
|
super
|
840
345
|
parts.each do |part|
|
841
|
-
part.part_of=self
|
346
|
+
part.part_of = self
|
842
347
|
end
|
843
348
|
end
|
844
|
-
|
845
349
|
end
|
846
350
|
end
|