blib/lib/Finance/Bank/ID/BPRKS.pm | |||
---|---|---|---|
Criterion | Covered | Total | % |
statement | 74 | 201 | 36.8 |
branch | 21 | 90 | 23.3 |
condition | 2 | 14 | 14.2 |
subroutine | 12 | 24 | 50.0 |
pod | 5 | 6 | 83.3 |
total | 114 | 335 | 34.0 |
line | stmt | bran | cond | sub | pod | time | code | |
---|---|---|---|---|---|---|---|---|
1 | package Finance::Bank::ID::BPRKS; | |||||||
2 | ||||||||
3 | our $DATE = '2015-12-16'; # DATE | |||||||
4 | our $VERSION = '0.05'; # VERSION | |||||||
5 | ||||||||
6 | 1 | 1 | 158464 | use 5.010001; | ||||
1 | 3 | |||||||
7 | 1 | 1 | 765 | use Moo; | ||||
1 | 12087 | |||||||
1 | 5 | |||||||
8 | 1 | 1 | 1558 | use DateTime; | ||||
1 | 2 | |||||||
1 | 23 | |||||||
9 | 1 | 1 | 1196 | use Log::Any::IfLOG '$log'; | ||||
1 | 12 | |||||||
1 | 5 | |||||||
10 | ||||||||
11 | 1 | 1 | 786 | use Parse::Number::ID qw(parse_number_id); | ||||
1 | 634 | |||||||
1 | 2527 | |||||||
12 | ||||||||
13 | extends 'Finance::Bank::ID::Base'; | |||||||
14 | ||||||||
15 | has _variant => (is => 'rw'); # 'individual' only, for now | |||||||
16 | ###bca | |||||||
17 | ###has skip_NEXT => (is => 'rw'); | |||||||
18 | ||||||||
19 | sub BUILD { | |||||||
20 | 1 | 1 | 0 | 4043 | my ($self, $args) = @_; | |||
21 | ||||||||
22 | 1 | 50 | 18 | $self->site("https://ib.bprks.co.id") unless $self->site; | ||||
23 | 1 | 50 | 29 | $self->https_host("ib.bprks.co.id") unless $self->https_host; | ||||
24 | } | |||||||
25 | ||||||||
26 | sub _req { | |||||||
27 | 0 | 0 | 0 | my ($self, @args) = @_; | ||||
28 | ||||||||
29 | # 2012-03-12 - KlikBCA server since a few week ago rejects TE request | |||||||
30 | # header, so we do not send them. | |||||||
31 | ###bca | |||||||
32 | ###local @LWP::Protocol::http::EXTRA_SOCK_OPTS = | |||||||
33 | ### @LWP::Protocol::http::EXTRA_SOCK_OPTS; | |||||||
34 | ###push(@LWP::Protocol::http::EXTRA_SOCK_OPTS, SendTE => 0); | |||||||
35 | #$log->tracef("EXTRA_SOCK_OPTS=%s", \@LWP::Protocol::http::EXTRA_SOCK_OPTS); | |||||||
36 | ||||||||
37 | 0 | 0 | $self->SUPER::_req(@args); | |||||
38 | } | |||||||
39 | ||||||||
40 | # XXX tmp, should be in an indo date utility module | |||||||
41 | sub _parse_mon { | |||||||
42 | 4 | 4 | 8 | my $self = shift; | ||||
43 | 4 | 25 | local $_ = lc(shift); | |||||
44 | 4 | 50 | 50 | if (/^(?:jan|januar[iy])$/) { | ||||
50 | ||||||||
50 | ||||||||
50 | ||||||||
100 | ||||||||
50 | ||||||||
0 | ||||||||
0 | ||||||||
0 | ||||||||
0 | ||||||||
0 | ||||||||
0 | ||||||||
45 | 0 | 0 | return 1; | |||||
46 | } elsif (/^(?:[fp]eb|[fp]ebruar[iy])$/) { | |||||||
47 | 0 | 0 | return 2; | |||||
48 | } elsif (/^(?:mar|mrt|maret|march)$/) { | |||||||
49 | 0 | 0 | return 3; | |||||
50 | } elsif (/^(?:apr|april)$/) { | |||||||
51 | 0 | 0 | return 4; | |||||
52 | } elsif (/^(?:mei|may)$/) { | |||||||
53 | 2 | 19 | return 5; | |||||
54 | } elsif (/^(?:jun|jun[eiy])$/) { | |||||||
55 | 2 | 12 | return 6; | |||||
56 | } elsif (/^(?:jul|jul[iy])$/) { | |||||||
57 | 0 | 0 | return 7; | |||||
58 | } elsif (/^(?:agu|aug|ags?t|august|agustus)$/) { | |||||||
59 | 0 | 0 | return 8; | |||||
60 | } elsif (/^(?:sep|september)$/) { | |||||||
61 | 0 | 0 | return 9; | |||||
62 | } elsif (/^(?:o[kc]t|o[ct]ober)$/) { | |||||||
63 | 0 | 0 | return 10; | |||||
64 | } elsif (/^(?:no[pv]|no[pv]ember)$/) { | |||||||
65 | 0 | 0 | return 11; | |||||
66 | } elsif (/^(?:de[sc]|de[sc]ember)$/) { | |||||||
67 | 0 | 0 | return 12; | |||||
68 | } else { | |||||||
69 | 0 | 0 | die "Can't parse month: $_"; | |||||
70 | #return 0; | |||||||
71 | } | |||||||
72 | } | |||||||
73 | ||||||||
74 | sub _parse_num { | |||||||
75 | 12 | 12 | 23 | my ($self, $s) = @_; | ||||
76 | 12 | 31 | my $neg = $s =~ s/^\((.+)\)$/$1/; | |||||
77 | 12 | 32 | my $n = parse_number_id(text => $s); | |||||
78 | 12 | 100 | 289 | $neg ? -$n : $n; | ||||
79 | } | |||||||
80 | ||||||||
81 | sub login { | |||||||
82 | 0 | 0 | 1 | 0 | die "Not yet implemented"; | |||
83 | 0 | 0 | my ($self) = @_; | |||||
84 | 0 | 0 | my $s = $self->site; | |||||
85 | ||||||||
86 | 0 | 0 | 0 | return 1 if $self->logged_in; | ||||
87 | 0 | 0 | 0 | die "400 Username not supplied" unless $self->username; | ||||
88 | 0 | 0 | 0 | die "400 Password not supplied" unless $self->password; | ||||
89 | ||||||||
90 | 0 | 0 | $self->logger->debug('Logging in ...'); | |||||
91 | 0 | 0 | $self->_req(get => [$s]); | |||||
92 | $self->_req(submit_form => [ | |||||||
93 | form_number => 1, | |||||||
94 | fields => {'value(user_id)'=>$self->username, | |||||||
95 | 'value(pswd)'=>$self->password, | |||||||
96 | }, | |||||||
97 | button => 'value(Submit)', | |||||||
98 | ], | |||||||
99 | sub { | |||||||
100 | 0 | 0 | 0 | my ($mech) = @_; | ||||
101 | 0 | 0 | 0 | $mech->content =~ /var err='(.+?)'/ and return $1; | ||||
102 | 0 | 0 | 0 | $mech->content =~ /=logout"/ and return; | ||||
103 | 0 | 0 | "unknown login result page"; | |||||
104 | } | |||||||
105 | 0 | 0 | ); | |||||
106 | 0 | 0 | $self->logged_in(1); | |||||
107 | 0 | 0 | $self->_req(get => ["$s/authentication.do?value(actions)=welcome"]); | |||||
108 | #$self->_req(get => ["$s/nav_bar_indo/menu_nav.htm"]); # failed? | |||||||
109 | } | |||||||
110 | ||||||||
111 | sub logout { | |||||||
112 | 0 | 0 | 1 | 0 | die "Not yet implemented"; | |||
113 | 0 | 0 | my ($self) = @_; | |||||
114 | ||||||||
115 | 0 | 0 | 0 | return 1 unless $self->logged_in; | ||||
116 | 0 | 0 | $self->logger->debug('Logging out ...'); | |||||
117 | 0 | 0 | $self->_req(get => [$self->site . "/authentication.do?value(actions)=logout"]); | |||||
118 | 0 | 0 | $self->logged_in(0); | |||||
119 | } | |||||||
120 | ||||||||
121 | sub _menu { | |||||||
122 | 0 | 0 | 0 | my ($self) = @_; | ||||
123 | 0 | 0 | my $s = $self->site; | |||||
124 | 0 | 0 | $self->_req(get => ["$s/nav_bar_indo/account_information_menu.htm"]); | |||||
125 | } | |||||||
126 | ||||||||
127 | sub list_cards { | |||||||
128 | 0 | 0 | 1 | 0 | die "Not yet implemented"; | |||
129 | 0 | 0 | my ($self) = @_; | |||||
130 | 0 | 0 | $self->login; | |||||
131 | 0 | 0 | $self->logger->info("Listing ATM cards"); | |||||
132 | 0 | 0 | map { $_->{account} } $self->_check_balances; | |||||
0 | 0 | |||||||
133 | } | |||||||
134 | ||||||||
135 | sub _check_balances { | |||||||
136 | 0 | 0 | 0 | my ($self) = @_; | ||||
137 | 0 | 0 | my $s = $self->site; | |||||
138 | ||||||||
139 | 0 | 0 | my $re = qr! | |||||
140 | ||||||||
141 | ]+>\s* ]+>\s*]+>\s*(\d+)\s*\s* \s* | \s*
|||||||
142 | ]+>\s* ]+>\s*]+>\s*([^<]*?)\s*\s* \s* | \s*
|||||||
143 | ]+>\s* ]+>\s*]+>\s*([A-Z]+)\s*\s* \s* | \s*
|||||||
144 | ]+>\s* ]+>\s*]+>\s*([0-9,.]+)\.(\d\d)\s*\s* \s* |
|||||||
145 | !x; | |||||||
146 | ||||||||
147 | 0 | 0 | $self->login; | |||||
148 | 0 | 0 | $self->_menu; | |||||
149 | $self->_req(post => ["$s/balanceinquiry.do"], | |||||||
150 | sub { | |||||||
151 | 0 | 0 | 0 | my ($mech) = @_; | ||||
152 | 0 | 0 | 0 | $mech->content =~ $re or | ||||
153 | return "can't find balances, maybe page layout changed?"; | |||||||
154 | 0 | 0 | ''; | |||||
155 | } | |||||||
156 | 0 | 0 | ); | |||||
157 | ||||||||
158 | 0 | 0 | my @res; | |||||
159 | 0 | 0 | my $content = $self->mech->content; | |||||
160 | 0 | 0 | while ($content =~ m/$re/og) { | |||||
161 | 0 | 0 | push @res, { account => $1, | |||||
162 | account_type => $2, | |||||||
163 | currency => $3, | |||||||
164 | balance => $self->_stripD($4) + 0.01*$5, | |||||||
165 | }; | |||||||
166 | } | |||||||
167 | 0 | 0 | @res; | |||||
168 | } | |||||||
169 | ||||||||
170 | sub check_balance { | |||||||
171 | 0 | 0 | 1 | 0 | die "Not yet implemented"; | |||
172 | 0 | 0 | my ($self, $account) = @_; | |||||
173 | 0 | 0 | my @bals = $self->_check_balances; | |||||
174 | 0 | 0 | 0 | return unless @bals; | ||||
175 | 0 | 0 | 0 | return $bals[0]{balance} if !$account; | ||||
176 | 0 | 0 | for (@bals) { | |||||
177 | 0 | 0 | 0 | return $_->{balance} if $_->{account} eq $account; | ||||
178 | } | |||||||
179 | 0 | 0 | return; | |||||
180 | } | |||||||
181 | ||||||||
182 | sub get_statement { | |||||||
183 | 0 | 0 | 1 | 0 | die "Not yet implemented"; | |||
184 | 0 | 0 | my ($self, %args) = @_; | |||||
185 | 0 | 0 | my $s = $self->site; | |||||
186 | 0 | 0 | my $max_days = 31; | |||||
187 | ||||||||
188 | 0 | 0 | $self->login; | |||||
189 | 0 | 0 | $self->_menu; | |||||
190 | $self->logger->info("Getting statement for ". | |||||||
191 | 0 | 0 | 0 | ($args{account} ? "account `$args{account}'" : "default account")." ..."); | ||||
192 | $self->_req(post => ["$s/accountstmt.do?value(actions)=acct_stmt"], | |||||||
193 | sub { | |||||||
194 | 0 | 0 | 0 | my ($mech) = @_; | ||||
195 | 0 | 0 | 0 | $mech->content =~ / | ||||
196 | return "no form found, maybe we got logged out?"; | |||||||
197 | 0 | 0 | ''; | |||||
198 | 0 | 0 | }); | |||||
199 | ||||||||
200 | 0 | 0 | my $form = $self->mech->form_number(1); | |||||
201 | ||||||||
202 | # in the site this is done by javascript onSubmit(), so we emulate it here | |||||||
203 | 0 | 0 | $form->action("$s/accountstmt.do?value(actions)=acctstmtview"); | |||||
204 | ||||||||
205 | # in the case of the current date being a saturday/sunday/holiday, end | |||||||
206 | # date will be forwarded 1 or more days from the current date by the site, | |||||||
207 | # so we need to know end date and optionally forward start date when needed, | |||||||
208 | # to avoid total number of days being > 31. | |||||||
209 | ||||||||
210 | 0 | 0 | my $today = DateTime->today; | |||||
211 | 0 | 0 | my $max_dt = DateTime->new(day => $form->value("value(endDt)"), | |||||
212 | month => $form->value("value(endMt)"), | |||||||
213 | year => $form->value("value(endYr)")); | |||||||
214 | 0 | 0 | my $cmp = DateTime->compare($today, $max_dt); | |||||
215 | 0 | 0 | my $delta_days = $cmp * $today->subtract_datetime($max_dt, $today)->days; | |||||
216 | 0 | 0 | 0 | if ($delta_days > 0) { | ||||
217 | 0 | 0 | $self->logger->warn("Something weird is going on, end date is being ". | |||||
218 | "set less than today's date by the site (". | |||||||
219 | $self->_fmtdate($max_dt)."). ". | |||||||
220 | "Please check your computer's date setting. ". | |||||||
221 | "Continuing anyway."); | |||||||
222 | } | |||||||
223 | 0 | 0 | my $min_dt = $max_dt->clone->subtract(days => ($max_days-1)); | |||||
224 | ||||||||
225 | 0 | 0 | 0 | my $end_dt = $args{end_date} || $max_dt; | ||||
226 | my $start_dt = $args{start_date} || | |||||||
227 | 0 | 0 | 0 | $end_dt->clone->subtract(days => (($args{days} || $max_days)-1)); | ||||
228 | 0 | 0 | 0 | if (DateTime->compare($start_dt, $min_dt) == -1) { | ||||
229 | 0 | 0 | $self->logger->warn("Start date ".$self->_fmtdate($start_dt)." is less than ". | |||||
230 | "minimum date ".$self->_fmtdate($min_dt).". Setting to ". | |||||||
231 | "minimum date instead."); | |||||||
232 | 0 | 0 | $start_dt = $min_dt; | |||||
233 | } | |||||||
234 | 0 | 0 | 0 | if (DateTime->compare($start_dt, $max_dt) == 1) { | ||||
235 | 0 | 0 | $self->logger->warn("Start date ".$self->_fmtdate($start_dt)." is greater than ". | |||||
236 | "maximum date ".$self->_fmtdate($max_dt).". Setting to ". | |||||||
237 | "maximum date instead."); | |||||||
238 | 0 | 0 | $start_dt = $max_dt; | |||||
239 | } | |||||||
240 | 0 | 0 | 0 | if (DateTime->compare($end_dt, $min_dt) == -1) { | ||||
241 | 0 | 0 | $self->logger->warn("End date ".$self->_fmtdate($end_dt)." is less than ". | |||||
242 | "minimum date ".$self->_fmtdate($min_dt).". Setting to ". | |||||||
243 | "minimum date instead."); | |||||||
244 | 0 | 0 | $end_dt = $min_dt; | |||||
245 | } | |||||||
246 | 0 | 0 | 0 | if (DateTime->compare($end_dt, $max_dt) == 1) { | ||||
247 | 0 | 0 | $self->logger->warn("End date ".$self->_fmtdate($end_dt)." is greater than ". | |||||
248 | "maximum date ".$self->_fmtdate($max_dt).". Setting to ". | |||||||
249 | "maximum date instead."); | |||||||
250 | 0 | 0 | $end_dt = $max_dt; | |||||
251 | } | |||||||
252 | 0 | 0 | 0 | if (DateTime->compare($start_dt, $end_dt) == 1) { | ||||
253 | 0 | 0 | $self->logger->warn("Start date ".$self->_fmtdate($start_dt)." is greater than ". | |||||
254 | "end date ".$self->_fmtdate($end_dt).". Setting to ". | |||||||
255 | "end date instead."); | |||||||
256 | 0 | 0 | $start_dt = $end_dt; | |||||
257 | } | |||||||
258 | ||||||||
259 | 0 | 0 | my $select = $form->find_input("value(D1)"); | |||||
260 | 0 | 0 | my $d1 = $select->value; | |||||
261 | 0 | 0 | 0 | if ($args{account}) { | ||||
262 | 0 | 0 | my @d1 = $select->possible_values; | |||||
263 | 0 | 0 | my @accts = $select->value_names; | |||||
264 | 0 | 0 | for (0..$#accts) { | |||||
265 | 0 | 0 | 0 | if ($args{account} eq $accts[$_]) { | ||||
266 | 0 | 0 | $d1 = $d1[$_]; | |||||
267 | 0 | 0 | last; | |||||
268 | } | |||||||
269 | } | |||||||
270 | } | |||||||
271 | ||||||||
272 | $self->_req(submit_form => [ | |||||||
273 | form_number => 1, | |||||||
274 | fields => { | |||||||
275 | "value(D1)" => $d1, | |||||||
276 | "value(startDt)" => $start_dt->day, | |||||||
277 | "value(startMt)" => $start_dt->month, | |||||||
278 | "value(startYr)" => $start_dt->year, | |||||||
279 | "value(endDt)" => $end_dt->day, | |||||||
280 | "value(endMt)" => $end_dt->month, | |||||||
281 | "value(endYr)" => $end_dt->year, | |||||||
282 | }, | |||||||
283 | ], | |||||||
284 | sub { | |||||||
285 | 0 | 0 | 0 | my ($mech) = @_; | ||||
286 | 0 | 0 | ''; # XXX check for error | |||||
287 | 0 | 0 | }); | |||||
288 | 0 | 0 | 0 | my $parse_opts = $args{parse_opts} // {}; | ||||
289 | 0 | 0 | my $resp = $self->parse_statement($self->mech->content, %$parse_opts); | |||||
290 | 0 | 0 | 0 | 0 | return if !$resp || $resp->[0] != 200; | |||
291 | 0 | 0 | $resp->[2]; | |||||
292 | } | |||||||
293 | ||||||||
294 | sub _ps_detect { | |||||||
295 | 2 | 2 | 8112 | my ($self, $page) = @_; | ||||
296 | 2 | 50 | 12 | unless ($page =~ />Detail Informasi Mutasi Rekening) { | ||||
297 | 0 | 0 | return "No BPR KS statement page signature found"; | |||||
298 | } | |||||||
299 | 2 | 9 | $self->_variant('individual'); | |||||
300 | 2 | 6 | ""; | |||||
301 | } | |||||||
302 | ||||||||
303 | sub _ps_get_metadata { | |||||||
304 | 2 | 2 | 13 | my ($self, $page, $stmt) = @_; | ||||
305 | ||||||||
306 | 2 | 50 | 52 | unless ($page =~ m! | \s*]*>(\d+) | !s) {|||
307 | 0 | 0 | return "can't get account number"; | |||||
308 | } | |||||||
309 | 2 | 7 | $stmt->{account} = $1; | |||||
310 | ||||||||
311 | 2 | 5 | my $adv1 = "probably the statement format changed, or input incomplete"; | |||||
312 | ||||||||
313 | 2 | 50 | 57 | unless ($page =~ m! | \s*\s*(? | !s) {
|||
314 | 0 | 0 | return "can't get statement period, $adv1"; | |||||
315 | } | |||||||
316 | 1 | 1 | 777 | $stmt->{start_date} = DateTime->new(day=>$+{d1}, month=>$self->_parse_mon($+{m1}), year=>$+{y1}); | ||||
1 | 410 | |||||||
1 | 730 | |||||||
2 | 14 | |||||||
317 | 2 | 632 | $stmt->{end_date} = DateTime->new(day=>$+{d2}, month=>$self->_parse_mon($+{m2}), year=>$+{y2}); | |||||
318 | ||||||||
319 | 2 | 50 | 411 | unless ($page =~ m! | \s*]*>(\w+) | !s) {|||
320 | 0 | 0 | return "can't get currency, $adv1"; | |||||
321 | } | |||||||
322 | 2 | 50 | 11 | $stmt->{currency} = ($1 eq 'Rp' ? 'IDR' : $1); | ||||
323 | ||||||||
324 | 2 | 50 | 50 | unless ($page =~ m! | \s*]*>([^<]+?)\s* | !s) {|||
325 | 0 | 0 | return "can't get account holder, $adv1"; | |||||
326 | } | |||||||
327 | 2 | 6 | $stmt->{account_holder} = $1; | |||||
328 | ||||||||
329 | # additional: Tipe: Tabungan | |||||||
330 | ||||||||
331 | 2 | 50 | 61 | unless ($page =~ m! | \s*]*>([^<]+) | !s) {|||
332 | 0 | 0 | return "can't get total credit, $adv1"; | |||||
333 | } | |||||||
334 | 2 | 6 | $stmt->{_total_credit_in_stmt} = $self->_parse_num($1); | |||||
335 | # no _num_credit_tx_in_stmt, pity cause it's required for proper checking | |||||||
336 | ||||||||
337 | 2 | 50 | 62 | unless ($page =~ m! | \s*]*>([^<]+) | !s) {|||
338 | 0 | 0 | return "can't get total credit, $adv1"; | |||||
339 | } | |||||||
340 | 2 | 6 | $stmt->{_total_debit_in_stmt} = -$self->_parse_num($1); | |||||
341 | # no _num_debit_tx_in_stmt, pity cause it's required for proper checking | |||||||
342 | 2 | 6 | ""; | |||||
343 | } | |||||||
344 | ||||||||
345 | sub _ps_get_transactions { | |||||||
346 | 2 | 2 | 12 | my ($self, $page, $stmt) = @_; | ||||
347 | ||||||||
348 | 2 | 3 | my @e; | |||||
349 | 2 | 110 | while ($page =~ m! | |||||
350 | ||||||||
351 | ]+>\s* (? | \s*
|||||||
352 | ]+>\s* (? | \s*
|||||||
353 | ]+>\s* (?[^<]+?) \s* | \s*|||||||
354 | ]+>\s* (? | \s*
|||||||
355 | ]+>\s* (? | \s*
|||||||
356 | ||||||||
357 | 4 | 97 | my %m = %+; | |||||
358 | 4 | 68 | push @e, \%m; | |||||
359 | } | |||||||
360 | ||||||||
361 | 2 | 5 | my @tx; | |||||
362 | my @skipped_tx; | |||||||
363 | 0 | 0 | my $last_date; | |||||
364 | 0 | 0 | my $seq; | |||||
365 | 2 | 3 | my $i = 0; | |||||
366 | 2 | 5 | for my $e (@e) { | |||||
367 | 4 | 6 | $i++; | |||||
368 | 4 | 6 | my $tx = {}; | |||||
369 | #$tx->{stmt_start_date} = $stmt->{start_date}; | |||||||
370 | ||||||||
371 | ### bca | |||||||
372 | ###if ($e->{date} =~ /NEXT/) { | |||||||
373 | ### $tx->{date} = $stmt->{end_date}; | |||||||
374 | ### $tx->{is_next} = 1; | |||||||
375 | ###} elsif ($e->{date} =~ /PEND/) { | |||||||
376 | ### $tx->{date} = $stmt->{end_date}; | |||||||
377 | ### $tx->{is_pending} = 1; | |||||||
378 | ###} else { | |||||||
379 | 4 | 17 | my ($day, $mon, $year) = split m!/!, $e->{date}; | |||||
380 | 4 | 19 | my $last_nonpend_date = DateTime->new( | |||||
381 | year => $year, | |||||||
382 | month => $mon, | |||||||
383 | day => $day); | |||||||
384 | 4 | 727 | $tx->{date} = $last_nonpend_date; | |||||
385 | ###$tx->{is_pending} = 0; | |||||||
386 | ###} | |||||||
387 | ||||||||
388 | 4 | 9 | $tx->{description} = $e->{desc}; | |||||
389 | ||||||||
390 | 4 | 10 | $tx->{amount} = $self->_parse_num($e->{amt}); | |||||
391 | 4 | 11 | $tx->{balance} = $self->_parse_num($e->{bal}); | |||||
392 | ||||||||
393 | ### bca | |||||||
394 | ###if ($tx->{is_next} && $self->skip_NEXT) { | |||||||
395 | ###} | |||||||
396 | ||||||||
397 | 4 | 100 | 66 | 18 | if (!$last_date || DateTime->compare($last_date, $tx->{date})) { | |||
398 | 2 | 4 | $seq = 1; | |||||
399 | 2 | 3 | $last_date = $tx->{date}; | |||||
400 | } else { | |||||||
401 | 2 | 226 | $seq++; | |||||
402 | } | |||||||
403 | 4 | 8 | $tx->{seq} = $seq; | |||||
404 | ||||||||
405 | ### bca | |||||||
406 | ###if ($self->_variant eq 'individual' && | |||||||
407 | ### $tx->{date}->dow =~ /6|7/ && | |||||||
408 | ### $tx->{description} !~ /^(BIAYA ADM|BUNGA|CR KOREKSI BUNGA|PAJAK BUNGA)$/) { | |||||||
409 | ### return "check failed in tx#$i: In KlikBCA Perorangan, all ". | |||||||
410 | ### "transactions must not be in Sat/Sun except for Interest and ". | |||||||
411 | ### "Admin Fee"; | |||||||
412 | ### # note: in Tahapan perorangan, BIAYA ADM is set on | |||||||
413 | ### # Fridays, but for Tapres (?) on last day of the month | |||||||
414 | ###} | |||||||
415 | ||||||||
416 | ###if ($self->_variant eq 'bisnis' && | |||||||
417 | ### $tx->{date}->dow =~ /6|7/ && | |||||||
418 | ### $tx->{description} !~ /^(BIAYA ADM|BUNGA|CR KOREKSI BUNGA|PAJAK BUNGA)$/) { | |||||||
419 | ### return "check failed in tx#$i: In KlikBCA Bisnis, all ". | |||||||
420 | ### "transactions must not be in Sat/Sun except for Interest and ". | |||||||
421 | ### "Admin Fee"; | |||||||
422 | ### # note: in KlikBCA bisnis, BIAYA ADM is set on the last day of the | |||||||
423 | ### # month, regardless of whether it's Sat/Sun or not | |||||||
424 | ###} | |||||||
425 | ||||||||
426 | ###if ($tx->{is_next} && $self->skip_NEXT) { | |||||||
427 | ### push @skipped_tx, $tx; | |||||||
428 | ### $seq--; | |||||||
429 | ###} else { | |||||||
430 | 4 | 10 | push @tx, $tx; | |||||
431 | ###} | |||||||
432 | } | |||||||
433 | 2 | 6 | $stmt->{transactions} = \@tx; | |||||
434 | 2 | 5 | $stmt->{skipped_transactions} = \@skipped_tx; | |||||
435 | 2 | 14 | ""; | |||||
436 | } | |||||||
437 | ||||||||
438 | 1; | |||||||
439 | # ABSTRACT: Check your BPR KS accounts from Perl | |||||||
440 | ||||||||
441 | __END__ |