elibri_onix_generator 0.1

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