sandozxmlconv 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gemtest +0 -0
- data/History.txt +6 -0
- data/LICENSE +339 -0
- data/README.txt +25 -0
- data/Rakefile +38 -0
- data/doc/index.rbx +14 -0
- data/doc/propharma.rbx +57 -0
- data/doc/resources/xmlconv.css +94 -0
- data/doc/soap.rbx +55 -0
- data/doc/sunstore.rbx +57 -0
- data/doc/unilog.rbx +57 -0
- data/doc/volksapotheke.rbx +57 -0
- data/doc/wbmb.rbx +56 -0
- data/etc/trans_handler.yml +0 -0
- data/lib/conversion/bdd_csv.rb +60 -0
- data/lib/conversion/pharmacieplus_bdd.rb +140 -0
- data/lib/conversion/propharma_bdd.rb +100 -0
- data/lib/conversion/sunstore_bdd.rb +222 -0
- data/lib/conversion/wbmb_bdd.rb +200 -0
- data/lib/conversion/xundart_bdd.rb +73 -0
- data/lib/postprocess/bbmb.rb +88 -0
- data/lib/postprocess/bbmb2.rb +102 -0
- data/lib/postprocess/soap.rb +15 -0
- data/lib/sandozxmlconv.rb +3 -0
- data/test/mock.rb +149 -0
- data/test/rcov +2 -0
- data/test/suite.rb +16 -0
- data/test/test_conversion/pharmacieplus_bdd.rb +224 -0
- data/test/test_conversion/propharma_bdd.rb +101 -0
- data/test/test_conversion/sunstore_bdd.rb +159 -0
- data/test/test_conversion/wbmb_bdd.rb +206 -0
- data/test/test_conversion/xundart_bdd.rb +93 -0
- data/test/test_integration/pharmacieplus_csv.rb +205 -0
- data/test/test_integration/propharma_csv.rb +129 -0
- data/test/test_integration/sunstore_csv.rb +79 -0
- data/test/test_integration/xundart_csv.rb +71 -0
- data/test/test_postprocess/bbmb.rb +229 -0
- data/test/test_postprocess/bbmb2.rb +441 -0
- data/test/test_postprocess/soap.rb +37 -0
- metadata +123 -0
@@ -0,0 +1,73 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Conversion::XundartBdd -- xmlconv -- 16.07.2009 -- hwyss@ywesee.com
|
3
|
+
|
4
|
+
require 'conversion/pharmacieplus_bdd'
|
5
|
+
|
6
|
+
module XmlConv
|
7
|
+
module Conversion
|
8
|
+
class XundartBdd < PharmaciePlusBdd
|
9
|
+
class << self
|
10
|
+
## this is essentially the same format as PharmaciePlus - with the exception of
|
11
|
+
# what fields are used to transmit names. The PharmaciePlus converter needs to
|
12
|
+
# uncross last-name and pharmacy-name. The following two overriding methods
|
13
|
+
# remove this switch. From a design point-of view this is really the wrong way
|
14
|
+
# around (it would be better to have specialized behavior in the descendent
|
15
|
+
# class), but historically, and in terms of the
|
16
|
+
# "Xundart-is-a-PharmaciePlus-Format"-Relation, this seems the best way to
|
17
|
+
# do it.
|
18
|
+
def _delivery_add_xml_customer(delivery, xml_delivery)
|
19
|
+
customer = Model::Party.new
|
20
|
+
customer.role = 'Customer'
|
21
|
+
_customer_add_party(customer, '1075', 'BillTo')
|
22
|
+
ship_to = _customer_add_party(customer,
|
23
|
+
_latin1(xml_delivery.attributes['ean']),
|
24
|
+
'ShipTo')
|
25
|
+
if(xml_header = REXML::XPath.first(xml_delivery, 'livraison'))
|
26
|
+
name = Model::Name.new
|
27
|
+
# Pharmacieplus delivers the Pharmacy-Name in 'last-name', and the name
|
28
|
+
# of the contact person in 'other-name' - we need to juggle the pieces
|
29
|
+
# around a bit. (see also _party_add_xml_address)
|
30
|
+
if(xml_name = REXML::XPath.first(xml_header, 'last-name'))
|
31
|
+
name.last = _latin1(xml_name.text)
|
32
|
+
end
|
33
|
+
if(xml_name = REXML::XPath.first(xml_header, 'first-name'))
|
34
|
+
name.first = _latin1(xml_name.text)
|
35
|
+
end
|
36
|
+
if(xml_name = REXML::XPath.first(xml_header, 'other-name'))
|
37
|
+
name.text = _latin1(xml_name.text)
|
38
|
+
end
|
39
|
+
customer.name = name
|
40
|
+
ship_to.name = name
|
41
|
+
_party_add_xml_address(ship_to, xml_header)
|
42
|
+
end
|
43
|
+
if(xml_email = REXML::XPath.first(xml_delivery, '//groupe/online/email'))
|
44
|
+
customer.add_id('email', _latin1(xml_email.text))
|
45
|
+
end
|
46
|
+
delivery.add_party(customer)
|
47
|
+
end
|
48
|
+
def _party_add_xml_address(party, xml_header)
|
49
|
+
if(xml_address = REXML::XPath.first(xml_header, 'address'))
|
50
|
+
address = Model::Address.new
|
51
|
+
address.zip_code = _text(xml_address, 'zip')
|
52
|
+
address.city = _text(xml_address, 'city')
|
53
|
+
if(line = _text(xml_address, 'street'))
|
54
|
+
address.add_line(line)
|
55
|
+
end
|
56
|
+
party.address = address
|
57
|
+
end
|
58
|
+
end
|
59
|
+
## this method adapts Xundart to the fact that the number of items is stored
|
60
|
+
# in the attribute qte_facture instead of qte-facture - for whatever reason...
|
61
|
+
def _delivery_add_xml_item(delivery, xml_item)
|
62
|
+
item = Model::DeliveryItem.new
|
63
|
+
item.line_no = _latin1(delivery.items.size.next.to_s)
|
64
|
+
item.add_id('ET-Nummer', _latin1(xml_item.attributes['ean']))
|
65
|
+
item.add_id('Pharmacode', _latin1(xml_item.attributes['pharmacode']))
|
66
|
+
item.qty = _latin1(xml_item.attributes['qte_facture'])
|
67
|
+
item.unit = 'PCE'
|
68
|
+
delivery.add_item(item)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# PostProcess::Bbmb -- xmlconv2 -- 25.08.2006 -- hwyss@ywesee.com
|
3
|
+
|
4
|
+
require 'drb'
|
5
|
+
|
6
|
+
module XmlConv
|
7
|
+
module PostProcess
|
8
|
+
module Bbmb
|
9
|
+
def Bbmb.inject(drb_url, name_short, inject_id, transaction=nil)
|
10
|
+
## inject with 4 arguments is a special case where the recipient is
|
11
|
+
# not known in BBMB. In all other cases we can take the inject_id
|
12
|
+
# directly from customer.acc_id. If so, inject is called with
|
13
|
+
# 3 arguments, with transaction as the third argument.
|
14
|
+
if(transaction.nil?)
|
15
|
+
transaction = inject_id
|
16
|
+
inject_id = nil
|
17
|
+
end
|
18
|
+
if(bdd = transaction.model)
|
19
|
+
bbmb = DRbObject.new(nil, drb_url)
|
20
|
+
bdd.deliveries.each { |delivery|
|
21
|
+
begin
|
22
|
+
iid = inject_id || delivery.customer.acc_id
|
23
|
+
order = order(delivery)
|
24
|
+
info = info(delivery)
|
25
|
+
bbmb.inject_order(name_short, iid, order, info)
|
26
|
+
rescue Exception => e
|
27
|
+
message = "Bestellung OK, Eintrag in BBMB Fehlgeschlagen:\n" \
|
28
|
+
<< e.class.to_s << "\n" \
|
29
|
+
<< e.message << "\n\n" \
|
30
|
+
<< e.backtrace.join("\n") << "\n\n" \
|
31
|
+
<< "name_short: #{name_short}\n" \
|
32
|
+
<< "hospital: #{inject_id}\n"
|
33
|
+
if(order)
|
34
|
+
message << "\norder: \n"
|
35
|
+
order.each { |k,v| message << "#{k.inspect} => #{v}\n" }
|
36
|
+
end
|
37
|
+
if(info)
|
38
|
+
message << "\ninfo: \n"
|
39
|
+
info.each { |k,v| message << "#{k} => #{v}\n" }
|
40
|
+
end
|
41
|
+
raise message
|
42
|
+
end
|
43
|
+
}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
def Bbmb.item_ids(item)
|
47
|
+
item.id_table.inject({}) { |memo, (domain, value)|
|
48
|
+
key = case domain
|
49
|
+
when 'et-nummer'
|
50
|
+
:article_ean13
|
51
|
+
when 'pharmacode'
|
52
|
+
:article_pcode
|
53
|
+
when 'lieferantenartikel'
|
54
|
+
:article_number
|
55
|
+
end
|
56
|
+
memo.store(key, value.gsub(/^0+/, ''))
|
57
|
+
memo
|
58
|
+
}
|
59
|
+
end
|
60
|
+
def Bbmb.order(delivery)
|
61
|
+
pairs = []
|
62
|
+
delivery.items.each { |item|
|
63
|
+
pairs.push([item_ids(item), item.qty.to_i])
|
64
|
+
}
|
65
|
+
pairs
|
66
|
+
end
|
67
|
+
def Bbmb.info(delivery)
|
68
|
+
info = {
|
69
|
+
:order_reference => delivery.customer_id,
|
70
|
+
}
|
71
|
+
if(date = delivery.delivery_date)
|
72
|
+
info.store(:order_expressdate, date)
|
73
|
+
end
|
74
|
+
lines = []
|
75
|
+
if((cust = delivery.customer) && ship = cust.ship_to)
|
76
|
+
lines.push(ship.acc_id)
|
77
|
+
lines.push(ship.name)
|
78
|
+
if(addr = ship.address)
|
79
|
+
lines.concat(addr.lines)
|
80
|
+
lines.push([addr.zip_code, addr.city].compact.join(' '))
|
81
|
+
end
|
82
|
+
info.store(:order_comment, lines.compact.join("\n"))
|
83
|
+
end
|
84
|
+
info
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# PostProcess::Bbmb2 -- xmlconv2 -- 25.08.2006 -- hwyss@ywesee.com
|
3
|
+
|
4
|
+
require 'drb'
|
5
|
+
require 'iconv'
|
6
|
+
|
7
|
+
module XmlConv
|
8
|
+
module PostProcess
|
9
|
+
module Bbmb2
|
10
|
+
def Bbmb2.inject(drb_url, idtype, transaction=nil)
|
11
|
+
if transaction.nil?
|
12
|
+
transaction, idtype = idtype, nil
|
13
|
+
end
|
14
|
+
if(bdd = transaction.model)
|
15
|
+
bbmb = DRbObject.new(nil, drb_url)
|
16
|
+
messages = []
|
17
|
+
bdd.deliveries.each_with_index { |delivery, idx|
|
18
|
+
inject_id, order, info = nil
|
19
|
+
begin
|
20
|
+
customer = delivery.customer
|
21
|
+
inject_id = customer.acc_id
|
22
|
+
name = customer.name
|
23
|
+
if ship = customer.ship_to
|
24
|
+
name = ship.name
|
25
|
+
if addr = ship.address
|
26
|
+
name = addr.lines.first
|
27
|
+
end
|
28
|
+
inject_id ||= ship.acc_id
|
29
|
+
end
|
30
|
+
inject_id ||= customer.party_id
|
31
|
+
order = order(delivery)
|
32
|
+
info = info(delivery)
|
33
|
+
options = {
|
34
|
+
:deliver => true,
|
35
|
+
:create_missing_customer => idtype.to_s,
|
36
|
+
:transaction => transaction.transaction_id.to_s,
|
37
|
+
:customer_name => name.to_s,
|
38
|
+
}
|
39
|
+
resp = bbmb.inject_order(inject_id, order, info, options)
|
40
|
+
transaction.respond(idx, resp)
|
41
|
+
rescue Exception => e
|
42
|
+
transaction.respond(idx, :products => order)
|
43
|
+
message = "Bestellung OK, Eintrag in BBMB Fehlgeschlagen:\n" \
|
44
|
+
<< e.class.to_s << "\n" \
|
45
|
+
<< e.message << "\n\n" \
|
46
|
+
<< e.backtrace.join("\n") << "\n\n" \
|
47
|
+
<< "hospital: #{inject_id}\n"
|
48
|
+
if(order)
|
49
|
+
message << "\norder: \n"
|
50
|
+
order.each { |k| message << "#{k.inspect}\n" }
|
51
|
+
end
|
52
|
+
if(info)
|
53
|
+
message << "\ninfo: \n"
|
54
|
+
info.each { |k,v| message << "#{k} => #{v}\n" }
|
55
|
+
end
|
56
|
+
messages.push message
|
57
|
+
end
|
58
|
+
}
|
59
|
+
unless messages.empty?
|
60
|
+
raise messages.join("\n\n")
|
61
|
+
end
|
62
|
+
transaction.status = :bbmb_ok
|
63
|
+
end
|
64
|
+
end
|
65
|
+
def Bbmb2.item_ids(item)
|
66
|
+
item.id_table.inject({}) { |memo, (domain, value)|
|
67
|
+
key = case domain
|
68
|
+
when 'et-nummer'
|
69
|
+
:ean13
|
70
|
+
when 'pharmacode'
|
71
|
+
:pcode
|
72
|
+
when 'lieferantenartikel'
|
73
|
+
:article_number
|
74
|
+
end
|
75
|
+
memo.store(key, value.gsub(/^0+/, ''))
|
76
|
+
memo
|
77
|
+
}
|
78
|
+
end
|
79
|
+
def Bbmb2.order(delivery)
|
80
|
+
delivery.items.collect { |item|
|
81
|
+
data = item_ids(item)
|
82
|
+
data.store(:quantity, item.qty.to_i)
|
83
|
+
data
|
84
|
+
}
|
85
|
+
end
|
86
|
+
def Bbmb2.info(delivery)
|
87
|
+
info = {
|
88
|
+
:reference => iconv(delivery.customer_id),
|
89
|
+
}
|
90
|
+
lines = []
|
91
|
+
if(text = delivery.free_text)
|
92
|
+
info.store(:comment, iconv(text))
|
93
|
+
end
|
94
|
+
info
|
95
|
+
end
|
96
|
+
def Bbmb2.iconv(str)
|
97
|
+
@iconv ||= Iconv.new('utf8', 'latin1')
|
98
|
+
@iconv.iconv str
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# PostProcess::Soap -- xmlconv2 -- 25.08.2006 -- hwyss@ywesee.com
|
3
|
+
|
4
|
+
module XmlConv
|
5
|
+
module PostProcess
|
6
|
+
module Soap
|
7
|
+
def Soap.update_partner(transaction)
|
8
|
+
if((bdd = transaction.model) && (delivery = bdd.deliveries.first) \
|
9
|
+
&& (bsr = delivery.bsr) && (customer = bsr.customer))
|
10
|
+
transaction.partner = customer.acc_id || customer.party_id
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/test/mock.rb
ADDED
@@ -0,0 +1,149 @@
|
|
1
|
+
# Ruby/Mock version 1.0
|
2
|
+
#
|
3
|
+
# A class for conveniently building mock objects in RUnit test cases.
|
4
|
+
# Copyright (c) 2001 Nat Pryce, all rights reserved
|
5
|
+
#
|
6
|
+
# This program is free software; you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU General Public License as published by
|
8
|
+
# the Free Software Foundation; either version 2 of the License.
|
9
|
+
#
|
10
|
+
# This program is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU General Public License
|
16
|
+
# along with this program; if not, write to the Free Software
|
17
|
+
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
18
|
+
|
19
|
+
require 'runit/error'
|
20
|
+
|
21
|
+
|
22
|
+
class Mock
|
23
|
+
# Creates a new, named mock object. The name is reported in exceptions
|
24
|
+
# thrown by the mock object when method invocations are incorrect.
|
25
|
+
#
|
26
|
+
def initialize( mock_name = self.to_s )
|
27
|
+
@mock_calls = []
|
28
|
+
@next_call = 0
|
29
|
+
@name = mock_name
|
30
|
+
end
|
31
|
+
|
32
|
+
# Mock the next method call to be made to this mock object.
|
33
|
+
#
|
34
|
+
# A mock method is defined by the method name (a symbol) and a block
|
35
|
+
# that defines the arity of the method and the mocked behaviour for
|
36
|
+
# this call. The mocked behaviour should assert preconditions and
|
37
|
+
# return a value. Mocked behaviour should rarely be any more complex
|
38
|
+
# than that. If it is, that's probably an indication that the tests
|
39
|
+
# need some restructuring or that the tested code needs refactoring.
|
40
|
+
#
|
41
|
+
# If no block is given and preconditions have been defined for the named
|
42
|
+
# method, a block is created for the mocked methodthat has the same arity
|
43
|
+
# as the precondition and returns self.
|
44
|
+
#
|
45
|
+
def __next( name, &test )
|
46
|
+
if test == nil
|
47
|
+
if respond_to?( Mock.__pre(name) )
|
48
|
+
test = proc { |*args| self }
|
49
|
+
else
|
50
|
+
raise "no block given for mocked method #{name}"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
@mock_calls.push( [name,test] )
|
54
|
+
end
|
55
|
+
|
56
|
+
# Call this at the end of a test to ensure that all scheduled calls
|
57
|
+
# have been made to the mock
|
58
|
+
#
|
59
|
+
def __verify
|
60
|
+
if @next_call != @mock_calls.length
|
61
|
+
raise RUNIT::AssertionFailedError,
|
62
|
+
"not all expected method calls were made to #{@name}",
|
63
|
+
caller
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
private
|
69
|
+
# Dispatches aribtrary method calls to the next mocked behaviour
|
70
|
+
#
|
71
|
+
def method_missing( name, *args, &block )
|
72
|
+
__mock_call( name, args, (block_given? ? block : nil) )
|
73
|
+
end
|
74
|
+
|
75
|
+
# Implements a method call using the next mocked behaviour and asserts
|
76
|
+
# that the expected method is called with the expected number of
|
77
|
+
# arguments.
|
78
|
+
#
|
79
|
+
def __mock_call( name, args, block )
|
80
|
+
if @next_call >= @mock_calls.length
|
81
|
+
raise RUNIT::AssertionFailedError,
|
82
|
+
"unexpected call to #{name} method of #{@name}",
|
83
|
+
caller(2)
|
84
|
+
end
|
85
|
+
|
86
|
+
expected_name,body = @mock_calls[@next_call]
|
87
|
+
@next_call += 1
|
88
|
+
|
89
|
+
if name != expected_name
|
90
|
+
raise RUNIT::AssertionFailedError,
|
91
|
+
"wrong method called on #{@name}; " +
|
92
|
+
"expected #{expected_name}, was #{name}",
|
93
|
+
caller(2)
|
94
|
+
end
|
95
|
+
|
96
|
+
args_length = args.length + (block ? 1 : 0)
|
97
|
+
|
98
|
+
if body.arity < 0
|
99
|
+
if (body.arity+1).abs > args_length
|
100
|
+
raise RUNIT::AssertionFailedError,
|
101
|
+
"too few arguments to #{name} method of #{@name}; " +
|
102
|
+
"require #{(body.arity+1).abs}, got #{args.length}",
|
103
|
+
caller(2)
|
104
|
+
end
|
105
|
+
else
|
106
|
+
if body.arity != args_length
|
107
|
+
raise RUNIT::AssertionFailedError,
|
108
|
+
"wrong number of arguments to " +
|
109
|
+
"#{name} method of #{@name}; " +
|
110
|
+
"require #{body.arity}, got #{args.length}",
|
111
|
+
caller(2)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
if respond_to? Mock.__pre(name)
|
116
|
+
if block
|
117
|
+
precondition_ok = __send__( Mock.__pre(name), *args, &block )
|
118
|
+
else
|
119
|
+
precondition_ok = __send__( Mock.__pre(name), *args )
|
120
|
+
end
|
121
|
+
|
122
|
+
if not precondition_ok
|
123
|
+
raise RUNIT::AssertionFailedError,
|
124
|
+
"precondition of #{name} method violated",
|
125
|
+
caller(2)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
if block
|
130
|
+
instance_eval { body.call( block, *args ) }
|
131
|
+
else
|
132
|
+
instance_eval { body.call( *args ) }
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
# The name of a precondition for a method
|
137
|
+
def Mock.__pre( method )
|
138
|
+
"__pre_#{method.to_i}".intern
|
139
|
+
end
|
140
|
+
|
141
|
+
|
142
|
+
def Mock.method_added( name )
|
143
|
+
unless(/^__pre_/.match(name.to_s))
|
144
|
+
pre = self.__pre(name)
|
145
|
+
alias_method( pre, name )
|
146
|
+
undef_method(name)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
data/test/rcov
ADDED
data/test/suite.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# TestSuite -- xmlconv2 -- 01.06.2004 -- hwyss@ywesee.com
|
3
|
+
|
4
|
+
$: << File.dirname(File.expand_path(__FILE__))
|
5
|
+
|
6
|
+
current_dir = File.dirname(__FILE__)
|
7
|
+
Dir.foreach(current_dir) { |dirname|
|
8
|
+
dirpath = File.expand_path(dirname, current_dir)
|
9
|
+
if(/^test_/o.match(dirname) && (File.ftype(dirpath) == 'directory'))
|
10
|
+
Dir.foreach(dirpath) { |filename|
|
11
|
+
if(/\.rb$/o.match(filename))
|
12
|
+
require "#{dirname}/#{filename}"
|
13
|
+
end
|
14
|
+
}
|
15
|
+
end
|
16
|
+
}
|