oddb2xml 2.7.1 → 2.7.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +1 -2
- data/.standard.yml +2 -0
- data/Gemfile +3 -3
- data/History.txt +24 -0
- data/README.md +3 -3
- data/Rakefile +24 -23
- data/bin/check_artikelstamm +11 -11
- data/bin/compare_v5 +23 -23
- data/bin/oddb2xml +14 -13
- data/lib/oddb2xml/builder.rb +1070 -1038
- data/lib/oddb2xml/calc.rb +232 -233
- data/lib/oddb2xml/chapter_70_hack.rb +38 -32
- data/lib/oddb2xml/cli.rb +252 -236
- data/lib/oddb2xml/compare.rb +70 -59
- data/lib/oddb2xml/compositions_syntax.rb +451 -430
- data/lib/oddb2xml/compressor.rb +20 -20
- data/lib/oddb2xml/downloader.rb +157 -129
- data/lib/oddb2xml/extractor.rb +295 -295
- data/lib/oddb2xml/options.rb +34 -35
- data/lib/oddb2xml/parslet_compositions.rb +265 -269
- data/lib/oddb2xml/semantic_check.rb +39 -33
- data/lib/oddb2xml/util.rb +163 -163
- data/lib/oddb2xml/version.rb +1 -1
- data/lib/oddb2xml/xml_definitions.rb +32 -33
- data/lib/oddb2xml.rb +1 -1
- data/oddb2xml.gemspec +34 -34
- data/shell.nix +17 -0
- data/spec/artikelstamm_spec.rb +111 -110
- data/spec/builder_spec.rb +490 -505
- data/spec/calc_spec.rb +552 -593
- data/spec/check_artikelstamm_spec.rb +26 -26
- data/spec/cli_spec.rb +173 -174
- data/spec/compare_spec.rb +9 -11
- data/spec/composition_syntax_spec.rb +390 -409
- data/spec/compressor_spec.rb +48 -48
- data/spec/data/transfer.dat +1 -0
- data/spec/data_helper.rb +47 -49
- data/spec/downloader_spec.rb +251 -260
- data/spec/extractor_spec.rb +171 -159
- data/spec/fixtures/vcr_cassettes/oddb2xml.json +1 -1
- data/spec/galenic_spec.rb +233 -256
- data/spec/options_spec.rb +116 -119
- data/spec/parslet_spec.rb +896 -863
- data/spec/spec_helper.rb +153 -153
- data/test_options.rb +39 -42
- data/tools/win_fetch_cacerts.rb +2 -3
- metadata +42 -12
data/lib/oddb2xml/builder.rb
CHANGED
@@ -1,21 +1,22 @@
|
|
1
|
-
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
5
|
-
|
1
|
+
require "nokogiri"
|
2
|
+
require "oddb2xml/util"
|
3
|
+
require "oddb2xml/calc"
|
4
|
+
require "csv"
|
5
|
+
|
6
6
|
class Numeric
|
7
7
|
# round a given number to the nearest step
|
8
8
|
def round_by(increment)
|
9
9
|
(self / increment).round * increment
|
10
10
|
end
|
11
11
|
end
|
12
|
+
|
12
13
|
module Nokogiri
|
13
14
|
module XML
|
14
15
|
class Document < Nokogiri::XML::Node
|
15
16
|
attr_writer :tag_suffix
|
16
|
-
|
17
|
+
alias_method :create_element_origin, :create_element
|
17
18
|
def create_element name, *args, &block
|
18
|
-
name += (@tag_suffix ||
|
19
|
+
name += (@tag_suffix || "")
|
19
20
|
create_element_origin(name, *args, &block)
|
20
21
|
end
|
21
22
|
end
|
@@ -24,108 +25,116 @@ end
|
|
24
25
|
|
25
26
|
module Oddb2xml
|
26
27
|
XML_OPTIONS = {
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
28
|
+
"xmlns:xsd" => "http://www.w3.org/2001/XMLSchema",
|
29
|
+
"xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance",
|
30
|
+
"xmlns" => "http://wiki.oddb.org/wiki.php?pagename=Swissmedic.Datendeklaration",
|
31
|
+
"CREATION_DATETIME" => Time.new.strftime("%FT%T%z"),
|
32
|
+
"PROD_DATE" => Time.new.strftime("%FT%T%z"),
|
33
|
+
"VALID_DATE" => Time.new.strftime("%FT%T%z"),
|
34
|
+
"GENERATED_BY" => "oddb2xml #{VERSION}"
|
34
35
|
}
|
35
36
|
class Builder
|
36
|
-
Data_dir = File.expand_path(File.join(File.dirname(__FILE__),
|
37
|
-
@@article_overrides
|
38
|
-
@@product_overrides
|
39
|
-
@@ignore_file
|
40
|
-
@@gtin2ignore
|
37
|
+
Data_dir = File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "data"))
|
38
|
+
@@article_overrides = YAML.load_file(File.join(Data_dir, "article_overrides.yaml"))
|
39
|
+
@@product_overrides = YAML.load_file(File.join(Data_dir, "product_overrides.yaml"))
|
40
|
+
@@ignore_file = File.join(Data_dir, "gtin2ignore.yaml")
|
41
|
+
@@gtin2ignore = YAML.load_file(@@ignore_file) if File.exist?(@@ignore_file)
|
41
42
|
@@gtin2ignore ||= []
|
42
43
|
attr_accessor :subject, :refdata, :items, :flags, :lppvs,
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
44
|
+
:actions, :migel, :orphan,
|
45
|
+
:infos, :packs, :infos_zur_rose,
|
46
|
+
:ean14, :tag_suffix,
|
47
|
+
:companies, :people,
|
48
|
+
:xsd
|
48
49
|
def initialize(args = {})
|
49
|
-
@options
|
50
|
-
@subject
|
51
|
-
@refdata
|
52
|
-
@items
|
53
|
-
@flags
|
54
|
-
@lppvs
|
55
|
-
@infos
|
56
|
-
@packs
|
57
|
-
@migel
|
58
|
-
@infos_zur_rose
|
59
|
-
@actions
|
60
|
-
@orphan
|
61
|
-
@ean14
|
62
|
-
@companies
|
63
|
-
@people
|
50
|
+
@options = args
|
51
|
+
@subject = nil
|
52
|
+
@refdata = {}
|
53
|
+
@items = {} # Spezailitäteniste: SL-Items from Preparations.xml in BAG, using GTINS as key
|
54
|
+
@flags = {}
|
55
|
+
@lppvs = {}
|
56
|
+
@infos = {}
|
57
|
+
@packs = {}
|
58
|
+
@migel = {}
|
59
|
+
@infos_zur_rose ||= {}
|
60
|
+
@actions = []
|
61
|
+
@orphan = []
|
62
|
+
@ean14 = false
|
63
|
+
@companies = []
|
64
|
+
@people = []
|
64
65
|
@tag_suffix = nil
|
65
66
|
@pharmacode = {} # index pharmacode => item
|
66
67
|
if block_given?
|
67
68
|
yield self
|
68
69
|
end
|
69
70
|
end
|
70
|
-
|
71
|
+
|
72
|
+
def to_xml(subject = nil)
|
71
73
|
Oddb2xml.log "to_xml subject #{subject || @subject}"
|
72
74
|
if subject
|
73
|
-
|
75
|
+
send("build_" + subject.to_s)
|
74
76
|
elsif @subject
|
75
|
-
|
77
|
+
send("build_" + @subject.to_s)
|
76
78
|
end
|
77
79
|
end
|
78
|
-
|
79
|
-
|
80
|
+
|
81
|
+
def to_dat(subject = nil)
|
82
|
+
Oddb2xml.log "to_dat subject #{subject ? subject.to_s : @subject.to_s} for #{self.class}"
|
80
83
|
if subject
|
81
|
-
|
84
|
+
send("build_" + subject.to_s)
|
82
85
|
elsif @subject
|
83
|
-
|
86
|
+
send("build_" + @subject.to_s)
|
84
87
|
end
|
85
88
|
end
|
86
|
-
|
87
|
-
|
89
|
+
|
90
|
+
private_class_method
|
91
|
+
|
92
|
+
def prepare_articles(reset = false)
|
88
93
|
@articles = nil if reset
|
89
94
|
unless @articles
|
90
|
-
Oddb2xml.log("prepare_articles starting with #{@articles ? @articles.size :
|
95
|
+
Oddb2xml.log("prepare_articles starting with #{@articles ? @articles.size : "no"} articles.")
|
91
96
|
@articles = []
|
92
97
|
@refdata.each do |ean13, obj|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
+
unless SKIP_MIGEL_DOWNLOADER
|
99
|
+
if @migel[ean13]
|
100
|
+
# delete duplicates
|
101
|
+
@migel[ean13] = nil
|
102
|
+
end
|
103
|
+
end
|
104
|
+
if (seq = @items[obj[:ean13]])
|
98
105
|
obj[:seq] = seq.clone
|
99
106
|
end
|
100
107
|
@articles << obj
|
101
108
|
@pharmacode[obj[:pharmacode]] = obj
|
102
109
|
end
|
103
110
|
# add
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
111
|
+
unless SKIP_MIGEL_DOWNLOADER
|
112
|
+
@migel.values.compact.each do |migel|
|
113
|
+
next unless migel[:pharmacode]
|
114
|
+
entry = {
|
115
|
+
ean13: migel[:ean13],
|
116
|
+
pharmacode: migel[:pharmacode],
|
117
|
+
stat_date: "",
|
118
|
+
desc_de: migel[:desc_de],
|
119
|
+
desc_fr: migel[:desc_fr],
|
120
|
+
atc_code: "",
|
121
|
+
quantity: migel[:quantity],
|
122
|
+
company_ean: migel[:company_ean],
|
123
|
+
company_name: migel[:company_name],
|
124
|
+
migel: true
|
117
125
|
}
|
118
|
-
|
119
|
-
|
120
|
-
|
126
|
+
@articles << entry
|
127
|
+
end
|
128
|
+
end
|
129
|
+
nr_added = 0
|
121
130
|
if @options[:extended] || @options[:artikelstamm]
|
122
131
|
Oddb2xml.log("prepare_articles extended prepare_local_index having already #{@articles.size} articles")
|
123
|
-
|
132
|
+
nr_items = 0
|
124
133
|
@infos_zur_rose.each do |ean13, info|
|
125
|
-
|
134
|
+
nr_items += 1
|
126
135
|
pharmacode = info[:pharmacode]
|
127
136
|
if @pharmacode[pharmacode]
|
128
|
-
@pharmacode[pharmacode][:price]
|
137
|
+
@pharmacode[pharmacode][:price] = info[:price]
|
129
138
|
@pharmacode[pharmacode][:pub_price] = info[:pub_price]
|
130
139
|
next
|
131
140
|
end
|
@@ -134,20 +143,20 @@ module Oddb2xml
|
|
134
143
|
existing = @refdata[ean13]
|
135
144
|
if existing
|
136
145
|
found = true
|
137
|
-
existing[:price]
|
146
|
+
existing[:price] = info[:price]
|
138
147
|
existing[:pub_price] = info[:pub_price]
|
139
148
|
else
|
140
149
|
entry = {
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
150
|
+
desc: info[:description],
|
151
|
+
desc_de: info[:description],
|
152
|
+
status: info[:status] == "3" ? "I" : "A", # from ZurRose, we got 1,2 or 3 means aktive, aka available in trade
|
153
|
+
atc_code: "",
|
154
|
+
ean13: ean13,
|
155
|
+
pharmacode: pharmacode,
|
156
|
+
price: info[:price],
|
157
|
+
pub_price: info[:pub_price],
|
158
|
+
type: info[:type]
|
159
|
+
}
|
151
160
|
if pharmacode
|
152
161
|
@refdata[pharmacode] = entry
|
153
162
|
else
|
@@ -155,15 +164,16 @@ module Oddb2xml
|
|
155
164
|
end
|
156
165
|
obj = entry
|
157
166
|
end
|
158
|
-
if
|
167
|
+
if !found && (obj.size > 0)
|
159
168
|
@articles << obj unless @options[:artikelstamm]
|
160
|
-
|
169
|
+
nr_added += 1
|
161
170
|
end
|
162
171
|
end
|
163
172
|
end
|
164
173
|
end
|
165
|
-
Oddb2xml.log("prepare_articles done. Added #{
|
174
|
+
Oddb2xml.log("prepare_articles done. Added #{nr_added} prices. Total #{@articles.size}")
|
166
175
|
end
|
176
|
+
|
167
177
|
def prepare_substances
|
168
178
|
unless @substances
|
169
179
|
Oddb2xml.log("prepare_substances from #{@items.size} items")
|
@@ -177,9 +187,10 @@ module Oddb2xml
|
|
177
187
|
@substances.uniq!
|
178
188
|
@substances.sort!
|
179
189
|
Oddb2xml.log("prepare_substances done. Total #{@substances.size} from #{@items.size} items")
|
180
|
-
exit 2 if (@options[:extended] || @options[:artikelstamm])
|
190
|
+
exit 2 if (@options[:extended] || @options[:artikelstamm]) && (@substances.size == 0)
|
181
191
|
end
|
182
192
|
end
|
193
|
+
|
183
194
|
def prepare_limitations
|
184
195
|
unless @limitations
|
185
196
|
Oddb2xml.log("prepare_limitations from #{@items.size} items")
|
@@ -193,10 +204,11 @@ module Oddb2xml
|
|
193
204
|
# ID is no longer fixed TAG (swissmedicNo8, swissmedicNo5, pharmacode)
|
194
205
|
# limitation.xml needs all duplicate entries by this keys.
|
195
206
|
limitations.uniq! { |lim| lim[:id].to_s + lim[:code] + lim[:type] }
|
196
|
-
@limitations = limitations.sort_by {|lim| lim[:code] }
|
207
|
+
@limitations = limitations.sort_by { |lim| lim[:code] }
|
197
208
|
Oddb2xml.log("prepare_limitations done. Total #{@limitations.size} from #{@items.size} items")
|
198
209
|
end
|
199
210
|
end
|
211
|
+
|
200
212
|
def prepare_interactions
|
201
213
|
unless @interactions
|
202
214
|
@interactions = []
|
@@ -205,60 +217,61 @@ module Oddb2xml
|
|
205
217
|
end
|
206
218
|
end
|
207
219
|
end
|
220
|
+
|
208
221
|
def prepare_codes
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
}
|
217
|
-
end
|
222
|
+
@codes ||= {
|
223
|
+
"X" => {int: 11, txt: "Kontraindiziert"},
|
224
|
+
"D" => {int: 13, txt: "Kombination meiden"},
|
225
|
+
"C" => {int: 14, txt: "Monitorisieren"},
|
226
|
+
"B" => {int: 15, txt: "Vorsichtsmassnahmen"},
|
227
|
+
"A" => {int: 16, txt: "keine Massnahmen"}
|
228
|
+
}
|
218
229
|
end
|
230
|
+
|
219
231
|
def prepare_products
|
220
232
|
unless @products
|
221
233
|
@products = {}
|
222
234
|
if @chapter70items
|
223
235
|
Chapter70xtractor::LIMITATIONS.each do |key, desc_de|
|
224
236
|
puts "Chapter70: Adding lim #{key} #{desc_de}" if $VERBOSE
|
225
|
-
@limitations<< {:
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
}
|
237
|
+
@limitations << {code: key,
|
238
|
+
id: (key.eql?("L") ? "70.02" : "70.01"),
|
239
|
+
desc_de: desc_de,
|
240
|
+
desc_fr: "",
|
241
|
+
chap70: true}
|
231
242
|
end
|
232
243
|
@chapter70items.values.each do |item|
|
233
244
|
next unless item[:limitation] && item[:limitation].length > 0
|
234
|
-
rose = @infos_zur_rose.values.find {|x| x[:pharmacode]
|
235
|
-
ean13 =
|
236
|
-
ean13 ||=
|
245
|
+
rose = @infos_zur_rose.values.find { |x| x[:pharmacode]&.eql?(item[:pharmacode]) }
|
246
|
+
ean13 = rose[:ean13] if rose
|
247
|
+
ean13 ||= "9999" + item[:pharmacode]
|
237
248
|
prodno = item[:pharmacode]
|
238
249
|
obj = {
|
239
|
-
:
|
240
|
-
:
|
241
|
-
:
|
242
|
-
:
|
250
|
+
chapter70: true,
|
251
|
+
ean13: ean13,
|
252
|
+
description: item[:description],
|
253
|
+
code: item[:limitation] # LIMNAMEBAG
|
243
254
|
}
|
244
255
|
@products[prodno] = obj
|
245
256
|
puts "Chapter70: Adding product #{ean13} #{obj}" if $VERBOSE
|
246
257
|
end
|
247
258
|
end
|
248
259
|
@refdata.each_pair do |ean13, item|
|
249
|
-
next if item
|
250
|
-
next if item[:prodno]
|
260
|
+
next if item&.is_a?(Hash) && item[:atc_code] && /^Q/i.match(item[:atc_code])
|
261
|
+
next if item[:prodno] && @products[item[:prodno]]
|
262
|
+
refdata_atc = item[:atc_code]
|
251
263
|
obj = {
|
252
|
-
:
|
253
|
-
:
|
254
|
-
:
|
255
|
-
:
|
256
|
-
:
|
257
|
-
:
|
258
|
-
:
|
259
|
-
:
|
260
|
-
:
|
261
|
-
:
|
264
|
+
seq: @items[ean13] || @items[item[:ean13]],
|
265
|
+
pac: nil,
|
266
|
+
no8: nil,
|
267
|
+
ean13: item[:ean13],
|
268
|
+
atc: refdata_atc,
|
269
|
+
ith: "",
|
270
|
+
siz: "",
|
271
|
+
eht: "",
|
272
|
+
sub: "",
|
273
|
+
comp: "",
|
274
|
+
data_origin: "refdata-product"
|
262
275
|
}
|
263
276
|
# obj[:pexf_refdata] = item[:price]
|
264
277
|
# obj[:ppub_refdata] = item[:pub_price=]
|
@@ -266,6 +279,15 @@ module Oddb2xml
|
|
266
279
|
if obj[:ean13] # via EAN-Code
|
267
280
|
obj[:no8] = obj[:ean13].to_s[4..11]
|
268
281
|
end
|
282
|
+
swissmedic_pack = @packs[item[:no8]]
|
283
|
+
if swissmedic_pack
|
284
|
+
swissmedic_atc = swissmedic_pack[:atc_code]
|
285
|
+
if swissmedic_atc && swissmedic_atc.length >= 3 && (refdata_atc.nil? || !refdata_atc.eql?(swissmedic_atc))
|
286
|
+
puts "WARNING: #{ean13} ATC-code #{swissmedic_atc} from swissmedic overrides #{refdata_atc} one from refdata #{item[:desc_de]}"
|
287
|
+
item[:data_origin] += "-swissmedic-ATC"
|
288
|
+
item[:atc] = swissmedic_atc
|
289
|
+
end
|
290
|
+
end
|
269
291
|
if obj[:no8] && (ppac = @packs[obj[:no8]]) && # Packungen.xls
|
270
292
|
!ppac[:is_tier]
|
271
293
|
# If RefData does not have EAN
|
@@ -273,7 +295,7 @@ module Oddb2xml
|
|
273
295
|
obj[:ean13] = ppac[:ean13]
|
274
296
|
end
|
275
297
|
# If RefData dose not have ATC-Code
|
276
|
-
if obj[:atc].nil?
|
298
|
+
if obj[:atc].nil? || obj[:atc].empty?
|
277
299
|
obj[:atc] = ppac[:atc_code].to_s
|
278
300
|
end
|
279
301
|
obj[:ith] = ppac[:ith_swissmedic]
|
@@ -285,7 +307,7 @@ module Oddb2xml
|
|
285
307
|
obj[:price] = item[:price]
|
286
308
|
obj[:pub_price] = item[:pub_price]
|
287
309
|
|
288
|
-
if obj[:ean13].to_s[0..3] ==
|
310
|
+
if obj[:ean13].to_s[0..3] == "7680"
|
289
311
|
if item[:prodno]
|
290
312
|
@products[item[:prodno]] = obj
|
291
313
|
else
|
@@ -296,118 +318,118 @@ module Oddb2xml
|
|
296
318
|
end
|
297
319
|
@products
|
298
320
|
end
|
321
|
+
|
299
322
|
def build_substance
|
300
323
|
prepare_substances
|
301
|
-
|
324
|
+
a_builder = Nokogiri::XML::Builder.new(encoding: "utf-8") do |xml|
|
302
325
|
xml.doc.tag_suffix = @tag_suffix
|
303
|
-
datetime = Time.new.strftime('%FT%T%z')
|
304
326
|
xml.SUBSTANCE(
|
305
327
|
XML_OPTIONS
|
306
328
|
) {
|
307
329
|
Oddb2xml.log "build_substance #{@substances.size} substances"
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
xml.SB(
|
330
|
+
exit 2 if (@options[:extended] || @options[:artikelstamm]) && (@substances.size == 0)
|
331
|
+
nbr_records = 0
|
332
|
+
@substances.each_with_index do |sub_name, i|
|
333
|
+
xml.SB("DT" => "") do
|
312
334
|
xml.SUBNO((i + 1).to_i)
|
313
|
-
#xml.NAMD
|
314
|
-
#xml.ANAMD
|
315
|
-
#xml.NAMF
|
335
|
+
# xml.NAMD
|
336
|
+
# xml.ANAMD
|
337
|
+
# xml.NAMF
|
316
338
|
xml.NAML sub_name
|
317
339
|
nbr_records += 1
|
318
340
|
end
|
319
341
|
end
|
320
342
|
xml.RESULT {
|
321
|
-
xml.OK_ERROR
|
343
|
+
xml.OK_ERROR "OK"
|
322
344
|
xml.NBR_RECORD nbr_records
|
323
|
-
xml.ERROR_CODE
|
324
|
-
xml.MESSAGE
|
345
|
+
xml.ERROR_CODE ""
|
346
|
+
xml.MESSAGE ""
|
325
347
|
}
|
326
348
|
}
|
327
349
|
end
|
328
|
-
Oddb2xml.add_hash(
|
350
|
+
Oddb2xml.add_hash(a_builder.to_xml)
|
329
351
|
end
|
352
|
+
|
330
353
|
def build_limitation
|
331
354
|
prepare_limitations
|
332
355
|
Oddb2xml.log "build_limitation #{@limitations.size} limitations"
|
333
|
-
|
356
|
+
a_builder = Nokogiri::XML::Builder.new(encoding: "utf-8") do |xml|
|
334
357
|
xml.doc.tag_suffix = @tag_suffix
|
335
|
-
datetime = Time.new.strftime('%FT%T%z')
|
336
358
|
xml.LIMITATION(XML_OPTIONS) do
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
xml.LIM(
|
359
|
+
nbr_records = 0
|
360
|
+
@limitations.each do |lim|
|
361
|
+
if lim[:id].empty?
|
362
|
+
puts "Skipping empty id of #{lim}"
|
363
|
+
next
|
364
|
+
end
|
365
|
+
xml.LIM("DT" => "") do
|
344
366
|
case lim[:key]
|
345
367
|
when :swissmedic_number8
|
346
368
|
xml.SwissmedicNo8 lim[:id]
|
347
369
|
when :swissmedic_number5
|
348
370
|
xml.SwissmedicNo5 lim[:id]
|
349
371
|
when :pharmacode
|
350
|
-
xml.Pharmacode
|
372
|
+
xml.Pharmacode lim[:id]
|
351
373
|
end
|
352
|
-
xml.IT
|
353
|
-
xml.LIMTYP
|
354
|
-
xml.LIMVAL
|
374
|
+
xml.IT lim[:it]
|
375
|
+
xml.LIMTYP lim[:type]
|
376
|
+
xml.LIMVAL lim[:value]
|
355
377
|
xml.LIMNAMEBAG lim[:code] # original LIMCD
|
356
|
-
xml.LIMNIV
|
357
|
-
xml.DSCRD
|
358
|
-
xml.DSCRF
|
359
|
-
xml.VDAT
|
378
|
+
xml.LIMNIV lim[:niv]
|
379
|
+
xml.DSCRD lim[:desc_de]
|
380
|
+
xml.DSCRF lim[:desc_fr]
|
381
|
+
xml.VDAT lim[:vdate]
|
360
382
|
nbr_records += 1
|
361
383
|
end
|
362
384
|
end
|
363
385
|
xml.RESULT do
|
364
|
-
xml.OK_ERROR
|
386
|
+
xml.OK_ERROR "OK"
|
365
387
|
xml.NBR_RECORD nbr_records
|
366
|
-
xml.ERROR_CODE
|
367
|
-
xml.MESSAGE
|
388
|
+
xml.ERROR_CODE ""
|
389
|
+
xml.MESSAGE ""
|
368
390
|
end
|
369
391
|
end
|
370
392
|
end
|
371
|
-
Oddb2xml.add_hash(
|
393
|
+
Oddb2xml.add_hash(a_builder.to_xml)
|
372
394
|
end
|
395
|
+
|
373
396
|
def build_interaction
|
374
397
|
prepare_interactions
|
375
398
|
prepare_codes
|
376
399
|
nbr_records = 0
|
377
|
-
|
400
|
+
a_builder = Nokogiri::XML::Builder.new(encoding: "utf-8") do |xml|
|
378
401
|
xml.doc.tag_suffix = @tag_suffix
|
379
|
-
datetime = Time.new.strftime('%FT%T%z')
|
380
402
|
xml.INTERACTION(XML_OPTIONS) {
|
381
403
|
Oddb2xml.log "build_interaction #{@interactions.size} interactions"
|
382
|
-
@interactions.sort_by{|ix| ix[:ixno] }.each do |ix|
|
383
|
-
xml.IX(
|
384
|
-
xml.IXNO
|
385
|
-
xml.TITD
|
386
|
-
#xml.TITF
|
404
|
+
@interactions.sort_by { |ix| ix[:ixno] }.each do |ix|
|
405
|
+
xml.IX("DT" => "") {
|
406
|
+
xml.IXNO ix[:ixno]
|
407
|
+
xml.TITD ix[:title]
|
408
|
+
# xml.TITF
|
387
409
|
xml.GRP1D ix[:atc1]
|
388
|
-
#xml.GRP1F
|
410
|
+
# xml.GRP1F
|
389
411
|
xml.GRP2D ix[:atc2]
|
390
|
-
#xml.GRP2F
|
391
|
-
xml.EFFD
|
392
|
-
#xml.EFFF
|
393
|
-
if @codes
|
394
|
-
if dict = @codes[ix[:grad].upcase]
|
395
|
-
xml.RLV
|
412
|
+
# xml.GRP2F
|
413
|
+
xml.EFFD ix[:effect]
|
414
|
+
# xml.EFFF
|
415
|
+
if @codes && ix[:grad]
|
416
|
+
if (dict = @codes[ix[:grad].upcase])
|
417
|
+
xml.RLV dict[:int]
|
396
418
|
xml.RLVD dict[:txt]
|
397
|
-
#xml.RLVF
|
419
|
+
# xml.RLVF
|
398
420
|
end
|
399
421
|
end
|
400
|
-
#xml.EFFTXTD
|
401
|
-
#xml.EFFTXTF
|
422
|
+
# xml.EFFTXTD
|
423
|
+
# xml.EFFTXTF
|
402
424
|
xml.MECHD ix[:mechanism]
|
403
|
-
#xml.MECHF
|
425
|
+
# xml.MECHF
|
404
426
|
xml.MEASD ix[:measures]
|
405
|
-
#xml.MEASF
|
406
|
-
#xml.REMD
|
407
|
-
#xml.REMF
|
408
|
-
#xml.LIT
|
427
|
+
# xml.MEASF
|
428
|
+
# xml.REMD
|
429
|
+
# xml.REMF
|
430
|
+
# xml.LIT
|
409
431
|
xml.DEL false
|
410
|
-
#xml.IXMCH {
|
432
|
+
# xml.IXMCH {
|
411
433
|
# xml.TYP
|
412
434
|
# xml.TYPD
|
413
435
|
# xml.TYPF
|
@@ -416,61 +438,61 @@ module Oddb2xml
|
|
416
438
|
# xml.CDF
|
417
439
|
# xml.TXTD
|
418
440
|
# xml.TXTF
|
419
|
-
#}
|
441
|
+
# }
|
420
442
|
nbr_records += 1
|
421
443
|
}
|
422
444
|
end
|
423
445
|
xml.RESULT {
|
424
|
-
xml.OK_ERROR
|
446
|
+
xml.OK_ERROR "OK"
|
425
447
|
xml.NBR_RECORD nbr_records
|
426
|
-
xml.ERROR_CODE
|
427
|
-
xml.MESSAGE
|
448
|
+
xml.ERROR_CODE ""
|
449
|
+
xml.MESSAGE ""
|
428
450
|
}
|
429
451
|
}
|
430
452
|
end
|
431
|
-
Oddb2xml.add_hash(
|
453
|
+
Oddb2xml.add_hash(a_builder.to_xml)
|
432
454
|
end
|
455
|
+
|
433
456
|
def build_code
|
434
457
|
prepare_codes
|
435
458
|
nbr_records = 0
|
436
459
|
Oddb2xml.log "build_code #{@codes.size} codes"
|
437
|
-
|
460
|
+
a_builder = Nokogiri::XML::Builder.new(encoding: "utf-8") do |xml|
|
438
461
|
xml.doc.tag_suffix = @tag_suffix
|
439
|
-
datetime = Time.new.strftime('%FT%T%z')
|
440
462
|
xml.CODE(XML_OPTIONS) {
|
441
463
|
@codes.each_pair do |val, definition|
|
442
|
-
xml.CD(
|
443
|
-
xml.CDTYP
|
444
|
-
xml.CDVAL
|
445
|
-
xml.DSCRSD
|
446
|
-
#xml.DSCRSF
|
447
|
-
#xml.DSCRMD
|
448
|
-
#xml.DSCRMF
|
449
|
-
#xml.DSCRD
|
450
|
-
#xml.DSCRF
|
451
|
-
xml.DEL
|
464
|
+
xml.CD("DT" => "") {
|
465
|
+
xml.CDTYP definition[:int]
|
466
|
+
xml.CDVAL val
|
467
|
+
xml.DSCRSD definition[:txt]
|
468
|
+
# xml.DSCRSF
|
469
|
+
# xml.DSCRMD
|
470
|
+
# xml.DSCRMF
|
471
|
+
# xml.DSCRD
|
472
|
+
# xml.DSCRF
|
473
|
+
xml.DEL false
|
452
474
|
nbr_records += 1
|
453
475
|
}
|
454
476
|
end
|
455
477
|
xml.RESULT {
|
456
|
-
xml.OK_ERROR
|
478
|
+
xml.OK_ERROR "OK"
|
457
479
|
xml.NBR_RECORD nbr_records
|
458
|
-
xml.ERROR_CODE
|
459
|
-
xml.MESSAGE
|
480
|
+
xml.ERROR_CODE ""
|
481
|
+
xml.MESSAGE ""
|
460
482
|
}
|
461
483
|
}
|
462
484
|
end
|
463
|
-
Oddb2xml.add_hash(
|
485
|
+
Oddb2xml.add_hash(a_builder.to_xml)
|
464
486
|
end
|
487
|
+
|
465
488
|
def add_missing_products_from_swissmedic(add_to_products = false)
|
466
489
|
Oddb2xml.log "build_product add_missing_products_from_swissmedic. Starting with #{@products.size} products and #{@packs.size} @packs"
|
467
490
|
ean13_to_product = {}
|
468
|
-
@products.each{
|
469
|
-
|
470
|
-
|
471
|
-
obj[:pharmacode] ||= @refdata[ean13][:pharmacode] if @refdata[ean13]
|
491
|
+
@products.each { |ean13, obj|
|
492
|
+
ean13_to_product[ean13] = obj
|
493
|
+
obj[:pharmacode] ||= @refdata[ean13][:pharmacode] if @refdata[ean13]
|
472
494
|
}
|
473
|
-
ausgabe = File.open(File.join(
|
495
|
+
ausgabe = File.open(File.join(WORK_DIR, "missing_in_refdata.txt"), "w+")
|
474
496
|
size_old = ean13_to_product.size
|
475
497
|
@missing = []
|
476
498
|
Oddb2xml.log "build_product add_missing_products_from_swissmedic. Imported #{size_old} ean13_to_product from @products. Checking #{@packs.size} @packs"
|
@@ -478,49 +500,48 @@ module Oddb2xml
|
|
478
500
|
ean = de_idx[1][:ean13]
|
479
501
|
next if @refdata[ean]
|
480
502
|
list_code = de_idx[1][:list_code]
|
481
|
-
next if list_code
|
503
|
+
next if list_code && /Tierarzneimittel/.match(list_code)
|
482
504
|
if add_to_products
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
:comp=> de_idx.last[:composition_swissmedic],
|
500
|
-
:drug_index => de_idx.last[:drug_index],
|
501
|
-
}
|
505
|
+
@products[ean] = {seq: {name_de: de_idx.last[:sequence_name],
|
506
|
+
desc_de: "",
|
507
|
+
name_fr: "",
|
508
|
+
desc_fr: "",
|
509
|
+
atc_code: de_idx.last[:atc_code]},
|
510
|
+
pac: nil,
|
511
|
+
sequence_name: de_idx.last[:sequence_name],
|
512
|
+
no8: de_idx.last[:prodno],
|
513
|
+
ean13: ean,
|
514
|
+
atc: de_idx.last[:atc_code],
|
515
|
+
ith: de_idx.last[:ith_swissmedic],
|
516
|
+
siz: de_idx.last[:package_size],
|
517
|
+
eht: de_idx.last[:einheit_swissmedic],
|
518
|
+
sub: de_idx.last[:substance_swissmedic],
|
519
|
+
comp: de_idx.last[:composition_swissmedic],
|
520
|
+
drug_index: de_idx.last[:drug_index]}
|
502
521
|
end
|
503
522
|
ean13_to_product[ean] = de_idx[1]
|
504
523
|
@missing << de_idx[1]
|
505
524
|
ausgabe.puts "#{ean},#{de_idx[1][:sequence_name]}"
|
506
525
|
end
|
507
526
|
corrected_size = ean13_to_product.size
|
508
|
-
Oddb2xml.log "build_product add_missing_products_from_swissmedic. Added #{
|
527
|
+
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."
|
509
528
|
end
|
510
529
|
|
511
530
|
def build_product
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
531
|
+
self.class.class_eval do
|
532
|
+
def check_name(obj, lang = :de)
|
533
|
+
ean = obj[:ean13]
|
534
|
+
refdata = @refdata[ean]
|
535
|
+
if lang == :de
|
536
|
+
name = refdata && refdata[:desc_de] ? refdata[:desc_de] : obj[:sequence_name]
|
537
|
+
elsif lang == :fr
|
538
|
+
name = refdata && refdata[:desc_fr] ? refdata[:desc_fr] : obj[:sequence_name]
|
539
|
+
else
|
540
|
+
return false
|
541
|
+
end
|
542
|
+
return false if !name || name.empty? || name.length < 3
|
543
|
+
name[0..119] # limit to maximal 120 chars as specified in the XSD
|
521
544
|
end
|
522
|
-
return false if !name || name.empty? || name.length < 3
|
523
|
-
name[0..119] # limit to maximal 120 chars as specified in the XSD
|
524
545
|
end
|
525
546
|
prepare_substances
|
526
547
|
prepare_products
|
@@ -528,303 +549,297 @@ module Oddb2xml
|
|
528
549
|
prepare_codes
|
529
550
|
add_missing_products_from_swissmedic
|
530
551
|
nbr_products = 0
|
531
|
-
Oddb2xml.log "build_product #{@products.size
|
532
|
-
|
552
|
+
Oddb2xml.log "build_product #{@products.size + @missing.size} products"
|
553
|
+
a_builder = Nokogiri::XML::Builder.new(encoding: "utf-8") do |xml|
|
533
554
|
xml.doc.tag_suffix = @tag_suffix
|
534
|
-
datetime = Time.new.strftime('%FT%T%z')
|
535
555
|
emitted = []
|
536
556
|
xml.PRODUCT(XML_OPTIONS) {
|
537
|
-
list = []
|
538
557
|
@missing.each do |obj|
|
539
558
|
ean = obj[:ean13]
|
540
559
|
next unless check_name(obj, :de)
|
541
560
|
next unless check_name(obj, :fr)
|
542
|
-
next if /^Q/i.match(obj[:atc])
|
561
|
+
next if /^Q/i.match?(obj[:atc])
|
543
562
|
if obj[:prodno]
|
544
563
|
next if emitted.index(obj[:prodno])
|
545
564
|
emitted << obj[:prodno]
|
546
565
|
end
|
547
|
-
xml.PRD(
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
566
|
+
xml.PRD("DT" => obj[:last_change]) {
|
567
|
+
nbr_products += 1
|
568
|
+
xml.GTIN ean
|
569
|
+
xml.PRODNO obj[:prodno] if obj[:prodno]
|
570
|
+
xml.DSCRD check_name(obj, :de)
|
571
|
+
xml.DSCRF check_name(obj, :fr)
|
572
|
+
xml.ATC obj[:atc_code] unless obj[:atc_code].empty?
|
573
|
+
xml.IT obj[:ith_swissmedic] if obj[:ith_swissmedic]
|
574
|
+
xml.CPT
|
575
|
+
xml.PackGrSwissmedic obj[:package_size] if obj[:package_size]
|
576
|
+
xml.EinheitSwissmedic obj[:einheit_swissmedic] if obj[:einheit_swissmedic]
|
577
|
+
xml.SubstanceSwissmedic obj[:substance_swissmedic] if obj[:substance_swissmedic]
|
578
|
+
xml.CompositionSwissmedic obj[:composition_swissmedic] if obj[:composition_swissmedic]
|
579
|
+
}
|
561
580
|
end
|
562
581
|
@products.sort.to_h.each do |ean13, obj|
|
563
|
-
next if /^Q/i.match(obj[:atc])
|
582
|
+
next if /^Q/i.match?(obj[:atc])
|
564
583
|
seq = obj[:seq]
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
584
|
+
ean = obj[:ean13]
|
585
|
+
next unless check_name(obj, :de)
|
586
|
+
next unless check_name(obj, :fr)
|
587
|
+
xml.PRD("DT" => obj[:last_change]) do
|
569
588
|
nbr_products += 1
|
570
589
|
xml.GTIN ean
|
571
|
-
ppac = ((
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
xml.
|
576
|
-
xml.
|
577
|
-
xml.
|
578
|
-
#xml.
|
579
|
-
#xml.
|
580
|
-
#xml.
|
581
|
-
#xml.ADNAMF
|
582
|
-
#xml.SIZE
|
590
|
+
ppac = ((a_ppac = @packs[ean.to_s[4..11]]) && !a_ppac[:is_tier] ? a_ppac : {})
|
591
|
+
ppac ||= @packs.find { |pac| pac.ean == ean }.first
|
592
|
+
xml.PRODNO ppac[:prodno] if ppac[:prodno] && !ppac[:prodno].empty?
|
593
|
+
xml.DSCRD check_name(obj, :de)
|
594
|
+
xml.DSCRF check_name(obj, :fr)
|
595
|
+
# xml.BNAMD
|
596
|
+
# xml.BNAMF
|
597
|
+
# xml.ADNAMD
|
598
|
+
# xml.ADNAMF
|
599
|
+
# xml.SIZE
|
583
600
|
if seq
|
584
|
-
xml.ADINFD seq[:comment_de]
|
585
|
-
xml.ADINFF seq[:comment_fr]
|
586
|
-
xml.GENCD
|
601
|
+
xml.ADINFD seq[:comment_de] unless seq[:comment_de] && seq[:comment_de].empty?
|
602
|
+
xml.ADINFF seq[:comment_fr] unless seq[:comment_fr] && seq[:comment_fr].empty?
|
603
|
+
xml.GENCD seq[:org_gen_code] unless seq[:org_gen_code] && seq[:org_gen_code].empty?
|
587
604
|
end
|
588
|
-
#xml.GENGRP
|
605
|
+
# xml.GENGRP
|
589
606
|
xml.ATC obj[:atc] unless obj[:atc].empty?
|
590
|
-
xml.IT
|
591
|
-
#xml.ITBAG
|
592
|
-
#xml.KONO
|
593
|
-
#xml.TRADE
|
594
|
-
#xml.PRTNO
|
595
|
-
#xml.MONO
|
596
|
-
#xml.CDGALD
|
597
|
-
#xml.CDGALF
|
598
|
-
#xml.FORMD
|
599
|
-
#xml.FORMF
|
600
|
-
#xml.DOSE
|
601
|
-
#xml.DOSEU
|
602
|
-
#xml.DRGFD
|
603
|
-
#xml.DRGFF
|
607
|
+
xml.IT obj[:ith] unless obj[:ith].empty?
|
608
|
+
# xml.ITBAG
|
609
|
+
# xml.KONO
|
610
|
+
# xml.TRADE
|
611
|
+
# xml.PRTNO
|
612
|
+
# xml.MONO
|
613
|
+
# xml.CDGALD
|
614
|
+
# xml.CDGALF
|
615
|
+
# xml.FORMD
|
616
|
+
# xml.FORMF
|
617
|
+
# xml.DOSE
|
618
|
+
# xml.DOSEU
|
619
|
+
# xml.DRGFD
|
620
|
+
# xml.DRGFF
|
604
621
|
obj[:no8] =~ /(\d{5})(\d{3})/
|
605
622
|
if @orphan.include?($1.to_s)
|
606
623
|
xml.ORPH true
|
607
624
|
end
|
608
|
-
#xml.BIOPHA
|
609
|
-
#xml.BIOSIM
|
610
|
-
#xml.BFS
|
611
|
-
#xml.BLOOD
|
612
|
-
#xml.MSCD # always empty
|
613
|
-
#xml.DEL
|
625
|
+
# xml.BIOPHA
|
626
|
+
# xml.BIOSIM
|
627
|
+
# xml.BFS
|
628
|
+
# xml.BLOOD
|
629
|
+
# xml.MSCD # always empty
|
630
|
+
# xml.DEL
|
614
631
|
xml.CPT {
|
615
|
-
#xml.CPTLNO
|
616
|
-
#xml.CNAMED
|
617
|
-
#xml.CNAMEF
|
618
|
-
#xml.IDXIND
|
619
|
-
#xml.DDDD
|
620
|
-
#xml.DDDU
|
621
|
-
#xml.DDDA
|
622
|
-
#xml.IDXIA
|
623
|
-
#xml.IXREL
|
624
|
-
#xml.GALF
|
625
|
-
#xml.DRGGRPCD
|
626
|
-
#xml.PRBSUIT
|
627
|
-
#xml.CSOLV
|
628
|
-
#xml.CSOLVQ
|
629
|
-
#xml.CSOLVQU
|
630
|
-
#xml.PHVAL
|
631
|
-
#xml.LSPNSOL
|
632
|
-
#xml.APDURSOL
|
633
|
-
#xml.EXCIP
|
634
|
-
#xml.EXCIPQ
|
635
|
-
#xml.EXCIPCD
|
636
|
-
#xml.EXCIPCF
|
637
|
-
#xml.PQTY
|
638
|
-
#xml.PQTYU
|
639
|
-
#xml.SIZEMM
|
640
|
-
#xml.WEIGHT
|
641
|
-
#xml.LOOKD
|
642
|
-
#xml.LOOKF
|
643
|
-
#xml.IMG2
|
632
|
+
# xml.CPTLNO
|
633
|
+
# xml.CNAMED
|
634
|
+
# xml.CNAMEF
|
635
|
+
# xml.IDXIND
|
636
|
+
# xml.DDDD
|
637
|
+
# xml.DDDU
|
638
|
+
# xml.DDDA
|
639
|
+
# xml.IDXIA
|
640
|
+
# xml.IXREL
|
641
|
+
# xml.GALF
|
642
|
+
# xml.DRGGRPCD
|
643
|
+
# xml.PRBSUIT
|
644
|
+
# xml.CSOLV
|
645
|
+
# xml.CSOLVQ
|
646
|
+
# xml.CSOLVQU
|
647
|
+
# xml.PHVAL
|
648
|
+
# xml.LSPNSOL
|
649
|
+
# xml.APDURSOL
|
650
|
+
# xml.EXCIP
|
651
|
+
# xml.EXCIPQ
|
652
|
+
# xml.EXCIPCD
|
653
|
+
# xml.EXCIPCF
|
654
|
+
# xml.PQTY
|
655
|
+
# xml.PQTYU
|
656
|
+
# xml.SIZEMM
|
657
|
+
# xml.WEIGHT
|
658
|
+
# xml.LOOKD
|
659
|
+
# xml.LOOKF
|
660
|
+
# xml.IMG2
|
644
661
|
if seq
|
645
662
|
seq[:substances].each do |sub|
|
646
663
|
xml.CPTCMP {
|
647
|
-
xml.LINE
|
664
|
+
xml.LINE sub[:index] unless sub[:index].empty?
|
648
665
|
xml.SUBNO(@substances.index(sub[:name]) + 1) if @substances.include?(sub[:name])
|
649
|
-
xml.QTY
|
650
|
-
xml.QTYU
|
651
|
-
#xml.WHK
|
666
|
+
xml.QTY sub[:quantity] unless sub[:quantity].empty?
|
667
|
+
xml.QTYU sub[:unit] unless sub[:unit].empty?
|
668
|
+
# xml.WHK
|
652
669
|
}
|
653
670
|
end
|
654
671
|
@interactions.each do |ix|
|
655
672
|
if [ix[:act1], ix[:act2]].include?(seq[:atc_code])
|
656
673
|
xml.CPTIX {
|
657
674
|
xml.IXNO ix[:ixno]
|
658
|
-
#xml.GRP
|
659
|
-
xml.RLV
|
675
|
+
# xml.GRP
|
676
|
+
xml.RLV @codes[ix[:grad]]
|
660
677
|
}
|
661
678
|
end
|
662
679
|
end
|
663
680
|
end
|
664
|
-
#xml.CPTROA {
|
665
|
-
|
666
|
-
|
667
|
-
#}
|
681
|
+
# xml.CPTROA {
|
682
|
+
# xml.SYSLOC
|
683
|
+
# xml.ROA
|
684
|
+
# }
|
668
685
|
}
|
669
|
-
#xml.PRDICD { # currently empty
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
#}
|
676
|
-
xml.PackGrSwissmedic
|
677
|
-
xml.EinheitSwissmedic
|
686
|
+
# xml.PRDICD { # currently empty
|
687
|
+
# xml.ICD
|
688
|
+
# xml.RTYP
|
689
|
+
# xml.RSIG
|
690
|
+
# xml.REMD
|
691
|
+
# xml.REMF
|
692
|
+
# }
|
693
|
+
xml.PackGrSwissmedic obj[:siz] unless obj[:siz].empty?
|
694
|
+
xml.EinheitSwissmedic obj[:eht] unless obj[:eht].empty?
|
678
695
|
xml.SubstanceSwissmedic obj[:sub] unless obj[:sub].empty?
|
679
696
|
xml.CompositionSwissmedic obj[:comp] unless obj[:comp].empty?
|
680
697
|
end
|
681
698
|
end
|
682
699
|
xml.RESULT {
|
683
|
-
xml.OK_ERROR
|
700
|
+
xml.OK_ERROR "OK"
|
684
701
|
xml.NBR_RECORD nbr_products
|
685
|
-
xml.ERROR_CODE
|
686
|
-
xml.MESSAGE
|
702
|
+
xml.ERROR_CODE ""
|
703
|
+
xml.MESSAGE ""
|
687
704
|
}
|
688
705
|
}
|
689
706
|
end
|
690
|
-
Oddb2xml.add_hash(
|
707
|
+
Oddb2xml.add_hash(a_builder.to_xml)
|
691
708
|
end
|
692
709
|
|
693
710
|
def prepare_calc_items(suppress_composition_parsing: false)
|
694
711
|
@calc_items = {}
|
695
|
-
packungen_xlsx = File.join(Oddb2xml::
|
696
|
-
|
697
|
-
return unless File.exists?(packungen_xlsx)
|
712
|
+
packungen_xlsx = File.join(Oddb2xml::DOWNLOADS, "swissmedic_package.xlsx")
|
713
|
+
return unless File.exist?(packungen_xlsx)
|
698
714
|
workbook = RubyXL::Parser.parse(packungen_xlsx)
|
699
715
|
row_nr = 0
|
700
716
|
workbook.worksheets[0].each do |row|
|
701
717
|
row_nr += 1
|
702
|
-
next unless row
|
703
|
-
iksnr
|
704
|
-
seqnr = "%02d" % row.cells[1].value.to_i
|
718
|
+
next unless row && row.cells[0] && row.cells[0].value && (row.cells[0].value.to_i > 0)
|
719
|
+
iksnr = "%05i" % row.cells[0].value.to_i
|
705
720
|
if row_nr % 250 == 0
|
706
|
-
|
707
|
-
|
721
|
+
puts "#{Time.now}: At row #{row_nr} iksnr #{iksnr}"
|
722
|
+
$stdout.flush
|
708
723
|
end
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
cat = COLUMNS_FEBRUARY_2019.keys.index(:ikscat)
|
714
|
-
siz = COLUMNS_FEBRUARY_2019.keys.index(:size)
|
715
|
-
atc = COLUMNS_FEBRUARY_2019.keys.index(:atc_class)
|
724
|
+
seq_name = COLUMNS_FEBRUARY_2019.keys.index(:name_base)
|
725
|
+
i_3 = COLUMNS_FEBRUARY_2019.keys.index(:ikscd)
|
726
|
+
siz = COLUMNS_FEBRUARY_2019.keys.index(:size)
|
727
|
+
atc = COLUMNS_FEBRUARY_2019.keys.index(:atc_class)
|
716
728
|
list_code = COLUMNS_FEBRUARY_2019.keys.index(:production_science)
|
717
|
-
unit
|
718
|
-
sub
|
719
|
-
comp
|
729
|
+
unit = COLUMNS_FEBRUARY_2019.keys.index(:unit)
|
730
|
+
sub = COLUMNS_FEBRUARY_2019.keys.index(:substances)
|
731
|
+
comp = COLUMNS_FEBRUARY_2019.keys.index(:composition)
|
720
732
|
|
721
|
-
no8
|
722
|
-
name
|
723
|
-
atc_code
|
724
|
-
list_code
|
725
|
-
package_size
|
726
|
-
unit
|
727
|
-
active_substance
|
728
|
-
composition
|
733
|
+
no8 = iksnr + sprintf("%03d", row.cells[i_3].value.to_i)
|
734
|
+
name = row.cells[seq_name] ? row.cells[seq_name].value : nil
|
735
|
+
atc_code = row.cells[atc] ? row.cells[atc].value : nil
|
736
|
+
list_code = row.cells[list_code] ? row.cells[list_code].value : nil
|
737
|
+
package_size = row.cells[siz] ? row.cells[siz].value : nil
|
738
|
+
unit = row.cells[unit] ? row.cells[unit].value : nil
|
739
|
+
active_substance = row.cells[sub] ? row.cells[sub].value : nil
|
740
|
+
composition = row.cells[comp] ? row.cells[comp].value : nil
|
729
741
|
|
730
742
|
# skip veterinary product
|
731
|
-
next if atc_code
|
732
|
-
next if list_code
|
743
|
+
next if atc_code && /^Q/i.match(atc_code)
|
744
|
+
next if list_code && /Tierarzneimittel/.match(list_code)
|
733
745
|
info = nil
|
734
746
|
begin
|
735
|
-
if suppress_composition_parsing
|
736
|
-
|
747
|
+
info = if suppress_composition_parsing
|
748
|
+
Calc.new(name, package_size, unit)
|
737
749
|
else
|
738
|
-
|
750
|
+
Calc.new(name, package_size, unit, active_substance, composition)
|
739
751
|
end
|
740
752
|
rescue
|
741
753
|
puts "#{Time.now}: #{row_nr} iksnr #{iksnr} rescue from Calc.new"
|
742
754
|
end
|
743
|
-
ean12 =
|
755
|
+
ean12 = "7680" + no8
|
744
756
|
ean13 = (ean12 + Oddb2xml.calc_checksum(ean12))
|
745
757
|
@calc_items[ean13] = info
|
746
758
|
end
|
747
759
|
end
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
|
769
|
-
|
770
|
-
|
771
|
-
end
|
772
|
-
end
|
760
|
+
|
761
|
+
def emit_substance(xml, substance, emit_active = false)
|
762
|
+
xml.MORE_INFO substance.more_info if substance.more_info
|
763
|
+
xml.SUBSTANCE_NAME substance.name
|
764
|
+
xml.IS_ACTIVE_AGENT substance.is_active_agent if emit_active
|
765
|
+
if substance.dose
|
766
|
+
if substance.qty.is_a?(Float) || substance.qty.is_a?(Integer)
|
767
|
+
xml.QTY substance.qty
|
768
|
+
xml.UNIT substance.unit
|
769
|
+
else
|
770
|
+
xml.DOSE_TEXT substance.dose.to_s
|
771
|
+
end
|
772
|
+
end
|
773
|
+
if substance.chemical_substance
|
774
|
+
xml.CHEMICAL_SUBSTANCE {
|
775
|
+
emit_substance(xml, substance.chemical_substance)
|
776
|
+
}
|
777
|
+
end
|
778
|
+
if substance.salts && (substance.salts.size > 0)
|
779
|
+
xml.SALTS do
|
780
|
+
substance.salts.each do |salt|
|
781
|
+
xml.SALT do
|
782
|
+
emit_substance(xml, salt)
|
773
783
|
end
|
774
784
|
end
|
785
|
+
end
|
775
786
|
end
|
787
|
+
end
|
788
|
+
|
789
|
+
def build_calc
|
776
790
|
prepare_calc_items
|
777
|
-
|
791
|
+
a_builder = Nokogiri::XML::Builder.new(encoding: "utf-8") do |xml|
|
778
792
|
xml.doc.tag_suffix = @tag_suffix
|
779
|
-
datetime = Time.new.strftime('%FT%T%z')
|
780
793
|
xml.ARTICLES(XML_OPTIONS) do
|
781
794
|
@calc_items.each do |ean13, info|
|
782
|
-
|
783
|
-
xml.
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
|
795
|
+
if info&.compositions
|
796
|
+
xml.ARTICLE do
|
797
|
+
xml.GTIN ean13
|
798
|
+
xml.NAME info.column_c
|
799
|
+
xml.PKG_SIZE info.pkg_size
|
800
|
+
xml.SELLING_UNITS info.selling_units
|
801
|
+
xml.MEASURE info.measure # Nur wenn Lösung wen Spalte M ml, Spritze
|
788
802
|
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
|
798
|
-
|
799
|
-
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
|
804
|
-
|
805
|
-
|
803
|
+
if info.galenic_form.is_a?(String)
|
804
|
+
xml.GALENIC_FORM info.galenic_form
|
805
|
+
xml.GALENIC_GROUP "Unknown"
|
806
|
+
else
|
807
|
+
xml.GALENIC_FORM info.galenic_form.description
|
808
|
+
xml.GALENIC_GROUP info.galenic_group ? info.galenic_group.description : "Unknown"
|
809
|
+
end
|
810
|
+
xml.COMPOSITIONS do
|
811
|
+
info.compositions.each do |composition|
|
812
|
+
xml.COMPOSITION do
|
813
|
+
xml.EXCIPIENS { emit_substance(xml, composition.excipiens) } if composition.excipiens
|
814
|
+
xml.LABEL composition.label if composition.label
|
815
|
+
xml.LABEL_DESCRIPTION composition.label_description if composition.label_description
|
816
|
+
xml.CORRESP composition.corresp if composition.corresp
|
817
|
+
if composition.substances && (composition.substances.size > 0)
|
818
|
+
xml.SUBSTANCES do
|
819
|
+
composition.substances.each { |substance| xml.SUBSTANCE { emit_substance(xml, substance, true) } }
|
820
|
+
end
|
806
821
|
end
|
807
822
|
end
|
808
823
|
end
|
809
824
|
end
|
810
825
|
end
|
811
|
-
end
|
826
|
+
end
|
812
827
|
end
|
813
828
|
end
|
814
829
|
end
|
815
830
|
|
816
|
-
csv_name = File.join(
|
817
|
-
CSV.open(csv_name, "w+", :
|
818
|
-
csv << [
|
831
|
+
csv_name = File.join(WORK_DIR, "oddb_calc.csv")
|
832
|
+
CSV.open(csv_name, "w+", col_sep: ";") do |csv|
|
833
|
+
csv << ["gtin"] + @calc_items.values.first.headers
|
819
834
|
@calc_items.each do |key, value|
|
820
|
-
if value
|
821
|
-
csv <<
|
835
|
+
if value&.to_array
|
836
|
+
csv << [key] + value.to_array
|
822
837
|
else
|
823
838
|
puts "key #{key.inspect} WITHOUT #{value.inspect}"
|
824
839
|
end
|
825
840
|
end
|
826
841
|
end
|
827
|
-
Oddb2xml.add_hash(
|
842
|
+
Oddb2xml.add_hash(a_builder.to_xml)
|
828
843
|
end
|
829
844
|
|
830
845
|
def build_article
|
@@ -834,21 +849,20 @@ module Oddb2xml
|
|
834
849
|
idx = 0
|
835
850
|
nbr_records = 0
|
836
851
|
Oddb2xml.log "build_article #{idx} of #{@articles.size} articles"
|
837
|
-
|
852
|
+
a_builder = Nokogiri::XML::Builder.new(encoding: "utf-8") do |xml|
|
838
853
|
xml.doc.tag_suffix = @tag_suffix
|
839
|
-
|
840
|
-
eans_from_refdata = @articles.collect{|refdata| refdata[:ean13] }
|
854
|
+
eans_from_refdata = @articles.collect { |refdata| refdata[:ean13] }
|
841
855
|
eans_from_preparations = @items.keys
|
842
856
|
missing_eans = []
|
843
857
|
eans_from_preparations.each do |ean|
|
844
858
|
next if ean.to_i == 0
|
845
|
-
unless @articles.collect {|x| x[:ean13] }.index(ean)
|
859
|
+
unless @articles.collect { |x| x[:ean13] }.index(ean)
|
846
860
|
item = @items[ean].clone
|
847
|
-
item[:pharmacode] ||=
|
861
|
+
item[:pharmacode] ||= "123456" if defined?(RSpec)
|
848
862
|
item[:ean13] = ean
|
849
863
|
item[:_type] = :preparations_xml
|
850
|
-
item[:desc_de] = item[:name_de] +
|
851
|
-
item[:desc_fr] = item[:name_fr] +
|
864
|
+
item[:desc_de] = item[:name_de] + " " + item[:desc_de]
|
865
|
+
item[:desc_fr] = item[:name_fr] + " " + item[:desc_fr]
|
852
866
|
@articles << item
|
853
867
|
end
|
854
868
|
unless eans_from_refdata.index(ean)
|
@@ -856,166 +870,166 @@ module Oddb2xml
|
|
856
870
|
end
|
857
871
|
end
|
858
872
|
xml.ARTICLE(XML_OPTIONS) {
|
859
|
-
|
860
|
-
|
861
|
-
|
862
|
-
|
863
|
-
|
864
|
-
pac,no8 = nil,obj[:ean13].to_s[4..11] # BAG-XML(SL/LS)
|
873
|
+
@articles.sort! { |a, b| a[:ean13] <=> b[:ean13] }
|
874
|
+
@articles.each do |obj|
|
875
|
+
idx += 1
|
876
|
+
Oddb2xml.log "build_article #{obj[:ean13]}: #{idx} of #{@articles.size} articles" if idx % 500 == 0
|
877
|
+
pac, no8 = nil, obj[:ean13].to_s[4..11] # BAG-XML(SL/LS)
|
865
878
|
pack_info = nil
|
866
879
|
pack_info = @packs[no8] if no8 # info from Packungen.xlsx from swissmedic_info
|
867
|
-
ppac = nil
|
880
|
+
ppac = nil # Packungen
|
868
881
|
ean = obj[:ean13]
|
869
|
-
next if pack_info
|
870
|
-
next if obj[:desc_de]
|
882
|
+
next if pack_info && /Tierarzneimittel/.match(pack_info[:list_code])
|
883
|
+
next if obj[:desc_de] && /ad us vet/i.match(obj[:desc_de])
|
871
884
|
pharma_code = obj[:pharmacode]
|
872
885
|
# ean = 0 if sprintf('%013d', ean).match(/^000000/)
|
873
886
|
if obj[:seq]
|
874
887
|
pac = obj[:seq][:packages][obj[:pharmacode]]
|
875
|
-
pac
|
876
|
-
|
877
|
-
pac = @items[ean][:packages][ean]
|
888
|
+
pac ||= obj[:seq][:packages][ean]
|
889
|
+
elsif @items && ean && @items[ean] && @items[ean][:packages]
|
890
|
+
pac = @items[ean][:packages][ean]
|
878
891
|
end
|
879
892
|
if no8
|
880
|
-
ppac = ((
|
893
|
+
ppac = ((a_ppac = pack_info) && !a_ppac[:is_tier] ? a_ppac : nil)
|
881
894
|
end
|
882
895
|
zur_rose = nil
|
883
896
|
if !@infos_zur_rose.empty? && ean && @infos_zur_rose[ean]
|
884
897
|
zur_rose = @infos_zur_rose[ean] # zurrose
|
885
898
|
end
|
886
|
-
xml.ART(
|
899
|
+
xml.ART("DT" => obj[:last_change] || "") do
|
887
900
|
nbr_records += 1
|
888
|
-
|
901
|
+
# Ignore Lint/RequireParentheses warning on next line!
|
902
|
+
xml.REF_DATA obj[:refdata] || @migel[pharma_code] ? "1" : "0"
|
889
903
|
if obj[:pharmacode] && obj[:pharmacode].length > 0
|
890
|
-
xml.PHAR
|
904
|
+
xml.PHAR obj[:pharmacode]
|
891
905
|
elsif zur_rose
|
892
906
|
puts "Adding #{zur_rose[:pharmacode]} to article GTIN #{ean}"
|
893
|
-
xml.PHAR
|
907
|
+
xml.PHAR zur_rose[:pharmacode]
|
894
908
|
end
|
895
|
-
#xml.GRPCD
|
896
|
-
#xml.CDS01
|
897
|
-
#xml.CDS02
|
909
|
+
# xml.GRPCD
|
910
|
+
# xml.CDS01
|
911
|
+
# xml.CDS02
|
898
912
|
if ppac
|
899
|
-
xml.SMCAT
|
900
|
-
xml.GEN_PRODUCTION
|
901
|
-
xml.INSULIN_CATEGORY
|
902
|
-
xml.DRUG_INDEX
|
913
|
+
xml.SMCAT ppac[:swissmedic_category] unless ppac[:swissmedic_category].empty?
|
914
|
+
xml.GEN_PRODUCTION ppac[:gen_production] unless ppac[:gen_production].empty?
|
915
|
+
xml.INSULIN_CATEGORY ppac[:insulin_category] unless ppac[:insulin_category].empty?
|
916
|
+
xml.DRUG_INDEX ppac[:drug_index] unless ppac[:drug_index].empty?
|
903
917
|
end
|
904
|
-
if no8
|
905
|
-
if ean
|
918
|
+
if no8 && !no8.to_s.empty?
|
919
|
+
if ean && (ean.to_s[0..3] == "7680")
|
906
920
|
xml.SMNO no8.to_s
|
907
921
|
end
|
908
922
|
end
|
909
923
|
if ppac
|
910
|
-
xml.PRODNO ppac[:prodno] if ppac[:prodno]
|
924
|
+
xml.PRODNO ppac[:prodno] if ppac[:prodno] && !ppac[:prodno].empty?
|
911
925
|
end
|
912
|
-
#xml.HOSPCD
|
913
|
-
#xml.CLINCD
|
914
|
-
#xml.ARTTYP
|
926
|
+
# xml.HOSPCD
|
927
|
+
# xml.CLINCD
|
928
|
+
# xml.ARTTYP
|
915
929
|
if zur_rose
|
916
930
|
xml.VAT zur_rose[:vat]
|
917
931
|
end
|
918
932
|
emit_salecd(xml, ean, obj)
|
919
|
-
if pac
|
920
|
-
#xml.INSLIM
|
933
|
+
if pac && pac[:limitation_points]
|
934
|
+
# xml.INSLIM
|
921
935
|
xml.LIMPTS pac[:limitation_points] unless pac[:limitation_points].empty?
|
922
936
|
end
|
923
|
-
#xml.GRDFR
|
937
|
+
# xml.GRDFR
|
924
938
|
xml.COOL 1 if ppac && /Blutprodukte|impfstoffe/.match(ppac[:list_code])
|
925
|
-
#xml.TEMP
|
939
|
+
# xml.TEMP
|
926
940
|
if ean
|
927
|
-
flag =
|
941
|
+
flag = ppac && !ppac[:drug_index].empty? ? true : false
|
928
942
|
# as same flag
|
929
|
-
xml.CDBG(flag ?
|
930
|
-
xml.BG(flag ?
|
943
|
+
xml.CDBG(flag ? "Y" : "N")
|
944
|
+
xml.BG(flag ? "Y" : "N")
|
931
945
|
end
|
932
|
-
|
933
|
-
|
934
|
-
|
935
|
-
|
936
|
-
xml.
|
937
|
-
xml.
|
938
|
-
xml.
|
939
|
-
xml.
|
940
|
-
xml.
|
941
|
-
xml.
|
942
|
-
#xml.
|
943
|
-
#xml.
|
944
|
-
#xml.
|
945
|
-
#xml.IMG2
|
946
|
-
#xml.PCKTYPD
|
947
|
-
#xml.PCKTYPF
|
948
|
-
#xml.MULT
|
946
|
+
xml.DSCRD obj[:desc_de] if obj[:desc_de] && !obj[:desc_de].empty?
|
947
|
+
xml.DSCRF obj[:desc_fr] if obj[:desc_fr] && !obj[:desc_fr].empty?
|
948
|
+
xml.DSCRF obj[:desc_de] if !obj[:desc_fr] || obj[:desc_fr].empty?
|
949
|
+
xml.SORTD obj[:desc_de].upcase if obj[:desc_de] && !obj[:desc_de].empty?
|
950
|
+
xml.SORTF obj[:desc_fr].upcase if obj[:desc_fr] && !obj[:desc_fr].empty?
|
951
|
+
xml.SORTF obj[:desc_de].upcase if !obj[:desc_fr] || obj[:desc_fr].empty?
|
952
|
+
# xml.QTYUD
|
953
|
+
# xml.QTYUF
|
954
|
+
# xml.IMG
|
955
|
+
# xml.IMG2
|
956
|
+
# xml.PCKTYPD
|
957
|
+
# xml.PCKTYPF
|
958
|
+
# xml.MULT
|
949
959
|
if obj[:seq]
|
950
960
|
xml.SYN1D obj[:seq][:name_de] unless obj[:seq][:name_de].empty?
|
951
961
|
xml.SYN1F obj[:seq][:name_fr] unless obj[:seq][:name_fr].empty?
|
952
962
|
end
|
953
963
|
if obj[:seq]
|
954
964
|
case obj[:seq][:deductible]
|
955
|
-
when
|
956
|
-
when
|
957
|
-
else
|
965
|
+
when "Y" then xml.SLOPLUS 1; # 20%
|
966
|
+
when "N" then xml.SLOPLUS 2; # 10%
|
967
|
+
else xml.SLOPLUS "" # k.A.
|
958
968
|
end
|
959
969
|
end
|
960
|
-
#xml.NOPCS
|
961
|
-
#xml.HSCD
|
962
|
-
#xml.MINI
|
963
|
-
#xml.DEPCD
|
964
|
-
#xml.DEPOT
|
965
|
-
#xml.BAGSL
|
966
|
-
#xml.BAGSLC
|
967
|
-
#xml.LOACD
|
970
|
+
# xml.NOPCS
|
971
|
+
# xml.HSCD
|
972
|
+
# xml.MINI
|
973
|
+
# xml.DEPCD
|
974
|
+
# xml.DEPOT
|
975
|
+
# xml.BAGSL
|
976
|
+
# xml.BAGSLC
|
977
|
+
# xml.LOACD
|
968
978
|
if obj[:stat_date]
|
969
|
-
xml.OUTSAL obj[:stat_date] if obj[:stat_date]
|
979
|
+
xml.OUTSAL obj[:stat_date] if obj[:stat_date] && !obj[:stat_date].empty?
|
970
980
|
end
|
971
|
-
#xml.STTOX
|
972
|
-
#xml.NOTI
|
973
|
-
#xml.GGL
|
974
|
-
#xml.CE
|
975
|
-
#xml.SMDAT
|
976
|
-
#xml.SMCDAT
|
977
|
-
#xml.SIST
|
978
|
-
#xml.ESIST
|
979
|
-
#xml.BIOCID
|
980
|
-
#xml.BAGNO
|
981
|
-
#xml.LIGHT
|
982
|
-
#xml.DEL
|
981
|
+
# xml.STTOX
|
982
|
+
# xml.NOTI
|
983
|
+
# xml.GGL
|
984
|
+
# xml.CE
|
985
|
+
# xml.SMDAT
|
986
|
+
# xml.SMCDAT
|
987
|
+
# xml.SIST
|
988
|
+
# xml.ESIST
|
989
|
+
# xml.BIOCID
|
990
|
+
# xml.BAGNO
|
991
|
+
# xml.LIGHT
|
992
|
+
# xml.DEL
|
983
993
|
xml.ARTCOMP {
|
984
994
|
# use ean13(gln) as COMPNO
|
985
|
-
xml.COMPNO obj[:company_ean] if obj[:company_ean]
|
986
|
-
#xml.ROLE
|
987
|
-
#xml.ARTNO1
|
988
|
-
#xml.ARTNO2
|
989
|
-
#xml.ARTNO3
|
995
|
+
xml.COMPNO obj[:company_ean] if obj[:company_ean] && (obj[:company_ean].to_s.length > 1)
|
996
|
+
# xml.ROLE
|
997
|
+
# xml.ARTNO1
|
998
|
+
# xml.ARTNO2
|
999
|
+
# xml.ARTNO3
|
990
1000
|
}
|
991
|
-
|
992
|
-
xml.
|
993
|
-
|
994
|
-
|
995
|
-
|
996
|
-
|
1001
|
+
if ean
|
1002
|
+
xml.ARTBAR {
|
1003
|
+
xml.CDTYP "E13"
|
1004
|
+
xml.BC ean # /^9999|^0000|^0$/.match(ean.to_s) ? 0 : sprintf('%013d', ean)
|
1005
|
+
xml.BCSTAT "A" # P is alternative
|
1006
|
+
}
|
1007
|
+
end
|
1008
|
+
if pac && pac[:prices]
|
997
1009
|
pac[:prices].each_pair do |key, price|
|
998
1010
|
xml.ARTPRI {
|
999
|
-
xml.VDAT
|
1000
|
-
xml.PTYP
|
1001
|
-
xml.PRICE price[:price]
|
1011
|
+
xml.VDAT price[:valid_date] unless price[:valid_date].empty?
|
1012
|
+
xml.PTYP price[:price_code] unless price[:price_code].empty?
|
1013
|
+
xml.PRICE price[:price] unless price[:price].empty?
|
1002
1014
|
}
|
1003
1015
|
end
|
1004
1016
|
end
|
1005
1017
|
if zur_rose
|
1006
1018
|
price = zur_rose[:price]
|
1007
1019
|
xml.ARTPRI {
|
1008
|
-
xml.PTYP
|
1020
|
+
xml.PTYP "ZURROSE"
|
1009
1021
|
xml.PRICE price
|
1010
1022
|
}
|
1011
1023
|
xml.ARTPRI {
|
1012
|
-
xml.PTYP
|
1024
|
+
xml.PTYP "ZURROSEPUB"
|
1013
1025
|
xml.PRICE zur_rose[:pub_price]
|
1014
1026
|
}
|
1015
|
-
|
1016
|
-
xml.
|
1017
|
-
|
1018
|
-
|
1027
|
+
unless @options[:percent].nil?
|
1028
|
+
xml.ARTPRI {
|
1029
|
+
xml.PTYP "RESELLERPUB"
|
1030
|
+
xml.PRICE (price.to_f * (1 + (@options[:percent].to_f / 100))).round_by(0.05).round(2)
|
1031
|
+
}
|
1032
|
+
end
|
1019
1033
|
end
|
1020
1034
|
nincd = detect_nincd(obj)
|
1021
1035
|
if nincd
|
@@ -1026,29 +1040,29 @@ module Oddb2xml
|
|
1026
1040
|
end
|
1027
1041
|
end
|
1028
1042
|
xml.RESULT {
|
1029
|
-
xml.OK_ERROR
|
1043
|
+
xml.OK_ERROR "OK"
|
1030
1044
|
xml.NBR_RECORD nbr_records
|
1031
|
-
xml.ERROR_CODE
|
1032
|
-
xml.MESSAGE
|
1045
|
+
xml.ERROR_CODE ""
|
1046
|
+
xml.MESSAGE ""
|
1033
1047
|
}
|
1034
1048
|
}
|
1035
1049
|
end
|
1036
1050
|
Oddb2xml.log "build_article. Done #{idx} of #{@articles.size} articles. Overrode #{@overriden_salecd.size} SALECD"
|
1037
|
-
Oddb2xml.add_hash(
|
1051
|
+
Oddb2xml.add_hash(a_builder.to_xml)
|
1038
1052
|
end
|
1053
|
+
|
1039
1054
|
def build_fi
|
1040
1055
|
nbr_records = 0
|
1041
|
-
|
1056
|
+
a_builder = Nokogiri::XML::Builder.new(encoding: "utf-8") do |xml|
|
1042
1057
|
xml.doc.tag_suffix = @tag_suffix
|
1043
|
-
datetime = Time.new.strftime('%FT%T%z')
|
1044
1058
|
xml.KOMPENDIUM(XML_OPTIONS) {
|
1045
1059
|
%w[de fr].each do |lang|
|
1046
|
-
infos = @infos[lang].uniq {|i| i[:monid] }
|
1060
|
+
infos = @infos[lang].uniq { |i| i[:monid] }
|
1047
1061
|
infos.each do |info|
|
1048
1062
|
xml.KMP(
|
1049
|
-
|
1050
|
-
|
1051
|
-
|
1063
|
+
"MONTYPE" => "fi", # only
|
1064
|
+
"LANG" => lang.upcase,
|
1065
|
+
"DT" => ""
|
1052
1066
|
) {
|
1053
1067
|
unless info[:name].empty?
|
1054
1068
|
xml.name info[:name]
|
@@ -1056,29 +1070,29 @@ module Oddb2xml
|
|
1056
1070
|
unless info[:owner].empty?
|
1057
1071
|
xml.owner info[:owner]
|
1058
1072
|
end
|
1059
|
-
xml.monid
|
1073
|
+
xml.monid info[:monid] unless info[:monid].empty?
|
1060
1074
|
xml.style { xml.cdata(info[:style]) } if info[:style]
|
1061
|
-
xml.paragraph { xml.cdata(Nokogiri::HTML.fragment(info[:paragraph].to_html).to_html(:
|
1075
|
+
xml.paragraph { xml.cdata(Nokogiri::HTML.fragment(info[:paragraph].to_html).to_html(encoding: "UTF-8")) } if info[:paragraph]
|
1062
1076
|
nbr_records += 1
|
1063
1077
|
}
|
1064
1078
|
end
|
1065
1079
|
end
|
1066
1080
|
xml.RESULT {
|
1067
|
-
xml.OK_ERROR
|
1081
|
+
xml.OK_ERROR "OK"
|
1068
1082
|
xml.NBR_RECORD nbr_records
|
1069
|
-
xml.ERROR_CODE
|
1070
|
-
xml.MESSAGE
|
1083
|
+
xml.ERROR_CODE ""
|
1084
|
+
xml.MESSAGE ""
|
1071
1085
|
}
|
1072
1086
|
}
|
1073
1087
|
end
|
1074
|
-
Oddb2xml.add_hash(
|
1088
|
+
Oddb2xml.add_hash(a_builder.to_xml)
|
1075
1089
|
end
|
1090
|
+
|
1076
1091
|
def build_fi_product
|
1077
1092
|
prepare_products
|
1078
1093
|
nbr_records = 0
|
1079
|
-
|
1094
|
+
a_builder = Nokogiri::XML::Builder.new(encoding: "utf-8") do |xml|
|
1080
1095
|
xml.doc.tag_suffix = @tag_suffix
|
1081
|
-
datetime = Time.new.strftime('%FT%T%z')
|
1082
1096
|
xml.KOMPENDIUM_PRODUCT(XML_OPTIONS) {
|
1083
1097
|
info_index = {}
|
1084
1098
|
%w[de fr].each do |lang|
|
@@ -1087,556 +1101,581 @@ module Oddb2xml
|
|
1087
1101
|
end
|
1088
1102
|
# prod
|
1089
1103
|
@products.each do |ean13, prod|
|
1090
|
-
next unless
|
1104
|
+
next unless prod[:seq] && prod[:seq][:packages]
|
1091
1105
|
seq = prod[:seq]
|
1092
|
-
prod[:seq][:packages].each {
|
1093
|
-
|
1094
|
-
|
1095
|
-
|
1096
|
-
|
1097
|
-
|
1098
|
-
|
1099
|
-
|
1100
|
-
|
1101
|
-
|
1102
|
-
|
1103
|
-
|
1104
|
-
|
1105
|
-
|
1106
|
-
|
1107
|
-
}
|
1108
|
-
end
|
1106
|
+
prod[:seq][:packages].each { |phar, package|
|
1107
|
+
next unless package[:swissmedic_number8]
|
1108
|
+
m = /(\d{5})(\d{3})/.match(package[:swissmedic_number8])
|
1109
|
+
next unless m
|
1110
|
+
number = m[1].to_s
|
1111
|
+
idx = info_index[number]
|
1112
|
+
next unless idx
|
1113
|
+
xml.KP("DT" => "") {
|
1114
|
+
xml.MONID @infos[lang][idx][:monid]
|
1115
|
+
xml.PRDNO seq[:product_key] unless seq[:product_key].empty?
|
1116
|
+
# as orphan ?
|
1117
|
+
xml.DEL @orphan.include?(number) ? true : false
|
1118
|
+
nbr_records += 1
|
1119
|
+
}
|
1120
|
+
}
|
1109
1121
|
end
|
1122
|
+
end
|
1110
1123
|
xml.RESULT {
|
1111
|
-
xml.OK_ERROR
|
1124
|
+
xml.OK_ERROR "OK"
|
1112
1125
|
xml.NBR_RECORD nbr_records
|
1113
|
-
xml.ERROR_CODE
|
1114
|
-
xml.MESSAGE
|
1126
|
+
xml.ERROR_CODE ""
|
1127
|
+
xml.MESSAGE ""
|
1115
1128
|
}
|
1116
1129
|
}
|
1117
1130
|
end
|
1118
|
-
Oddb2xml.add_hash(
|
1131
|
+
Oddb2xml.add_hash(a_builder.to_xml)
|
1119
1132
|
end
|
1133
|
+
|
1120
1134
|
def build_company
|
1121
1135
|
nbr_records = 0
|
1122
1136
|
Oddb2xml.log "build_company #{@companies.size} companies"
|
1123
|
-
|
1137
|
+
a_builder = Nokogiri::XML::Builder.new(encoding: "utf-8") do |xml|
|
1124
1138
|
xml.doc.tag_suffix = @tag_suffix
|
1125
|
-
datetime = Time.new.strftime('%FT%T%z')
|
1126
1139
|
xml.Betriebe(XML_OPTIONS) {
|
1127
1140
|
@companies.each do |c|
|
1128
|
-
xml.Betrieb(
|
1129
|
-
xml.GLN_Betrieb
|
1130
|
-
xml.Betriebsname_1
|
1131
|
-
xml.Betriebsname_2
|
1132
|
-
xml.Strasse
|
1133
|
-
xml.Nummer
|
1134
|
-
xml.PLZ
|
1135
|
-
xml.Ort
|
1136
|
-
xml.Bewilligungskanton c[:region]
|
1137
|
-
xml.Land
|
1138
|
-
xml.Betriebstyp
|
1139
|
-
xml.BTM_Berechtigung
|
1141
|
+
xml.Betrieb("DT" => "") {
|
1142
|
+
xml.GLN_Betrieb c[:gln] unless c[:gln].empty?
|
1143
|
+
xml.Betriebsname_1 c[:name_1] unless c[:name_1].empty?
|
1144
|
+
xml.Betriebsname_2 c[:name_2] unless c[:name_2].empty?
|
1145
|
+
xml.Strasse c[:address] unless c[:address].empty?
|
1146
|
+
xml.Nummer c[:number] unless c[:number].empty?
|
1147
|
+
xml.PLZ c[:post] unless c[:post].empty?
|
1148
|
+
xml.Ort c[:place] unless c[:place].empty?
|
1149
|
+
xml.Bewilligungskanton c[:region] unless c[:region].empty?
|
1150
|
+
xml.Land c[:country] unless c[:country].empty?
|
1151
|
+
xml.Betriebstyp c[:type] unless c[:type].empty?
|
1152
|
+
xml.BTM_Berechtigung c[:authorization] unless c[:authorization].empty?
|
1140
1153
|
nbr_records += 1
|
1141
1154
|
}
|
1142
1155
|
end
|
1143
1156
|
xml.RESULT {
|
1144
|
-
xml.OK_ERROR
|
1157
|
+
xml.OK_ERROR "OK"
|
1145
1158
|
xml.NBR_RECORD nbr_records
|
1146
|
-
xml.ERROR_CODE
|
1147
|
-
xml.MESSAGE
|
1159
|
+
xml.ERROR_CODE ""
|
1160
|
+
xml.MESSAGE ""
|
1148
1161
|
}
|
1149
1162
|
}
|
1150
1163
|
end
|
1151
|
-
Oddb2xml.add_hash(
|
1164
|
+
Oddb2xml.add_hash(a_builder.to_xml)
|
1152
1165
|
end
|
1166
|
+
|
1153
1167
|
def build_person
|
1154
1168
|
nbr_records = 0
|
1155
1169
|
Oddb2xml.log "build_person #{@people.size} persons"
|
1156
|
-
|
1170
|
+
a_builder = Nokogiri::XML::Builder.new(encoding: "utf-8") do |xml|
|
1157
1171
|
xml.doc.tag_suffix = @tag_suffix
|
1158
|
-
datetime = Time.new.strftime('%FT%T%z')
|
1159
1172
|
xml.Personen(XML_OPTIONS) {
|
1160
1173
|
@people.each do |p|
|
1161
|
-
xml.Person(
|
1162
|
-
xml.GLN_Person
|
1163
|
-
xml.Name
|
1164
|
-
xml.Vorname
|
1165
|
-
xml.PLZ
|
1166
|
-
xml.Ort
|
1167
|
-
xml.Bewilligungskanton p[:region]
|
1168
|
-
xml.Land
|
1174
|
+
xml.Person("DT" => "") {
|
1175
|
+
xml.GLN_Person p[:gln] unless p[:gln].empty?
|
1176
|
+
xml.Name p[:last_name] unless p[:last_name].empty?
|
1177
|
+
xml.Vorname p[:first_name] unless p[:first_name].empty?
|
1178
|
+
xml.PLZ p[:post] unless p[:post].empty?
|
1179
|
+
xml.Ort p[:place] unless p[:place].empty?
|
1180
|
+
xml.Bewilligungskanton p[:region] unless p[:region].empty?
|
1181
|
+
xml.Land p[:country] unless p[:country].empty?
|
1169
1182
|
xml.Bewilligung_Selbstdispensation p[:license] unless p[:license].empty?
|
1170
|
-
xml.Diplom
|
1171
|
-
xml.BTM_Berechtigung
|
1183
|
+
xml.Diplom p[:certificate] unless p[:certificate].empty?
|
1184
|
+
xml.BTM_Berechtigung p[:authorization] unless p[:authorization].empty?
|
1172
1185
|
nbr_records += 1
|
1173
1186
|
}
|
1174
1187
|
end
|
1175
1188
|
xml.RESULT {
|
1176
|
-
xml.OK_ERROR
|
1189
|
+
xml.OK_ERROR "OK"
|
1177
1190
|
xml.NBR_RECORD nbr_records
|
1178
|
-
xml.ERROR_CODE
|
1179
|
-
xml.MESSAGE
|
1191
|
+
xml.ERROR_CODE ""
|
1192
|
+
xml.MESSAGE ""
|
1180
1193
|
}
|
1181
1194
|
}
|
1182
1195
|
end
|
1183
|
-
Oddb2xml.add_hash(
|
1196
|
+
Oddb2xml.add_hash(a_builder.to_xml)
|
1184
1197
|
end
|
1198
|
+
|
1185
1199
|
def detect_nincd(de_idx)
|
1186
1200
|
if @lppvs[de_idx[:ean13].to_s] # LPPV
|
1187
1201
|
20
|
1188
1202
|
elsif @items[de_idx[:pharmacode]] # BAG-XML (SL/LS)
|
1189
1203
|
10
|
1190
|
-
elsif
|
1191
|
-
|
1204
|
+
elsif de_idx[:migel] || # MiGel (xls)
|
1205
|
+
(de_idx[:_type] == :nonpharma) # MiGel (swissindex)
|
1192
1206
|
13
|
1193
1207
|
else
|
1194
1208
|
# fallback via EAN
|
1195
1209
|
bag_entry_via_ean = @items.values.select do |item|
|
1196
1210
|
next unless item[:packages]
|
1197
|
-
item[:packages].values.
|
1211
|
+
item[:packages].values.count { |a_pac| a_pac[:ean13].to_s.eql?(de_idx[:ean13].to_s) } != 0
|
1198
1212
|
end
|
1199
1213
|
if bag_entry_via_ean.length > 0
|
1200
1214
|
10
|
1201
|
-
else
|
1202
|
-
nil
|
1203
1215
|
end
|
1204
1216
|
end
|
1205
1217
|
end
|
1206
1218
|
|
1207
1219
|
### --- see oddb2tdat
|
1208
1220
|
DAT_LEN = {
|
1209
|
-
:
|
1210
|
-
:
|
1211
|
-
:
|
1212
|
-
:
|
1213
|
-
:
|
1214
|
-
:
|
1215
|
-
:
|
1216
|
-
:
|
1217
|
-
:
|
1218
|
-
:
|
1219
|
-
:
|
1220
|
-
:
|
1221
|
-
:
|
1221
|
+
RECA: 2,
|
1222
|
+
CMUT: 1,
|
1223
|
+
PHAR: 7,
|
1224
|
+
ABEZ: 50,
|
1225
|
+
PRMO: 6,
|
1226
|
+
PRPU: 6,
|
1227
|
+
CKZL: 1,
|
1228
|
+
CLAG: 1,
|
1229
|
+
CBGG: 1,
|
1230
|
+
CIKS: 1,
|
1231
|
+
ITHE: 7,
|
1232
|
+
CEAN: 13,
|
1233
|
+
CMWS: 1
|
1222
1234
|
}
|
1223
|
-
def format_price(price_str, len=6, int_len=4, frac_len=2)
|
1224
|
-
price = price_str.split(
|
1225
|
-
pre =
|
1226
|
-
las = ''
|
1227
|
-
pre = "%0#{int_len}d" % (price[0] ? price[0] : '0')
|
1235
|
+
def format_price(price_str, len = 6, int_len = 4, frac_len = 2)
|
1236
|
+
price = price_str.split(".")
|
1237
|
+
pre = "%0#{int_len}d" % (price[0] || "0")
|
1228
1238
|
las = if price[1]
|
1229
|
-
|
1230
|
-
|
1231
|
-
|
1232
|
-
|
1233
|
-
|
1234
|
-
|
1235
|
-
|
1236
|
-
|
1237
|
-
(pre.to_s + las.to_s)[0,len]
|
1239
|
+
if price[1].size < frac_len
|
1240
|
+
price[1] + "0" * (frac_len - price[2].to_s.size)
|
1241
|
+
else
|
1242
|
+
price[1][0, frac_len]
|
1243
|
+
end
|
1244
|
+
else
|
1245
|
+
"0" * frac_len
|
1246
|
+
end
|
1247
|
+
(pre.to_s + las.to_s)[0, len]
|
1238
1248
|
end
|
1239
|
-
|
1240
|
-
|
1249
|
+
|
1250
|
+
def format_date(date_str, len = 7)
|
1251
|
+
date = date_str.delete(".")
|
1241
1252
|
if date.size < len
|
1242
|
-
date
|
1253
|
+
date += "0" * (len - date.size)
|
1243
1254
|
end
|
1244
|
-
date[0,len]
|
1255
|
+
date[0, len]
|
1245
1256
|
end
|
1246
1257
|
|
1247
1258
|
# The migel name must be always 50 chars wide and in ISO 8859-1 format
|
1248
1259
|
def format_name(name, length)
|
1249
|
-
("%-#{length}s" % name)[0..length-1]
|
1260
|
+
("%-#{length}s" % name)[0..length - 1]
|
1250
1261
|
end
|
1262
|
+
|
1251
1263
|
def build_dat
|
1252
1264
|
prepare_articles
|
1253
1265
|
rows = []
|
1254
1266
|
@articles.each do |obj|
|
1255
|
-
|
1256
|
-
|
1257
|
-
|
1258
|
-
|
1259
|
-
|
1260
|
-
|
1261
|
-
|
1262
|
-
|
1263
|
-
|
1264
|
-
|
1265
|
-
|
1266
|
-
|
1267
|
-
|
1268
|
-
|
1269
|
-
|
1270
|
-
|
1271
|
-
|
1272
|
-
|
1273
|
-
|
1274
|
-
|
1275
|
-
|
1276
|
-
|
1277
|
-
|
1278
|
-
|
1279
|
-
|
1280
|
-
|
1281
|
-
|
1282
|
-
|
1283
|
-
|
1284
|
-
|
1285
|
-
|
1286
|
-
|
1287
|
-
|
1288
|
-
|
1289
|
-
|
1290
|
-
|
1291
|
-
|
1292
|
-
|
1293
|
-
|
1294
|
-
|
1295
|
-
|
1296
|
-
|
1297
|
-
end
|
1298
|
-
elsif pac and pac[:prices]
|
1299
|
-
price_exf = sprintf('%06i', (pac[:prices][:exf_price][:price].to_f*100).to_i) if pac[:prices][:exf_price] and pac[:prices][:exf_price][:price]
|
1300
|
-
price_public = sprintf('%06i', (pac[:prices][:pub_price][:price].to_f*100).to_i) if pac[:prices][:pub_price] and pac[:prices][:pub_price][:price]
|
1301
|
-
end
|
1302
|
-
row << format_name(Oddb2xml.patch_some_utf8(abez), DAT_LEN[:ABEZ])
|
1303
|
-
if price_exf.to_s.length > DAT_LEN[:PRMO] ||
|
1304
|
-
price_public.to_s.length > DAT_LEN[:PRPU]
|
1305
|
-
puts "Price exfactory #{price_exf} or public #{price_public} is too high to be added into transfer.dat"
|
1306
|
-
break
|
1267
|
+
ean = obj[:ean13]
|
1268
|
+
next if (ean.to_s.length != 13) && !ean14
|
1269
|
+
next if obj[:type] == :nonpharma
|
1270
|
+
row = ""
|
1271
|
+
pack_info = nil
|
1272
|
+
if (x = @packs.find { |k, v| v[:ean13].eql?(ean) })
|
1273
|
+
pack_info = x[1]
|
1274
|
+
end
|
1275
|
+
# Oddb2tdat.parse
|
1276
|
+
pac, no8 = nil, nil
|
1277
|
+
if obj[:seq] && obj[:seq][:packages]
|
1278
|
+
pac = obj[:seq][:packages][obj[:pharmacode]]
|
1279
|
+
pac ||= obj[:seq][:packages][ean]
|
1280
|
+
elsif @items && @items[ean] && @items[ean][:packages]
|
1281
|
+
pac = @items[ean][:packages][ean]
|
1282
|
+
end
|
1283
|
+
# :swissmedic_numbers
|
1284
|
+
if pac
|
1285
|
+
no8 = pac[:swissmedic_number8]
|
1286
|
+
end
|
1287
|
+
if pac && pac[:prices].nil? && no8
|
1288
|
+
ppac = ((ppac = pack_info) && ppac[:is_tier] ? ppac : nil)
|
1289
|
+
pac = ppac if ppac
|
1290
|
+
end
|
1291
|
+
row << "%#{DAT_LEN[:RECA]}s" % "11"
|
1292
|
+
zur_rose = @infos_zur_rose[ean] # zurrose
|
1293
|
+
row << if zur_rose && zur_rose[:cmut]
|
1294
|
+
zur_rose[:cmut]
|
1295
|
+
else
|
1296
|
+
"1"
|
1297
|
+
end
|
1298
|
+
row << "%0#{DAT_LEN[:PHAR]}d" % obj[:pharmacode].to_i
|
1299
|
+
abez = ( # de name
|
1300
|
+
obj[:desc_de].to_s + " " +
|
1301
|
+
(pac ? pac[:name_de].to_s : "") +
|
1302
|
+
(obj[:quantity] || "")
|
1303
|
+
).delete('"')
|
1304
|
+
if @infos_zur_rose[ean]
|
1305
|
+
price_exf = sprintf("%06i", (@infos_zur_rose[ean][:price].to_f * 100).to_i)
|
1306
|
+
price_public = sprintf("%06i", (@infos_zur_rose[ean][:pub_price].to_f * 100).to_i)
|
1307
|
+
if !@options[:percent].nil?
|
1308
|
+
price_public = sprintf("%06i", (price_exf.to_f * (1 + (@options[:percent].to_f / 100))).round_by(0.05).round(2))
|
1307
1309
|
end
|
1308
|
-
|
1309
|
-
|
1310
|
-
|
1311
|
-
|
1312
|
-
|
1313
|
-
|
1314
|
-
|
1315
|
-
|
1316
|
-
|
1317
|
-
|
1318
|
-
|
1319
|
-
|
1320
|
-
|
1321
|
-
|
1322
|
-
|
1323
|
-
|
1324
|
-
|
1325
|
-
|
1326
|
-
|
1327
|
-
|
1328
|
-
|
1329
|
-
|
1330
|
-
|
1331
|
-
|
1332
|
-
|
1333
|
-
|
1334
|
-
|
1335
|
-
|
1336
|
-
|
1337
|
-
|
1338
|
-
|
1339
|
-
|
1310
|
+
elsif pac && pac[:prices]
|
1311
|
+
price_exf = sprintf("%06i", (pac[:prices][:exf_price][:price].to_f * 100).to_i) if pac[:prices][:exf_price] && pac[:prices][:exf_price][:price]
|
1312
|
+
price_public = sprintf("%06i", (pac[:prices][:pub_price][:price].to_f * 100).to_i) if pac[:prices][:pub_price] && pac[:prices][:pub_price][:price]
|
1313
|
+
end
|
1314
|
+
row << format_name(Oddb2xml.patch_some_utf8(abez), DAT_LEN[:ABEZ])
|
1315
|
+
if price_exf.to_s.length > DAT_LEN[:PRMO] ||
|
1316
|
+
price_public.to_s.length > DAT_LEN[:PRPU]
|
1317
|
+
puts "Price exfactory #{price_exf} or public #{price_public} is too high to be added into transfer.dat"
|
1318
|
+
break
|
1319
|
+
end
|
1320
|
+
row << "%#{DAT_LEN[:PRMO]}s" % (price_exf ? price_exf.to_s : ("0" * DAT_LEN[:PRMO]))
|
1321
|
+
row << "%#{DAT_LEN[:PRPU]}s" % (price_public ? price_public.to_s : ("0" * DAT_LEN[:PRPU]))
|
1322
|
+
row << "%#{DAT_LEN[:CKZL]}s" % if @lppvs[ean]
|
1323
|
+
"2"
|
1324
|
+
elsif pac # sl_entry
|
1325
|
+
"1"
|
1326
|
+
else
|
1327
|
+
"3"
|
1328
|
+
end
|
1329
|
+
row << "%#{DAT_LEN[:CLAG]}s" % if ppac && /Blutproduct|impfstoffe/.match(ppac[:list_code]) # COOL
|
1330
|
+
"1"
|
1331
|
+
else
|
1332
|
+
"0"
|
1333
|
+
end
|
1334
|
+
row << "%#{DAT_LEN[:CBGG]}s" % if pack_info && pack_info[:drug_index]
|
1335
|
+
"3"
|
1336
|
+
else
|
1337
|
+
"0"
|
1338
|
+
end
|
1339
|
+
row << "%#{DAT_LEN[:CIKS]}s" % if no8 && pack_info && !pack_info[:is_tier] # Packungen.xls
|
1340
|
+
pack_info[:swissmedic_category]
|
1341
|
+
else
|
1342
|
+
"0"
|
1343
|
+
end.gsub(/(\+|\s)/, "")
|
1344
|
+
row << "%0#{DAT_LEN[:ITHE]}d" % if no8 && pack_info && !pack_info[:is_tier]
|
1345
|
+
format_date(pack_info[:ith_swissmedic])
|
1346
|
+
else
|
1347
|
+
("0" * DAT_LEN[:ITHE])
|
1348
|
+
end.to_i
|
1349
|
+
row << "%0#{DAT_LEN[:CEAN]}d" % (/^000000/.match?(sprintf("%013d", ean.to_i)) ? 0 : ean.to_i)
|
1350
|
+
row << "%#{DAT_LEN[:CMWS]}s" % "2" # pharma
|
1351
|
+
rows << row
|
1340
1352
|
end
|
1341
1353
|
rows.join("\n")
|
1342
1354
|
end
|
1355
|
+
|
1343
1356
|
def build_with_migel_dat
|
1344
1357
|
reset = true
|
1345
1358
|
prepare_articles(reset)
|
1346
1359
|
rows = []
|
1347
1360
|
@articles.each do |obj|
|
1348
|
-
|
1349
|
-
|
1350
|
-
|
1351
|
-
|
1352
|
-
|
1353
|
-
|
1354
|
-
|
1355
|
-
|
1356
|
-
|
1357
|
-
|
1358
|
-
|
1359
|
-
|
1360
|
-
|
1361
|
-
|
1362
|
-
|
1363
|
-
|
1364
|
-
|
1365
|
-
|
1366
|
-
|
1367
|
-
|
1368
|
-
|
1369
|
-
|
1370
|
-
|
1371
|
-
|
1372
|
-
|
1373
|
-
|
1361
|
+
row = ""
|
1362
|
+
next if (obj[:ean13].to_s.length != 13) && !ean14
|
1363
|
+
# Oddb2tdat.parse_migel
|
1364
|
+
row << "%#{DAT_LEN[:RECA]}s" % "11"
|
1365
|
+
row << "%#{DAT_LEN[:CMUT]}s" % if (phar = obj[:pharmacode]) && (phar.size > 3)
|
1366
|
+
"1"
|
1367
|
+
else
|
1368
|
+
"3"
|
1369
|
+
end
|
1370
|
+
row << "%0#{DAT_LEN[:PHAR]}d" % obj[:pharmacode].to_i
|
1371
|
+
abez = ( # de name
|
1372
|
+
obj[:desc_de].to_s + " " +
|
1373
|
+
(obj[:quantity] || "")
|
1374
|
+
).delete('"')
|
1375
|
+
row << format_name(Oddb2xml.patch_some_utf8(abez), DAT_LEN[:ABEZ])
|
1376
|
+
row << "0" * DAT_LEN[:PRMO]
|
1377
|
+
row << "0" * DAT_LEN[:PRPU]
|
1378
|
+
row << "%#{DAT_LEN[:CKZL]}s" % "3" # sl_entry and lppv
|
1379
|
+
row << "%#{DAT_LEN[:CLAG]}s" % "0"
|
1380
|
+
row << "%#{DAT_LEN[:CBGG]}s" % "0"
|
1381
|
+
row << "%#{DAT_LEN[:CIKS]}s" % " " # no category
|
1382
|
+
row << "%0#{DAT_LEN[:ITHE]}d" % 0
|
1383
|
+
row << obj[:ean13].to_s.rjust(DAT_LEN[:CEAN], "0")
|
1384
|
+
row << "%#{DAT_LEN[:CMWS]}s" % "1" # nonpharma
|
1385
|
+
rows << row
|
1386
|
+
end
|
1374
1387
|
rows.join("\n")
|
1375
1388
|
end
|
1389
|
+
|
1376
1390
|
def emit_salecd(xml, ean13, obj)
|
1377
1391
|
zur_rose = nil
|
1378
1392
|
if !@infos_zur_rose.empty? && ean13 && @infos_zur_rose[ean13]
|
1379
1393
|
zur_rose = @infos_zur_rose[ean13] # zurrose
|
1380
1394
|
end
|
1381
1395
|
nincd = detect_nincd(obj)
|
1382
|
-
in_refdata = !!(
|
1383
|
-
status =
|
1384
|
-
|
1396
|
+
in_refdata = !!(obj[:seq] && obj[:seq][:packages] && obj[:seq][:packages][ean13] && obj[:seq][:packages][ean13][:swissmedic_number8])
|
1397
|
+
status = if nincd && nincd == 13
|
1398
|
+
"A"
|
1399
|
+
else
|
1400
|
+
zur_rose && zur_rose[:cmut] != "3" ? "A" : "I"
|
1401
|
+
end
|
1402
|
+
if in_refdata && !"A".eql?(status)
|
1385
1403
|
msg = "Overriding status #{status} nincd #{nincd} for #{ean13} as in refdata_pharma"
|
1386
1404
|
# Oddb2xml.log msg
|
1387
1405
|
@overriden_salecd << ean13
|
1388
|
-
xml.SALECD(
|
1406
|
+
xml.SALECD("A") { xml.comment(msg) }
|
1389
1407
|
else
|
1390
|
-
xml.SALECD(status) { xml.comment(
|
1408
|
+
xml.SALECD(status) { xml.comment("expiry_date #{obj[:expiry_date]}") if obj[:expiry_date] }
|
1391
1409
|
end
|
1392
1410
|
end
|
1393
1411
|
|
1394
1412
|
def build_artikelstamm
|
1395
1413
|
@@emitted_v5_gtins = []
|
1396
|
-
@csv_file = CSV.open(File.join(
|
1397
|
-
@csv_file << [
|
1414
|
+
@csv_file = CSV.open(File.join(WORK_DIR, "artikelstamm_#{Date.today.strftime("%d%m%Y")}_v5.csv"), "w+")
|
1415
|
+
@csv_file << ["gtin", "name", "pkg_size", "galenic_form", "price_ex_factory", "price_public", "prodno", "atc_code", "active_substance", "original", "it-code", "sl-liste"]
|
1398
1416
|
@csv_file.sync = true
|
1399
1417
|
variant = "build_artikelstamm"
|
1400
1418
|
# @infos_zur_rose.delete_if { |key, val| val[:cmut].eql?('3') } # collect only active zur rose item
|
1401
1419
|
# No. Marco did not filter it, eg. 8804121 in rtikelstamm_oddb2xml_051217_v5.xm
|
1402
|
-
|
1403
|
-
|
1404
|
-
|
1405
|
-
|
1406
|
-
|
1407
|
-
|
1408
|
-
|
1409
|
-
|
1410
|
-
|
1420
|
+
self.class.class_eval do
|
1421
|
+
def check_article_name(obj, lang = :de)
|
1422
|
+
ean = obj[:ean13]
|
1423
|
+
refdata = @refdata[ean]
|
1424
|
+
if lang == :de
|
1425
|
+
name = refdata && refdata[:desc_de] ? refdata[:desc_de] : obj[:sequence_name]
|
1426
|
+
elsif lang == :fr
|
1427
|
+
name = refdata && refdata[:desc_fr] ? refdata[:desc_fr] : obj[:sequence_name]
|
1428
|
+
else
|
1429
|
+
return "--missing--"
|
1430
|
+
end
|
1431
|
+
return "--missing--" if !name || name.empty? || name.length < 3
|
1432
|
+
name[0..119] # limit to maximal 120 chars as specified in the XSD
|
1411
1433
|
end
|
1412
|
-
return '--missing--' if !name || name.empty? || name.length < 3
|
1413
|
-
name[0..119] # limit to maximal 120 chars as specified in the XSD
|
1414
1434
|
end
|
1415
|
-
|
1416
|
-
|
1417
|
-
|
1418
|
-
|
1419
|
-
|
1420
|
-
|
1421
|
-
|
1422
|
-
|
1435
|
+
self.class.class_eval do
|
1436
|
+
def override(xml, id, field, default_value)
|
1437
|
+
has_overrides = /\d{13}/.match?(id.to_s) ? @@article_overrides[id.to_i] : @@product_overrides[id.to_i]
|
1438
|
+
if has_overrides && has_overrides[field.to_s]
|
1439
|
+
new_value = has_overrides[field.to_s]
|
1440
|
+
if new_value.to_s.eql?(default_value.to_s)
|
1441
|
+
xml.comment("obsolete override")
|
1442
|
+
else
|
1443
|
+
xml.comment("override #{default_value} with")
|
1444
|
+
end
|
1423
1445
|
cmd = "xml.#{field} \"#{new_value}\""
|
1424
1446
|
else
|
1425
|
-
xml
|
1426
|
-
cmd ="xml.#{field} \"#{new_value}\""
|
1447
|
+
cmd = "xml.#{field} \"#{default_value.to_s.delete('"')}\""
|
1427
1448
|
end
|
1449
|
+
eval cmd if default_value
|
1428
1450
|
end
|
1429
|
-
eval cmd if default_value
|
1430
1451
|
end
|
1431
|
-
|
1432
|
-
|
1433
|
-
|
1434
|
-
|
1435
|
-
|
1436
|
-
|
1437
|
-
|
1438
|
-
|
1439
|
-
|
1440
|
-
|
1441
|
-
|
1442
|
-
|
1443
|
-
|
1444
|
-
|
1445
|
-
|
1446
|
-
|
1447
|
-
|
1448
|
-
nr_items += 1
|
1449
|
-
Oddb2xml.log "build_artikelstamm #{ean13}: #{nr_items} of #{gtins.size} articles" if nr_items % 5000 == 0
|
1450
|
-
item = @items[ean13]
|
1451
|
-
pack_info = nil
|
1452
|
-
pack_info = @packs[no8] if no8 && /#{ean13}/.match(@packs[no8].to_s) # info from Packungen.xlsx from swissmedic_info
|
1453
|
-
next if pack_info && /Tierarzneimittel/.match(pack_info[:list_code])
|
1454
|
-
next if obj[:desc_de] && /ad us vet/i.match(obj[:desc_de])
|
1455
|
-
sequence = obj[:seq]
|
1456
|
-
if sequence.nil? && @packs[no8] && /#{ean13}/.match(@packs[no8].to_s)
|
1457
|
-
sequence = {:packages =>{ean13 => @packs[no8]}}
|
1458
|
-
obj[:seq] = sequence.clone
|
1459
|
-
end
|
1460
|
-
if sequence.nil? && @items[ean13] && @items[ean13][:packages][ean13]
|
1461
|
-
sequence = @items[ean13]
|
1462
|
-
end
|
1463
|
-
if sequence
|
1464
|
-
if obj[:seq] && !obj[:seq][:packages].keys.index(ean13)
|
1465
|
-
# puts "unable to find #{ean13} in #{obj[:seq][:packages].keys}"
|
1466
|
-
next
|
1452
|
+
self.class.class_eval do
|
1453
|
+
def emit_items(xml)
|
1454
|
+
nr_items = 0
|
1455
|
+
gtins_to_article = {}
|
1456
|
+
@articles.each { |article| gtins_to_article[article[:ean13]] = article }
|
1457
|
+
sl_gtins = @items.values.collect { |x| x[:packages].keys }.flatten.uniq
|
1458
|
+
gtins = gtins_to_article.keys + @infos_zur_rose.keys + @packs.values.collect { |x| x[:ean13] } + sl_gtins
|
1459
|
+
gtins = (gtins - @@gtin2ignore)
|
1460
|
+
gtins.sort!.uniq!
|
1461
|
+
gtins.each do |ean13|
|
1462
|
+
no8 = ean13.to_s[4..11] # BAG-XML(SL/LS)
|
1463
|
+
next if ean13 == 0
|
1464
|
+
obj = gtins_to_article[ean13] || @items.values.find { |x| x[:packages].keys.index(ean13) } || @infos_zur_rose[ean13]
|
1465
|
+
if obj
|
1466
|
+
obj = @packs[no8].merge(obj) if @packs[no8]
|
1467
|
+
else
|
1468
|
+
obj = @packs[no8] # obj not yet in refdata. Use data from swissmedic_package.xlsx
|
1467
1469
|
end
|
1468
|
-
|
1469
|
-
|
1470
|
-
|
1471
|
-
|
1472
|
-
|
1473
|
-
|
1474
|
-
|
1475
|
-
|
1476
|
-
|
1477
|
-
|
1478
|
-
|
1479
|
-
|
1480
|
-
|
1481
|
-
|
1482
|
-
|
1483
|
-
|
1484
|
-
|
1485
|
-
|
1470
|
+
nr_items += 1
|
1471
|
+
Oddb2xml.log "build_artikelstamm #{ean13}: #{nr_items} of #{gtins.size} articles" if nr_items % 5000 == 0
|
1472
|
+
item = @items[ean13]
|
1473
|
+
pack_info = nil
|
1474
|
+
pack_info = @packs[no8] if no8 && /#{ean13}/.match(@packs[no8].to_s) # info from Packungen.xlsx from swissmedic_info
|
1475
|
+
next if pack_info && /Tierarzneimittel/.match(pack_info[:list_code])
|
1476
|
+
next if obj[:desc_de] && /ad us vet/i.match(obj[:desc_de])
|
1477
|
+
sequence = obj[:seq]
|
1478
|
+
if sequence.nil? && @packs[no8] && /#{ean13}/.match(@packs[no8].to_s)
|
1479
|
+
sequence = {packages: {ean13 => @packs[no8]}}
|
1480
|
+
obj[:seq] = sequence.clone
|
1481
|
+
end
|
1482
|
+
if sequence.nil? && @items[ean13] && @items[ean13][:packages][ean13]
|
1483
|
+
sequence = @items[ean13]
|
1484
|
+
end
|
1485
|
+
if sequence
|
1486
|
+
if obj[:seq] && !obj[:seq][:packages].keys.index(ean13)
|
1487
|
+
# puts "unable to find #{ean13} in #{obj[:seq][:packages].keys}"
|
1486
1488
|
next
|
1487
|
-
else
|
1488
|
-
@@emitted_v5_gtins << pkg_gtin.clone
|
1489
1489
|
end
|
1490
|
-
|
1491
|
-
|
1492
|
-
|
1493
|
-
|
1494
|
-
|
1495
|
-
|
1496
|
-
|
1497
|
-
|
1498
|
-
|
1499
|
-
|
1500
|
-
|
1501
|
-
xml.DSCR(name) # for description for zur_rose
|
1502
|
-
name_fr = item[:name_fr] + ' ' + item[:desc_fr].strip + ' ' + package[:desc_fr] if package && package[:desc_fr]
|
1503
|
-
name_fr ||= @refdata[pkg_gtin] ? @refdata[pkg_gtin][:desc_fr] : nil
|
1504
|
-
# Zugelassenen Packungen has only german names
|
1505
|
-
name_fr ||= (obj[:name_fr] + ', ' + obj[:desc_fr]).strip if obj[:name_fr]
|
1506
|
-
# ZuRorse has only german names
|
1507
|
-
name_fr ||= (item[:name_fr] + ', ' + item[:desc_fr]) if item
|
1508
|
-
name_fr ||= name
|
1509
|
-
xml.DSCRF(name_fr)
|
1510
|
-
xml.COMP do # Manufacturer
|
1511
|
-
xml.NAME obj[:company_name]
|
1512
|
-
xml.GLN obj[:company_ean]
|
1513
|
-
end if obj[:company_name] || obj[:company_ean]
|
1514
|
-
pexf = ppub = nil
|
1515
|
-
if package[:prices]
|
1516
|
-
pexf ||= package[:prices][:exf_price][:price]
|
1517
|
-
ppub ||= package[:prices][:pub_price][:price]
|
1518
|
-
elsif @items[ean13] && @items[ean13][:packages] && @items[ean13][:packages][ean13] && (bag_prices = @items[ean13][:packages][ean13][:prices])
|
1519
|
-
pexf ||= bag_prices[:exf_price][:price]
|
1520
|
-
ppub ||= bag_prices[:pub_price][:price]
|
1490
|
+
sequence[:packages].each do |gtin, package|
|
1491
|
+
pkg_gtin = package[:ean13].clone
|
1492
|
+
if package[:no8] && (new_ean_13 = Oddb2xml.getEan13forNo8(package[:no8]))
|
1493
|
+
if !new_ean_13.eql?(pkg_gtin)
|
1494
|
+
puts "Setting #{new_ean_13} for #{pkg_gtin}"
|
1495
|
+
pkg_gtin = new_ean_13
|
1496
|
+
end
|
1497
|
+
end
|
1498
|
+
info = @calc_items[pkg_gtin]
|
1499
|
+
if @@emitted_v5_gtins.index(pkg_gtin)
|
1500
|
+
next
|
1521
1501
|
else
|
1522
|
-
|
1523
|
-
ppub ||= obj[:pub_price]
|
1502
|
+
@@emitted_v5_gtins << pkg_gtin.clone
|
1524
1503
|
end
|
1525
|
-
|
1526
|
-
|
1527
|
-
|
1528
|
-
|
1529
|
-
|
1530
|
-
|
1531
|
-
|
1532
|
-
|
1533
|
-
xml.
|
1534
|
-
|
1535
|
-
|
1536
|
-
|
1537
|
-
|
1538
|
-
|
1539
|
-
|
1504
|
+
options = {"PHARMATYPE" => "P"}
|
1505
|
+
xml.ITEM(options) do
|
1506
|
+
name = item[:name_de] + " " + item[:desc_de].strip + " " + package[:desc_de] if package && package[:desc_de]
|
1507
|
+
name ||= @refdata[pkg_gtin] ? @refdata[pkg_gtin][:desc_de] : nil
|
1508
|
+
name ||= @infos_zur_rose[ean13][:description] if @infos_zur_rose[ean13]
|
1509
|
+
name ||= obj[:name_de] + ", " + obj[:desc_de].strip if obj[:name_de]
|
1510
|
+
name ||= (item[:desc_de] + item[:name_de]) if item
|
1511
|
+
name ||= obj[:sequence_name]
|
1512
|
+
xml.GTIN pkg_gtin.to_s.rjust(13, "0")
|
1513
|
+
xml.SALECD("A")
|
1514
|
+
# maxLength for DSCR is 50 for Artikelstamm v3
|
1515
|
+
xml.DSCR(name) # for description for zur_rose
|
1516
|
+
name_fr = item[:name_fr] + " " + item[:desc_fr].strip + " " + package[:desc_fr] if package && package[:desc_fr]
|
1517
|
+
name_fr ||= @refdata[pkg_gtin] ? @refdata[pkg_gtin][:desc_fr] : nil
|
1518
|
+
# Zugelassenen Packungen has only german names
|
1519
|
+
name_fr ||= (obj[:name_fr] + ", " + obj[:desc_fr]).strip if obj[:name_fr]
|
1520
|
+
# ZuRorse has only german names
|
1521
|
+
name_fr ||= (item[:name_fr] + ", " + item[:desc_fr]) if item
|
1522
|
+
name_fr ||= name
|
1523
|
+
xml.DSCRF(name_fr)
|
1524
|
+
if obj[:company_name] || obj[:company_ean]
|
1525
|
+
xml.COMP do # Manufacturer
|
1526
|
+
xml.NAME obj[:company_name][0..99] # limit to 100 chars as in XSD
|
1527
|
+
xml.GLN obj[:company_ean]
|
1528
|
+
end
|
1540
1529
|
end
|
1541
|
-
|
1542
|
-
|
1543
|
-
|
1544
|
-
|
1545
|
-
|
1546
|
-
|
1547
|
-
|
1548
|
-
|
1549
|
-
|
1550
|
-
|
1551
|
-
|
1552
|
-
|
1553
|
-
|
1554
|
-
|
1555
|
-
|
1556
|
-
|
1557
|
-
|
1558
|
-
|
1559
|
-
|
1560
|
-
|
1561
|
-
|
1562
|
-
|
1563
|
-
|
1564
|
-
|
1565
|
-
|
1530
|
+
pexf = ppub = nil
|
1531
|
+
if package[:prices]
|
1532
|
+
pexf ||= package[:prices][:exf_price][:price]
|
1533
|
+
ppub ||= package[:prices][:pub_price][:price]
|
1534
|
+
elsif @items[ean13] && @items[ean13][:packages] && @items[ean13][:packages][ean13] && (bag_prices = @items[ean13][:packages][ean13][:prices])
|
1535
|
+
pexf ||= bag_prices[:exf_price][:price]
|
1536
|
+
ppub ||= bag_prices[:pub_price][:price]
|
1537
|
+
else
|
1538
|
+
pexf ||= obj[:price]
|
1539
|
+
ppub ||= obj[:pub_price]
|
1540
|
+
end
|
1541
|
+
ppub = nil if ppub && ppub.size == 0
|
1542
|
+
pexf = nil if pexf && pexf.size == 0
|
1543
|
+
if !(obj[:price] && !obj[:price].empty?) || !(obj[:pub_price] && !obj[:pub_price].empty?)
|
1544
|
+
zur_rose_detail = @infos_zur_rose.values.find { |x| x[:ean13].to_i == ean13.to_i }
|
1545
|
+
if zur_rose_detail
|
1546
|
+
pexf ||= zur_rose_detail[:price]
|
1547
|
+
ppub ||= zur_rose_detail[:pub_price]
|
1548
|
+
end
|
1549
|
+
end
|
1550
|
+
xml.PEXF pexf if pexf
|
1551
|
+
xml.PPUB ppub if ppub
|
1552
|
+
measure = ""
|
1553
|
+
if info
|
1554
|
+
# MEASSURE Measurement Unit,e.g. Pills or milliliters
|
1555
|
+
# <DSCR>HIRUDOID Creme 3 mg/g 40 g</DSCR>
|
1556
|
+
xml.PKG_SIZE info.pkg_size.to_i if info.pkg_size
|
1557
|
+
if info.measure
|
1558
|
+
measure = info.measure
|
1559
|
+
elsif info.pkg_size && info.unit
|
1560
|
+
measure = info.pkg_size + " " + info.unit
|
1561
|
+
elsif info.pkg_size
|
1562
|
+
measure = info.pkg_size
|
1563
|
+
end
|
1564
|
+
xml.MEASURE measure
|
1565
|
+
xml.MEASUREF measure
|
1566
|
+
# Die Darreichungsform dieses Items. zB Tablette(n) oder Spritze(n)
|
1567
|
+
xml.DOSAGE_FORM info.galenic_form.descriptions["de"] if info.galenic_form.descriptions["de"]
|
1568
|
+
xml.DOSAGE_FORMF info.galenic_form.descriptions["fr"] if info.galenic_form.descriptions["fr"]
|
1569
|
+
end
|
1570
|
+
xml.SL_ENTRY "true" if sl_gtins.index(pkg_gtin)
|
1571
|
+
xml.IKSCAT package[:swissmedic_category] if package[:swissmedic_category] && package[:swissmedic_category].length > 0
|
1572
|
+
xml.GENERIC_TYPE sequence[:org_gen_code] if sequence[:org_gen_code] && !sequence[:org_gen_code].empty?
|
1573
|
+
xml.LPPV "true" if @lppvs[pkg_gtin.to_s] # detect_nincd
|
1574
|
+
if item && item[:deductible]
|
1575
|
+
case item[:deductible]
|
1576
|
+
when "Y" then xml.DEDUCTIBLE 20; # 20%
|
1577
|
+
when "N" then xml.DEDUCTIBLE 10; # 10%
|
1578
|
+
end
|
1579
|
+
end
|
1580
|
+
prodno = Oddb2xml.getProdnoForEan13(pkg_gtin)
|
1581
|
+
atc = package[:atc_code]
|
1582
|
+
refdata_atc = @refdata[pkg_gtin][:atc_code] if @refdata && @refdata[pkg_gtin] && @refdata[pkg_gtin]
|
1583
|
+
if refdata_atc && atc.nil?
|
1584
|
+
puts "WARNING: #{pkg_gtin} ATC-code from refdata #{refdata_atc} as Swissmedic ATC is nil #{name}"
|
1585
|
+
atc = refdata_atc
|
1586
|
+
end
|
1587
|
+
unless prodno # find a prodno from packages for vaccinations
|
1588
|
+
if atc && /^J07/.match(atc) && !/^J07AX/.match(atc)
|
1589
|
+
pack = @packs.values.find { |v| v && v[:atc_code].eql?(atc) }
|
1590
|
+
if pack
|
1591
|
+
prodno = pack[:prodno]
|
1592
|
+
Oddb2xml.log "Patching vaccination for #{pkg_gtin} #{atc} #{name} via prodno #{prodno}"
|
1593
|
+
else
|
1594
|
+
Oddb2xml.log "unable to find a pack/prodno for vaccination for #{pkg_gtin} #{atc} #{name}"
|
1595
|
+
end
|
1566
1596
|
end
|
1567
1597
|
end
|
1598
|
+
xml.PRODNO prodno if prodno
|
1599
|
+
@csv_file << [pkg_gtin, name, package[:unit], measure,
|
1600
|
+
pexf || "",
|
1601
|
+
ppub || "",
|
1602
|
+
prodno, atc, package[:substance_swissmedic],
|
1603
|
+
sequence[:org_gen_code], package[:ith_swissmedic],
|
1604
|
+
@items[pkg_gtin] ? "SL" : ""]
|
1568
1605
|
end
|
1569
|
-
xml.PRODNO prodno if prodno
|
1570
|
-
csv = []
|
1571
|
-
@csv_file << [pkg_gtin, name, package[:unit], measure,
|
1572
|
-
pexf ? pexf : '',
|
1573
|
-
ppub ? ppub : '',
|
1574
|
-
prodno, atc, package[:substance_swissmedic],
|
1575
|
-
sequence[:org_gen_code], package[:ith_swissmedic],
|
1576
|
-
@items[pkg_gtin] ? 'SL' : '',
|
1577
|
-
]
|
1578
1606
|
end
|
1579
|
-
|
1580
|
-
|
1581
|
-
|
1582
|
-
|
1583
|
-
|
1584
|
-
@@emitted_v5_gtins << ean13.clone
|
1585
|
-
end
|
1586
|
-
# Set the pharmatype to 'Y' for outdated products, which are no longer found
|
1587
|
-
# in refdata/packungen
|
1588
|
-
chap70 = nil
|
1589
|
-
if @chapter70items.values.find {|x| x[:pharmacode] && x[:pharmacode].eql?(obj[:pharmacode])}
|
1590
|
-
Oddb2xml.log "found chapter #{obj[:pharmacode]}" if $VERBOSE
|
1591
|
-
chap70 = true
|
1592
|
-
end
|
1593
|
-
patched_pharma_type = (/^7680/.match(ean13.to_s.rjust(13, '0')) || chap70 ? 'P': 'N' )
|
1594
|
-
next if /^#{Oddb2xml::FAKE_GTIN_START}/.match(ean13.to_s)
|
1595
|
-
xml.ITEM({'PHARMATYPE' => patched_pharma_type }) do
|
1596
|
-
xml.GTIN ean13.to_s.rjust(13, '0')
|
1597
|
-
xml.PHAR obj[:pharmacode] if obj[:pharmacode] && obj[:pharmacode].length > 0
|
1598
|
-
emit_salecd(xml, ean13, obj)
|
1599
|
-
description = obj[:desc_de] || obj[:description] # for description for zur_rose
|
1600
|
-
xml.DSCR(description)
|
1601
|
-
xml.DSCRF(obj[:desc_fr] || '--missing--')
|
1602
|
-
xml.COMP do
|
1603
|
-
xml.GLN obj[:company_ean]
|
1604
|
-
end if obj[:company_ean] && !obj[:company_ean].empty?
|
1605
|
-
if !(obj[:price] && !obj[:price].empty?) || !(obj[:pub_price] && !obj[:pub_price].empty?)
|
1606
|
-
zur_rose_detail = @infos_zur_rose.values.find{|x| x[:ean13].to_i == ean13.to_i}
|
1607
|
+
else # non pharma
|
1608
|
+
if @@emitted_v5_gtins.index(ean13)
|
1609
|
+
next
|
1610
|
+
else
|
1611
|
+
@@emitted_v5_gtins << ean13.clone
|
1607
1612
|
end
|
1608
|
-
|
1609
|
-
|
1610
|
-
|
1611
|
-
|
1612
|
-
|
1613
|
-
|
1614
|
-
xml.PEXF (pexf = zur_rose_detail[:price])
|
1615
|
-
end
|
1613
|
+
# Set the pharmatype to 'Y' for outdated products, which are no longer found
|
1614
|
+
# in refdata/packungen
|
1615
|
+
chap70 = nil
|
1616
|
+
if @chapter70items.values.find { |x| x[:pharmacode]&.eql?(obj[:pharmacode]) }
|
1617
|
+
Oddb2xml.log "found chapter #{obj[:pharmacode]}" if $VERBOSE
|
1618
|
+
chap70 = true
|
1616
1619
|
end
|
1617
|
-
|
1618
|
-
|
1619
|
-
|
1620
|
-
|
1621
|
-
|
1622
|
-
|
1620
|
+
patched_pharma_type = (/^7680/.match(ean13.to_s.rjust(13, "0")) || chap70 ? "P" : "N")
|
1621
|
+
next if /^#{Oddb2xml::FAKE_GTIN_START}/o.match?(ean13.to_s)
|
1622
|
+
next if obj[:data_origin].eql?("zur_rose") && /^7680/.match(ean13) # must skip inactiv items
|
1623
|
+
xml.ITEM({"PHARMATYPE" => patched_pharma_type}) do
|
1624
|
+
xml.GTIN ean13.to_s.rjust(13, "0")
|
1625
|
+
if obj[:pharmacode] && obj[:pharmacode].length > 0
|
1626
|
+
xml.PHAR obj[:pharmacode]
|
1627
|
+
elsif (zur_rose = @infos_zur_rose[ean13])
|
1628
|
+
puts "Artikelstamm: Adding #{zur_rose[:pharmacode]} to article GTIN #{ean13}"
|
1629
|
+
xml.PHAR zur_rose[:pharmacode]
|
1630
|
+
elsif /^7680/.match?(ean13)
|
1631
|
+
puts "Artikelstamm: No pharmacode for article GTIN #{ean13} via ZurRose"
|
1632
|
+
end
|
1633
|
+
emit_salecd(xml, ean13, obj)
|
1634
|
+
description = obj[:desc_de] || obj[:description] # for description for zur_rose
|
1635
|
+
xml.DSCR(description)
|
1636
|
+
xml.DSCRF(obj[:desc_fr] || "--missing--")
|
1637
|
+
if obj[:company_ean] && !obj[:company_ean].empty?
|
1638
|
+
xml.COMP do
|
1639
|
+
xml.GLN obj[:company_ean]
|
1640
|
+
end
|
1641
|
+
end
|
1642
|
+
if !(obj[:price] && !obj[:price].empty?) || !(obj[:pub_price] && !obj[:pub_price].empty?)
|
1643
|
+
zur_rose_detail = @infos_zur_rose.values.find { |x| x[:ean13].to_i == ean13.to_i }
|
1644
|
+
end
|
1645
|
+
ppub = nil
|
1646
|
+
pexf = nil
|
1647
|
+
if obj[:price] && !obj[:price].empty?
|
1648
|
+
xml.PEXF(pexf = obj[:price])
|
1649
|
+
elsif zur_rose_detail
|
1650
|
+
if zur_rose_detail[:price] && !zur_rose_detail[:price].empty? && !zur_rose_detail[:price].eql?("0.00")
|
1651
|
+
# Oddb2xml.log "NonPharma: #{ean13} adding PEXF #{zur_rose_detail[:price]} #{description}"
|
1652
|
+
xml.PEXF(pexf = zur_rose_detail[:price])
|
1653
|
+
end
|
1654
|
+
end
|
1655
|
+
if obj[:pub_price] && !obj[:pub_price].empty?
|
1656
|
+
xml.PPUB(ppub = obj[:pub_price])
|
1657
|
+
elsif zur_rose_detail
|
1658
|
+
if zur_rose_detail[:pub_price] && !zur_rose_detail[:pub_price].empty? && !zur_rose_detail[:pub_price].eql?("0.00")
|
1659
|
+
# Oddb2xml.log "NonPharma: #{ean13} adding PPUB #{zur_rose_detail[:pub_price]} #{description}"
|
1660
|
+
xml.PPUB(ppub = zur_rose_detail[:pub_price])
|
1661
|
+
end
|
1662
|
+
end
|
1663
|
+
@csv_file << [ean13, description, "", "", pexf, ppub, "", "", "", "", "", ""]
|
1664
|
+
if chap70
|
1665
|
+
xml.comment "Chapter70 hack #{ean13.to_s.rjust(13, "0")} #{description.encode(xml: :text).gsub("--", "-")}"
|
1666
|
+
xml.SL_ENTRY "true"
|
1667
|
+
xml.PRODNO obj[:pharmacode]
|
1623
1668
|
end
|
1624
|
-
end
|
1625
|
-
@csv_file << [ ean13, description, '', '', pexf, ppub, '', '', '', '', '', '' ]
|
1626
|
-
if chap70
|
1627
|
-
xml.comment "Chapter70 hack #{ean13.to_s.rjust(13, '0')} #{description.encode(:xml => :text).gsub('--','-')}"
|
1628
|
-
xml.SL_ENTRY 'true'
|
1629
|
-
xml.PRODNO obj[:pharmacode]
|
1630
1669
|
end
|
1631
1670
|
end
|
1632
1671
|
end
|
1672
|
+
@csv_file.close if @csv_file && !@csv_file.closed?
|
1673
|
+
nr_items
|
1633
1674
|
end
|
1634
|
-
@csv_file.close if @csv_file && !@csv_file.closed?
|
1635
|
-
nr_items
|
1636
1675
|
end
|
1637
1676
|
unless @prepared
|
1638
|
-
require
|
1639
|
-
Oddb2xml::Chapter70xtractor.parse
|
1677
|
+
require "oddb2xml/chapter_70_hack"
|
1678
|
+
Oddb2xml::Chapter70xtractor.parse
|
1640
1679
|
@chapter70items = Oddb2xml::Chapter70xtractor.items
|
1641
1680
|
prepare_limitations
|
1642
1681
|
prepare_articles
|
@@ -1645,33 +1684,25 @@ module Oddb2xml
|
|
1645
1684
|
prepare_calc_items(suppress_composition_parsing: true)
|
1646
1685
|
@prepared = true
|
1647
1686
|
@old_rose_size = @infos_zur_rose.size
|
1648
|
-
@infos_zur_rose.each do |ean13, value|
|
1649
|
-
if /^7680/.match(ean13.to_s) && @options[:artikelstamm]
|
1650
|
-
@infos_zur_rose.delete(ean13)
|
1651
|
-
end
|
1652
|
-
end if false
|
1653
|
-
@new_rose_size = @infos_zur_rose.size
|
1654
1687
|
end
|
1655
1688
|
nr_products = 0
|
1656
1689
|
nr_articles = 0
|
1657
1690
|
@nr_articles = 0
|
1658
1691
|
used_limitations = []
|
1659
|
-
Oddb2xml.log "#{variant}: Deleted #{@old_rose_size - @new_rose_size} entries from ZurRose where GTIN start with 7680 (Swissmedic)"
|
1660
1692
|
# Oddb2xml.log "#{variant} #{nr_products} of #{@products.size} articles and ignore #{@@gtin2ignore.size} gtins specified via #{@@ignore_file}"
|
1661
|
-
|
1693
|
+
a_builder = Nokogiri::XML::Builder.new(encoding: "utf-8") do |xml|
|
1662
1694
|
xml.doc.tag_suffix = @tag_suffix
|
1663
|
-
datetime = Time.new.strftime('%FT%T%z')
|
1664
1695
|
elexis_strftime_format = "%FT%T\.%L%:z"
|
1665
|
-
@@cumul_ver = (Date.today.year-2013)*12+Date.today.month
|
1666
|
-
options_xml =
|
1667
|
-
|
1668
|
-
|
1669
|
-
|
1670
|
-
|
1696
|
+
@@cumul_ver = (Date.today.year - 2013) * 12 + Date.today.month
|
1697
|
+
options_xml = {
|
1698
|
+
"xmlns" => "http://elexis.ch/Elexis_Artikelstamm_v5",
|
1699
|
+
"CREATION_DATETIME" => Time.new.strftime(elexis_strftime_format),
|
1700
|
+
"BUILD_DATETIME" => Time.new.strftime(elexis_strftime_format),
|
1701
|
+
"DATA_SOURCE" => "oddb2xml"
|
1671
1702
|
}
|
1672
1703
|
emitted_prodno = []
|
1673
1704
|
no8_to_prodno = {}
|
1674
|
-
@packs.collect{ |key, val| no8_to_prodno[key] = val [:prodno] }
|
1705
|
+
@packs.collect { |key, val| no8_to_prodno[key] = val [:prodno] }
|
1675
1706
|
xml.comment("Produced by #{__FILE__} version #{VERSION} at #{Time.now}")
|
1676
1707
|
xml.ARTIKELSTAMM(options_xml) do
|
1677
1708
|
xml.PRODUCTS do
|
@@ -1679,22 +1710,22 @@ module Oddb2xml
|
|
1679
1710
|
products.each do |product|
|
1680
1711
|
ean13 = product[0]
|
1681
1712
|
obj = product[1]
|
1682
|
-
next if /^Q/i.match(obj[:atc])
|
1713
|
+
next if /^Q/i.match?(obj[:atc])
|
1683
1714
|
ean = obj[:ean13]
|
1684
1715
|
sequence = obj[:seq]
|
1685
1716
|
sequence ||= @products[ean][:seq] if @products[ean]
|
1686
|
-
next unless
|
1687
|
-
ppac = ((
|
1688
|
-
prodno = ppac[:prodno] if ppac[:prodno]
|
1717
|
+
next unless check_article_name(obj, :de)
|
1718
|
+
ppac = ((a_ppac = @packs[ean.to_s[4..11]]) && !a_ppac[:is_tier] ? a_ppac : {})
|
1719
|
+
prodno = ppac[:prodno] if ppac[:prodno] && !ppac[:prodno].empty?
|
1689
1720
|
prodno = obj[:pharmacode] if obj[:chapter70]
|
1690
|
-
|
1691
|
-
if
|
1692
|
-
prodno ||=
|
1693
|
-
puts "Setting prodno #{prodno} for #{ean13} #{
|
1721
|
+
my_pack = @packs.values.find { |x| x[:iksnr].to_i == obj[:seq][:swissmedic_number5].to_i } if obj[:seq]
|
1722
|
+
if my_pack && !prodno
|
1723
|
+
prodno ||= my_pack[:prodno]
|
1724
|
+
puts "Setting prodno #{prodno} for #{ean13} #{my_pack[:sequence_name]}"
|
1694
1725
|
end
|
1695
1726
|
next unless prodno
|
1696
1727
|
next if emitted_prodno.index(prodno)
|
1697
|
-
sequence ||= @articles.find{|x| x[:ean13].eql?(ean)}
|
1728
|
+
sequence ||= @articles.find { |x| x[:ean13].eql?(ean) }
|
1698
1729
|
unless obj[:chapter70]
|
1699
1730
|
next unless sequence && (sequence[:name_de] || sequence[:desc_de])
|
1700
1731
|
if Oddb2xml.getEan13forProdno(prodno).size == 0 && !obj[:no8].eql?(Oddb2xml.getNo8ForEan13(ean))
|
@@ -1707,44 +1738,43 @@ module Oddb2xml
|
|
1707
1738
|
xml.PRODUCT do
|
1708
1739
|
xml.PRODNO prodno
|
1709
1740
|
if sequence
|
1710
|
-
xml.SALECD(
|
1741
|
+
xml.SALECD("A") # these products are always active!
|
1711
1742
|
name_de = "#{sequence[:name_de]} #{sequence[:desc_de]}".strip if sequence[:name_de]
|
1712
|
-
|
1713
|
-
|
1714
|
-
|
1715
|
-
|
1716
|
-
name_de = sequence[:desc_de]
|
1717
|
-
end
|
1743
|
+
name_de ||= if ppac && /stk/i.match(sequence[:desc_de])
|
1744
|
+
ppac[:sequence_name]
|
1745
|
+
else
|
1746
|
+
sequence[:desc_de]
|
1718
1747
|
end
|
1719
1748
|
name_fr = "#{sequence[:name_fr]} #{sequence[:desc_fr]}".strip if sequence[:name_fr]
|
1720
1749
|
name_fr ||= (ppac && ppac[:sequence_name])
|
1721
|
-
override(xml, prodno, :DSCR,
|
1750
|
+
override(xml, prodno, :DSCR, name_de.strip)
|
1722
1751
|
override(xml, prodno, :DSCRF, name_fr.strip)
|
1752
|
+
# use overriden ATC if possibel
|
1753
|
+
atc = sequence[:atc] || sequence[:atc_code]
|
1754
|
+
xml.ATC atc if atc && !atc.empty?
|
1723
1755
|
end
|
1724
|
-
xml.ATC sequence[:atc_code] if sequence && sequence[:atc_code] && !sequence[:atc_code].empty?
|
1725
1756
|
if sequence && sequence[:packages] && (first_package = sequence[:packages].values.first) &&
|
1726
|
-
|
1757
|
+
(first_limitation = first_package[:limitations].first)
|
1727
1758
|
lim_code = first_limitation[:code]
|
1728
1759
|
used_limitations << lim_code unless used_limitations.index(lim_code)
|
1729
1760
|
xml.LIMNAMEBAG lim_code
|
1730
1761
|
elsif obj[:chapter70]
|
1731
|
-
xml.comment "Chapter70 hack prodno #{prodno} #{obj[:description].encode(:
|
1732
|
-
xml.SALECD(
|
1762
|
+
xml.comment "Chapter70 hack prodno #{prodno} #{obj[:description].encode(xml: :text).gsub("--", "-")}"
|
1763
|
+
xml.SALECD("A") # these products are always active!
|
1733
1764
|
xml.DSCR obj[:description]
|
1734
|
-
xml.DSCRF
|
1765
|
+
xml.DSCRF ""
|
1735
1766
|
if @limitations.index(obj[:code])
|
1736
1767
|
xml.LIMNAMEBAG obj[:code]
|
1737
1768
|
used_limitations << obj[:code]
|
1738
1769
|
end
|
1739
1770
|
end
|
1740
1771
|
if sequence && sequence[:substances]
|
1741
|
-
value =
|
1742
|
-
|
1743
|
-
value = 'Verschiedene Kombinationen'
|
1772
|
+
value = if sequence[:substances].size > 1
|
1773
|
+
"Verschiedene Kombinationen"
|
1744
1774
|
elsif sequence[:substances].first
|
1745
|
-
|
1775
|
+
sequence[:substances].first[:name]
|
1746
1776
|
else
|
1747
|
-
|
1777
|
+
obj[:sub]
|
1748
1778
|
end
|
1749
1779
|
override(xml, prodno, :SUBSTANCE, value) if value
|
1750
1780
|
end
|
@@ -1763,9 +1793,9 @@ module Oddb2xml
|
|
1763
1793
|
xml.LIMITATION do
|
1764
1794
|
xml.comment "Chapter70 2 hack" if lim[:chap70]
|
1765
1795
|
xml.LIMNAMEBAG lim[:code] # original LIMCD
|
1766
|
-
xml.DSCR
|
1767
|
-
xml.DSCRF
|
1768
|
-
xml.LIMITATION_PTS
|
1796
|
+
xml.DSCR Oddb2xml.html_decode(lim[:desc_de])
|
1797
|
+
xml.DSCRF Oddb2xml.html_decode(lim[:desc_fr])
|
1798
|
+
xml.LIMITATION_PTS(lim[:value].to_s.length > 1 ? lim[:value] : 1)
|
1769
1799
|
end
|
1770
1800
|
end
|
1771
1801
|
end
|
@@ -1776,17 +1806,19 @@ module Oddb2xml
|
|
1776
1806
|
end
|
1777
1807
|
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}"
|
1778
1808
|
# we don't add a SHA256 hash for each element in the article
|
1779
|
-
# Oddb2xml.add_hash(
|
1809
|
+
# Oddb2xml.add_hash(a_builder.to_xml)
|
1780
1810
|
# doc = REXML::Document.new( source, { :raw => :all })
|
1781
1811
|
# doc.write( $stdout, 0 )
|
1782
1812
|
lines = []
|
1783
|
-
lines << " - #{sprintf(
|
1784
|
-
lines << " - #{sprintf(
|
1785
|
-
lines << " - #{sprintf(
|
1786
|
-
lines << " - #{sprintf(
|
1813
|
+
lines << " - #{sprintf("%5d", @products.size)} products"
|
1814
|
+
lines << " - #{sprintf("%5d", @limitations.size)} limitations"
|
1815
|
+
lines << " - #{sprintf("%5d", @nr_articles)} articles"
|
1816
|
+
lines << " - #{sprintf("%5d", @@gtin2ignore.size)} ignored GTINS"
|
1787
1817
|
@@articlestamm_v5_info_lines = lines
|
1788
|
-
|
1818
|
+
a_builder.to_xml({indent: 4, encoding: "UTF-8"})
|
1789
1819
|
end
|
1820
|
+
|
1821
|
+
private_class_method
|
1790
1822
|
def self.articlestamm_v5_info_lines
|
1791
1823
|
@@articlestamm_v5_info_lines
|
1792
1824
|
end
|