line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Business::TW::TSIB::VirtualAccount; |
2
|
|
|
|
|
|
|
|
3
|
3
|
|
|
3
|
|
85917
|
use warnings; |
|
3
|
|
|
|
|
7
|
|
|
3
|
|
|
|
|
104
|
|
4
|
3
|
|
|
3
|
|
18
|
use strict; |
|
3
|
|
|
|
|
5
|
|
|
3
|
|
|
|
|
103
|
|
5
|
3
|
|
|
3
|
|
2004
|
use Business::TW::TSIB::VirtualAccount::Entry; |
|
3
|
|
|
|
|
11
|
|
|
3
|
|
|
|
|
27
|
|
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
=head1 NAME |
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
Business::TW::TSIB::VirtualAccount - Module for Taishin Bank Virtual Account Management |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
=head1 VERSION |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
Version 0.03 |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
=cut |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
our $VERSION = '0.04'; |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
=head1 SYNOPSIS |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
use Business::TW::TSIB::VirtualAccount; |
22
|
|
|
|
|
|
|
my $va = Business::TW::TSIB::VirtualAccount->new({ corp_code => '95678' }); |
23
|
|
|
|
|
|
|
my $acc = $va->generate( { due => DateTime->new( year => 2007, month => 4, day => 2 ) |
24
|
|
|
|
|
|
|
amount => 3900, |
25
|
|
|
|
|
|
|
ar_id => '2089' } ); |
26
|
|
|
|
|
|
|
# $acc should be '95286092208929' |
27
|
|
|
|
|
|
|
# total 14 columns |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
my $entries = Business::TW::TSIB::VirtualAccount->parse_summary($fh); |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
# entries is arrayref of Business::TW::TSIB::VirtualAccount::Entry objects, |
32
|
|
|
|
|
|
|
# which has the following accessors: |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
* response_code |
35
|
|
|
|
|
|
|
* account |
36
|
|
|
|
|
|
|
* date |
37
|
|
|
|
|
|
|
* seqno |
38
|
|
|
|
|
|
|
* flag |
39
|
|
|
|
|
|
|
* time |
40
|
|
|
|
|
|
|
* txn_type |
41
|
|
|
|
|
|
|
* amount |
42
|
|
|
|
|
|
|
* postive |
43
|
|
|
|
|
|
|
* entry_type |
44
|
|
|
|
|
|
|
* virtual_account |
45
|
|
|
|
|
|
|
* id |
46
|
|
|
|
|
|
|
* from_bank |
47
|
|
|
|
|
|
|
* comment |
48
|
|
|
|
|
|
|
* preserved |
49
|
|
|
|
|
|
|
* status |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
=head1 DESCRIPTION |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
This module provides utility functions for the virtual account service |
54
|
|
|
|
|
|
|
by TSIB (Taishin International Bank, Taiwan). |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
=head1 METHODS |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
=head2 new( { corp_code => $corp_code} ) |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
Initialize the virtual account context with C provided by |
61
|
|
|
|
|
|
|
TSIB. |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
=cut |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
sub new { |
66
|
0
|
|
|
0
|
1
|
0
|
my $class = shift; |
67
|
0
|
|
|
|
|
0
|
my $args = shift; |
68
|
0
|
|
|
|
|
0
|
my $self = {}; |
69
|
0
|
0
|
|
|
|
0
|
die("No Given Corperation Code") |
70
|
|
|
|
|
|
|
if ( !exists( $args->{corp_code} ) ); |
71
|
|
|
|
|
|
|
|
72
|
0
|
0
|
|
|
|
0
|
die("Coperation code needs 5 columns") |
73
|
|
|
|
|
|
|
if ( length( "$args->{corp_code}" ) != 5 ); |
74
|
|
|
|
|
|
|
|
75
|
0
|
|
|
|
|
0
|
$self->{corp_code} = $args->{corp_code}; |
76
|
0
|
|
|
|
|
0
|
return bless $self, $class; |
77
|
|
|
|
|
|
|
} |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
=head2 $va->generate( $args ) |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
Generate a virtual account with the given arguments. $args is a hash ref and must contain: |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
=over |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
=item due |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
A L object for due day of the payment |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
=item amount |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
The expected amount of the transaction. |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
=item ar_id |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
The arbitary account receivable identifier. |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
=back |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
=cut |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
sub generate { |
102
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
103
|
0
|
|
|
|
|
0
|
my $args = shift; |
104
|
|
|
|
|
|
|
|
105
|
0
|
0
|
|
|
|
0
|
map { die("No Given $_") if ( !exists( $args->{$_} ) ) } |
|
0
|
|
|
|
|
0
|
|
106
|
|
|
|
|
|
|
qw/due amount ar_id/; |
107
|
|
|
|
|
|
|
|
108
|
0
|
0
|
|
|
|
0
|
die("ar_id needs 4 columns") if ( length("$args->{ar_id}") != 4 ) ; |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
# generate account |
111
|
|
|
|
|
|
|
# |
112
|
|
|
|
|
|
|
# format: |
113
|
|
|
|
|
|
|
# | corp_code ( 5 ) | date_code ( 4 ) | ar_id ( 4 ) | checksum ( 1 ) | |
114
|
|
|
|
|
|
|
# |
115
|
|
|
|
|
|
|
# total 14 columns |
116
|
|
|
|
|
|
|
|
117
|
0
|
|
|
|
|
0
|
my $account |
118
|
|
|
|
|
|
|
= $self->{corp_code} |
119
|
|
|
|
|
|
|
. $self->_gen_datecode($args) |
120
|
|
|
|
|
|
|
. $args->{ar_id}; # 13 columns |
121
|
|
|
|
|
|
|
|
122
|
0
|
0
|
|
|
|
0
|
die('Error: Column lenght of account don\'t correspond to 13') |
123
|
|
|
|
|
|
|
if ( length($account) != 13 ); |
124
|
|
|
|
|
|
|
|
125
|
0
|
|
|
|
|
0
|
return $self->_gen_checksum( $account, $args ); # 14 columns , checksum appended |
126
|
|
|
|
|
|
|
} |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
sub _gen_datecode { |
129
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
130
|
0
|
|
|
|
|
0
|
my $args = shift; |
131
|
0
|
|
|
|
|
0
|
return sprintf( "%d%03d", |
132
|
|
|
|
|
|
|
( $args->{due}->year - 1 ) % 10, |
133
|
|
|
|
|
|
|
$args->{due}->day_of_year ); |
134
|
|
|
|
|
|
|
} |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
sub _get_amountcode { |
137
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
138
|
0
|
|
|
|
|
0
|
my $args = shift; |
139
|
0
|
|
|
|
|
0
|
my @as = reverse split( //, "$args->{amount}" ); |
140
|
0
|
|
|
|
|
0
|
my $amount_code = 0; |
141
|
0
|
|
0
|
|
|
0
|
map { |
|
|
|
0
|
|
|
|
|
142
|
0
|
|
|
|
|
0
|
$amount_code += ( ( $as[$_] || 0 ) + ( $as[ 6 - $_ ] || 0 ) ) * ( 5 - $_ ) |
143
|
|
|
|
|
|
|
} ( 0, 1, 2 ); |
144
|
0
|
|
0
|
|
|
0
|
$amount_code += ( $as[3] || 0 ) * 2; |
145
|
0
|
|
|
|
|
0
|
return $amount_code; |
146
|
|
|
|
|
|
|
} |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
sub _gen_checksum { |
149
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
150
|
0
|
|
|
|
|
0
|
my $account = shift; |
151
|
0
|
|
|
|
|
0
|
my $args = shift; |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
# gen amount code |
154
|
0
|
|
|
|
|
0
|
my $amount_code = $self->_get_amountcode( $args ); |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
# gen checksum |
157
|
0
|
|
|
|
|
0
|
my @c = split( //, $account ); |
158
|
0
|
|
|
|
|
0
|
my @c_odd = @c[ 0, 2, 4, 6, 8, 10, 12 ]; |
159
|
0
|
|
|
|
|
0
|
my @c_even = @c[ 1, 3, 5, 7, 9, 11 ]; |
160
|
0
|
|
|
|
|
0
|
my ( $sum_odd, $sum_even ) = ( 0, 0 ); |
161
|
0
|
|
|
|
|
0
|
map { $sum_odd += $_; } @c_odd; |
|
0
|
|
|
|
|
0
|
|
162
|
0
|
|
|
|
|
0
|
map { $sum_even += $_; } @c_even; |
|
0
|
|
|
|
|
0
|
|
163
|
0
|
|
|
|
|
0
|
my $checksum = $sum_odd * 3 + $sum_even + $amount_code ; |
164
|
0
|
|
|
|
|
0
|
$checksum %= 10; # mod |
165
|
0
|
|
|
|
|
0
|
$checksum = 10 - $checksum; # 10's complement |
166
|
0
|
0
|
|
|
|
0
|
$checksum = 0 if ( $checksum == 10 ); |
167
|
0
|
|
|
|
|
0
|
return $account . $checksum; |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
} |
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
=head2 $self->parse_summary($fh) |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
=cut |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
sub parse_summary { |
176
|
1
|
|
|
1
|
1
|
2283
|
my $self = shift; |
177
|
1
|
|
|
|
|
3
|
my $fh = shift; |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
# format: |
180
|
|
|
|
|
|
|
# |
181
|
|
|
|
|
|
|
# 4 # response code |
182
|
|
|
|
|
|
|
# 14 # account |
183
|
|
|
|
|
|
|
# 8 # date |
184
|
|
|
|
|
|
|
# 6 # sequence number (seqno) |
185
|
|
|
|
|
|
|
# 1 # flag |
186
|
|
|
|
|
|
|
# 6 # time |
187
|
|
|
|
|
|
|
# 4 # transaction type |
188
|
|
|
|
|
|
|
# 12 # amount |
189
|
|
|
|
|
|
|
# 1 # postive |
190
|
|
|
|
|
|
|
# 1 # entry type |
191
|
|
|
|
|
|
|
# 16 # virtual account |
192
|
|
|
|
|
|
|
# 10 # ID Card |
193
|
|
|
|
|
|
|
# 3 # from bank |
194
|
|
|
|
|
|
|
# 20 # comment |
195
|
|
|
|
|
|
|
# 18 # preserve |
196
|
|
|
|
|
|
|
# 1 # status |
197
|
|
|
|
|
|
|
|
198
|
1
|
|
|
|
|
2
|
my @entries; |
199
|
1
|
|
|
|
|
14
|
while (<$fh>) { |
200
|
10
|
|
|
|
|
25
|
chomp; |
201
|
10
|
50
|
|
|
|
31
|
next unless length $_; |
202
|
|
|
|
|
|
|
|
203
|
10
|
|
|
|
|
18
|
my %cols; |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
@cols{ |
206
|
10
|
|
|
|
|
181
|
qw/ |
207
|
|
|
|
|
|
|
response_code |
208
|
|
|
|
|
|
|
account |
209
|
|
|
|
|
|
|
date |
210
|
|
|
|
|
|
|
seqno |
211
|
|
|
|
|
|
|
flag |
212
|
|
|
|
|
|
|
time |
213
|
|
|
|
|
|
|
txn_type |
214
|
|
|
|
|
|
|
amount |
215
|
|
|
|
|
|
|
postive |
216
|
|
|
|
|
|
|
entry_type |
217
|
|
|
|
|
|
|
virtual_account |
218
|
|
|
|
|
|
|
id |
219
|
|
|
|
|
|
|
from_bank |
220
|
|
|
|
|
|
|
comment |
221
|
|
|
|
|
|
|
preserved |
222
|
|
|
|
|
|
|
status/ |
223
|
|
|
|
|
|
|
} |
224
|
|
|
|
|
|
|
= ( |
225
|
|
|
|
|
|
|
m/ |
226
|
|
|
|
|
|
|
(.{4}) # response code |
227
|
|
|
|
|
|
|
(.{14}) # account |
228
|
|
|
|
|
|
|
(.{8}) # date |
229
|
|
|
|
|
|
|
(.{6}) # seqno |
230
|
|
|
|
|
|
|
(.{1}) # flag |
231
|
|
|
|
|
|
|
(.{6}) # time |
232
|
|
|
|
|
|
|
(.{4}) # transaction type |
233
|
|
|
|
|
|
|
(.{12}) # amount |
234
|
|
|
|
|
|
|
(.{1}) # postive |
235
|
|
|
|
|
|
|
(.{1}) # entry type |
236
|
|
|
|
|
|
|
(.{16}) # virtual account |
237
|
|
|
|
|
|
|
(.{10}) # ID Card |
238
|
|
|
|
|
|
|
(.{3}) # from_bank |
239
|
|
|
|
|
|
|
(.{20}) # comment |
240
|
|
|
|
|
|
|
(.{18}) # preserve |
241
|
|
|
|
|
|
|
(.{1}) # status |
242
|
|
|
|
|
|
|
/x |
243
|
|
|
|
|
|
|
); |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
# trim |
246
|
10
|
|
|
|
|
58
|
map { $cols{$_} =~ s/\s*$//g; $cols{$_} =~ s/^\s*//g; } keys %cols; |
|
160
|
|
|
|
|
717
|
|
|
160
|
|
|
|
|
572
|
|
247
|
10
|
|
|
|
|
37
|
$cols{amount} /= 10; |
248
|
10
|
|
|
|
|
42
|
my $entry = Business::TW::TSIB::VirtualAccount::Entry->new( \%cols ); |
249
|
10
|
|
|
|
|
595
|
push @entries, $entry; |
250
|
|
|
|
|
|
|
} |
251
|
1
|
|
|
|
|
6
|
return \@entries; |
252
|
|
|
|
|
|
|
} |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
=head1 AUTHOR |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
Chia-liang Kao, C<< >> , |
257
|
|
|
|
|
|
|
You-An Lin, C<< >> |
258
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
=head1 BUGS |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
Please report any bugs or feature requests to |
262
|
|
|
|
|
|
|
C, or through the web interface at |
263
|
|
|
|
|
|
|
L. |
264
|
|
|
|
|
|
|
I will be notified, and then you'll automatically be notified of progress on |
265
|
|
|
|
|
|
|
your bug as I make changes. |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
=head1 SUPPORT |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
You can find documentation for this module with the perldoc command. |
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
perldoc Business::TW::TSIB::VirtualAccount |
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
You can also look for information at: |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
=over 4 |
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
=item * AnnoCPAN: Annotated CPAN documentation |
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
L |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
=item * CPAN Ratings |
282
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
L |
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
=item * RT: CPAN's request tracker |
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
L |
288
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
=item * Search CPAN |
290
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
L |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
=back |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
=head1 ACKNOWLEDGEMENTS |
296
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
=head1 COPYRIGHT & LICENSE |
298
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
Copyright 2007 AIINK co., ltd, all rights reserved. |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify it |
302
|
|
|
|
|
|
|
under the same terms as Perl itself. |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
=cut |
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
1; # End of Business::TW::TSIB::VirtualAccount |