einvoice 1.0.0 → 1.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.
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.