einvoice 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a5e69679681ee049f816b49bfed94ba21d79ea4f
4
- data.tar.gz: f35afbf92608c3076ef352ac849bc36a94043620
3
+ metadata.gz: 2a1f45ab4b79a056cef91bec983a30cebb611a40
4
+ data.tar.gz: 44146482f5667e0164e002be7eecae296a74607c
5
5
  SHA512:
6
- metadata.gz: 7f6f42e32120b6752c2431cdb5db4d2c7fe0dd6d8fdeff460d4ee9e8d84afbde1c1cee4935ccc58c20fb77a9d968073dc622abafb136d9df81885e8e7a83fc5a
7
- data.tar.gz: b0952bfcc6df37d28a120620a1bbe11ddf400aa99b996e0f6b817ba9c789fdfdae04fb5366b82736a4b7198f2d18be81a8cac0d9a929b682a6889f3deb546abb
6
+ metadata.gz: a6edd9c6b8f3c97ca9aadcfa40b4da485c06d2f3a104a893850d1084c84de61fa4aeaf4942c622ff556abf45e6e85af9ea2b0e6e8bd2ad7b4b2eaf0a86ba6a00
7
+ data.tar.gz: c96a98796942c8958d44897fbad60293f607f6f335f7355fe50cb54764a43f774269aed3c24ca3b207ebfa7a6430820a282631c3a117f13e6010037103aa8391
data/lib/einvoice.rb CHANGED
@@ -9,6 +9,7 @@ require "einvoice/utils"
9
9
  require "einvoice/version"
10
10
 
11
11
  require "einvoice/neweb/provider"
12
+ require "einvoice/tradevan/provider"
12
13
 
13
14
  module Einvoice
14
15
  extend Configuration
@@ -6,24 +6,8 @@ module Einvoice
6
6
  @provider = provider
7
7
  end
8
8
 
9
- def issue(payload, options = {})
10
- provider.issue(payload, options)
11
- end
12
-
13
- def query(payload, options = {})
14
- provider.query(payload, options = {})
15
- end
16
-
17
- def cancel(payload, options = {})
18
- provider.cancel(payload, options = {})
19
- end
20
-
21
- def allowance_for(payload, options = {})
22
- provider.allowance_for(payload, options = {})
23
- end
24
-
25
- def cancel_allowance(payload, options = {})
26
- provider.cancel_allowance(payload, options = {})
9
+ def method_missing(m, *args, &block)
10
+ provider.send(m, *args, &block)
27
11
  end
28
12
  end
29
13
  end
@@ -4,10 +4,11 @@ module Einvoice
4
4
  module Configuration
5
5
  VALID_OPTIONS_KEYS = [
6
6
  :endpoint,
7
+ :endpoint_url,
7
8
  :client_id,
8
9
  :client_secret,
9
- :format,
10
- :endpoint_url
10
+ :encryption_keys,
11
+ :format
11
12
  ].freeze
12
13
 
13
14
  DEFAULT_CLIENT_ID = nil
@@ -15,6 +16,7 @@ module Einvoice
15
16
  DEFAULT_ENDPOINT = "".freeze
16
17
  DEFAULT_ENDPOINT_URL = nil
17
18
  DEFAULT_FORMAT = ""
19
+ DEFAULT_ENCRYPTION_KEYS = {}
18
20
 
19
21
  attr_accessor *VALID_OPTIONS_KEYS
20
22
 
@@ -33,11 +35,12 @@ module Einvoice
33
35
  end
34
36
 
35
37
  def reset
36
- self.client_id = DEFAULT_CLIENT_ID
37
- self.client_secret = DEFAULT_CLIENT_SECRET
38
- self.endpoint = DEFAULT_ENDPOINT
39
- self.endpoint_url = DEFAULT_ENDPOINT_URL
40
- self.format = DEFAULT_FORMAT
38
+ self.client_id = DEFAULT_CLIENT_ID
39
+ self.client_secret = DEFAULT_CLIENT_SECRET
40
+ self.endpoint = DEFAULT_ENDPOINT
41
+ self.endpoint_url = DEFAULT_ENDPOINT_URL
42
+ self.encryption_keys = DEFAULT_ENCRYPTION_KEYS
43
+ self.format = DEFAULT_FORMAT
41
44
  end
42
45
  end
43
46
  end
@@ -1,21 +1,28 @@
1
1
  require "faraday_middleware"
2
2
  require "faraday/request/digest_neweb"
3
+ require "faraday/response/decode_tradevan"
3
4
 
4
5
  module Einvoice
5
6
  module Connection
6
7
  private
7
8
 
8
- def connection
9
- options = {
9
+ def connection(options = {})
10
+ connection_options = {
10
11
  headers: { "Accept" => "application/#{format}; charset=utf-8" },
11
12
  url: endpoint
12
- }
13
+ }.merge(options)
13
14
 
14
- ::Faraday::Connection.new(options) do |connection|
15
- connection.request :digest_neweb, client_secret if self.class == Einvoice::Neweb::Provider
15
+ ::Faraday::Connection.new(connection_options) do |connection|
16
+ case self.class.to_s
17
+ when "Einvoice::Neweb::Provider"
18
+ connection.request :digest_neweb, client_secret
19
+ when "Einvoice::Tradevan::Provider"
20
+ connection.response :decode_tradevan, encryption_keys[:key1]
21
+ else
22
+ # none
23
+ end
16
24
  connection.request :url_encoded
17
25
 
18
- # Parser
19
26
  case format.to_s.downcase
20
27
  when "xml" then connection.response :xml
21
28
  when "json" then connection.response :json
@@ -16,7 +16,7 @@ module Einvoice
16
16
  class Provider < Einvoice::Provider
17
17
  include Einvoice::Utils
18
18
 
19
- def issue(payload, options)
19
+ def issue(payload, options = {})
20
20
  case options[:type]
21
21
  when :seller_invoice
22
22
  action = "IN_SellerInvoiceS.action"
@@ -43,7 +43,7 @@ module Einvoice
43
43
  end
44
44
  end
45
45
 
46
- def query(payload, options)
46
+ def query(payload, options = {})
47
47
  action = "IN_InvoiceMapS.action"
48
48
  query = Einvoice::Neweb::Model::Query.new
49
49
  query.from_json(payload.to_json)
@@ -0,0 +1,34 @@
1
+ require "active_model"
2
+
3
+ require "einvoice/tradevan/validator/issue_data_validator"
4
+
5
+ module Einvoice
6
+ module Tradevan
7
+ module Model
8
+ class Base
9
+ include ActiveModel::Model
10
+ include ActiveModel::Validations
11
+ include ActiveModel::Serialization
12
+ include ActiveModel::Serializers::JSON
13
+
14
+ include Einvoice::Tradevan::Validator
15
+
16
+ def attributes=(hash)
17
+ @itemList ||= []
18
+ hash.each do |key, value|
19
+ case key.to_sym
20
+ when :itemList
21
+ value.each { |v| @itemList << IssueItem.new(v) }
22
+ else
23
+ send("#{key}=", value)
24
+ end
25
+ end
26
+ end
27
+
28
+ def attributes
29
+ instance_values
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,113 @@
1
+ module Einvoice
2
+ module Tradevan
3
+ module Model
4
+ class IssueData < Base
5
+ VALID_OPTIONS_KEYS = [
6
+ :companyUn,
7
+ :orgId,
8
+ :orgUn,
9
+ :type,
10
+ :saleIdentifier,
11
+ :transactionNumber,
12
+ :transactionDate,
13
+ :transactionTime,
14
+ :total,
15
+ :transactionSource,
16
+ :transactionTarget,
17
+ :invoiceNumber,
18
+ :allowanceNumber,
19
+ :allowanceDate,
20
+ :allowanceExclusiveAmount,
21
+ :allowanceTax,
22
+ :allowancePaperReturned,
23
+ :paperPrintMode,
24
+ :invoiceAlarmMode,
25
+ :InvoicePaperReturned,
26
+ :donate,
27
+ :donationUnit,
28
+ :carrierType,
29
+ :carrierId,
30
+ :carrierIdHidden,
31
+ :buyerUn,
32
+ :buyerTitle,
33
+ :receiverName,
34
+ :receiverAddrZip,
35
+ :receiverAddrRoad,
36
+ :receiverEmail,
37
+ :receiverMobile,
38
+ :idViewId,
39
+ :memberId,
40
+ :checkNumber,
41
+ :invoiceDate,
42
+ :invoiceTime,
43
+ :texclusiveAmount,
44
+ :oeclusiveAmount,
45
+ :zexclusiveAmount,
46
+ :tax,
47
+ :mainRemark,
48
+ :invoiceType,
49
+ :itemList
50
+ ].freeze
51
+
52
+ attr_accessor *VALID_OPTIONS_KEYS
53
+
54
+ validates :companyUn, presence: true, length: { is: 8 }
55
+ validates :orgId, presence: true, length: { is: 5 }
56
+ validates :orgUn, length: { is: 8 }, allow_blank: true
57
+ validates :type, presence: true, length: { is: 1 }, inclusion: { in: %w(I R G H) }
58
+ validates :saleIdentifier, presence: true, length: { maximum: 100 }, saleIdentifier: true
59
+ validates :transactionNumber, presence: true, length: { maximum: 50 }
60
+ validates :transactionDate, presence: true, length: { is: 8 }, format: { with: /\A\d{8}\Z/ }
61
+ validates :transactionTime, presence: true, length: { is: 8 }, format: { with: /\A\d{2}\:\d{2}\:\d{2}\Z/ }
62
+ validates :total, presence: true, length: { maximum: 20 }, total: true
63
+ validates :transactionSource, length: { maximum: 50 } # Public Affair Firm presence: true
64
+ validates :transactionTarget, length: { maximum: 50 } # Public Affair Firm presence: true
65
+ validates :paperPrintMode, presence: true, length: { is: 1 }, inclusion: { in: %w(0 1 2 3 4) }
66
+ validates :invoiceAlarmMode, presence: true, length: { is: 1 }, inclusion: { in: %w(0 1 2 3 4 5 6) }
67
+ validates :InvoicePaperReturned, allow_blank: true, length: { maximum: 1 }, inclusion: { in: %w(Y N) }, InvoicePaperReturned: true
68
+ validates :carrierType, presence: true, length: { is: 6 }, if: proc { self.paperPrintMode.to_i == 0 && self.donate == 'N' }
69
+ validates :buyerUn, length: { is: 8 }, allow_blank: true
70
+ validates :buyerTitle, length: { maximum: 60 }, allow_blank: true
71
+ validates :idViewId, allow_blank: true, length: { maximum: 10 }
72
+ validates :memberId, allow_blank: true, length: { maximum: 50 }
73
+ validates :itemList, presence: true, itemList: true
74
+
75
+ # Type R G H
76
+ validates :invoiceNumber, presence: true, length: { is: 10 }, if: proc { %w(R G H).include?(self.type) }
77
+
78
+ # Type H
79
+ validates :allowanceNumber, presence: true, length: { is: 16 }, allowanceNumber: true, if: proc { self.type == 'H' }
80
+ validates :allowanceDate, presence: true, length: { is: 8 }, format: { with: /\A\d{8}\Z/ }, if: proc { self.type == 'H' }
81
+ validates :allowanceExclusiveAmount, presence: true, length: { maximum: 20 }, if: proc { self.type == 'H' }
82
+ validates :allowanceTax, presence: true, length: { maximum: 8 }, if: proc { self.type == 'H' }
83
+ validates :allowancePaperReturned, presence: true, length: { is: 1 }, inclusion: { in: %w(Y N) }, if: proc { self.type == 'H' }
84
+
85
+ # Type I R G
86
+ validates :donate, presence: true, length: { is: 1 }, if: proc { %w(I R G).include?(self.type) && self.buyerUn.blank? && self.paperPrintMode.to_i == 0 }
87
+ validates :donationUnit, presence: true, length: { maximum: 10 }, if: proc { %w(I R G).include?(self.type) && self.donate == 'Y' }
88
+ validates :carrierId, presence: true, length: { maximum: 64 }, if: proc { %w(I R G).include?(self.type) && self.paperPrintMode.to_i == 0 && self.donate == 'N' }
89
+ validates :carrierIdHidden, presence: true, length: { maximum: 64 }, if: proc { %w(I R G).include?(self.type) && self.paperPrintMode.to_i == 0 && self.donate == 'N' }
90
+ validates :receiverName, allow_blank: true, length: { maximum: 30 }, if: proc { %w(I R G).include?(self.type) }
91
+ validates :receiverAddrZip, allow_blank: true, length: { maximum: 5 }, if: proc { %w(I R G).include?(self.type) }
92
+ validates :receiverAddrRoad, allow_blank: true, length: { maximum: 100 }, if: proc { %w(I R G).include?(self.type) }
93
+ validates :receiverEmail, allow_blank: true, length: { maximum: 80 }, if: proc { %w(I R G).include?(self.type) }
94
+ validates :receiverMobile, allow_blank: true, length: { maximum: 15 }, if: proc { %w(I R G).include?(self.type) }
95
+
96
+ # Type G
97
+ validates :checkNumber, allow_blank: true, length: { is: 4 }, if: proc { self.type == 'G' }
98
+ validates :invoiceDate, allow_blank: true, length: { is: 8 }, if: proc { self.type == 'G' }
99
+ validates :invoiceTime, allow_blank: true, length: { is: 8 }, if: proc { self.type == 'G' }
100
+ validates :texclusiveAmount, allow_blank: true, length: { maximum: 20 }, if: proc { self.type == 'G' }
101
+ validates :oeclusiveAmount, allow_blank: true, length: { maximum: 20 }, if: proc { self.type == 'G' }
102
+ validates :zexclusiveAmount, allow_blank: true, length: { maximum: 20 }, if: proc { self.type == 'G' }
103
+ validates :tax, allow_blank: true, length: { maximum: 20 }, if: proc { self.type == 'G' }
104
+ validates :mainRemark, allow_blank: true, length: { maximum: 300 }, if: proc { self.type == 'G' }
105
+ validates :invoiceType, allow_blank: true, length: { is: 2 }, inclusion: { in: %w(07 08) }, if: proc { self.type == 'G' }
106
+
107
+ def payload
108
+ serializable_hash(except: [:errors, :validation_context], include: [:itemList])
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,41 @@
1
+ module Einvoice
2
+ module Tradevan
3
+ module Model
4
+ class IssueItem < Base
5
+ VALID_OPTIONS_KEYS = [
6
+ :saleIdentifier, # Same as invoice.saleIdentifier
7
+ :serialNumber,
8
+ :invoiceNumber,
9
+ :invoiceDate,
10
+ :invoiceTime,
11
+ :productCode,
12
+ :productName,
13
+ :qty,
14
+ :price,
15
+ :tax,
16
+ :itemExclude,
17
+ :itemTotal,
18
+ :taxType,
19
+ :description
20
+ ]
21
+
22
+ attr_accessor *VALID_OPTIONS_KEYS
23
+
24
+ validates :saleIdentifier, presence: true, length: { maximum: 100 }
25
+ validates :serialNumber, presence: true, length: { is: 4 }, numericality: true
26
+ validates :invoiceNumber, allow_blank: true, length: { is: 10 }
27
+ validates :invoiceDate, allow_blank: true, length: { is: 8 }, numericality: true
28
+ validates :invoiceTime, allow_blank: true, length: { is: 8 }, format: { with: /\Ad{2}\:\d{2}\:\d{2}\Z/ }
29
+ validates :productCode, allow_blank: true, length: { maximum: 30 }
30
+ validates :productName, presence: true, length: { maximum: 300 }
31
+ validates :qty, presence: true, length: { maximum: 20 }
32
+ validates :price, presence: true, length: { maximum: 20 }
33
+ validates :tax, allow_blank: true, length: { maximum: 20 }
34
+ validates :itemExclude, allow_blank: true, length: { maximum: 20 }
35
+ validates :itemTotal, allow_blank: true, length: { maximum: 20 }
36
+ validates :taxType, allow_blank: true, length: { is: 1 }, inclusion: { in: %w(T O Z) }
37
+ validates :description, allow_blank: true, length: { maximum: 300 }
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,33 @@
1
+ module Einvoice
2
+ module Tradevan
3
+ module Model
4
+ class VoidData < Base
5
+ VALID_OPTIONS_KEYS = [
6
+ :type,
7
+ :saleIdentifier,
8
+ :invoiceNumber,
9
+ :invoicePaperReturned,
10
+ :allowanceNumber,
11
+ :allowancePaperReturned,
12
+ ].freeze
13
+
14
+ attr_accessor *VALID_OPTIONS_KEYS
15
+
16
+ validates :type, presence: true, length: { is: 1 }, inclusion: { in: %w(C I A) }
17
+
18
+ # Type C I
19
+ validates :saleIdentifier, presence: true, length: { maximum: 100 }, if: proc { %w(C I).include?(self.type) }
20
+ validates :invoiceNumber, presence: true, length: { is: 10 }, if: proc { %w(C I).include?(self.type) }
21
+ validates :invoicePaperReturned, presence: true, length: { maximum: 1 }, inclusion: { in: %w(Y N) }, if: proc { %w(C I).include?(self.type) }
22
+
23
+ # Type A
24
+ validates :allowanceNumber, presence: true, length: { is: 16 }, allowanceNumber: true, if: proc { self.type == 'A' }
25
+ validates :allowancePaperReturned, presence: true, length: { is: 1 }, inclusion: { in: %w(Y N) }, if: proc { self.type == 'A' }
26
+
27
+ def payload
28
+ serializable_hash(except: [:errors, :validation_context, :itemList])
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,164 @@
1
+ require 'base64'
2
+ require 'openssl'
3
+
4
+ require "einvoice/utils"
5
+
6
+ require "einvoice/tradevan/model/base"
7
+ require "einvoice/tradevan/model/issue_data"
8
+ require "einvoice/tradevan/model/issue_item"
9
+ require "einvoice/tradevan/model/void_data"
10
+
11
+ require "einvoice/tradevan/result"
12
+
13
+ module Einvoice
14
+ module Tradevan
15
+ class Provider < Einvoice::Provider
16
+ def issue(payload, options = {})
17
+ issue_data = Einvoice::Tradevan::Model::IssueData.new
18
+ issue_data.from_json(payload.to_json)
19
+
20
+ if issue_data.valid?
21
+ response = connection(
22
+ ssl: {
23
+ verify: false
24
+ }
25
+ ).post do |request|
26
+ request.url endpoint_url || endpoint + "/DEFAULTAPI/post/issue"
27
+ request.params[:v] = encrypted_params(issueData: issue_data.payload)
28
+ end.body
29
+
30
+ Einvoice::Tradevan::Result.new(response)
31
+ else
32
+ Einvoice::Tradevan::Result.new(issue_data.errors)
33
+ end
34
+ end
35
+
36
+ def cancel(payload, options = {})
37
+ void_data = Einvoice::Tradevan::Model::VoidData.new
38
+ void_data.from_json(payload.to_json)
39
+
40
+ if void_data.valid?
41
+ response = connection(
42
+ ssl: {
43
+ verify: false
44
+ }
45
+ ).post do |request|
46
+ request.url endpoint_url || endpoint + "/DEFAULTAPI/post/cancel"
47
+ request.params[:v] = encrypted_params(voidData: void_data.payload)
48
+ end.body
49
+
50
+ Einvoice::Tradevan::Result.new(response)
51
+ else
52
+ Einvoice::Tradevan::Result.new(void_data.errors)
53
+ end
54
+ end
55
+
56
+ def search_invoice_by_member_id(payload, options = {})
57
+ response = connection(
58
+ ssl: {
59
+ verify: false
60
+ }
61
+ ).get do |request|
62
+ request.url endpoint_url || endpoint + "/DEFAULTAPI/get/searchInvoiceByMemberId"
63
+ request.params[:v] = encrypted_params(payload)
64
+ end.body
65
+
66
+ Einvoice::Tradevan::Result.new(response)
67
+ end
68
+
69
+ def search_invoice_detail(invoice_number)
70
+ response = connection(
71
+ ssl: {
72
+ verify: false
73
+ }
74
+ ).get do |request|
75
+ request.url endpoint_url || endpoint + "/DEFAULTAPI/get/searchInvoiceDetail"
76
+ request.params[:v] = encrypted_params(invoiceNumber: invoice_number)
77
+ end.body
78
+
79
+ Einvoice::Tradevan::Result.new(response)
80
+ end
81
+
82
+ def send_card_info_to_cust(payload, options = {})
83
+ response = connection(
84
+ ssl: {
85
+ verify: false
86
+ }
87
+ ).get do |request|
88
+ request.url endpoint_url || endpoint + "/DEFAULTAPI/get/sendCardInfotoCust"
89
+ request.params[:v] = encrypted_params(payload)
90
+ end.body
91
+
92
+ Einvoice::Tradevan::Result.new(response)
93
+ end
94
+
95
+ def get_invoice_mark_info(payload, options = {})
96
+ response = connection(
97
+ ssl: {
98
+ verify: false
99
+ }
100
+ ).get do |request|
101
+ request.url endpoint_url || endpoint + "/DEFAULTAPI/get/sendCardInfotoCust"
102
+ request.params[:v] = encrypted_params(payload)
103
+ end.body
104
+
105
+ Einvoice::Tradevan::Result.new(response)
106
+ end
107
+
108
+ def get_donate_unit_list(companyUn, options = {})
109
+ response = connection(
110
+ ssl: {
111
+ verify: false
112
+ }
113
+ ).get do |request|
114
+ request.url endpoint_url || endpoint + "/DEFAULTAPI/get/getDonateUnitList"
115
+ request.params[:v] = encrypted_params(companyUn: companyUn)
116
+ end.body
117
+
118
+ Einvoice::Tradevan::Result.new(response)
119
+ end
120
+
121
+ def get_invoice_content(payload, options = {})
122
+ response = connection(
123
+ ssl: {
124
+ verify: false
125
+ }
126
+ ).get do |request|
127
+ request.url endpoint_url || endpoint + "/DEFAULTAPI/get/getInvoiceContent"
128
+
129
+ request.params[:v] = encrypted_params(payload)
130
+ end.body
131
+
132
+ Einvoice::Tradevan::Result.new(response)
133
+ end
134
+
135
+ private
136
+
137
+ def encrypted_params(params)
138
+ encrypted_params = params.dup
139
+ encrypted_params.each do |key, value|
140
+ value = value.is_a?(Hash) ? value.to_json : value.to_s
141
+ encrypted_params[key] = encrypt(encryption_keys[:key1], value)
142
+ end
143
+
144
+ v = { acnt: client_id, acntp: client_secret }.merge(encrypted_params).to_json
145
+ encrypt(encryption_keys[:key2], v)
146
+ end
147
+
148
+ def encrypt(key, content)
149
+ cipher = OpenSSL::Cipher::AES.new(128, :CBC)
150
+ cipher.encrypt
151
+ cipher.key = key
152
+ cipher.iv = key
153
+ cipher.padding = 0
154
+
155
+ # padding with "\u0000"
156
+ q, m = content.bytesize.divmod(cipher.block_size)
157
+ content_bytes_with_padding = content.bytes.fill(0, content.bytesize..(cipher.block_size * (q + 1) - 1))
158
+ content = content_bytes_with_padding.pack('C*').force_encoding('utf-8') if m!= 0
159
+
160
+ Base64.strict_encode64(cipher.update(content) + cipher.final)
161
+ end
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,25 @@
1
+ module Einvoice
2
+ module Tradevan
3
+ class Result < Einvoice::Result
4
+ def errors
5
+ if response.is_a? ActiveModel::Errors
6
+ response.full_messages.join('; ')
7
+ else
8
+ response && response["Message"]
9
+ end
10
+ end
11
+
12
+ def successful?
13
+ response && !response.is_a?(ActiveModel::Errors) && response["Success"] == 'Y'
14
+ end
15
+
16
+ def data
17
+ if response.is_a? ActiveModel::Errors
18
+ nil
19
+ else
20
+ response && response["Message"]
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,71 @@
1
+ module Einvoice
2
+ module Tradevan
3
+ module Validator
4
+ class SaleIdentifierValidator < ActiveModel::EachValidator
5
+ def validate_each(record, attribute, value)
6
+ unless record.saleIdentifier =~ Regexp.new("\\A#{record.companyUn}_#{record.orgId}_")
7
+ record.errors.add attribute, options[:message] || :invalid
8
+ end
9
+ end
10
+ end
11
+
12
+ class TotalValidator < ActiveModel::EachValidator
13
+ def validate_each(record, attribute, value)
14
+ unless value != record.itemList.map(&:itemTotal).map(&:to_i).inject(&:+)
15
+ record.errors.add attribute, options[:message] || :invalid
16
+ end
17
+ end
18
+ end
19
+
20
+ class AllowanceNumberValidator < ActiveModel::EachValidator
21
+ def validate_each(record, attribute, value)
22
+ unless record.allowanceNumber =~ Regexp.new("\A#{record.orgId}")
23
+ record.errors.add attribute, options[:message] || :invalid
24
+ end
25
+ end
26
+ end
27
+
28
+ class InvoicePaperReturnedValidator < ActiveModel::EachValidator
29
+ def validate_each(record, attribute, value)
30
+ if value.blank? && record.buyerUn.blank? && record.paperPrintMode != '0'
31
+ record.errors.add attribute, options[:message] || :invalid
32
+ end
33
+ end
34
+ end
35
+
36
+ class ItemListValidator < ActiveModel::EachValidator
37
+ def validate_each(record, attribtue, value)
38
+ if %w(A G H).include?(record.type) && record.itemList.map(&:itemExclude).map(&:blank?).reduce(&:|)
39
+ record.errors[:itemList] << options[:message] || :invalid
40
+ else
41
+ # none
42
+ end
43
+
44
+ if %w(I R).include?(record.type) && record.itemList.map(&:itemTotal).map(&:blank?).reduce(&:|)
45
+ record.errors[:itemList] << options[:message] || :invalid
46
+ else
47
+ # none
48
+ end
49
+
50
+ if record.type == 'I' && record.itemList.map(&:taxType).map(&:blank?).reduce(&:|)
51
+ record.errors[:itemList] << options[:message] || :invalid
52
+ else
53
+ # none
54
+ end
55
+
56
+ if record.type == 'H'
57
+ if record.itemList.map(&:invoiceNumber).map(&:blank?).reduce(&:|)
58
+ record.errors[:itemList] << options[:message] || :invalid
59
+ elsif record.itemList.map(&:invoiceDate).map(&:blank?).reduce(&:|)
60
+ record.errors[:itemList] << options[:message] || :invalid
61
+ elsif record.itemList.map(&:invoiceTime).map(&:blank?).reduce(&:|)
62
+ record.errors[:itemList] << options[:message] || :invalid
63
+ else
64
+ # none
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -1,3 +1,3 @@
1
1
  module Einvoice
2
- VERSION = "1.0.0"
2
+ VERSION = "1.1.0"
3
3
  end
@@ -0,0 +1,38 @@
1
+ require 'faraday'
2
+
3
+ module Faraday
4
+ class Response::DecodeTradevan < Faraday::Middleware
5
+ dependency do
6
+ require 'base64' unless defined?(::Base64)
7
+ require 'openssl' unless defined?(::OpenSSL)
8
+ end
9
+
10
+ def initialize(app, key)
11
+ super(app)
12
+ @key = key
13
+ end
14
+
15
+ def call(env)
16
+ @app.call(env).on_complete do |env|
17
+ if env[:body] && env[:body]['Success'] != 'E'
18
+ env[:body]['Message'] = decrypt(@key, env[:body]['Message'])
19
+ end
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def decrypt(key, content)
26
+ cipher = OpenSSL::Cipher::AES.new(128, :CBC)
27
+ cipher.decrypt
28
+ cipher.key = key
29
+ cipher.iv = key
30
+ cipher.padding = 0
31
+
32
+ decrypted = cipher.update(Base64.decode64(content)) + cipher.final
33
+ JSON.load(decrypted.strip!)
34
+ end
35
+ end
36
+ end
37
+
38
+ Faraday::Response.register_middleware decode_tradevan: Faraday::Response::DecodeTradevan
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: einvoice
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Yun
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-01-25 00:00:00.000000000 Z
11
+ date: 2016-04-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -242,9 +242,17 @@ files:
242
242
  - lib/einvoice/neweb/validator/invoice_validator.rb
243
243
  - lib/einvoice/provider.rb
244
244
  - lib/einvoice/result.rb
245
+ - lib/einvoice/tradevan/model/base.rb
246
+ - lib/einvoice/tradevan/model/issue_data.rb
247
+ - lib/einvoice/tradevan/model/issue_item.rb
248
+ - lib/einvoice/tradevan/model/void_data.rb
249
+ - lib/einvoice/tradevan/provider.rb
250
+ - lib/einvoice/tradevan/result.rb
251
+ - lib/einvoice/tradevan/validator/issue_data_validator.rb
245
252
  - lib/einvoice/utils.rb
246
253
  - lib/einvoice/version.rb
247
254
  - lib/faraday/request/digest_neweb.rb
255
+ - lib/faraday/response/decode_tradevan.rb
248
256
  homepage: https://github.com/abookyun/einvoice
249
257
  licenses:
250
258
  - MIT
@@ -265,7 +273,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
265
273
  version: '0'
266
274
  requirements: []
267
275
  rubyforge_project:
268
- rubygems_version: 2.4.5.1
276
+ rubygems_version: 2.5.1
269
277
  signing_key:
270
278
  specification_version: 4
271
279
  summary: A API wrapper for Taiwan e-invoice services.