sandozxmlconv 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
}
|