elibri_onix_generator 0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,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