editx 0.1.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/CHANGELOG +2 -0
- data/README.markdown +33 -0
- data/lib/editx.rb +78 -0
- data/lib/editx/order.rb +116 -0
- data/lib/editx/order/date_coded.rb +13 -0
- data/lib/editx/order/discount_detail.rb +18 -0
- data/lib/editx/order/discount_part.rb +14 -0
- data/lib/editx/order/item_detail.rb +33 -0
- data/lib/editx/order/message.rb +18 -0
- data/lib/editx/order/order_item_qualifier_coded.rb +14 -0
- data/lib/editx/order/party.rb +12 -0
- data/lib/editx/order/price.rb +16 -0
- data/lib/editx/order/product_id.rb +15 -0
- data/lib/editx/order/reference_coded.rb +15 -0
- data/lib/editx/order/returns_condition_coded.rb +14 -0
- data/lib/editx/order/returns_conditions.rb +19 -0
- data/schemas/editx_codelists.xsd +1753 -0
- data/schemas/order_1_1.xsd +475 -0
- data/schemas/order_1_2.xsd +487 -0
- data/spec/order_spec.rb +97 -0
- data/spec/sample_data_spec.rb +37 -0
- metadata +103 -0
data/CHANGELOG
ADDED
data/README.markdown
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
## EDItX
|
2
|
+
|
3
|
+
The EDItX standard specifies a collection of XML schemas for Electronic
|
4
|
+
Document Interchange between trading partners in the publishing industry.
|
5
|
+
|
6
|
+
This library provides a slim layer of the formats and simplifies both reading a
|
7
|
+
nd writing of EDItX files in your ruby applications.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
gem install editx
|
12
|
+
|
13
|
+
## Usage
|
14
|
+
|
15
|
+
See the comments for each root message type for detailed usage info.
|
16
|
+
|
17
|
+
* EDItX::Order - For working with EDItX Trade Order messages
|
18
|
+
|
19
|
+
## Licensing
|
20
|
+
|
21
|
+
This library is distributed under the terms of the MIT License. See the included file for
|
22
|
+
more detail.
|
23
|
+
|
24
|
+
## Contributing
|
25
|
+
|
26
|
+
All suggestions and patches welcome, preferably via a git repository I can pull from.
|
27
|
+
To be honest, I'm not really expecting any, this is a niche library.
|
28
|
+
|
29
|
+
## Further Reading
|
30
|
+
|
31
|
+
- The source: [http://github.com/yob/editx/tree/master](http://github.com/yob/editx/tree/master)
|
32
|
+
- Rubyforge project: [http://rubyforge.org/projects/rbook/](http://rubyforge.org/projects/rbook/)
|
33
|
+
- The official specs [http://www.editeur.org/editx/editx.html](http://www.editeur.org/editx/editx.html)
|
data/lib/editx.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bigdecimal'
|
3
|
+
|
4
|
+
# ensure we load the correct gem versions
|
5
|
+
gem 'roxml', '2.5.2'
|
6
|
+
gem 'andand'
|
7
|
+
|
8
|
+
# and now load the actual gems
|
9
|
+
require 'roxml'
|
10
|
+
require 'andand'
|
11
|
+
|
12
|
+
module EDItX
|
13
|
+
module Version #:nodoc:
|
14
|
+
Major = 0
|
15
|
+
Minor = 1
|
16
|
+
Tiny = 0
|
17
|
+
|
18
|
+
String = [Major, Minor, Tiny].join('.')
|
19
|
+
end
|
20
|
+
|
21
|
+
class Formatters
|
22
|
+
def self.decimal
|
23
|
+
lambda do |val|
|
24
|
+
if val.kind_of?(BigDecimal)
|
25
|
+
val.to_s("F")
|
26
|
+
else
|
27
|
+
val.to_s
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.yyyymmdd
|
33
|
+
lambda do |val|
|
34
|
+
if val.nil? || !val.respond_to?(:strftime)
|
35
|
+
nil
|
36
|
+
else
|
37
|
+
val.strftime("%Y%m%d")
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.two_digit
|
43
|
+
lambda do |val|
|
44
|
+
if val.nil?
|
45
|
+
nil
|
46
|
+
elsif val < 10
|
47
|
+
"0#{val}"
|
48
|
+
elsif val > 99
|
49
|
+
val.to_s[-2,2]
|
50
|
+
else
|
51
|
+
val.to_s
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# silence some warnings from ROXML
|
59
|
+
unless ROXML.const_defined?("SILENCE_XML_NAME_WARNING")
|
60
|
+
ROXML::SILENCE_XML_NAME_WARNING = true
|
61
|
+
end
|
62
|
+
|
63
|
+
# core files
|
64
|
+
# - ordering is important, classes need to be defined before any
|
65
|
+
# other class can use them
|
66
|
+
require File.join(File.dirname(__FILE__), "editx", "order", "reference_coded")
|
67
|
+
require File.join(File.dirname(__FILE__), "editx", "order", "date_coded")
|
68
|
+
require File.join(File.dirname(__FILE__), "editx", "order", "party")
|
69
|
+
require File.join(File.dirname(__FILE__), "editx", "order", "product_id")
|
70
|
+
require File.join(File.dirname(__FILE__), "editx", "order", "order_item_qualifier_coded")
|
71
|
+
require File.join(File.dirname(__FILE__), "editx", "order", "price")
|
72
|
+
require File.join(File.dirname(__FILE__), "editx", "order", "discount_part")
|
73
|
+
require File.join(File.dirname(__FILE__), "editx", "order", "discount_detail")
|
74
|
+
require File.join(File.dirname(__FILE__), "editx", "order", "returns_condition_coded")
|
75
|
+
require File.join(File.dirname(__FILE__), "editx", "order", "returns_conditions")
|
76
|
+
require File.join(File.dirname(__FILE__), "editx", "order", "message")
|
77
|
+
require File.join(File.dirname(__FILE__), "editx", "order", "item_detail")
|
78
|
+
require File.join(File.dirname(__FILE__), "editx", "order")
|
data/lib/editx/order.rb
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'tempfile'
|
2
|
+
|
3
|
+
module EDItX
|
4
|
+
# A abstraction for creating and generating EDItX Trade Order messages
|
5
|
+
#
|
6
|
+
# == Generating a new order
|
7
|
+
#
|
8
|
+
# The fundamentals are fairly simple. You create a new Order object, set various
|
9
|
+
# attributes on it, then add 1 or more ItemDetail objects - 1 for each item you
|
10
|
+
# would like to order.
|
11
|
+
#
|
12
|
+
# msg = EDItX::Order.new
|
13
|
+
# msg.order_number = self.id
|
14
|
+
# msg.issue_date_time = self.created_at
|
15
|
+
# msg.fill_terms_code = "FillPartBackorderRemainderShipAsAvailable"
|
16
|
+
#
|
17
|
+
# (1..10).each do |idx|
|
18
|
+
# item = EDItX::Order::ItemDetail.new
|
19
|
+
# item.line_number = idx
|
20
|
+
#
|
21
|
+
# ean = EDItX::Order::ProductID.new$
|
22
|
+
# ean.type = "EAN13"$
|
23
|
+
# ean.identifier = "product code goes here"$
|
24
|
+
# item.identifiers << ean
|
25
|
+
#
|
26
|
+
# p = EDItX::Order::Price.new
|
27
|
+
# p.monetary_amount = 10.00
|
28
|
+
# item.prices << p
|
29
|
+
#
|
30
|
+
# item.title_detail = "Title and author here"
|
31
|
+
# item.order_quantity
|
32
|
+
# msg.items << item
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# puts msg.to_s
|
36
|
+
#
|
37
|
+
# The challenge comes in making sure you output a VALID order file. Several elements are
|
38
|
+
# compulsory - I reccommend having a copy of the spec open to check which elements you
|
39
|
+
# need to include, and valid options for elements like fill_terms_code.
|
40
|
+
#
|
41
|
+
# To check the validity of your output, save it to a file and run xmllint on it, using
|
42
|
+
# the schemas distributed with this gem:
|
43
|
+
#
|
44
|
+
# xmllint --valid --nonet --schema schemas/order_1_2.xsd testfile.ord
|
45
|
+
#
|
46
|
+
# Continue testing with that command until you get output like "testfile.ord validates".
|
47
|
+
#
|
48
|
+
class Order
|
49
|
+
include ROXML
|
50
|
+
|
51
|
+
xml_name "Order"
|
52
|
+
|
53
|
+
# header values
|
54
|
+
xml_accessor :version, :from => "@version", :as => BigDecimal, :to_xml => EDItX::Formatters.decimal
|
55
|
+
xml_accessor :order_number, :in => "Header", :from => "OrderNumber"
|
56
|
+
xml_accessor :issue_date_time, :in => "Header", :from => "IssueDateTime", :as => DateTime, :to_xml => EDItX::Formatters.yyyymmdd
|
57
|
+
xml_accessor :references, :in => "Header", :from => "ReferenceCoded", :as => [EDItX::Order::ReferenceCoded]
|
58
|
+
xml_accessor :purpose_code, :in => "Header", :from => "PurposeCode"
|
59
|
+
xml_accessor :currency_code, :in => "Header", :from => "CurrencyCode"
|
60
|
+
xml_accessor :country_code, :in => "Header", :from => "CountryCode"
|
61
|
+
xml_accessor :dates, :in => "Header", :from => "DateCoded", :as => [EDItX::Order::DateCoded]
|
62
|
+
xml_accessor :fill_terms_code, :in => "Header", :from => "FillTermsCode"
|
63
|
+
xml_accessor :buyer_party, :in => "Header", :from => "BuyerParty", :as => EDItX::Order::Party
|
64
|
+
xml_accessor :seller_party, :in => "Header", :from => "SellerParty", :as => EDItX::Order::Party
|
65
|
+
xml_accessor :ship_to_party, :in => "Header", :from => "ShipToParty", :as => EDItX::Order::Party
|
66
|
+
xml_accessor :bill_to_party, :in => "Header", :from => "BillToParty", :as => EDItX::Order::Party
|
67
|
+
xml_accessor :shipping_instructions_code, :in => "Header", :from => "ShippingInstructionsCode"
|
68
|
+
xml_accessor :invoice_instructions_code, :in => "Header", :from => "InvoiceInstructionsCode"
|
69
|
+
xml_accessor :discount_percentage, :in => "Header", :from => "DiscountPercentage", :as => BigDecimal, :to_xml => EDItX::Formatters.decimal
|
70
|
+
|
71
|
+
# detail
|
72
|
+
xml_accessor :items, :from => "ItemDetail", :as => [EDItX::Order::ItemDetail]
|
73
|
+
|
74
|
+
# footer / summary
|
75
|
+
xml_accessor :number_of_lines, :in => "Summary", :from => "NumberOfLines", :as => Fixnum
|
76
|
+
xml_accessor :units_ordered, :in => "Summary", :from => "UnitsOrdered", :as => Fixnum
|
77
|
+
|
78
|
+
def initialize
|
79
|
+
self.version = BigDecimal.new("1.2")
|
80
|
+
self.references = []
|
81
|
+
self.dates = []
|
82
|
+
self.items = []
|
83
|
+
end
|
84
|
+
|
85
|
+
def to_s
|
86
|
+
self.to_xml.to_s
|
87
|
+
end
|
88
|
+
|
89
|
+
def valid?
|
90
|
+
schema_path = File.join(File.dirname(__FILE__), "..", "..", "schemas")
|
91
|
+
case self.version
|
92
|
+
when BigDecimal.new("1.0")
|
93
|
+
schema_path << "/order_1_0.xsd"
|
94
|
+
when BigDecimal.new("1.1")
|
95
|
+
schema_path << "/order_1_1.xsd"
|
96
|
+
when BigDecimal.new("1.2")
|
97
|
+
schema_path << "/order_1_2.xsd"
|
98
|
+
else
|
99
|
+
raise ArgumentError, "version #{self.version} is invalid"
|
100
|
+
end
|
101
|
+
|
102
|
+
Tempfile.open("editx") do |tf|
|
103
|
+
tf.write self.to_s
|
104
|
+
tf.close
|
105
|
+
|
106
|
+
system("xmllint --schema #{schema_path} #{tf.path} &> /dev/null")
|
107
|
+
if $?.exitstatus == 0
|
108
|
+
return true
|
109
|
+
else
|
110
|
+
return false
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module EDItX
|
2
|
+
class Order
|
3
|
+
class DateCoded
|
4
|
+
include ROXML
|
5
|
+
|
6
|
+
xml_name "DateCoded"
|
7
|
+
|
8
|
+
xml_accessor :date, :from => "Date", :as => Date, :to_xml => EDItX::Formatters.yyyymmdd
|
9
|
+
xml_accessor :qualifier_code, :from => "DateQualifierCode"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module EDItX
|
2
|
+
class Order
|
3
|
+
class DiscountDetail
|
4
|
+
include ROXML
|
5
|
+
|
6
|
+
xml_name "DiscountDetail"
|
7
|
+
|
8
|
+
xml_accessor :code_type, :from => "DiscountCodeType"
|
9
|
+
xml_accessor :parts, :from => "DiscountPart", :as => [EDItX::Order::DiscountPart]
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@parts = []
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module EDItX
|
2
|
+
class Order
|
3
|
+
class ItemDetail
|
4
|
+
include ROXML
|
5
|
+
|
6
|
+
xml_name "ItemDetail"
|
7
|
+
|
8
|
+
xml_accessor :line_number, :from => "LineNumber", :as => Fixnum
|
9
|
+
xml_accessor :identifiers, :from => "ProductID", :as => [EDItX::Order::ProductID]
|
10
|
+
xml_accessor :title_detail, :in => "ItemDescription", :from => "TitleDetail"
|
11
|
+
xml_accessor :order_quantity, :from => "OrderQuantity", :as => Fixnum
|
12
|
+
xml_accessor :references, :from => "ReferenceCoded", :as => [EDItX::Order::ReferenceCoded]
|
13
|
+
xml_accessor :dates, :from => "DateCoded", :as => [EDItX::Order::DateCoded]
|
14
|
+
xml_accessor :fill_terms_code, :from => "FillTermsCode"
|
15
|
+
xml_accessor :qualifiers, :from => "OrderItemQualifierCoded", :as => [EDItX::Order::OrderItemQualifierCoded]
|
16
|
+
xml_accessor :prices, :in => "PricingDetail", :from => "Price", :as => [EDItX::Order::Price]
|
17
|
+
xml_accessor :discount_percentage, :in => "PricingDetail", :from => "DiscountPercentage", :as => BigDecimal
|
18
|
+
xml_accessor :discount_detail, :in => "PricingDetail", :from => "DiscountDetail", :as => [EDItX::Order::DiscountDetail]
|
19
|
+
xml_accessor :returns_conditions, :from => "ReturnsConditions", :as => [EDItX::Order::ReturnsConditions]
|
20
|
+
xml_accessor :messages, :from => "Message", :as => [EDItX::Order::Message]
|
21
|
+
|
22
|
+
def initialize
|
23
|
+
@identifiers = []
|
24
|
+
@references = []
|
25
|
+
@dates = []
|
26
|
+
@qualifiers = []
|
27
|
+
@prices = []
|
28
|
+
@returns_conditions = []
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module EDItX
|
2
|
+
class Order
|
3
|
+
class Message
|
4
|
+
include ROXML
|
5
|
+
|
6
|
+
xml_name "Message"
|
7
|
+
|
8
|
+
xml_accessor :type, :from => "MessageType"
|
9
|
+
xml_accessor :lines, :from => "MessageLine", :as => []
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@lines = []
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module EDItX
|
2
|
+
class Order
|
3
|
+
class OrderItemQualifierCoded
|
4
|
+
include ROXML
|
5
|
+
|
6
|
+
xml_name "OrderItemQualifierCoded"
|
7
|
+
|
8
|
+
xml_accessor :code_type, :from => "OrderItemQualifierCodeType"
|
9
|
+
xml_accessor :code, :from => "OrderItemQualifierCode"
|
10
|
+
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module EDItX
|
2
|
+
class Order
|
3
|
+
class Party
|
4
|
+
include ROXML
|
5
|
+
|
6
|
+
xml_accessor :id_type, :in => "PartyID", :from => "PartyIDType"
|
7
|
+
xml_accessor :id, :in => "PartyID", :from => "Identifier"
|
8
|
+
xml_accessor :name, :in => "PartyName", :from => "NameLine"
|
9
|
+
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module EDItX
|
2
|
+
class Order
|
3
|
+
class Price
|
4
|
+
include ROXML
|
5
|
+
|
6
|
+
xml_name "Price"
|
7
|
+
|
8
|
+
xml_accessor :monetary_amount, :from => "MonetaryAmount", :as => BigDecimal
|
9
|
+
xml_accessor :currency_code, :from => "CurrencyCode"
|
10
|
+
xml_accessor :country_code, :from => "CountryCode"
|
11
|
+
xml_accessor :qualifier_code, :from => "PriceQualifierCode"
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module EDItX
|
2
|
+
class Order
|
3
|
+
class ProductID
|
4
|
+
include ROXML
|
5
|
+
|
6
|
+
xml_name "ProductID"
|
7
|
+
|
8
|
+
xml_accessor :type, :from => "ProductIDType"
|
9
|
+
xml_accessor :type_name, :from => "ProductIDType"
|
10
|
+
xml_accessor :identifier, :from => "Identifier"
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module EDItX
|
2
|
+
class Order
|
3
|
+
class ReferenceCoded
|
4
|
+
|
5
|
+
include ROXML
|
6
|
+
|
7
|
+
xml_name "ReferenceCoded"
|
8
|
+
|
9
|
+
xml_accessor :type_code, :from => "ReferenceTypeCode"
|
10
|
+
xml_accessor :number, :from => "ReferenceNumber"
|
11
|
+
xml_accessor :date, :from => "ReferenceDate", :as => Date, :to_xml => EDItX::Formatters.yyyymmdd
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module EDItX
|
2
|
+
class Order
|
3
|
+
class ReturnsConditions
|
4
|
+
include ROXML
|
5
|
+
|
6
|
+
xml_name "ReturnsConditions"
|
7
|
+
|
8
|
+
xml_accessor :last_date_for_returns, :from => "LastDateForReturns", :as => DateTime, :to_xml => EDItX::Formatters.yyyymmdd
|
9
|
+
xml_accessor :max_returns_percentage, :from => "MaximumReturnsPercentage"
|
10
|
+
xml_accessor :max_returns_quantity, :from => "MaximumReturnsQuantity"
|
11
|
+
xml_accessor :conditions, :from => "ReturnsConditionCoded", :as => [EDItX::Order::ReturnsConditionCoded]
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@conditions = []
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|