oddb2xml 2.5.0 → 2.5.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.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -1
  3. data/Elexis_Artikelstamm_v003.xsd +387 -0
  4. data/Elexis_Artikelstamm_v5.xsd +513 -0
  5. data/Gemfile +2 -6
  6. data/History.txt +11 -0
  7. data/README.md +35 -27
  8. data/artikelstamm.md +68 -0
  9. data/bin/compare_v5 +41 -0
  10. data/bin/oddb2xml +3 -15
  11. data/data/article_overrides.yaml +51859 -0
  12. data/data/gtin2ignore.yaml +30510 -0
  13. data/data/product_overrides.yaml +4 -0
  14. data/lib/oddb2xml/builder.rb +543 -192
  15. data/lib/oddb2xml/cli.rb +82 -62
  16. data/lib/oddb2xml/compare.rb +189 -0
  17. data/lib/oddb2xml/compressor.rb +6 -3
  18. data/lib/oddb2xml/downloader.rb +79 -64
  19. data/lib/oddb2xml/extractor.rb +67 -40
  20. data/lib/oddb2xml/options.rb +76 -77
  21. data/lib/oddb2xml/parslet_compositions.rb +18 -1
  22. data/lib/oddb2xml/util.rb +25 -3
  23. data/lib/oddb2xml/version.rb +1 -1
  24. data/oddb2xml.gemspec +8 -5
  25. data/oddb2xml.xsd +1 -0
  26. data/spec/artikelstamm_spec.rb +383 -0
  27. data/spec/builder_spec.rb +147 -118
  28. data/spec/calc_spec.rb +3 -15
  29. data/spec/cli_spec.rb +24 -35
  30. data/spec/compare_spec.rb +24 -0
  31. data/spec/compressor_spec.rb +1 -3
  32. data/spec/data/Elexis_Artikelstamm_v5.xsd +513 -0
  33. data/spec/data/Preparations.xml +2200 -0
  34. data/spec/data/Publications.xls +0 -0
  35. data/spec/data/artikelstamm_N_010917.xml +39 -0
  36. data/spec/data/artikelstamm_N_011217.xml +17 -0
  37. data/spec/data/artikelstamm_P_010917.xml +86 -0
  38. data/spec/data/artikelstamm_P_011217.xml +63 -0
  39. data/spec/data/oddb2xml_files_lppv.txt +2 -0
  40. data/spec/data/refdata_NonPharma.xml +38 -0
  41. data/spec/data/refdata_Pharma.xml +220 -0
  42. data/spec/data/swissmedic_orphan.xlsx +0 -0
  43. data/spec/data/swissmedic_package.xlsx +0 -0
  44. data/spec/data/transfer.dat +59 -19
  45. data/spec/data/v5_first.xml +102 -0
  46. data/spec/data/v5_second.xml +184 -0
  47. data/spec/data_helper.rb +72 -0
  48. data/spec/downloader_spec.rb +19 -27
  49. data/spec/extractor_spec.rb +27 -33
  50. data/spec/fixtures/vcr_cassettes/artikelstamm.json +1 -0
  51. data/spec/options_spec.rb +73 -66
  52. data/spec/spec_helper.rb +73 -24
  53. data/test_options.rb +4 -2
  54. metadata +100 -21
  55. data/spec/data/XMLPublications.zip +0 -0
  56. data/spec/data/compressor/oddb_article.xml +0 -0
  57. data/spec/data/compressor/oddb_fi.xml +0 -0
  58. data/spec/data/compressor/oddb_fi_product.xml +0 -0
  59. data/spec/data/compressor/oddb_limitation.xml +0 -0
  60. data/spec/data/compressor/oddb_product.xml +0 -0
  61. data/spec/data/compressor/oddb_substance.xml +0 -0
@@ -0,0 +1,4 @@
1
+ ---
2
+ 3536601:
3
+ DSCRF: '--missing--'
4
+ DSCR: 'KENDURAL Depottabl'
@@ -35,6 +35,12 @@ module Oddb2xml
35
35
  'GENERATED_BY' => "oddb2xml #{VERSION}"
36
36
  }
37
37
  class Builder
38
+ Data_dir = File.expand_path(File.join(File.dirname(__FILE__),'..','..', 'data'))
39
+ @@article_overrides = YAML.load_file(File.join(Data_dir, 'article_overrides.yaml'))
40
+ @@product_overrides = YAML.load_file(File.join(Data_dir, 'product_overrides.yaml'))
41
+ @@ignore_file = File.join(Data_dir, 'gtin2ignore.yaml')
42
+ @@gtin2ignore = YAML.load_file(@@ignore_file) if File.exist?(@@ignore_file)
43
+ @@gtin2ignore ||= []
38
44
  attr_accessor :subject, :refdata, :items, :flags, :lppvs,
39
45
  :actions, :migel, :orphan,
40
46
  :infos, :packs, :infos_zur_rose,
@@ -51,7 +57,7 @@ module Oddb2xml
51
57
  @infos = {}
52
58
  @packs = {}
53
59
  @migel = {}
54
- @infos_zur_rose = {} # zurrose
60
+ @infos_zur_rose ||= {}
55
61
  @actions = []
56
62
  @orphan = []
57
63
  @ean14 = false
@@ -64,7 +70,7 @@ module Oddb2xml
64
70
  end
65
71
  end
66
72
  def to_xml(subject=nil)
67
- Oddb2xml.log "to_xml subject #{subject} #{@subject} for #{self.class}"
73
+ Oddb2xml.log "to_xml subject #{subject || @subject}"
68
74
  if subject
69
75
  self.send('build_' + subject.to_s)
70
76
  elsif @subject
@@ -90,8 +96,8 @@ module Oddb2xml
90
96
  # delete duplicates
91
97
  @migel[ean13] = nil
92
98
  end unless SkipMigelDownloader
93
- if seq = @items[obj[:ean]]
94
- obj[:seq] = seq
99
+ if seq = @items[obj[:ean13]]
100
+ obj[:seq] = seq.clone
95
101
  end
96
102
  @articles << obj
97
103
  @pharmacode[obj[:pharmacode]] = obj
@@ -100,11 +106,11 @@ module Oddb2xml
100
106
  @migel.values.compact.each do |migel|
101
107
  next unless migel[:pharmacode]
102
108
  entry = {
103
- :ean => migel[:ean].to_i,
109
+ :ean13 => migel[:ean13],
104
110
  :pharmacode => migel[:pharmacode],
105
111
  :stat_date => '',
106
- :desc_de => migel[:desc_de],
107
- :desc_fr => migel[:desc_fr],
112
+ :desc_de => migel[:desc_de],
113
+ :desc_fr => migel[:desc_fr],
108
114
  :atc_code => '',
109
115
  :quantity => migel[:quantity],
110
116
  :company_ean => migel[:company_ean],
@@ -114,50 +120,48 @@ module Oddb2xml
114
120
  @articles << entry
115
121
  end unless SkipMigelDownloader
116
122
  nrAdded = 0
117
- if @options[:extended]
123
+ if @options[:extended] || @options[:artikelstamm]
118
124
  Oddb2xml.log("prepare_articles extended prepare_local_index having already #{@articles.size} articles")
119
125
  nrItems = 0
120
- @infos_zur_rose.each{
121
- |ean13, info|
122
- nrItems += 1
123
- pharmacode = info[:pharmacode]
124
- if @pharmacode[pharmacode]
125
- @pharmacode[pharmacode][:price] = info[:price]
126
- @pharmacode[pharmacode][:pub_price] = info[:pub_price]
127
- next
128
- end
129
- obj = {}
130
- found = false
131
- # existing = @refdata.values.find{ |x| x[:ean].eql? ean13 }
132
- existing = @refdata[ean13]
133
- if existing
134
- found = true
135
- existing[:price] = info[:price]
136
- existing[:pub_price] = info[:pub_price]
137
- else
138
- entry = {
139
- :desc => info[:description],
140
- :desc_de => info[:description],
141
- :status => info[:status] == '3' ? 'I' : 'A', # from ZurRose, we got 1,2 or 3 means aktive, aka available in trade
142
- :atc_code => '',
143
- :ean => ean13,
144
- :pharmacode => pharmacode,
145
- :price => info[:price],
146
- :pub_price => info[:pub_price],
147
- :type => info[:type],
148
- }
149
- if pharmacode
150
- @refdata[pharmacode] = entry
126
+ @infos_zur_rose.each do |ean13, info|
127
+ nrItems += 1
128
+ pharmacode = info[:pharmacode]
129
+ if @pharmacode[pharmacode]
130
+ @pharmacode[pharmacode][:price] = info[:price]
131
+ @pharmacode[pharmacode][:pub_price] = info[:pub_price]
132
+ next
133
+ end
134
+ obj = {}
135
+ found = false
136
+ existing = @refdata[ean13]
137
+ if existing
138
+ found = true
139
+ existing[:price] = info[:price]
140
+ existing[:pub_price] = info[:pub_price]
151
141
  else
152
- @refdata[ean13] = entry
142
+ entry = {
143
+ :desc => info[:description],
144
+ :desc_de => info[:description],
145
+ :status => info[:status] == '3' ? 'I' : 'A', # from ZurRose, we got 1,2 or 3 means aktive, aka available in trade
146
+ :atc_code => '',
147
+ :ean13 => ean13,
148
+ :pharmacode => pharmacode,
149
+ :price => info[:price],
150
+ :pub_price => info[:pub_price],
151
+ :type => info[:type],
152
+ }
153
+ if pharmacode
154
+ @refdata[pharmacode] = entry
155
+ else
156
+ @refdata[ean13] = entry
157
+ end
158
+ obj = entry
159
+ end
160
+ if not found and obj.size > 0
161
+ @articles << obj unless @options[:artikelstamm]
162
+ nrAdded += 1
153
163
  end
154
- obj = entry
155
- end
156
- if not found and obj.size > 0
157
- @articles << obj
158
- nrAdded += 1
159
164
  end
160
- }
161
165
  end
162
166
  end
163
167
  Oddb2xml.log("prepare_articles done. Added #{nrAdded} prices. Total #{@articles.size}")
@@ -175,7 +179,7 @@ module Oddb2xml
175
179
  @substances.uniq!
176
180
  @substances.sort!
177
181
  Oddb2xml.log("prepare_substances done. Total #{@substances.size} from #{@items.size} items")
178
- exit 2 if @options[:extended] and @substances.size == 0
182
+ exit 2 if (@options[:extended] || @options[:artikelstamm]) and @substances.size == 0
179
183
  end
180
184
  end
181
185
  def prepare_limitations
@@ -220,12 +224,11 @@ module Oddb2xml
220
224
  @refdata.each_pair do |ean13, item|
221
225
  next if item and item.is_a?(Hash) and item[:atc_code] and /^Q/i.match(item[:atc_code])
222
226
  next if item[:prodno] and @products[item[:prodno]]
223
- next unless ean13.to_s.length == 13
224
227
  obj = {
225
- :seq => @items[ean13] ? @items[ean13] : @items[item[:ean]],
228
+ :seq => @items[ean13] ? @items[ean13] : @items[item[:ean13]],
226
229
  :pac => nil,
227
230
  :no8 => nil,
228
- :ean => item[:ean],
231
+ :ean13 => item[:ean13],
229
232
  :atc => item[:atc_code],
230
233
  :ith => '',
231
234
  :siz => '',
@@ -233,14 +236,17 @@ module Oddb2xml
233
236
  :sub => '',
234
237
  :comp => '',
235
238
  }
236
- if obj[:ean] # via EAN-Code
237
- obj[:no8] = obj[:ean].to_s[4..11]
239
+ # obj[:pexf_refdata] = item[:price]
240
+ # obj[:ppub_refdata] = item[:pub_price=]
241
+ # obj[:pharmacode=] = item[:pharmacode=]
242
+ if obj[:ean13] # via EAN-Code
243
+ obj[:no8] = obj[:ean13].to_s[4..11]
238
244
  end
239
- if obj[:no8] and ppac = @packs[obj[:no8].intern] and # Packungen.xls
245
+ if obj[:no8] && (ppac = @packs[obj[:no8]]) && # Packungen.xls
240
246
  !ppac[:is_tier]
241
247
  # If RefData does not have EAN
242
- if obj[:ean].nil?
243
- obj[:ean] = ppac[:ean].to_i
248
+ if obj[:ean13].nil?
249
+ obj[:ean13] = ppac[:ean13]
244
250
  end
245
251
  # If RefData dose not have ATC-Code
246
252
  if obj[:atc].nil? or obj[:atc].empty?
@@ -252,7 +258,10 @@ module Oddb2xml
252
258
  obj[:sub] = ppac[:substance_swissmedic]
253
259
  obj[:comp] = ppac[:composition_swissmedic]
254
260
  end
255
- if obj[:ean].to_s[0..3] == '7680'
261
+ obj[:price] = item[:price]
262
+ obj[:pub_price] = item[:pub_price]
263
+
264
+ if obj[:ean13].to_s[0..3] == '7680'
256
265
  if item[:prodno]
257
266
  @products[item[:prodno]] = obj
258
267
  else
@@ -272,7 +281,7 @@ module Oddb2xml
272
281
  XML_OPTIONS
273
282
  ) {
274
283
  Oddb2xml.log "build_substance #{@substances.size} substances"
275
- exit 2 if @options[:extended] and @substances.size == 0
284
+ exit 2 if (@options[:extended] || @options[:artikelstamm]) and @substances.size == 0
276
285
  nbr_records = 0
277
286
  @substances.each_with_index do |sub_name, i|
278
287
  xml.SB('DT' => '') do
@@ -429,34 +438,55 @@ module Oddb2xml
429
438
  end
430
439
  Oddb2xml.add_hash(_builder.to_xml)
431
440
  end
432
- def add_missing_products_from_swissmedic
441
+ def add_missing_products_from_swissmedic(add_to_products = false)
433
442
  Oddb2xml.log "build_product add_missing_products_from_swissmedic. Starting with #{@products.size} products and #{@packs.size} @packs"
434
443
  ean13_to_product = {}
435
444
  @products.each{
436
445
  |ean13, obj|
437
- ean13_to_product[ean13] = obj
446
+ ean13_to_product[ean13] = obj
447
+ obj[:pharmacode] ||= @refdata[ean13][:pharmacode]
438
448
  }
439
449
  ausgabe = File.open(File.join(WorkDir, 'missing_in_refdata.txt'), 'w+')
440
450
  size_old = ean13_to_product.size
441
451
  @missing = []
442
452
  Oddb2xml.log "build_product add_missing_products_from_swissmedic. Imported #{size_old} ean13_to_product from @products. Checking #{@packs.size} @packs"
443
- @packs.each_with_index {
444
- |de_idx, i|
445
- ean = de_idx[1][:ean].to_i
446
- next if @refdata[ean]
447
- list_code = de_idx[1][:list_code]
448
- next if list_code and /Tierarzneimittel/.match(list_code)
449
- ean13_to_product[ean] = de_idx[1]
450
- @missing << de_idx[1]
451
- ausgabe.puts "#{ean},#{de_idx[1][:sequence_name]}"
452
- }
453
+ @packs.each_with_index do |de_idx, index|
454
+ ean = de_idx[1][:ean13]
455
+ next if @refdata[ean]
456
+ list_code = de_idx[1][:list_code]
457
+ next if list_code and /Tierarzneimittel/.match(list_code)
458
+ if add_to_products
459
+ @products[ean] = { :seq=>
460
+ {:name_de => de_idx.last[:sequence_name],
461
+ :desc_de => '',
462
+ :name_fr => '',
463
+ :desc_fr => '',
464
+ :atc_code => de_idx.last[:atc_code],
465
+ },
466
+ :pac=>nil,
467
+ :sequence_name => de_idx.last[:sequence_name],
468
+ :no8=> de_idx.last[:prodno],
469
+ :ean13=> ean,
470
+ :atc=> de_idx.last[:atc_code],
471
+ :ith=> de_idx.last[:ith_swissmedic],
472
+ :siz=> de_idx.last[:package_size],
473
+ :eht=> de_idx.last[:einheit_swissmedic],
474
+ :sub=> de_idx.last[:substance_swissmedic],
475
+ :comp=> de_idx.last[:composition_swissmedic],
476
+ :drug_index => de_idx.last[:drug_index],
477
+ }
478
+ end
479
+ ean13_to_product[ean] = de_idx[1]
480
+ @missing << de_idx[1]
481
+ ausgabe.puts "#{ean},#{de_idx[1][:sequence_name]}"
482
+ end
453
483
  corrected_size = ean13_to_product.size
454
484
  Oddb2xml.log "build_product add_missing_products_from_swissmedic. Added #{(corrected_size - size_old)} corrected_size #{corrected_size} size_old #{size_old} ean13_to_product."
455
485
  end
456
486
 
457
487
  def build_product
458
488
  def check_name(obj, lang = :de)
459
- ean = obj[:ean].to_i
489
+ ean = obj[:ean13]
460
490
  refdata = @refdata[ean]
461
491
  if lang == :de
462
492
  name = (refdata && refdata[:desc_de]) ? refdata[:desc_de] : obj[:sequence_name]
@@ -482,7 +512,7 @@ module Oddb2xml
482
512
  xml.PRODUCT(XML_OPTIONS) {
483
513
  list = []
484
514
  @missing.each do |obj|
485
- ean = obj[:ean].to_i
515
+ ean = obj[:ean13]
486
516
  next unless check_name(obj, :de)
487
517
  next unless check_name(obj, :fr)
488
518
  next if /^Q/i.match(obj[:atc])
@@ -496,7 +526,6 @@ module Oddb2xml
496
526
  xml.PRODNO obj[:prodno] if obj[:prodno]
497
527
  xml.DSCRD check_name(obj, :de)
498
528
  xml.DSCRF check_name(obj, :fr)
499
- xml.ATC obj[:atc_code] if obj[:atc_code] and !obj[:atc_code].empty?
500
529
  xml.IT obj[:ith_swissmedic] if obj[:ith_swissmedic]
501
530
  xml.CPT
502
531
  xml.PackGrSwissmedic obj[:package_size] if obj[:package_size]
@@ -505,17 +534,16 @@ module Oddb2xml
505
534
  xml.CompositionSwissmedic obj[:composition_swissmedic] if obj[:composition_swissmedic]
506
535
  }
507
536
  end
508
- # @products.sort! { |a,b| a[:ean] <=> b[:ean] }
509
- @products.each do |ean13, obj|
537
+ @products.sort.to_h.each do |ean13, obj|
510
538
  next if /^Q/i.match(obj[:atc])
511
539
  seq = obj[:seq]
512
- ean = obj[:ean]
540
+ ean = obj[:ean13]
513
541
  next unless check_name(obj, :de)
514
542
  next unless check_name(obj, :fr)
515
543
  xml.PRD('DT' => obj[:last_change]) do
516
544
  nbr_products += 1
517
545
  xml.GTIN ean
518
- ppac = ((_ppac = @packs[ean.to_s[4..11].intern] and !_ppac[:is_tier]) ? _ppac : {})
546
+ ppac = ((_ppac = @packs[ean.to_s[4..11]] and !_ppac[:is_tier]) ? _ppac : {})
519
547
  unless ppac
520
548
  ppac = @packs.find{|pac| pac.ean == ean }.first
521
549
  end
@@ -528,9 +556,9 @@ module Oddb2xml
528
556
  #xml.ADNAMF
529
557
  #xml.SIZE
530
558
  if seq
531
- xml.ADINFD seq[:comment_de] unless seq[:comment_de].empty?
532
- xml.ADINFF seq[:comment_fr] unless seq[:comment_fr].empty?
533
- xml.GENCD seq[:org_gen_code] unless seq[:org_gen_code].empty?
559
+ xml.ADINFD seq[:comment_de] unless seq[:comment_de] && seq[:comment_de].empty?
560
+ xml.ADINFF seq[:comment_fr] unless seq[:comment_fr] && seq[:comment_fr].empty?
561
+ xml.GENCD seq[:org_gen_code] unless seq[:org_gen_code] && seq[:org_gen_code].empty?
534
562
  end
535
563
  #xml.GENGRP
536
564
  xml.ATC obj[:atc] unless obj[:atc].empty?
@@ -637,6 +665,61 @@ module Oddb2xml
637
665
  Oddb2xml.add_hash(_builder.to_xml)
638
666
  end
639
667
 
668
+ def prepare_calc_items(suppress_composition_parsing: false)
669
+ @calc_items = {}
670
+ packungen_xlsx = File.join(Oddb2xml::WorkDir, "swissmedic_package.xlsx")
671
+ idx = 0
672
+ return unless File.exists?(packungen_xlsx)
673
+ workbook = RubyXL::Parser.parse(packungen_xlsx)
674
+ row_nr = 0
675
+ workbook.worksheets[0].each do |row|
676
+ row_nr += 1
677
+ next unless row and row.cells[0] and row.cells[0].value and row.cells[0].value.to_i > 0
678
+ iksnr = "%05i" % row.cells[0].value.to_i
679
+ seqnr = "%02d" % row.cells[1].value.to_i
680
+ if row_nr % 250 == 0
681
+ puts "#{Time.now}: At row #{row_nr} iksnr #{iksnr}";
682
+ $stdout.flush
683
+ end
684
+ ith = COLUMNS_JULY_2015.keys.index(:index_therapeuticus)
685
+ seq_name = COLUMNS_JULY_2015.keys.index(:name_base)
686
+ i_3 = COLUMNS_JULY_2015.keys.index(:ikscd)
687
+ p_1_2 = COLUMNS_JULY_2015.keys.index(:seqnr)
688
+ cat = COLUMNS_JULY_2015.keys.index(:ikscat)
689
+ siz = COLUMNS_JULY_2015.keys.index(:size)
690
+ atc = COLUMNS_JULY_2015.keys.index(:atc_class)
691
+ list_code = COLUMNS_JULY_2015.keys.index(:production_science)
692
+ unit = COLUMNS_JULY_2015.keys.index(:unit)
693
+ sub = COLUMNS_JULY_2015.keys.index(:substances)
694
+ comp = COLUMNS_JULY_2015.keys.index(:composition)
695
+
696
+ no8 = iksnr + sprintf('%03d',row.cells[i_3].value.to_i)
697
+ name = row.cells[seq_name] ? row.cells[seq_name].value : nil
698
+ atc_code = row.cells[atc] ? row.cells[atc].value : nil
699
+ list_code = row.cells[list_code] ? row.cells[list_code].value : nil
700
+ package_size = row.cells[siz] ? row.cells[siz].value : nil
701
+ unit = row.cells[unit] ? row.cells[unit].value : nil
702
+ active_substance = row.cells[sub] ? row.cells[sub].value : nil
703
+ composition = row.cells[comp] ? row.cells[comp].value : nil
704
+
705
+ # skip veterinary product
706
+ next if atc_code and /^Q/i.match(atc_code)
707
+ next if list_code and /Tierarzneimittel/.match(list_code)
708
+ info = nil
709
+ begin
710
+ if suppress_composition_parsing
711
+ info = Calc.new(name, package_size, unit)
712
+ else
713
+ info = Calc.new(name, package_size, unit, active_substance, composition)
714
+ end
715
+ rescue
716
+ puts "#{Time.now}: #{row_nr} iksnr #{iksnr} rescue from Calc.new"
717
+ end
718
+ ean12 = '7680' + no8
719
+ ean13 = (ean12 + Oddb2xml.calc_checksum(ean12))
720
+ @calc_items[ean13] = info
721
+ end
722
+ end
640
723
  def build_calc
641
724
  def emit_substance(xml, substance, emit_active=false)
642
725
  xml.MORE_INFO substance.more_info if substance.more_info
@@ -656,69 +739,22 @@ module Oddb2xml
656
739
  }
657
740
  end
658
741
  if substance.salts and substance.salts.size > 0
659
- xml.SALTS {
660
- substance.salts.each { |salt|
661
- xml.SALT {
742
+ xml.SALTS do
743
+ substance.salts.each do |salt|
744
+ xml.SALT do
662
745
  emit_substance(xml, salt)
663
- }
664
- }
665
- }
666
-
746
+ end
747
+ end
748
+ end
667
749
  end
668
750
  end
669
- packungen_xlsx = File.join(Oddb2xml::WorkDir, "swissmedic_package.xlsx")
670
- idx = 0
671
- return unless File.exists?(packungen_xlsx)
672
- workbook = RubyXL::Parser.parse(packungen_xlsx)
673
- items = {}
674
- row_nr = 0
751
+ prepare_calc_items
675
752
  _builder = Nokogiri::XML::Builder.new(:encoding => 'utf-8') do |xml|
676
753
  xml.doc.tag_suffix = @tag_suffix
677
754
  datetime = Time.new.strftime('%FT%T%z')
678
- xml.ARTICLES(XML_OPTIONS) {
679
- workbook.worksheets[0].each do |row|
680
- row_nr += 1
681
- next unless row and row.cells[0] and row.cells[0].value and row.cells[0].value.to_i > 0
682
- iksnr = "%05i" % row.cells[0].value.to_i
683
- seqnr = "%02d" % row.cells[1].value.to_i
684
- if row_nr % 250 == 0
685
- puts "#{Time.now}: At row #{row_nr} iksnr #{iksnr}";
686
- $stdout.flush
687
- end
688
- ith = COLUMNS_JULY_2015.keys.index(:index_therapeuticus)
689
- seq_name = COLUMNS_JULY_2015.keys.index(:name_base)
690
- i_3 = COLUMNS_JULY_2015.keys.index(:ikscd)
691
- p_1_2 = COLUMNS_JULY_2015.keys.index(:seqnr)
692
- cat = COLUMNS_JULY_2015.keys.index(:ikscat)
693
- siz = COLUMNS_JULY_2015.keys.index(:size)
694
- atc = COLUMNS_JULY_2015.keys.index(:atc_class)
695
- list_code = COLUMNS_JULY_2015.keys.index(:production_science)
696
- unit = COLUMNS_JULY_2015.keys.index(:unit)
697
- sub = COLUMNS_JULY_2015.keys.index(:substances)
698
- comp = COLUMNS_JULY_2015.keys.index(:composition)
699
-
700
- no8 = iksnr + sprintf('%03d',row.cells[i_3].value.to_i)
701
- name = row.cells[seq_name] ? row.cells[seq_name].value : nil
702
- atc_code = row.cells[atc] ? row.cells[atc].value : nil
703
- list_code = row.cells[list_code] ? row.cells[list_code].value : nil
704
- package_size = row.cells[siz] ? row.cells[siz].value : nil
705
- unit = row.cells[unit] ? row.cells[unit].value : nil
706
- active_substance = row.cells[sub] ? row.cells[sub].value : nil
707
- composition = row.cells[comp] ? row.cells[comp].value : nil
708
-
709
- # skip veterinary product
710
- next if atc_code and /^Q/i.match(atc_code)
711
- next if list_code and /Tierarzneimittel/.match(list_code)
712
- begin
713
- info = Calc.new(name, package_size, unit, active_substance, composition)
714
- rescue
715
- puts "#{Time.now}: #{row_nr} iksnr #{iksnr} rescue from Calc.new"
716
- info = nil
717
- end
718
- ean12 = '7680' + no8
719
- ean13 = (ean12 + Oddb2xml.calc_checksum(ean12))
720
- items[ean13] = info
721
- xml.ARTICLE {
755
+ xml.ARTICLES(XML_OPTIONS) do
756
+ @calc_items.each do |ean13, info|
757
+ xml.ARTICLE do
722
758
  xml.GTIN ean13
723
759
  xml.NAME info.column_c
724
760
  xml.PKG_SIZE info.pkg_size
@@ -732,29 +768,30 @@ module Oddb2xml
732
768
  xml.GALENIC_FORM info.galenic_form.description
733
769
  xml.GALENIC_GROUP info.galenic_group ? info.galenic_group.description : "Unknown"
734
770
  end
735
- xml.COMPOSITIONS {
736
- info.compositions.each { |composition|
737
- xml.COMPOSITION {
771
+ xml.COMPOSITIONS do
772
+ info.compositions.each do |composition|
773
+ xml.COMPOSITION do
738
774
  xml.EXCIPIENS { emit_substance(xml, composition.excipiens) } if composition.excipiens
739
775
  xml.LABEL composition.label if composition.label
740
776
  xml.LABEL_DESCRIPTION composition.label_description if composition.label_description
741
777
  xml.CORRESP composition.corresp if composition.corresp
742
778
  if composition.substances and composition.substances.size > 0
743
- xml.SUBSTANCES {
744
- composition.substances.each { |substance| xml.SUBSTANCE { emit_substance(xml, substance, true) }}
745
- }
779
+ xml.SUBSTANCES do
780
+ composition.substances.each { |substance| xml.SUBSTANCE { emit_substance(xml, substance, true) }}
781
+ end
746
782
  end
747
- }
748
- }
749
- }
750
- } if info and info.compositions
783
+ end
784
+ end
785
+ end
786
+ end if info and info.compositions
751
787
  end
752
- }
788
+ end
753
789
  end
790
+
754
791
  csv_name = File.join(WorkDir, 'oddb_calc.csv')
755
792
  CSV.open(csv_name, "w+", :col_sep => ';') do |csv|
756
- csv << ['gtin'] + items.values.first.headers
757
- items.each do |key, value|
793
+ csv << ['gtin'] + @calc_items.values.first.headers
794
+ @calc_items.each do |key, value|
758
795
  if value and value.to_array
759
796
  csv << [key] + value.to_array
760
797
  else
@@ -764,67 +801,67 @@ module Oddb2xml
764
801
  end
765
802
  Oddb2xml.add_hash(_builder.to_xml)
766
803
  end
804
+
767
805
  def build_article
768
806
  prepare_limitations
769
807
  prepare_articles
770
808
  idx = 0
771
809
  nbr_records = 0
772
- @preparations_only = []
773
810
  Oddb2xml.log "build_article #{idx} of #{@articles.size} articles"
774
811
  _builder = Nokogiri::XML::Builder.new(:encoding => 'utf-8') do |xml|
775
812
  xml.doc.tag_suffix = @tag_suffix
776
813
  datetime = Time.new.strftime('%FT%T%z')
777
- eans_from_refdata = @articles.collect{|refdata| refdata[:ean] }
814
+ eans_from_refdata = @articles.collect{|refdata| refdata[:ean13] }
778
815
  eans_from_preparations = @items.keys
779
816
  missing_eans = []
780
817
  eans_from_preparations.each do |ean|
781
818
  next if ean.to_i == 0
782
- unless eans_from_refdata.index(ean)
783
- @preparations_only << ean
784
- missing_eans << ean
819
+ unless @articles.collect {|x| x[:ean13] }.index(ean)
785
820
  item = @items[ean].clone
786
- next if defined?(RSpec) && !item[:pharmacode]
787
- item[:ean] = ean
821
+ item[:pharmacode] ||= '123456' if defined?(RSpec)
822
+ item[:ean13] = ean
788
823
  item[:_type] = :preparations_xml
789
824
  item[:desc_de] = item[:name_de] + ' ' + item[:desc_de]
790
825
  item[:desc_fr] = item[:name_fr] + ' ' + item[:desc_fr]
791
826
  @articles << item
792
827
  end
828
+ unless eans_from_refdata.index(ean)
829
+ missing_eans << ean
830
+ end
793
831
  end
794
832
  xml.ARTICLE(XML_OPTIONS) {
795
- @articles.sort! { |a,b| a[:ean] <=> b[:ean] }
833
+ @articles.sort! { |a,b| a[:ean13] <=> b[:ean13] }
796
834
  @articles.each do |obj|
797
835
  idx += 1
798
836
  Oddb2xml.log "build_article #{idx} of #{@articles.size} articles" if idx % 500 == 0
799
- item = @items[obj[:ean]]
800
- pac,no8 = nil,obj[:ean].to_s[4..11] # BAG-XML(SL/LS)
801
- pack_info = nil
802
- pack_info = @packs[no8.intern] if no8 # info from Packungen.xlsx from swissmedic_info
837
+ item = @items[obj[:ean13]]
838
+ pac,no8 = nil,obj[:ean13].to_s[4..11] # BAG-XML(SL/LS)
839
+ pack_info = nil
840
+ pack_info = @packs[no8] if no8 # info from Packungen.xlsx from swissmedic_info
803
841
  ppac = nil # Packungen
804
- ean = obj[:ean]
842
+ ean = obj[:ean13]
805
843
  next if pack_info and /Tierarzneimittel/.match(pack_info[:list_code])
806
844
  next if obj[:desc_de] and /ad us vet/i.match(obj[:desc_de])
807
- next if obj[:desc_de] and obj[:desc_de].length < 3
808
- next if obj[:desc_fr] and obj[:desc_fr].length < 3
809
845
  pharma_code = obj[:pharmacode]
810
- ean = 0 if sprintf('%013d', ean).match(/^000000/)
846
+ # ean = 0 if sprintf('%013d', ean).match(/^000000/)
811
847
  if obj[:seq]
812
848
  pac = obj[:seq][:packages][obj[:pharmacode]]
813
849
  pac = obj[:seq][:packages][ean] unless pac
814
- elsif ean > 0
850
+ else
815
851
  pac = @items[ean][:packages][ean] if @items and ean and @items[ean] and @items[ean][:packages]
816
852
  end
817
853
  if no8
818
854
  ppac = ((_ppac = pack_info and !_ppac[:is_tier]) ? _ppac : nil)
819
855
  end
820
- info_zur_rose = nil
856
+ zur_rose = nil
821
857
  if !@infos_zur_rose.empty? && ean && @infos_zur_rose[ean]
822
- info_zur_rose = @infos_zur_rose[ean] # zurrose
858
+ zur_rose = @infos_zur_rose[ean] # zurrose
823
859
  end
824
860
  xml.ART('DT' => obj[:last_change] ? obj[:last_change] : '') do
825
861
  nbr_records += 1
826
862
  xml.REF_DATA (obj[:refdata] || @migel[pharma_code]) ? '1' : '0'
827
- xml.PHAR sprintf('%07d', obj[:pharmacode]) if obj[:pharmacode]
863
+ # xml.PHAR sprintf('%07d', obj[:pharmacode]) if obj[:pharmacode]
864
+ xml.PHAR obj[:pharmacode] if obj[:pharmacode]
828
865
  #xml.GRPCD
829
866
  #xml.CDS01
830
867
  #xml.CDS02
@@ -845,12 +882,10 @@ module Oddb2xml
845
882
  #xml.HOSPCD
846
883
  #xml.CLINCD
847
884
  #xml.ARTTYP
848
- if info_zur_rose
849
- xml.VAT info_zur_rose[:vat]
885
+ if zur_rose
886
+ xml.VAT zur_rose[:vat]
850
887
  end
851
-
852
- nincd = detect_nincd(obj)
853
- (nincd and nincd == 13) ? xml.SALECD('A') : xml.SALECD( (info_zur_rose && info_zur_rose[:cmut] != '3') ? 'A' : 'I') # XML_OPTIONS
888
+ emit_salecd(xml, ean, obj)
854
889
  if pac and pac[:limitation_points]
855
890
  #xml.INSLIM
856
891
  xml.LIMPTS pac[:limitation_points] unless pac[:limitation_points].empty?
@@ -865,6 +900,9 @@ module Oddb2xml
865
900
  xml.BG(flag ? 'Y' : 'N')
866
901
  end
867
902
  #xml.EXP
903
+ if item and item[:substances] and substance = item[:substances].first
904
+ xml.QTY "#{substance[:quantity]}#{substance[:unit] ? ' ' + substance[:unit] : ''}"
905
+ end if false # TODO: get qty/unit from refdata name
868
906
  xml.DSCRD obj[:desc_de] if obj[:desc_de] and not obj[:desc_de].empty?
869
907
  xml.DSCRF obj[:desc_fr] if obj[:desc_fr] and not obj[:desc_fr].empty?
870
908
  xml.DSCRF obj[:desc_de] if !obj[:desc_fr] or obj[:desc_fr].empty?
@@ -922,7 +960,7 @@ module Oddb2xml
922
960
  }
923
961
  xml.ARTBAR {
924
962
  xml.CDTYP 'E13'
925
- xml.BC /^9999|^0000|^0$/.match(ean.to_s) ? 0 : sprintf('%013d', ean)
963
+ xml.BC ean # /^9999|^0000|^0$/.match(ean.to_s) ? 0 : sprintf('%013d', ean)
926
964
  xml.BCSTAT 'A' # P is alternative
927
965
  #xml.PHAR2
928
966
  } if ean
@@ -941,21 +979,22 @@ module Oddb2xml
941
979
  }
942
980
  end
943
981
  end
944
- if info_zur_rose
945
- price = info_zur_rose[:price]
982
+ if zur_rose
983
+ price = zur_rose[:price]
946
984
  xml.ARTPRI {
947
985
  xml.PTYP "ZURROSE"
948
986
  xml.PRICE price
949
987
  }
950
988
  xml.ARTPRI {
951
989
  xml.PTYP "ZURROSEPUB"
952
- xml.PRICE info_zur_rose[:pub_price]
990
+ xml.PRICE zur_rose[:pub_price]
953
991
  }
954
992
  xml.ARTPRI {
955
993
  xml.PTYP "RESELLERPUB"
956
994
  xml.PRICE (price.to_f*(1 + (@options[:percent].to_f/100))).round_by(0.05).round(2)
957
995
  } if @options[:percent] != nil
958
996
  end
997
+ nincd = detect_nincd(obj)
959
998
  if nincd
960
999
  xml.ARTINS {
961
1000
  xml.NINCD nincd
@@ -971,7 +1010,7 @@ module Oddb2xml
971
1010
  }
972
1011
  }
973
1012
  end
974
- Oddb2xml.log "build_article. Done #{idx} of #{@articles.size} articles " + ( @preparations_only.size > 0 ? ('. Only in preparations.xml '+ @preparations_only.join(' ')) : '')
1013
+ Oddb2xml.log "build_article. Done #{idx} of #{@articles.size} articles"
975
1014
  Oddb2xml.add_hash(_builder.to_xml)
976
1015
  end
977
1016
  def build_fi
@@ -1121,19 +1160,18 @@ module Oddb2xml
1121
1160
  Oddb2xml.add_hash(_builder.to_xml)
1122
1161
  end
1123
1162
  def detect_nincd(de_idx)
1124
- if @lppvs[de_idx[:ean].to_s] # LPPV
1163
+ if @lppvs[de_idx[:ean13].to_s] # LPPV
1125
1164
  20
1126
1165
  elsif @items[de_idx[:pharmacode]] # BAG-XML (SL/LS)
1127
1166
  10
1128
1167
  elsif (de_idx[:migel] or # MiGel (xls)
1129
- de_idx[:_type] == :preparations_xml or
1130
1168
  de_idx[:_type] == :nonpharma) # MiGel (swissindex)
1131
1169
  13
1132
1170
  else
1133
1171
  # fallback via EAN
1134
- bag_entry_via_ean = @items.values.select do |i|
1135
- next unless i[:packages]
1136
- i[:packages].values.select {|_pac| _pac[:ean].to_s == de_idx[:ean].to_s }.length != 0
1172
+ bag_entry_via_ean = @items.values.select do |item|
1173
+ next unless item[:packages]
1174
+ item[:packages].values.select {|_pac| _pac[:ean13].to_s.eql?(de_idx[:ean13].to_s) }.length != 0
1137
1175
  end
1138
1176
  if bag_entry_via_ean.length > 0
1139
1177
  10
@@ -1191,12 +1229,12 @@ module Oddb2xml
1191
1229
  prepare_articles
1192
1230
  rows = []
1193
1231
  @articles.each do |obj|
1194
- ean = obj[:ean]
1232
+ ean = obj[:ean13]
1195
1233
  next if ((ean.to_s.length != 13) and !ean14)
1196
1234
  next if obj[:type] == :nonpharma
1197
1235
  row = ''
1198
1236
  pack_info = nil
1199
- if (x = @packs.find{|k,v| v[:ean].to_i == ean})
1237
+ if (x = @packs.find{|k,v| v[:ean13].eql?(ean)})
1200
1238
  pack_info = x[1]
1201
1239
  end
1202
1240
  # Oddb2tdat.parse
@@ -1209,16 +1247,16 @@ module Oddb2xml
1209
1247
  end
1210
1248
  # :swissmedic_numbers
1211
1249
  if pac
1212
- no8 = pac[:swissmedic_number8].intern
1250
+ no8 = pac[:swissmedic_number8]
1213
1251
  end
1214
1252
  if pac and pac[:prices] == nil and no8
1215
1253
  ppac = ((ppac = pack_info and ppac[:is_tier]) ? ppac : nil)
1216
1254
  pac = ppac if ppac
1217
1255
  end
1218
1256
  row << "%#{DAT_LEN[:RECA]}s" % '11'
1219
- info_zur_rose = @infos_zur_rose[ean] # zurrose
1220
- if info_zur_rose && info_zur_rose[:cmut]
1221
- row << info_zur_rose[:cmut]
1257
+ zur_rose = @infos_zur_rose[ean] # zurrose
1258
+ if zur_rose && zur_rose[:cmut]
1259
+ row << zur_rose[:cmut]
1222
1260
  else
1223
1261
  row << '1'
1224
1262
  end
@@ -1285,7 +1323,7 @@ module Oddb2xml
1285
1323
  rows = []
1286
1324
  @articles.each do |obj|
1287
1325
  row = ''
1288
- next if ((obj[:ean].to_s.length != 13) and !ean14)
1326
+ next if ((obj[:ean13].to_s.length != 13) and !ean14)
1289
1327
  # Oddb2tdat.parse_migel
1290
1328
  row << "%#{DAT_LEN[:RECA]}s" % '11'
1291
1329
  row << "%#{DAT_LEN[:CMUT]}s" % if (phar = obj[:pharmacode] and phar.size > 3)
@@ -1306,11 +1344,324 @@ module Oddb2xml
1306
1344
  row << "%#{DAT_LEN[:CBGG]}s" % '0'
1307
1345
  row << "%#{DAT_LEN[:CIKS]}s" % ' ' # no category
1308
1346
  row << "%0#{DAT_LEN[:ITHE]}d" % 0
1309
- row << obj[:ean].to_s.rjust(DAT_LEN[:CEAN], '0')
1347
+ row << obj[:ean13].to_s.rjust(DAT_LEN[:CEAN], '0')
1310
1348
  row << "%#{DAT_LEN[:CMWS]}s" % '1' # nonpharma
1311
1349
  rows << row
1312
1350
  end
1313
1351
  rows.join("\n")
1314
1352
  end
1353
+ def emit_salecd(xml, ean13, obj)
1354
+ zur_rose = nil
1355
+ if !@infos_zur_rose.empty? && ean13 && @infos_zur_rose[ean13]
1356
+ zur_rose = @infos_zur_rose[ean13] # zurrose
1357
+ end
1358
+ nincd = detect_nincd(obj)
1359
+ status = (nincd && nincd == 13) ? 'A' : (zur_rose && zur_rose[:cmut] != '3') ? 'A' : 'I'
1360
+ xml.SALECD(status) { xml.comment( "expiry_date #{obj[:expiry_date]}") if obj[:expiry_date] }
1361
+ end
1362
+
1363
+ def build_artikelstamm
1364
+ @@emitted_v5_gtins = []
1365
+ @csv_file = CSV.open(File.join(WorkDir, "Elexis_Artikelstamm_v5.csv"), "w+")
1366
+ @csv_file << ['gtin', 'price', 'galenic_form', 'pkg_size', 'pexf', 'ppub', 'iksnr', 'atc_code', 'active_substance', 'original', 'it-code', 'sl-liste']
1367
+ @csv_file.sync = true
1368
+ variant = "build_artikelstamm"
1369
+ # @infos_zur_rose.delete_if { |key, val| val[:cmut].eql?('3') } # collect only active zur rose item
1370
+ # No. Marco did not filter it, eg. 8804121 in rtikelstamm_oddb2xml_051217_v5.xm
1371
+ def check_name(obj, lang = :de)
1372
+ ean = obj[:ean13]
1373
+ refdata = @refdata[ean]
1374
+ if lang == :de
1375
+ name = (refdata && refdata[:desc_de]) ? refdata[:desc_de] : obj[:sequence_name]
1376
+ elsif lang == :fr
1377
+ name = (refdata && refdata[:desc_fr]) ? refdata[:desc_fr] : obj[:sequence_name]
1378
+ else
1379
+ return '--missing--'
1380
+ end
1381
+ return '--missing--' if !name || name.empty? || name.length < 3
1382
+ name
1383
+ end
1384
+ def override(xml, id, field, default_value)
1385
+ has_overrides = /\d{13}/.match(id.to_s) ? @@article_overrides[id.to_i] : @@product_overrides[id.to_i]
1386
+ unless (has_overrides && has_overrides[field.to_s])
1387
+ cmd = "xml.#{field} \"#{default_value.to_s.gsub('"','')}\""
1388
+ else
1389
+ new_value = has_overrides[field.to_s]
1390
+ if new_value.to_s.eql?(default_value.to_s)
1391
+ xml.comment('obsolete override')
1392
+ cmd = "xml.#{field} \"#{new_value}\""
1393
+ else
1394
+ xml.comment("override #{default_value.to_s} with")
1395
+ cmd ="xml.#{field} \"#{new_value}\""
1396
+ end
1397
+ end
1398
+ eval cmd if default_value
1399
+ end
1400
+ def emit_items(xml)
1401
+ nr_items = 0
1402
+ gtins_to_article = {}
1403
+ @articles.each {|article| gtins_to_article[article[:ean13]] = article }
1404
+ gtins = gtins_to_article.keys + @infos_zur_rose.keys + @packs.values.collect{|x| x[:ean13]}
1405
+ gtins = (gtins-@@gtin2ignore)
1406
+ gtins.sort!.uniq!
1407
+ @nr_items = gtins.size
1408
+ gtins.each do |ean13|
1409
+ pac,no8 = nil,ean13.to_s[4..11] # BAG-XML(SL/LS)
1410
+ next if ean13 == 0
1411
+ obj = gtins_to_article[ean13] || @infos_zur_rose[ean13]
1412
+ if obj
1413
+ obj = @packs[no8].merge(obj) if @packs[no8]
1414
+ else
1415
+ obj = @packs[no8] # obj not yet in refdata. Use data from swissmedic_package.xlsx
1416
+ end
1417
+ nr_items += 1
1418
+ Oddb2xml.log "build_article #{nr_items} of #{gtins.size} articles" if nr_items % 5000 == 0
1419
+ item = @items[ean13]
1420
+ pack_info = nil
1421
+ pack_info = @packs[no8] if no8 && /#{ean13}/.match(@packs[no8].to_s) # info from Packungen.xlsx from swissmedic_info
1422
+ next if pack_info && /Tierarzneimittel/.match(pack_info[:list_code])
1423
+ next if obj[:desc_de] && /ad us vet/i.match(obj[:desc_de])
1424
+ sequence = obj[:seq]
1425
+ unless sequence
1426
+ if @packs[no8] && /#{ean13}/.match(@packs[no8].to_s)
1427
+ sequence = {:packages =>{ean13 => @packs[no8]}}
1428
+ obj[:seq] = sequence.clone
1429
+ end
1430
+ end
1431
+ if no8
1432
+ ppac = ((_ppac = pack_info and !_ppac[:is_tier]) ? _ppac : nil)
1433
+ end
1434
+ if sequence
1435
+ unless obj[:seq][:packages].keys.index(ean13)
1436
+ # puts "unable to find #{ean13} in #{obj[:seq][:packages].keys}"
1437
+ next
1438
+ end
1439
+ sequence[:packages].each do |gtin, package|
1440
+ pkg_gtin = package[:ean13].clone
1441
+ pharma_code = @refdata[pkg_gtin]
1442
+ if @refdata[pkg_gtin] && @refdata[pkg_gtin][:pharmacode]
1443
+ pharma_code = @refdata[pkg_gtin][:pharmacode]
1444
+ else
1445
+ pharma_code = obj[:pharmacode]
1446
+ end
1447
+ #
1448
+ info = @calc_items[pkg_gtin]
1449
+ if @@emitted_v5_gtins.index(pkg_gtin)
1450
+ next
1451
+ else
1452
+ @@emitted_v5_gtins << pkg_gtin.clone
1453
+ end
1454
+ options = {'PHARMATYPE' => 'P'}
1455
+ xml.ITEM(options) do
1456
+ name = (@refdata[pkg_gtin] ? @refdata[pkg_gtin][:desc_de] : nil) || obj[:desc_de] || obj[:sequence_name]
1457
+ xml.GTIN pkg_gtin.to_s.rjust(13, '0')
1458
+ override(xml, pkg_gtin, :PHAR, pharma_code)
1459
+ xml.SALECD('A')
1460
+ # maxLength for DSCR is 50 for Artikelstamm v3
1461
+ xml.DSCR(name) # for description for zur_rose
1462
+ xml.DSCRF(obj[:desc_fr] || '--missing--')
1463
+ xml.COMP do # Manufacturer
1464
+ xml.NAME obj[:company_name]
1465
+ xml.GLN obj[:company_ean]
1466
+ end
1467
+ pexf = ppub = nil
1468
+ if package[:prices]
1469
+ pexf ||= package[:prices][:exf_price][:price]
1470
+ ppub ||= package[:prices][:pub_price][:price]
1471
+ elsif @items[ean13] && @items[ean13][:packages] && @items[ean13][:packages][ean13] && (bag_prices = @items[ean13][:packages][ean13][:prices])
1472
+ pexf ||= bag_prices[:exf_price][:price]
1473
+ ppub ||= bag_prices[:pub_price][:price]
1474
+ else
1475
+ pexf ||= obj[:price]
1476
+ ppub ||= obj[:pub_price]
1477
+ end
1478
+ ppub = nil if ppub && ppub.size == 0
1479
+ pexf = nil if pexf && pexf.size == 0
1480
+ xml.PEXF pexf if pexf
1481
+ xml.PPUB ppub if ppub
1482
+ measure = ''
1483
+ if info
1484
+ # MEASSURE Measurement Unit,e.g. Pills or milliliters
1485
+ # <DSCR>HIRUDOID Creme 3 mg/g 40 g</DSCR>
1486
+ xml.PKG_SIZE info.pkg_size.to_i if info.pkg_size
1487
+ if info.measure
1488
+ measure = info.measure
1489
+ elsif info.pkg_size && info.unit
1490
+ measure = info.pkg_size + ' ' + info.unit
1491
+ elsif info.pkg_size
1492
+ measure = info.pkg_size
1493
+ end
1494
+ xml.MEASURE measure
1495
+ xml.MEASUREF measure
1496
+ # Die Darreichungsform dieses Items. zB Tablette(n) oder Spritze(n)
1497
+ xml.DOSAGE_FORM info.galenic_form.descriptions['de'] if info.galenic_form.descriptions['de']
1498
+ xml.DOSAGE_FORMF info.galenic_form.descriptions['fr'] if info.galenic_form.descriptions['fr']
1499
+ end
1500
+ xml.SL_ENTRY 'true' if @items[pkg_gtin]
1501
+ xml.IKSCAT package[:swissmedic_category] if package[:swissmedic_category] && package[:swissmedic_category].length > 0
1502
+ xml.GENERIC_TYPE sequence[:org_gen_code] if sequence[:org_gen_code] && !sequence[:org_gen_code].empty?
1503
+ xml.LPPV 'true' if @lppvs[pkg_gtin.to_s] # detect_nincd
1504
+ case item[:deductible]
1505
+ when 'Y'; xml.DEDUCTIBLE 20; # 20%
1506
+ when 'N'; xml.DEDUCTIBLE 10; # 10%
1507
+ else # xml.DEDUCTIBLE '' # k.A.
1508
+ end if item && item[:deductible]
1509
+ xml.PRODNO ppac[:prodno] if ppac && ppac[:prodno] # pkg_gtin.to_s[4..11]
1510
+ csv = []
1511
+ @csv_file << [pkg_gtin, name, package[:unit], measure,
1512
+ pexf ? pexf : '',
1513
+ ppub ? ppub : '',
1514
+ package[:prodno], package[:atc_code], package[:substance_swissmedic],
1515
+ sequence[:org_gen_code], package[:ith_swissmedic],
1516
+ @items[pkg_gtin] ? 'SL' : '',
1517
+ ]
1518
+ end
1519
+ end
1520
+ else # non pharma
1521
+ @csv_file << [ ean13, (obj[:desc_de] || obj[:description]), '', '',
1522
+ obj[:price], obj[:pub_price], '', '', '', '', '', '' ]
1523
+ if @@emitted_v5_gtins.index(ean13)
1524
+ next
1525
+ else
1526
+ @@emitted_v5_gtins << ean13.clone
1527
+ end
1528
+ # Set the pharmatype to 'Y' for outdated products, which are no longer found
1529
+ # in refdata/packungen
1530
+ patched_pharma_type = (/^7680/.match(ean13.to_s.rjust(13, '0')) ? 'P': 'N' )
1531
+ xml.ITEM({'PHARMATYPE' => patched_pharma_type }) do
1532
+ xml.GTIN ean13.to_s.rjust(13, '0')
1533
+ xml.PHAR obj[:pharmacode]
1534
+ emit_salecd(xml, ean13, obj)
1535
+ xml.DSCR(obj[:desc_de] || obj[:description]) # for description for zur_rose
1536
+ xml.DSCRF(obj[:desc_fr] || '--missing--')
1537
+ xml.COMP do
1538
+ xml.GLN obj[:company_ean]
1539
+ end if obj[:company_ean] && !obj[:company_ean].empty?
1540
+ xml.PEXF obj[:price] if obj[:price] && !obj[:price].empty?
1541
+ xml.PPUB obj[:pub_price] if obj[:pub_price] && !obj[:pub_price].empty?
1542
+ end
1543
+ end
1544
+ end
1545
+ @csv_file.close if @csv_file && !@csv_file.closed?
1546
+ nr_items
1547
+ end
1548
+ unless @prepared
1549
+ prepare_limitations
1550
+ prepare_articles
1551
+ prepare_products
1552
+ add_missing_products_from_swissmedic(true)
1553
+ prepare_calc_items(suppress_composition_parsing: true)
1554
+ @prepared = true
1555
+ @old_rose_size = @infos_zur_rose.size
1556
+ @infos_zur_rose.each do |ean13, value|
1557
+ if /^7680/.match(ean13.to_s) && @options[:artikelstamm]
1558
+ @infos_zur_rose.delete(ean13)
1559
+ end
1560
+ end if false
1561
+ @new_rose_size = @infos_zur_rose.size
1562
+ end
1563
+ nr_products = 0
1564
+ nr_articles = 0
1565
+ @nr_articles = 0
1566
+ used_limitations = []
1567
+ Oddb2xml.log "#{variant}: Deleted #{@old_rose_size - @new_rose_size} entries from ZurRose where GTIN start with 7680 (Swissmedic)"
1568
+ # Oddb2xml.log "#{variant} #{nr_products} of #{@products.size} articles and ignore #{@@gtin2ignore.size} GTINS specified via #{@@ignore_file}"
1569
+ _builder = Nokogiri::XML::Builder.new(:encoding => 'utf-8') do |xml|
1570
+ xml.doc.tag_suffix = @tag_suffix
1571
+ datetime = Time.new.strftime('%FT%T%z')
1572
+ elexis_strftime_format = "%FT%T\.%L%:z"
1573
+ @@cumul_ver = (Date.today.year-2013)*12+Date.today.month
1574
+ options_xml = {
1575
+ 'xmlns' => 'http://elexis.ch/Elexis_Artikelstamm_v5',
1576
+ 'CREATION_DATETIME' => Time.new.strftime(elexis_strftime_format),
1577
+ 'BUILD_DATETIME' => Time.new.strftime(elexis_strftime_format),
1578
+ 'DATA_SOURCE' => 'oddb2xml'
1579
+ }
1580
+ emitted_prodno = []
1581
+ xml.comment("Produced by #{__FILE__} version #{VERSION} at #{Time.now}")
1582
+ xml.ARTIKELSTAMM(options_xml) do
1583
+ xml.PRODUCTS do
1584
+ products = @products.sort_by { |ean13, obj| ean13 }
1585
+ products.each do |product|
1586
+ ean13 = product[0]
1587
+ obj = product[1]
1588
+ next if /^Q/i.match(obj[:atc])
1589
+ sequence = obj[:seq]
1590
+ ean = obj[:ean13]
1591
+ next unless check_name(obj, :de)
1592
+ ppac = ((_ppac = @packs[ean.to_s[4..11]] and !_ppac[:is_tier]) ? _ppac : {})
1593
+ unless ppac
1594
+ ppac = @packs.find{|pac| pac.ean == ean }.first
1595
+ end
1596
+ prodno = ppac[:prodno] if ppac[:prodno] and !ppac[:prodno].empty?
1597
+ next unless prodno
1598
+ next unless sequence && sequence[:name_de]
1599
+ next if emitted_prodno.index(prodno)
1600
+ emitted_prodno << prodno
1601
+ nr_products += 1
1602
+ xml.PRODUCT do
1603
+ xml.PRODNO prodno
1604
+ if sequence
1605
+ xml.SALECD('A') # these products are always active!
1606
+ override(xml, prodno, :DSCR, (sequence[:name_de] + ' ' + sequence[:desc_de]).strip)
1607
+ override(xml, prodno, :DSCRF, (sequence[:name_fr] + ' ' + sequence[:desc_fr]).strip)
1608
+ end
1609
+ xml.ATC sequence[:atc_code] if sequence[:atc_code] && !sequence[:atc_code].empty?
1610
+ if sequence[:packages] && (first_package = sequence[:packages].values.first) &&
1611
+ (first_limitation = first_package[:limitations].first)
1612
+ lim_code = first_limitation[:code]
1613
+ used_limitations << lim_code unless used_limitations.index(lim_code)
1614
+ xml.LIMNAMEBAG lim_code
1615
+ end
1616
+ if sequence && sequence[:substances]
1617
+ value = nil
1618
+ if sequence[:substances].size > 1
1619
+ value = 'Verschiedene Kombinationen'
1620
+ elsif sequence[:substances].first
1621
+ value = sequence[:substances].first[:name]
1622
+ else
1623
+ value = obj[:sub]
1624
+ end
1625
+ override(xml, prodno, :SUBSTANCE, value) if value
1626
+ end
1627
+ end
1628
+ end
1629
+ end
1630
+ emitted_lim_code = []
1631
+ xml.LIMITATIONS do
1632
+ @limitations.sort! { |left, right| left[:code] <=> right[:code] }
1633
+ @limitations.each do |lim|
1634
+ next unless used_limitations.index(lim[:code])
1635
+ next if emitted_lim_code.index(lim[:code])
1636
+ emitted_lim_code << lim[:code]
1637
+ xml.LIMITATION do
1638
+ xml.LIMNAMEBAG lim[:code] # original LIMCD
1639
+ xml.DSCR lim[:desc_de]
1640
+ xml.DSCRF lim[:desc_fr]
1641
+ xml.LIMITATION_PTS (lim[:value].to_s.length > 1 ? lim[:value] : 1)
1642
+ end
1643
+ end
1644
+ end
1645
+ xml.ITEMS do
1646
+ nr_articles = emit_items(xml)
1647
+ end
1648
+ end
1649
+ end
1650
+ Oddb2xml.log "#{variant}. Done #{nr_products} of #{@products.size} products, #{@limitations.size} limitations and #{nr_articles}/#{@nr_articles} articles. @@emitted_v5_gtins #{@@emitted_v5_gtins.size}"
1651
+ # we don't add a SHA256 hash for each element in the article
1652
+ # Oddb2xml.add_hash(_builder.to_xml)
1653
+ # doc = REXML::Document.new( source, { :raw => :all })
1654
+ # doc.write( $stdout, 0 )
1655
+ lines = []
1656
+ lines << " - #{sprintf('%5d', @products.size)} products"
1657
+ lines << " - #{sprintf('%5d', @limitations.size)} limitations"
1658
+ lines << " - #{sprintf('%5d', @nr_articles)} articles"
1659
+ lines << " - #{sprintf('%5d', @@gtin2ignore.size)} ignored GTINS"
1660
+ @@articlestamm_v5_info_lines = lines
1661
+ _builder.to_xml({:indent => 4, :encoding => 'UTF-8'})
1662
+ end
1663
+ def self.articlestamm_v5_info_lines
1664
+ @@articlestamm_v5_info_lines
1665
+ end
1315
1666
  end
1316
1667
  end