elibri_onix_mocks 0.1.0

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,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
@@ -0,0 +1,10 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.8.7
4
+ - 1.9.2
5
+ - 1.9.3
6
+ - ree
7
+ notifications:
8
+ email:
9
+ - p.szmielew@ava.waw.pl
10
+ - tomek@gildia.pl
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in elibri_onix_mocks.gemspec
4
+ gemspec
@@ -0,0 +1,32 @@
1
+ [![Build Status](https://secure.travis-ci.org/elibri/elibri_onix_mocks.png?branch=master)](http://travis-ci.org/elibri/elibri_onix_mocks)
2
+
3
+ Gem created for Mocking eLibri xml objects.
4
+ More info coming soon.
5
+
6
+ Basic usage:
7
+ ``Elibri::XmlMocks::Example.basic_product``
8
+
9
+ methods to create mock objects:
10
+ `basic_product` `book_example` `onix_record_identifiers_example` `onix_product_form_example`
11
+ `onix_epub_details_example` `onix_categories_example` `onix_languages_example`
12
+ `onix_measurement_example` `onix_sale_restrictions_example` `onix_audience_range_example`
13
+ `onix_publisher_info_example` `onix_subjects_example` `onix_edition_example` `onix_ebook_extent_example`
14
+ `onix_audiobook_extent_example` `onix_no_contributors_example` `onix_collective_work_example`
15
+ `onix_contributors_example` `onix_announced_product_example` `onix_preorder_product_example`
16
+ `onix_published_product_example` `onix_out_of_print_product_example` `onix_titles_example`
17
+ `onix_title_with_collection_example` `onix_texts_example` `onix_related_products_example`
18
+ `onix_supply_details_example` `onix_series_memberships_example` `onix_supporting_resources_example`
19
+ `onix_elibri_extensions_example` `contributor_mock` `review_mock` `supply_detail_mock` `imprint_mock`
20
+ `description_mock`
21
+
22
+ Each method take arguments in a hash form, where key is the name of attribute in mock object. Value can be string, array, another mock object - depend on situation.
23
+
24
+ For list of important attributes please look into lib/mocks/xml_mocks.rb file.
25
+
26
+ If you want to create eLibri xml from mock:
27
+ ``Elibri::ONIX::XMLGenerator.new(mock_object).to_s``
28
+
29
+ Creating product from xml:
30
+ ``Elibri::ONIX::Release_3_0::ONIXMessage.from_xml(xml_string)``
31
+
32
+ Therefore you can use it to test your api handling function - you will receive same xml from mock, as you will receive from actual elibri api (with different data ofc :))
@@ -0,0 +1,8 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ desc "Running specs"
4
+ task :spec do |t|
5
+ exec "cd spec/ && bundle exec rspec elibri_onix_mocks_spec.rb"
6
+ end
7
+
8
+ task :default => ["spec"]
@@ -0,0 +1,32 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "elibri_onix_mocks/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "elibri_onix_mocks"
7
+ s.version = ElibriOnixMocks::VERSION
8
+ s.authors = ["Piotr Szmielew"]
9
+ s.email = ["p.szmielew@ava.waw.pl"]
10
+ s.homepage = ""
11
+ s.summary = %q{Gem that allows you to mock eLibri style xmls}
12
+ s.description = %q{Usage: Elibri::XmlGenerator.basic_product etc}
13
+
14
+ s.rubyforge_project = "elibri_onix_mocks"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ # specify any dependencies here; for example:
22
+ s.add_development_dependency "rspec"
23
+ # s.add_development_dependency 'ruby-debug'
24
+ s.add_development_dependency 'rake'
25
+
26
+ # s.add_runtime_dependency "rest-client"
27
+ s.add_runtime_dependency "elibri_onix_dict"
28
+ s.add_runtime_dependency 'elibri_api_client'
29
+ s.add_runtime_dependency "mocha"
30
+ s.add_runtime_dependency "builder"
31
+ s.add_runtime_dependency "activesupport"
32
+ end
@@ -0,0 +1,14 @@
1
+ require 'active_support'
2
+ require 'ostruct'
3
+ require 'elibri_onix_dict'
4
+ require 'elibri_api_client'
5
+ require 'elibri_onix_mocks/onix_helpers'
6
+ require "elibri_onix_mocks/version"
7
+ require 'elibri_onix_mocks/mocks/mock_method_missing'
8
+ require 'elibri_onix_mocks/mocks/xml_mocks'
9
+ require 'elibri_onix_mocks/generators/xml_generator'
10
+ require 'elibri_onix_mocks/xml_variant'
11
+
12
+ module Elibri
13
+
14
+ end
@@ -0,0 +1,995 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'builder'
4
+
5
+ module Elibri
6
+ module ONIX
7
+
8
+ class XMLGenerator
9
+
10
+
11
+ SHORT_TAGS = YAML::load_file(File.dirname(__FILE__) + "/xml_tags.yml")
12
+ DOC_ORDER = ["record_identifiers", "publishing_status", "product_form", "contributors", "titles", "series_memberships", "measurement",
13
+ "sale_restrictions", "audience_range", "publisher_info", "extent", "edition", "languages", "epub_details",
14
+ "texts", "supporting_resources", "elibri_extensions"]
15
+ #"subjects", "related_products", "supply_details"
16
+ attr_reader :builder, :tags_type
17
+
18
+
19
+ def self.tag(builder, short_tags, tag_id, *args, &block)
20
+ if short_tags
21
+ if SHORT_TAGS[tag_id]
22
+ tag_id = SHORT_TAGS[tag_id]
23
+ elsif tag_id.starts_with?("elibri")
24
+ tag_id
25
+ else
26
+ raise "Unknow short tag for: #{tag_id}"
27
+ end
28
+ end
29
+ builder.__send__(tag_id, *args, &block)
30
+ end
31
+
32
+ def self.render_header(builder, options = {}, &block)
33
+ options.reverse_merge!({:short_tags => false, :elibri_onix_dialect => '3.0.1'})
34
+ short_tags = options[:short_tags]
35
+
36
+ builder.instruct! :xml, :version=>"1.0", :encoding=>"UTF-8"
37
+ message_attributes = {:release => "3.0", :xmlns => "http://www.editeur.org/onix/3.0/reference", "xmlns:elibri" => "http://elibri.com.pl/ns/extensions"}
38
+ message_attributes.delete('xmlns:elibri') if options[:pure_onix]
39
+
40
+ builder.ONIXMessage message_attributes do
41
+ unless options[:pure_onix]
42
+ builder.elibri :Dialect, options[:elibri_onix_dialect] # potrzebne, aby parser wiedział jak interpretować niektóre tagi
43
+ end
44
+ tag(builder, short_tags, :Header) do
45
+ tag(builder, short_tags, :Sender) do
46
+ tag(builder, short_tags, :SenderName, "Elibri.com.pl")
47
+ tag(builder, short_tags, :ContactName, "Tomasz Meka")
48
+ tag(builder, short_tags, :EmailAddress, "kontakt@elibri.com.pl")
49
+ end
50
+ tag(builder, short_tags, :SentDateTime, Date.today.strftime("%Y%m%d"))
51
+ end
52
+ yield(builder) if block_given?
53
+ end
54
+ end
55
+
56
+
57
+ def initialize(products, options = {})
58
+ options.reverse_merge!({
59
+ :tags_type => :full,
60
+ :export_headers => true,
61
+ :elibri_onix_dialect => '3.0.1',
62
+ :comments => false,
63
+ :xml_variant => Elibri::XmlVariant::FULL_VARIANT
64
+ })
65
+
66
+ @xml_variant = options[:xml_variant]
67
+ raise ":xml_variant unspecified" if @xml_variant.blank? or !@xml_variant.kind_of?(::Elibri::XmlVariant)
68
+
69
+ @products = Array(products)
70
+ @tags_type = ActiveSupport::StringInquirer.new(options[:tags_type].to_s)
71
+ @comments = options[:comments]
72
+ @comment_kinds = options[:comment_kinds]
73
+ @out = []
74
+ @builder = Builder::XmlMarkup.new(:indent => 2, :target => @out)
75
+ @elibri_onix_dialect = options[:elibri_onix_dialect]
76
+ # Gdy true, ignorujemy rozszerzenia eLibri
77
+ @pure_onix = options[:pure_onix]
78
+
79
+ # W testach często nie chcę żadnych nagłówków - interesuje mnie tylko tag <Product>
80
+ if options[:export_headers]
81
+ ONIX::XMLGenerator.render_header(builder, :short_tags => tags_type.short?, :elibri_onix_dialect => @elibri_onix_dialect, :pure_onix => @pure_onix) do
82
+ render_products!
83
+ end
84
+ else
85
+ render_products!
86
+ end
87
+ end
88
+
89
+
90
+ def to_s
91
+ @out.join
92
+ end
93
+
94
+
95
+ # Zwróć dokumentację dla metod sekcji ONIX z bieżącego pliku. Dzięki temu dokumentacja ONIX jest generowana w locie
96
+ # i nie rozjedzie się z kodem. Dokumentacje można wygenerować tylko dla metod 'def export_XXX!'.
97
+ #
98
+ # Przykład dokumentowania:
99
+ # # @hidden_tags RecordReference NotificationType
100
+ # # @title Wymiary produktu
101
+ # # eLibri zachowuje tylko <strong>wysokość, szerokość, grubość oraz masę produktu.</strong> Dla produktów typu mapa, eksportujemy również jej skalę
102
+ # # w tagu &lt;MapScale&gt;
103
+ # def export_measurement!(product)
104
+ # [...]
105
+ # end
106
+ #
107
+ # @hidden_tags określa tagi ONIX, które należy zwinąć w celu zachowania czytelności XML`a.
108
+ # @title to tytuł sekcji a reszta to wieloliniowy opis.
109
+ #
110
+ # Dla każdej sekcji trzeba utworzyć odpowiednią metodę w Product::Examples, która zwróci stub produktu.
111
+ # Np. dla metody 'export_measurement!' w Product::Examples należy utworzyć metodę 'onix_measurement_example'.
112
+ def self.onix_sections_docs
113
+ # Wczytaj bieżący plik
114
+ code_lines = File.readlines(__FILE__)
115
+
116
+ section_docs = Array.new
117
+ # Dla każdej metody o sygnaturze 'def export_NAZWA_SEKCJI!' czytaj otaczające linie komentarzy:
118
+ ONIX::XMLGenerator.instance_methods.grep(/export_/).each_with_index do |method_name, section_idx|
119
+ section_docs[section_idx] ||= Hash.new
120
+ section_docs[section_idx][:section_name] = method_name[/export_(.*)!/, 1] # "export_supplier_identifier!" => "supplier_identifier"
121
+ section_docs[section_idx][:hidden_tags] = Array.new
122
+ section_docs[section_idx][:auto_render] = true
123
+
124
+ # Znajdź numer linii z definicją metody:
125
+ method_definition_line_idx = code_lines.find_index {|line| line.match(/^\s*def #{method_name}/)}
126
+
127
+ # Cofamy się w gorę kodu, aż do początku pliku w poszukiwaniu linii zawierającej tag @title w komentarzu.
128
+ (method_definition_line_idx-1).downto(0).each do |line_idx|
129
+ # Oczyść linię kodu ze znaku komentarza i zbędnych spacji:
130
+ line = code_lines[line_idx].strip.sub(/^\s*#\s*/, '#')
131
+ raise "Nieprawidłowy format dokumentacji dla metody #{method_name}" if line.match(/^end$/)
132
+ if md = line.match(/^\s*$/)
133
+ break
134
+ elsif md = line.match(/@render (.*)$/)
135
+ section_docs[section_idx][:auto_render] = false
136
+ render_call = "\n= render :partial => 'example', :locals => { :method => '#{md[1].strip}' }\n"
137
+ section_docs[section_idx][:description] = [render_call, section_docs[section_idx][:description]].join("\n")
138
+ elsif md = line.match(/@title (.*)$/)
139
+ section_docs[section_idx][:section_title] = md[1].gsub(/^#/, '')
140
+ elsif md = line.match(/@hidden_tags (.*)$/)
141
+ section_docs[section_idx][:hidden_tags] = md[1].gsub(/^#/, '').scan(/\w+/)
142
+ elsif line == '#'
143
+ section_docs[section_idx][:description] = ["\n%br/\n", section_docs[section_idx][:description]].join("\n")
144
+ else
145
+ section_docs[section_idx][:description] = [line.gsub(/^#/, ''), section_docs[section_idx][:description]].join("\n")
146
+ end
147
+ end
148
+ end
149
+ # Zwróć posortowane według kolejności sekcji:
150
+ section_docs.find_all { |section_hash| DOC_ORDER.index(section_hash[:section_name]) }.sort_by {|section_hash| DOC_ORDER.index(section_hash[:section_name]) }
151
+ end
152
+
153
+
154
+ protected
155
+
156
+ # Wstaw do XML`a komentarz
157
+ def comment(text, options = {})
158
+ if options[:kind] && @comment_kinds
159
+ Array(options[:kind]).each do |kind|
160
+ builder.comment!("$#{kind}$ #{text}")
161
+ end
162
+ elsif @comments
163
+ builder.comment!(text)
164
+ end
165
+ end
166
+
167
+
168
+ def comment_dictionary(description, dict, options = {})
169
+ indent = options[:indent] || 4
170
+ pretty_string = lambda {|dict_item| "\n" + " " * indent + "#{dict_item.onix_code} - #{dict_item.name}" }
171
+ case dict
172
+ when Symbol
173
+ dictionary_items = Elibri::ONIX::Dict::Release_3_0.const_get(dict)::ALL.map(&pretty_string)
174
+ when Array
175
+ dictionary_items = dict.map {|dict_item| "\n" + " " * indent + dict_item }
176
+ end
177
+
178
+ comment(description + dictionary_items.join, options)
179
+ end
180
+
181
+
182
+ def render_products!
183
+ @products.each do |product|
184
+ next unless product.public?
185
+
186
+ tag(:Product) do
187
+ export_record_identifiers!(product) #PR.1 + PR.2
188
+ if @xml_variant.includes_basic_meta?
189
+ tag(:DescriptiveDetail) do
190
+ export_product_form!(product) #PR.3
191
+ export_epub_details!(product) #PR.3
192
+ export_measurement!(product)
193
+ #jak się dodaje tytuł, który nie jest samodzielne w sprzedaży?
194
+ #jak się dodaje tytuł, który zostanie przeceniony?
195
+ export_series_memberships!(product) #P.5
196
+ export_titles!(product) #PR.6
197
+ export_contributors!(product) #PR.7
198
+ #PR.8 - konferencje
199
+ export_edition!(product) #PR.9
200
+ export_languages!(product) #PR.10
201
+ export_extent!(product) #PR.11
202
+ export_subjects!(product) #PR.12
203
+ export_audience_range!(product) if product.audience_range_present? #PR.13
204
+ end
205
+ end
206
+ if @xml_variant.includes_other_texts? || @xml_variant.includes_media_files?
207
+ tag(:CollateralDetail) do
208
+ if @xml_variant.includes_other_texts? #TODO zmienić nazwę wariantu
209
+ export_texts!(product) #PR.14
210
+ end
211
+ #P.15 - citywany kontent - sprawdzić
212
+ if @xml_variant.includes_media_files?
213
+ export_supporting_resources!(product) #PR.16 #TODO - to jest też do przerobienia
214
+ end
215
+ #P.17 - nagrody
216
+ end
217
+ end
218
+ remove_tag_if_empty!(:CollateralDetail)
219
+ #P.18 - ContentItem
220
+ tag(:PublishingDetail) do
221
+ export_publisher_info!(product) #P.19
222
+ export_publishing_status!(product) #PR.20
223
+ export_sale_restrictions!(product) if product.sale_restricted? #PR.21
224
+ end
225
+ remove_tag_if_empty!(:PublishingDetail)
226
+ #P.23 - related products
227
+ if product.facsimiles.present? or product.similar_products.present?
228
+ export_related_products!(product)
229
+ end
230
+ #P.24 - Market
231
+ #P.25 - market representation
232
+ if @xml_variant.includes_stocks?
233
+ export_supply_details!(product) #PR.26
234
+ end
235
+ #fake dla exportu ze sklepu - w elibri ten kod nie zadziała
236
+ # if @xml_variant.respond_to?(:cover_price?) && @xml_variant.cover_price?
237
+ # export_cover_price!(product) #fake, żeby jakoś te dane wysłać
238
+ # end
239
+ export_elibri_extensions!(product) unless @pure_onix
240
+ end
241
+ end
242
+ end
243
+
244
+
245
+ # Renderuj tag w XML`u za pomocą Buildera. Jeśli wybrano opcję krótkich tagów,
246
+ # wstaw krótki tag z hasha SHORT_TAGS.
247
+ def tag(tag_id, *args, &block)
248
+ self.class.tag(builder, tags_type.short?, tag_id, *args, &block)
249
+ end
250
+
251
+ def remove_tag_if_empty!(tag_name)
252
+ idx = @out.rindex("<#{tag_name}")
253
+ if idx
254
+ tail = @out[idx..-1].join.strip.gsub("\n", "").gsub(" ", "")
255
+ if tail == "<#{tag_name}></#{tag_name}>" #wycinaj końcówkę
256
+ (@out.size - idx + 1).times do #zmieniaj listę in-place
257
+ @out.pop
258
+ end
259
+ end
260
+ end
261
+ end
262
+
263
+ # @hidden_tags NotificationType DescriptiveDetail ProductSupply PublishingDetail
264
+ # @title Identyfikatory rekordu
265
+ # Każdy rekord w ONIX-ie posiada wewnętrzny identyfikator, który jest dowolnym ciągiem znaków, i jest przekazywany
266
+ # w tagu <strong>&lt;ProductIdentifier&gt;</strong>. Gwarantowana jest jego niezmienność.
267
+ #
268
+ #
269
+ # Oprócz tego każdy produkt może posiadać numer ISBN, EAN oraz jeden lub więcej identyfikatorów dostawców (np. numer w bazie Olesiejuka)
270
+ # Tutaj już nie ma gwarancji niezmienności, choć jest to bardzo rzadka sytuacja (np. wydrukowany został jednak inny numer ISBN na okładce,
271
+ # i wydawcnictwo zmienia wpis w eLibri)
272
+ def export_record_identifiers!(product)
273
+ comment 'Unikalne ID rekordu produktu', :kind => :onix_record_identifiers
274
+ tag(:RecordReference, product.record_reference) #doc
275
+ comment_dictionary "Typ powiadomienia", :NotificationType, :kind => :onix_publishing_status
276
+ tag(:NotificationType, product.notification_type.onix_code) # Notification confirmed on publication - Lista 1
277
+ if product.respond_to?(:deletion_text) && product.deletion_text.present?
278
+ comment "Występuje tylko gdy NotificationType == #{Elibri::ONIX::Dict::Release_3_0::NotificationType::DELETE}", :kind => :onix_record_identifiers
279
+ tag(:DeletionText, product.deletion_text)
280
+ end
281
+
282
+ if product.isbn_value
283
+ comment 'ISBN', :kind => :onix_record_identifiers
284
+ tag(:ProductIdentifier) do
285
+ tag(:ProductIDType, Elibri::ONIX::Dict::Release_3_0::ProductIDType::ISBN13) #lista 5
286
+ tag(:IDValue, product.isbn_value)
287
+ end
288
+ end
289
+
290
+ if product.ean.present? && product.ean != product.isbn_value
291
+ comment 'EAN-13 - gdy inny niż ISBN', :kind => :onix_record_identifiers
292
+
293
+ tag(:ProductIdentifier) do
294
+ tag(:ProductIDType, Elibri::ONIX::Dict::Release_3_0::ProductIDType::EAN)
295
+ tag(:IDValue, product.ean)
296
+ end
297
+ end
298
+
299
+ if @xml_variant.includes_stocks?
300
+ product.product_availabilities.each do |pa|
301
+ if pa.supplier_identifier.present?
302
+ comment "Identyfikator dostawcy: #{pa.supplier.name}", :kind => :onix_record_identifiers
303
+ tag(:ProductIdentifier) do
304
+ tag(:ProductIDType, Elibri::ONIX::Dict::Release_3_0::ProductIDType::PROPRIETARY) #lista 5
305
+ tag(:IDTypeName, pa.supplier.name)
306
+ tag(:IDValue, pa.supplier_identifier)
307
+ end
308
+ end
309
+ end
310
+ end
311
+ end
312
+
313
+
314
+ # @hidden_tags RecordReference NotificationType ProductIdentifier TitleDetail
315
+ # @title Forma produktu
316
+ # <strong>&lt;ProductForm&gt;</strong> określa typ produktu. Np. BA to książka.<br/>
317
+ # <strong>&lt;ProductComposition&gt;</strong> przybiera aktualnie zawsze wartość 00 - czyli że przedmiotem handlu jest pojedyncza książka.
318
+ # W przyszłości ulegnie to zmianie, gdy dodamy do eLibri obsługę pakietów książek. Pakiet książek to komplet kilku książek,
319
+ # z nowym numerem ISBN, i nową ceną, na ogół niższą, niż suma cen książek zawartych w pakiecie.
320
+ def export_product_form!(product)
321
+ comment 'W tej chwili tylko 00 - pojedynczy element', :kind => :onix_product_form
322
+ tag(:ProductComposition, '00') #lista 2 - Single-item retail product
323
+
324
+ if product.product_form_onix_code
325
+ comment_dictionary "Format produktu", :ProductFormCode, :indent => 10, :kind => [:onix_product_form, :onix_epub_details]
326
+ tag(:ProductForm, product.product_form_onix_code)
327
+ end
328
+
329
+ if product.product_form_detail_onix_code
330
+ comment_dictionary "Szczegóły formatu produktu", :ProductFormDetail, :indent => 10, :kind => :onix_epub_details
331
+ tag(:ProductFormDetail, product.product_form_detail_onix_code)
332
+ end
333
+ end
334
+
335
+
336
+ # @hidden_tags RecordReference NotificationType ProductIdentifier TitleDetail PublishingDetail ProductComposition
337
+ # @title Zabezpieczenia e-booków
338
+ # <strong>&lt;EpubTechnicalProtection&gt;</strong> Określa typ zabezpieczenia stosowanego przy publikacji e-booka.<br/>
339
+ # <strong>&lt;ProductFormDetail&gt;</strong> zawiera format w jakim rozprowadzany jest e-book.
340
+ # Aktualnie może przyjąć wartości takie jak:
341
+ # #{Elibri::ONIX::Dict::Release_3_0::ProductFormDetail::ALL.map(&:name).to_sentence(:last_word_connector => ' lub ')}
342
+ def export_epub_details!(product)
343
+ return unless product.kind_of_ebook?
344
+ if product.epub_technical_protection
345
+ comment_dictionary "Zabezpieczenie", :EpubTechnicalProtection, :indent => 10, :kind => :onix_epub_details
346
+ tag(:EpubTechnicalProtection, product.epub_technical_protection_onix_code)
347
+ end
348
+ end
349
+
350
+
351
+ # @hidden_tags RecordReference NotificationType ProductIdentifier ProductComposition ProductForm TitleDetail
352
+ # @title Wymiary produktu
353
+ # Następujące atrybuty są udostępniane: wysokość, szerokość, grubość oraz masę produktu. Pierwsze trzy podajemy zawsze w milimetrach, masę w gramach.
354
+ # W przypadku map eksportujemy również jej skalę w tagu &lt;MapScale&gt;
355
+ def export_measurement!(product)
356
+ return unless product.kind_of_measurable?
357
+ [[product.height, Elibri::ONIX::Dict::Release_3_0::MeasureType::HEIGHT, Product::HEIGHT_UNIT, 'Wysokość'],
358
+ [product.width, Elibri::ONIX::Dict::Release_3_0::MeasureType::WIDTH, Product::WIDTH_UNIT, 'Szerokość'],
359
+ [product.thickness, Elibri::ONIX::Dict::Release_3_0::MeasureType::THICKNESS, Product::THICKNESS_UNIT, 'Grubość'],
360
+ [product.weight, Elibri::ONIX::Dict::Release_3_0::MeasureType::WEIGHT, Product::WEIGHT_UNIT, 'Masa']
361
+ ].each do |value, measure_type_code, unit_code, name|
362
+ if value
363
+ tag(:Measure) do
364
+ comment "#{name}: #{value}#{unit_code}", :kind => :onix_measurement
365
+ tag(:MeasureType, measure_type_code) #lista 48
366
+ tag(:Measurement, value)
367
+ tag(:MeasureUnitCode, unit_code)
368
+ end
369
+ end
370
+ end
371
+
372
+ if product.kind_of_map? and product.map_scale
373
+ comment 'Skala mapy - tylko dla produktów typu mapa'
374
+ tag(:MapScale, product.map_scale)
375
+ end
376
+ end
377
+
378
+
379
+ # @hidden_tags ProductIdentifier DescriptiveDetail ProductSupply
380
+ # @title Cykl życia rekordu
381
+ # W ONIX-ie status rekordu jest reprezentowany za pomocą kombinacji tagów <strong>&lt;NotificationType&gt;</strong> i <strong>&lt;PublishingStatus&gt;</strong>
382
+ #
383
+ #
384
+ # Tuż po założeniu przez wydawnictwo każdy rekord ma status prywatny. Jest to wtedy widoczny tylko i wyłącznie dla pracowników wydawnictwa,
385
+ # i nie jest udostępniany na zewnątrz. Po wypełnieniu kilku podstawowych danych (autor, tytuł) pracownik wydawnictwa może zmienić status
386
+ # rekordu na <i>zapowiedź</i>. W tym przypadku wartość <strong>&lt;NotificationType&gt;</strong> to 01 - wczesne powiadomienie.
387
+ #
388
+ # @render onix_announced_product_example
389
+ #
390
+ # Po uzupełnieniu większości danych niezbędnych (okładka, cena, ISBN, okładka, dokładna data premiery) wydawnictwo może zmienić
391
+ # status rekordu na <i>przedsprzedaż</i>.
392
+ # W zależności od naszego poziomu zaufania do terminowości wydawnictwa można uruchomić dla takiego tytułu przedsprzedaż na stronie księgarni.
393
+ # Wydawnictwo może zmienić datę premiery, jeśli produkt znajduje się w przedsprzedaży, warto zaimplementować procedurę poinformowania klientów
394
+ # o zmianie.
395
+ # Rekord o takim statusie ma wartość 02 w <strong>&lt;NotificationType&gt;</strong>
396
+ #
397
+ # @render onix_preorder_product_example
398
+ #
399
+ # Po ukazaniu się książki jej status zostaje zmieniony na <i>dostępna na rynku</i>.
400
+ # Rekord o takim statusie ma wartość 03 w <strong>&lt;NotificationType&gt;</strong> i 04 w <strong>&lt;PublishingDetail&gt;</strong>
401
+ #
402
+ # @render onix_published_product_example
403
+ #
404
+ # Każde wydawnictwo oczywiście liczy na to, że nakład książki się wyprzeda. Bardzo trudno jest poprawnie zdefiniować, co oznacza wyczerpany nakład.
405
+ # Bardzo długo egzemplarze książki, której nie ma już w magazynie wydawnictwa, mogą znajdować się w księgarniach i być zwracane co jakiś czas do hurtowni,
406
+ # co powoduje, że dany tytuł będzie się stawał na jakiś czas dostępny. W związku z tym informacje o wyczerpaniu się nakładu podejmuje wydawnictwo, i oznacza
407
+ # to, że nie będzie akceptować zamówień na określony tytuł. Nie oznacza to jednak, że tytuł jest w ogóle niedostępny, w dalszym ciągu może być w ofercie
408
+ # hurtowni.
409
+ # Rekord o statusie <i>nakład wyczerpany</i> ma wartość 03 w <strong>&lt;NotificationType&gt;</strong> i 07 w <strong>&lt;PublishingDetail&gt;</strong>
410
+ #
411
+ # @render onix_out_of_print_product_example
412
+ #
413
+ # Status 08 (niedostępny) w <strong>&lt;PublishingDetail&gt;</strong> nie jest w tej chwili używany przez eLibri. W przyszłości proszę się spodziewać również
414
+ # dodania informacji o dodrukach realizowanych pod tym samym numerem ISBN.
415
+ #
416
+ def export_publishing_status!(product)
417
+ if product.publishing_status_onix_code.present?
418
+ comment_dictionary 'Status publikacji', :PublishingStatusCode, :indent => 10, :kind => :onix_publishing_status
419
+ tag(:PublishingStatus, product.publishing_status_onix_code) #lista 64 #TODO sprawdzić
420
+ end
421
+
422
+ #TODO - tu można również zawrzeć datę, przed którą produkt nie może być importowany do baz sklepów
423
+ date, format_code = product.publication_date_with_onix_format_code
424
+ if date && format_code
425
+ tag(:PublishingDate) do
426
+ comment 'Zawsze 01 - data publikacji', :kind => :onix_publishing_status
427
+ tag(:PublishingDateRole, Elibri::ONIX::Dict::Release_3_0::PublishingDateRole::PUBLICATION_DATE) #lista 163
428
+ comment_dictionary "Format daty", :DateFormat, :indent => 12, :kind => :onix_publishing_status
429
+ tag(:DateFormat, format_code) #lista 55
430
+ tag(:Date, date)
431
+ end
432
+ end
433
+ end
434
+
435
+
436
+ # @hidden_tags RecordReference NotificationType ProductIdentifier DescriptiveDetail
437
+ # @title Ograniczenia sprzedaży
438
+ # eLibri umożliwia przechowywanie informacji o ograniczaniach dotyczących sprzedaży produktu.
439
+ # Obecnie obsługuje tylko 1 typ ograniczenia: wyłączność na produkt dla określonego detalisty.
440
+ # Opcjonalnie może się też pojawić data wygaśnięcia ograniczenia - w innym przypadku oznacza to, że produkt został przeznaczony tylko
441
+ # dla jednej, określonej sieci.
442
+ # W przypadku, gdy jest podana data wyłączności na sprzedaż produktu, należy ją traktować jako faktyczną datę premiery.
443
+ def export_sale_restrictions!(product)
444
+ if product.sale_restricted?
445
+ tag(:SalesRestriction) do
446
+ #lista 71 - For sale only through designated retailer, though not under retailer's own brand/imprint.
447
+ comment "Typ restrykcji - używamy tylko #{Elibri::ONIX::Dict::Release_3_0::SalesRestrictionType::RETAILER_EXCLUSIVE} (sprzedaż tylko poprzez wybranego detalistę)", :kind => :onix_sale_restrictions
448
+ tag(:SalesRestrictionType, Elibri::ONIX::Dict::Release_3_0::SalesRestrictionType::RETAILER_EXCLUSIVE)
449
+ tag(:SalesOutlet) do
450
+ tag(:SalesOutletName, product.sale_restricted_for)
451
+ end
452
+ comment "Ograniczenie wygasa #{product.sale_restricted_to.strftime("%d.%m.%Y")}", :kind => :onix_sale_restrictions
453
+ tag(:EndDate, product.sale_restricted_to.strftime("%Y%m%d"))
454
+ end
455
+ end
456
+ end
457
+
458
+
459
+ # @hidden_tags RecordReference NotificationType ProductIdentifier ProductComposition ProductForm TitleDetail
460
+ # @title Wiek czytelnika
461
+ # Zarówno wiek 'od', jak i wiek 'do' są opcjonalne.
462
+ def export_audience_range!(product)
463
+ if product.audience_age_from.present?
464
+ tag(:AudienceRange) do
465
+ comment "Ograniczenie dotyczy wieku czytelnika - zawsze #{Elibri::ONIX::Dict::Release_3_0::AudienceRangeQualifier::READING_AGE}", :kind => :onix_audience_range
466
+ tag(:AudienceRangeQualifier, Elibri::ONIX::Dict::Release_3_0::AudienceRangeQualifier::READING_AGE)
467
+ comment "Wiek od #{product.audience_age_from} lat", :kind => :onix_audience_range
468
+ tag(:AudienceRangePrecision, Elibri::ONIX::Dict::Release_3_0::AudienceRangePrecision::FROM)
469
+ tag(:AudienceRangeValue, product.audience_age_from)
470
+ end
471
+ end
472
+
473
+ if product.audience_age_to.present?
474
+ tag(:AudienceRange) do
475
+ comment "Ograniczenie dotyczy wieku czytelnika - zawsze #{Elibri::ONIX::Dict::Release_3_0::AudienceRangeQualifier::READING_AGE}", :kind => :onix_audience_range
476
+ tag(:AudienceRangeQualifier, Elibri::ONIX::Dict::Release_3_0::AudienceRangeQualifier::READING_AGE)
477
+ comment "Wiek do #{product.audience_age_to} lat", :kind => :onix_audience_range
478
+ tag(:AudienceRangePrecision, Elibri::ONIX::Dict::Release_3_0::AudienceRangePrecision::TO)
479
+ tag(:AudienceRangeValue, product.audience_age_to)
480
+ end
481
+ end
482
+ end
483
+
484
+
485
+ # @hidden_tags RecordReference NotificationType ProductIdentifier DescriptiveDetail
486
+ # @title Informacje o wydawcy
487
+ # W rekordzie znajduje się oczywiście nazwa wydawnictwa. Wydawca może określić też imprint.
488
+ # Z imprintem mamy do czynienia wtedy, gdy książki są wydawane pod różnymi markami, pula ISBN jest jednak wspólna.
489
+ # Jeśli wydawnictwo uzupełnia nazwę imprintu, to powinna być ona traktowana jako nazwa wydawnictwa przy prezentacji
490
+ # książki klientowi końcowemu.
491
+ def export_publisher_info!(product)
492
+ if product.imprint
493
+ tag(:Imprint) do
494
+ comment "Nazwa imprintu", :kind => :onix_publisher_info
495
+ tag(:ImprintName, product.imprint.name)
496
+ end
497
+ end
498
+
499
+ if product.publisher_name
500
+ tag(:Publisher) do
501
+ comment "Wydawca - używamy tylko kodu 01 (główny wydawca)", :kind => :onix_publisher_info
502
+ tag(:PublishingRole, '01') # Publisher, lista 45 #TODO jeszcze może być współwydawca
503
+ tag(:PublisherIdentifier) do
504
+ tag(:PublisherIDType, '01') #prioprietary
505
+ tag(:IDTypeName, 'ElibriPublisherCode')
506
+ tag(:IDValue, product.publisher_id)
507
+ end
508
+ tag(:PublisherName, product.publisher_name)
509
+ end
510
+ end
511
+ end
512
+
513
+
514
+ # @hidden_tags RecordReference NotificationType ProductIdentifier ProductComposition ProductForm TitleDetail
515
+ # @title Pozostałe atrybuty
516
+ # Dodatkowo każdy produkt może mieć kilka dodatkowych atrybutów: wielkość pliku (w Mb, tylko e-book),
517
+ # czas trwania (tylko audiobook, w minutach), ilość stron, ilość ilustracji.
518
+ #
519
+ # Poniżej przykład dla e-booka (wielkość pliku, ilość stron i ilustracji)
520
+ # @render onix_ebook_extent_example
521
+ #
522
+ # I przykład dla audiobook-a, z czasem trwania nagrania:
523
+ # @render onix_audiobook_extent_example
524
+ def export_extent!(product)
525
+ if product.kind_of_ebook? and product.file_size
526
+ tag(:Extent) do
527
+ comment 'Rozmiar pliku (w MB) - tylko dla produktów typu e-book', :kind => :onix_extent
528
+ tag(:ExtentType, Elibri::ONIX::Dict::Release_3_0::ExtentType::FILE_SIZE)
529
+ tag(:ExtentValue, product.file_size)
530
+ comment 'W MB', :kind => :onix_extent
531
+ tag(:ExtentUnit, Elibri::ONIX::Dict::Release_3_0::ExtentUnit::MEGABYTES)
532
+ end
533
+ end
534
+
535
+ if product.kind_of_audio? and product.duration
536
+ tag(:Extent) do
537
+ comment 'Czas trwania (w minutach) - tylko dla produktów typu audio', :kind => :onix_extent
538
+ tag(:ExtentType, Elibri::ONIX::Dict::Release_3_0::ExtentType::DURATION)
539
+ tag(:ExtentValue, product.duration)
540
+ comment 'W minutach', :kind => :onix_extent
541
+ tag(:ExtentUnit, Elibri::ONIX::Dict::Release_3_0::ExtentUnit::MINUTES)
542
+ end
543
+ end
544
+
545
+ if (product.kind_of_book? or product.kind_of_ebook?) && product.number_of_pages #number_of_pages to int
546
+ tag(:Extent) do
547
+ comment 'Liczba stron - tylko dla produktów typu książka', :kind => :onix_extent
548
+ tag(:ExtentType, Elibri::ONIX::Dict::Release_3_0::ExtentType::PAGE_COUNT)
549
+ tag(:ExtentValue, product.number_of_pages)
550
+ tag(:ExtentUnit, Elibri::ONIX::Dict::Release_3_0::ExtentUnit::PAGES)
551
+ end
552
+ end
553
+
554
+ if (product.kind_of_book? or product.kind_of_ebook?) and product.number_of_illustrations
555
+ comment 'Liczba ilustracji - tylko dla produktów typu książka', :kind => :onix_extent
556
+ tag(:NumberOfIllustrations, product.number_of_illustrations)
557
+ end
558
+ end
559
+
560
+ # @hidden_tags RecordReference NotificationType ProductIdentifier ProductComposition ProductForm TitleDetail
561
+ # @title Kategorie
562
+ # eLibri stosuje wewnętrzną hierarchę kategorii, dostępną w formacie JSON pod adresem
563
+ # = link_to product_categories_json_url, product_categories_json_url
564
+ def export_subjects!(product)
565
+ if product.elibri_product_categories.present? or product.publisher_product_categories.present?
566
+ comment 'Stosujemy wewnętrzną kategoryzację. Lista kategorii Elibri w formacie JSON: http://www.elibri.com.pl/assets/product_categories.js'
567
+ end
568
+
569
+ # Kategorie wg. eLibri
570
+ product.elibri_product_categories.each_with_index do |product_category,i|
571
+ tag(:Subject) do
572
+ tag(:MainSubject) if i.zero?
573
+ tag(:SubjectSchemeIdentifier, Elibri::ONIX::Dict::Release_3_0::SubjectSchemeIdentifier::PROPRIETARY)
574
+ tag(:SubjectSchemeName, 'elibri.com.pl')
575
+ tag(:SubjectSchemeVersion, '1.0')
576
+ tag(:SubjectCode, product_category.id)
577
+ tag(:SubjectHeadingText, product_category.full_node_path_name)
578
+ end
579
+ end
580
+
581
+ # Kategorie wg. wydawnictwa
582
+ product.publisher_product_categories.each do |product_category|
583
+ tag(:Subject) do
584
+ tag(:SubjectSchemeIdentifier, Elibri::ONIX::Dict::Release_3_0::SubjectSchemeIdentifier::PROPRIETARY)
585
+ tag(:SubjectSchemeName, product.publisher_name)
586
+ tag(:SubjectHeadingText, product_category.name)
587
+ end
588
+ end
589
+ end
590
+
591
+
592
+ # @hidden_tags RecordReference NotificationType ProductIdentifier ProductComposition ProductForm TitleDetail
593
+ # @title Autorzy, redaktorzy ...
594
+ # Każdy produkt może mieć wymienionych autorów, może być informacja, że jest to praca zbiorowa, produkt może nie mieć też żadnego autora (np. mapa)
595
+ #
596
+ #
597
+ # Każdy twórca ma przypisaną jedną z wielu ról (&lt;ContributorRole&gt;). W przypadku tłumacza dodawana jest informacja, z jakiego języka
598
+ # nastąpiło tłumaczenie (&lt;FromLanguage&gt;)
599
+ #
600
+ #
601
+ # W przypadku właściwego uzupełnienia danych w eLibri, system potrafi podzielić fragmenty personaliów
602
+ # autora i wyeksportować je w oddzielnych tagach ONIX. Rozróżniane są następujące fragmenty: tytuł naukowy (&lt;TitlesBeforeNames&gt;),
603
+ # imię (&lt;NamesBeforeKey&gt;), prefix nazwiska (von, van - &lt;PrefixToKey&gt;), nazwisko (&lt;KeyNames&gt;),
604
+ # postfix nazwiska (najczęściej określenie zakonu, np. OP - &lt;NamesAfterKey&gt;).
605
+ # Zawsze jest jednak exportowane pełne brzemienie imienia i nazwiska (&lt;PersonName&gt;)
606
+ #
607
+ #
608
+ # Zdarzają się również przypadki, gdzie wyżej podany podział nie jest albo znany, albo możliwy (np. św. Tomasz z Akwinu), wtedy
609
+ # exportowany jest tylko tag &lt;PersonName&gt;
610
+ #
611
+ # Jeśli wydawnictwo uzupełniło biogram autora, to jest on dostępny w tagu &lt;BiographicalNote&gt;
612
+ #
613
+ # @render onix_contributors_example
614
+ #
615
+ # W przypadku pracy zbiorowej rekord wygląda następująco:
616
+ #
617
+ # @render onix_collective_work_example
618
+ #
619
+ # Jeśli produkt nie ma żadnego autora, użyty zostaje tag &lt;NoContributor&gt;
620
+ #
621
+ # @render onix_no_contributors_example
622
+ #
623
+ #
624
+ def export_contributors!(product)
625
+ if product.authorship_kind.user_given?
626
+ comment 'Gdy wyszczególniono autorów', :kind => :onix_contributors if product.contributors.present?
627
+ product.contributors.each_with_index do |contributor, idx|
628
+ tag(:Contributor, :sourcename => "contributorid:#{contributor.id}", :datestamp => contributor.updated_at.to_s(:onix)) do
629
+ tag(:SequenceNumber, idx + 1)
630
+ comment_dictionary 'Rola autora', :ContributorRole, :indent => 10, :kind => :onix_contributors
631
+ tag(:ContributorRole, contributor.role_onix_code) #lista 17
632
+ comment 'Tylko w przypadku tłumaczy:', :kind => :onix_contributors
633
+ tag(:FromLanguage, contributor.language_onix_code) if contributor.language_onix_code
634
+ tag(:PersonName, contributor.generated_full_name) #zawsze jest TODO - dodać takie pole do bazy danych
635
+
636
+ tag(:TitlesBeforeNames, contributor.title) if contributor.title.present? #tytuł
637
+ tag(:NamesBeforeKey, contributor.name) if contributor.name.present? #imię
638
+ tag(:PrefixToKey, contributor.last_name_prefix) if contributor.last_name_prefix.present? #van, von
639
+ tag(:KeyNames, contributor.last_name) if contributor.last_name.present?
640
+ tag(:NamesAfterKey, contributor.last_name_postfix) if contributor.last_name_postfix.present?
641
+ tag(:BiographicalNote, contributor.biography.text) if contributor.biography
642
+ end
643
+ end
644
+ end
645
+
646
+ if product.authorship_kind.collective?
647
+ comment 'Gdy jest to praca zbiorowa', :kind => :onix_contributors
648
+ tag(:Contributor) do
649
+ comment "Autor - #{Elibri::ONIX::Dict::Release_3_0::ContributorRole::AUTHOR}", :kind => :onix_contributors
650
+ tag(:ContributorRole, Elibri::ONIX::Dict::Release_3_0::ContributorRole::AUTHOR)
651
+ comment "Różne osoby - #{Elibri::ONIX::Dict::Release_3_0::UnnamedPersons::VARIOUS_AUTHORS}", :kind => :onix_contributors
652
+ tag(:UnnamedPersons, Elibri::ONIX::Dict::Release_3_0::UnnamedPersons::VARIOUS_AUTHORS)
653
+ end
654
+ end
655
+
656
+ if product.authorship_kind.no_contributor?
657
+ comment 'Gdy brak autorów', :kind => :onix_contributors
658
+ tag(:NoContributor)
659
+ end
660
+ end
661
+
662
+ # @hidden_tags RecordReference NotificationType ProductIdentifier ProductComposition ProductForm
663
+ # @title Tytuły
664
+ # W dokumencie XML mogą pojawić się 3 typy tytułu: pełen tytuł produktu, tytuł w języku oryginału (jeśli jest tłumaczeniem)
665
+ # oraz roboczy tytuł nadany przez wydawcę.
666
+ # @render onix_titles_example
667
+ #
668
+ # Tytuł może być też kaskadowy. Dobrym przykładem jest na przykład Thorgal, komiks, który ma wiele tomów, każdy tom ma swój numer, jak i tytuł.
669
+ # W kategoriach ONIX-a Thorgal jest kolekcją. Od serii odróżnia go to, że jest częścią tytułu. Innym dobrym przykładem jest książka "Gra o Tron",
670
+ # która należy do kolekcji "Pieśń Lodu i Ognia" - ponieważ jest to częścią tytułu.
671
+ # @render onix_title_with_collection_example
672
+ #
673
+ #
674
+ def export_titles!(product)
675
+ if product.title_parts.present? or product.or_title.present? or product.trade_title.present?
676
+ #comment_dictionary 'Rozróżniane typy tytułów', :TitleType, :indent => 10, :kind => :onix_titles
677
+ end
678
+
679
+ if product.title_parts.present?
680
+ tag(:TitleDetail) do
681
+ comment "Pełen tytuł produktu - #{Elibri::ONIX::Dict::Release_3_0::TitleType::DISTINCTIVE_TITLE}", :kind => :onix_titles
682
+ tag(:TitleType, Elibri::ONIX::Dict::Release_3_0::TitleType::DISTINCTIVE_TITLE)
683
+ product.title_parts.each do |title_part|
684
+ tag(:TitleElement) do
685
+ if title_part.level == Elibri::ONIX::Dict::Release_3_0::TitleElementLevel::PRODUCT
686
+ comment "Tytuł na poziomie produktu - #{Elibri::ONIX::Dict::Release_3_0::TitleElementLevel::PRODUCT}", :kind => :onix_titles
687
+ elsif title_part.level == Elibri::ONIX::Dict::Release_3_0::TitleElementLevel::COLLECTION
688
+ comment "Tytuł na poziomie kolekcji - #{Elibri::ONIX::Dict::Release_3_0::TitleElementLevel::COLLECTION}", :kind => :onix_titles
689
+ end
690
+ tag(:TitleElementLevel, title_part.level) #odnosi się do tego produktu tylko
691
+ tag(:PartNumber, title_part.part) if title_part.part.present?
692
+ tag(:TitleText, title_part.title) if title_part.title.present?
693
+ tag(:Subtitle, title_part.subtitle) if title_part.subtitle.present?
694
+ end
695
+ end
696
+ end
697
+ end
698
+ if product.or_title.present?
699
+ tag(:TitleDetail) do
700
+ comment "Tytuł w języku oryginału - #{Elibri::ONIX::Dict::Release_3_0::TitleType::DISTINCTIVE_TITLE}", :kind => :onix_titles
701
+ tag(:TitleType, Elibri::ONIX::Dict::Release_3_0::TitleType::ORIGINAL_TITLE)
702
+ tag(:TitleElement) do
703
+ comment "Tytuł na poziomie produktu - #{Elibri::ONIX::Dict::Release_3_0::TitleElementLevel::PRODUCT}", :kind => :onix_titles
704
+ tag(:TitleElementLevel, Elibri::ONIX::Dict::Release_3_0::TitleElementLevel::PRODUCT)
705
+ tag(:TitleText, product.or_title)
706
+ end
707
+ end
708
+ end
709
+ if product.trade_title.present?
710
+ tag(:TitleDetail) do
711
+ comment "Tytuł handlowy używany przez wydawnictwo - #{Elibri::ONIX::Dict::Release_3_0::TitleType::DISTRIBUTORS_TITLE}", :kind => :onix_titles
712
+ tag(:TitleType, Elibri::ONIX::Dict::Release_3_0::TitleType::DISTRIBUTORS_TITLE) #tytuł produktu
713
+ tag(:TitleElement) do
714
+ comment "Tytuł na poziomie produktu - #{Elibri::ONIX::Dict::Release_3_0::TitleElementLevel::PRODUCT}", :kind => :onix_titles
715
+ tag(:TitleElementLevel, Elibri::ONIX::Dict::Release_3_0::TitleElementLevel::PRODUCT)
716
+ tag(:TitleText, product.trade_title)
717
+ end
718
+ end
719
+ end
720
+ end
721
+
722
+
723
+ # @hidden_tags RecordReference NotificationType ProductIdentifier ProductComposition ProductForm TitleDetail
724
+ # @title Opis wydania
725
+ def export_edition!(product)
726
+ if product.edition_statement.present?
727
+ comment 'Opis wydania', :kind => :onix_edition
728
+ tag(:EditionStatement, product.edition_statement)
729
+ end
730
+ end
731
+
732
+
733
+ # @hidden_tags RecordReference NotificationType ProductIdentifier ProductComposition ProductForm TitleDetail
734
+ # @title Języki
735
+ # Języki, w których dostępny jest produkt.
736
+ def export_languages!(product)
737
+ comment_dictionary 'Rola języka', :LanguageRole, :indent => 10, :kind => :onix_languages if product.languages.present?
738
+ product.languages.each do |language|
739
+ tag(:Language) do
740
+ tag(:LanguageRole, language.role_onix_code) #lista 22
741
+ tag(:LanguageCode, language.language_onix_code) #lista 74
742
+ end
743
+ end
744
+ end
745
+
746
+
747
+ # @hidden_tags RecordReference NotificationType ProductIdentifier DescriptiveDetail
748
+ # @title Teksty
749
+ # Wydawca może wprowadzić do eLibri różne informacje tekstowe - opis produktu, spis treści, recenzję (jeśli ma prawa ją udostępnić), jak i fragment książki.
750
+ # Biogramy autorów są udostępniane wraz z informacjami o
751
+ # = link_to "autorach", doc_api_path("onix_contributors")
752
+ def export_texts!(product)
753
+ comment_dictionary 'Typy tekstów', :OtherTextType, :indent => 10, :kind => :onix_texts if product.other_texts.present?
754
+ product.other_texts.each do |other_text|
755
+ #jeśli jest pusty tekst, nie nadaje się do umieszczania w ONIX albo tekst dotyczy autora (type_onix_code.blank?) -> nie exportuj
756
+ next if other_text.text.blank? || other_text.type_onix_code.blank? || !other_text.exportable?
757
+
758
+ tag(:TextContent, :sourcename => "textid:#{other_text.id}", :datestamp => other_text.updated_at.to_s(:onix)) do
759
+ tag(:TextType, other_text.type_onix_code) #lista 153
760
+ comment "Zawsze #{Elibri::ONIX::Dict::Release_3_0::ContentAudience::UNRESTRICTED} - Unrestricted", :kind => :onix_texts
761
+ tag(:ContentAudience, Elibri::ONIX::Dict::Release_3_0::ContentAudience::UNRESTRICTED)
762
+
763
+ if other_text.is_a_review? && other_text.resource_link.present?
764
+ text_source = {:sourcename => other_text.resource_link}
765
+ else
766
+ text_source = {}
767
+ end
768
+
769
+ tag(:Text, text_source) do |builder|
770
+ builder.cdata!(other_text.text)
771
+ end
772
+
773
+ tag(:TextAuthor, other_text.text_author) if other_text.text_author.present?
774
+ tag(:SourceTitle, other_text.source_title) if other_text.source_title.present?
775
+ end
776
+ end
777
+ end
778
+
779
+
780
+ # @hidden_tags RecordReference NotificationType ProductIdentifier DescriptiveDetail
781
+ # @title Załączniki
782
+ # Wydawca może załączyć do każdego produktu dowolną liczbę plików - a przynajmniej jeden, okładkę. Proszę za każdym razem
783
+ # tworzyć kopię pliku na swoim serwerze, hotlinking jest niedozwolony.
784
+ def export_supporting_resources!(product)
785
+ product.attachments.each do |attachment|
786
+ if attachment.onix_resource_mode #jeśli klient coś dzikiego wgrał, to ignoruj to
787
+ tag(:SupportingResource, :sourcename => "resourceid:#{attachment.id}", :datestamp => attachment.updated_at.to_s(:onix)) do
788
+ comment_dictionary 'Typ załącznika', :ResourceContentType, :indent => 12, :kind => :onix_supporting_resources
789
+ tag(:ResourceContentType, attachment.attachment_type_code) #lista 158
790
+ comment 'Zawsze 00 - Unrestricted', :kind => :onix_supporting_resources
791
+ tag(:ContentAudience, Elibri::ONIX::Dict::Release_3_0::ContentAudience::UNRESTRICTED)
792
+ comment_dictionary 'Rodzaj załącznika', :ResourceMode, :indent => 12, :kind => :onix_supporting_resources
793
+ tag(:ResourceMode, attachment.onix_resource_mode) #lista 159
794
+ tag(:ResourceVersion) do
795
+ comment 'Zawsze 02 - Downloadable file', :kind => :onix_supporting_resources
796
+ tag(:ResourceForm, Elibri::ONIX::Dict::Release_3_0::ResourceForm::DOWNLOADABLE_FILE)
797
+ url = attachment.file.url
798
+ if url.index("http://") #w sklepie zwraca mi całego linka, wygodniej mi jest to tutaj wychwycić
799
+ tag(:ResourceLink, URI.escape(url))
800
+ else
801
+ tag(:ResourceLink, URI.escape('http://' + HOST_NAME + url))
802
+ end
803
+ end
804
+ end
805
+ end
806
+ end
807
+ end
808
+
809
+
810
+ # @hidden_tags RecordReference NotificationType ProductIdentifier ProductComposition ProductForm
811
+ # @title Serie wydawnicze
812
+ # Serie wydawnicze są opisywane w podobny sposób co tytuł, zawarte są jednak w tagu <strong>&lt;Collection&gt;</strong>
813
+ # Struktura jest dosyć zawiła, ale wszystkie wartości są sztywne, więc nie powinno być problemu z odczytaniem informacji.
814
+ # Oprócz nazwy serii może zostać również podany numer wewnątrz serii, jeśli seria jest numerowana.
815
+ # Książka może należeć do kilku serii, wtedy tag <strong>&lt;Collection&gt;</strong> występuje kilkukrotnie.
816
+ def export_series_memberships!(product)
817
+ if product.series_membership_kind.user_given?
818
+ product.series_memberships.each_with_index do |series_membership, idx|
819
+ tag(:Collection) do
820
+ comment "Używamy tylko #{Elibri::ONIX::Dict::Release_3_0::CollectionType::PUBLISHER_COLLECTION} - seria wydawnictwa", :kind => :onix_series_memberships
821
+ tag(:CollectionType, Elibri::ONIX::Dict::Release_3_0::CollectionType::PUBLISHER_COLLECTION) #lista 148
822
+ comment "Teraz następuje podobna struktura, jak w przypadku tytułu", :kind => :onix_series_memberships
823
+ tag(:TitleDetail) do
824
+ comment "Używamy tylko #{Elibri::ONIX::Dict::Release_3_0::TitleType::DISTINCTIVE_TITLE}", :kind => :onix_series_memberships
825
+ tag(:TitleType, Elibri::ONIX::Dict::Release_3_0::TitleType::DISTINCTIVE_TITLE)
826
+ tag(:TitleElement) do
827
+ comment "Używamy tylko #{Elibri::ONIX::Dict::Release_3_0::TitleElementLevel::COLLECTION}", :kind => :onix_series_memberships
828
+ tag(:TitleElementLevel, Elibri::ONIX::Dict::Release_3_0::TitleElementLevel::COLLECTION)
829
+ tag(:PartNumber, series_membership.number_within_series) if series_membership.number_within_series.present?
830
+ tag(:TitleText, series_membership.series_name)
831
+ end
832
+ end
833
+ end
834
+ end
835
+ end
836
+ end
837
+
838
+
839
+ # @hidden_tags RecordReference NotificationType ProductIdentifier DescriptiveDetail
840
+ # @title Powiązane produkty
841
+ def export_related_products!(product)
842
+ tag(:RelatedMaterial) do
843
+
844
+ comment_dictionary "Typy relacji", :ProductRelationType, :indent => 10, :kind => :onix_related_products
845
+ product.facsimiles.each do |facsimile|
846
+ tag(:RelatedProduct) do
847
+ tag(:ProductRelationCode, Elibri::ONIX::Dict::Release_3_0::ProductRelationType::FACSIMILES)
848
+ tag(:ProductIdentifier) do
849
+ comment "Zawsze #{Elibri::ONIX::Dict::Release_3_0::ProductIDType::PROPRIETARY} - symbol wydawcy", :kind => :onix_related_products
850
+ tag(:ProductIDType, Elibri::ONIX::Dict::Release_3_0::ProductIDType::PROPRIETARY)
851
+ tag(:IDTypeName, facsimile.publisher_name)
852
+ tag(:IDValue, facsimile.publisher_symbol)
853
+ end
854
+
855
+ if facsimile.isbn_value.present?
856
+ tag(:ProductIdentifier) do
857
+ comment 'Zawsze ISBN-13'
858
+ tag(:ProductIDType, Elibri::ONIX::Dict::Release_3_0::ProductIDType::ISBN13)
859
+ tag(:IDValue, facsimile.isbn_value)
860
+ end
861
+ end
862
+ end
863
+ end
864
+
865
+ # Nie powtarzaj w zbiorze produktów podobnych faksymili:
866
+ (product.similar_products - product.facsimiles).each do |similar_product|
867
+ tag(:RelatedProduct) do
868
+ tag(:ProductRelationCode, Elibri::ONIX::Dict::Release_3_0::ProductRelationType::SIMILAR_PRODUCTS)
869
+ tag(:ProductIdentifier) do
870
+ comment "Zawsze #{Elibri::ONIX::Dict::Release_3_0::ProductIDType::PROPRIETARY} - symbol wydawcy"
871
+ tag(:ProductIDType, Elibri::ONIX::Dict::Release_3_0::ProductIDType::PROPRIETARY)
872
+ tag(:IDTypeName, similar_product.publisher_name)
873
+ tag(:IDValue, similar_product.publisher_symbol)
874
+ end
875
+
876
+ if similar_product.isbn_value.present?
877
+ tag(:ProductIdentifier) do
878
+ tag(:ProductIDType, Elibri::ONIX::Dict::Release_3_0::ProductIDType::ISBN13)
879
+ tag(:IDValue, similar_product.isbn_value)
880
+ end
881
+ end
882
+ end
883
+ end
884
+
885
+ end
886
+ end
887
+
888
+
889
+ # @hidden_tags RecordReference NotificationType DescriptiveDetail Supplier ProductAvailability Price
890
+ # @title Stany magazynowe
891
+ # Tag <strong>&lt;Stock&gt;</strong> może zawierać konkretną ilość produktu w <strong>&ltOnHand&gt;</strong>,
892
+ # lub słownie określoną ilość w <strong>&lt;StockQuantityCoded&gt;</strong>.<br/><br/>
893
+ # Dokument XML zawierający dane o stanach magazynowych produktu najczęściej zawiera także rozszerzoną listę
894
+ # identyfikatorów produktu. Poza podstawowym <strong>&lt;ProductIdentifier&gt;</strong>, znajdują się także identyfikatory
895
+ # poszczególnych dostawców. Tag <strong>&lt;IDTypeName&gt;</strong> zawiera nazwę dostawcy.
896
+ def export_supply_details!(product)
897
+ return if product.try(:skip_ProductSupply)
898
+ unless product.product_availabilities.empty?
899
+ tag(:ProductSupply) do
900
+ product.product_availabilities.each do |pa|
901
+ tag(:SupplyDetail) do
902
+ tag(:Supplier) do
903
+ comment_dictionary "Rola dostawcy", :SupplierRole, :indent => 12
904
+ tag(:SupplierRole, pa.supplier_role_onix_code) #lista 93
905
+ tag(:SupplierIdentifier) do
906
+ comment "Zawsze 02 - Proprietary. Identyfikujemy dostawcę po NIP"
907
+ tag(:SupplierIDType, '02') #lista 92, Proprietary
908
+ tag(:IDTypeName, 'NIP')
909
+ tag(:IDValue, pa.supplier.nip.gsub("-", ""))
910
+ end
911
+ tag(:SupplierName, pa.supplier.name)
912
+ tag(:TelephoneNumber, pa.supplier.phone) if pa.supplier.phone.present?
913
+ tag(:EmailAddress, pa.supplier.email) if pa.supplier.email.present?
914
+ if pa.supplier.website.present?
915
+ tag(:Website) do
916
+ tag(:WebsiteLink, pa.supplier.website)
917
+ end
918
+ end
919
+ end
920
+
921
+ comment_dictionary "Typ dostępności", :ProductAvailabilityType, :indent => 10
922
+ tag(:ProductAvailability, pa.product_availability_onix_code) #lista 65
923
+ if pa.stock_info
924
+ tag(:Stock) do
925
+ if pa.stock_info.exact_info?
926
+ tag(:OnHand, pa.stock_info.on_hand)
927
+ else
928
+ comment 'Nie znamy konkretnej ilości produktów na stanie'
929
+ tag(:StockQuantityCoded) do
930
+ comment 'Zawsze 01 - Proprietary'
931
+ tag(:StockQuantityCodeType, '01') #lista 70 - proprietary
932
+ tag(:StockQuantityCode, pa.stock_info.quantity_code) #low/high
933
+ end
934
+ end
935
+ end
936
+ end
937
+ if product.pack_quantity.present?
938
+ comment 'Ile produktów dostawca umieszcza w paczce'
939
+ tag(:PackQuantity, product.pack_quantity)
940
+ end
941
+
942
+ pa.price_infos.each do |price_info|
943
+ tag(:Price) do
944
+ comment_dictionary "Typ ceny", :PriceTypeCode, :indent => 12
945
+ tag(:PriceType, Elibri::ONIX::Dict::Release_3_0::PriceTypeCode::RRP_WITH_TAX) #lista 58
946
+ tag(:MinimumOrderQuantity, price_info.minimum_order_quantity) if price_info.minimum_order_quantity
947
+ tag(:PriceAmount, price_info.amount)
948
+ tag(:Tax) do
949
+ comment 'VAT'
950
+ tag(:TaxType, '01') #lista 174, VAT
951
+ tag(:TaxRatePercent, price_info.vat)
952
+ end
953
+ tag(:CurrencyCode, price_info.currency_code)
954
+ if product.price_printed_on_product_onix_code.present?
955
+ comment_dictionary "Cena na okładce?", :PricePrintedOnProduct, :indent => 12
956
+ tag(:PrintedOnProduct, product.price_printed_on_product_onix_code) #lista 174
957
+ comment 'Zawsze 00 - Unknown / unspecified'
958
+ tag(:PositionOnProduct, '00') #lista 142 - Position unknown or unspecified
959
+ end
960
+ end
961
+ end
962
+ end
963
+ end
964
+ end
965
+ end
966
+ end
967
+
968
+
969
+ # @title Rozszerzenia eLibri dla ONIX
970
+ # @hidden_tags RecordReference NotificationType ProductIdentifier DescriptiveDetail
971
+ # Standard ONIX nie przewiduje w chwili obecnej atrybutów produktów niezbędnych z punktu widzenia polskiego rynku wydawniczego.
972
+ # Są to atrybuty takie jak np. Vat czy PKWiU. eLibri rozszerza więc ONIX o kilka użytecznych tagów, wprowadzając nową przestrzeń nazw
973
+ # w generowanych XML`ach.
974
+ def export_elibri_extensions!(product)
975
+ if @elibri_onix_dialect >= '3.0.1'
976
+
977
+ if product.cover_type
978
+ comment_dictionary "Format okładki", Product::CoverType.all.map(&:name), :indent => 10
979
+ tag("elibri:CoverType", product.cover_type.name)
980
+ end
981
+
982
+ comment 'Cena na okładce'
983
+ tag("elibri:CoverPrice", product.price_amount) if product.price_amount.present?
984
+ comment 'Vat w procentach'
985
+ tag("elibri:Vat", product.vat) if product.vat.present?
986
+ tag("elibri:PKWiU", product.pkwiu) if product.pkwiu.present?
987
+ tag("elibri:preview_exists", product.preview_exists?.to_s)
988
+ end
989
+ end
990
+
991
+ end
992
+
993
+ end
994
+
995
+ end