1 package FS::usage_elec;
4 use vars qw( @ISA @EXPORT_OK $me);
5 use FS::Record qw( qsearch qsearchs );
6 use FS::UID qw( getotaker dbh );
7 use FS::usage_elec_transaction867;
10 use List::Util qw[min max];
12 use HTTP::Date qw( str2time );
14 use Date::Calc qw(Delta_Days);
15 @ISA = qw(FS::Record Exporter);
17 @EXPORT_OK = qw( most_current_date curr_read edi_to_usage );
21 FS::usage_elec - Object methods for usage_elec records
27 $record = new FS::usage_elec \%hash;
28 $record = new FS::usage_elec { 'column' => 'value' };
30 $error = $record->insert;
32 $error = $new_record->replace($old_record);
34 $error = $record->delete;
36 $error = $record->check;
40 An FS::usage_elec object represents an example. FS::usage_elec inherits from
41 FS::Record. The following fields are currently supported:
45 =item id - primary key
70 Creates a new example. To add the example to the database, see L<"insert">.
72 Note that this stores the hash reference, not a distinct copy of the hash it
73 points to. You can ask the object for a copy with the I<hash> method.
77 # the new method can be inherited from FS::Record, if a table method is defined
79 sub table { 'usage_elec'; }
83 Adds this record to the database. If there is an error, returns the error,
84 otherwise returns false.
88 # the insert method can be inherited from FS::Record
92 Delete this record from the database.
96 # the delete method can be inherited from FS::Record
98 =item replace OLD_RECORD
100 Replaces the OLD_RECORD with this one in the database. If there is an error,
101 returns the error, otherwise returns false.
105 # the replace method can be inherited from FS::Record
109 Checks all fields to make sure this is a valid example. If there is
110 an error, returns the error, otherwise returns false. Called by the insert
115 # the check method should currently be supplied - FS::Record contains some
116 # data checking routines
122 $self->ut_numbern('id')
123 || $self->ut_numbern('prev_date')
124 || $self->ut_numbern('curr_date')
125 || $self->ut_number('prev_read')
126 || $self->ut_number('curr_read')
127 || $self->ut_money('tdsp')
128 || $self->ut_number('svcnum')
129 || $self->ut_numbern('_date')
130 || $self->ut_float('meter_multiplier')
131 || $self->ut_numbern('demand_measure')
132 || $self->ut_numbern('demand_bill')
134 return $error if $error;
143 The author forgot to customize this manpage.
147 L<FS::Record>, schema.html from the base documentation.
150 sub most_current_date {
153 my @custs = qsearch('usage_elec',{ 'cust_nr' => $cust_nr});
155 my $most_current_date = 0;
159 foreach my $cust (@custs) {
160 if ($cust->curr_date > $most_current_date){
161 $most_current_date = $cust;
166 return $most_current_date;
172 return $self->total_usage;
176 # my $prev_read=$self->prev_read;
177 # my $curr_read=$self->curr_read;
179 # if ($prev_read<=$curr_read) {
180 # $usage= ($curr_read-$prev_read);
183 # $usage=(($curr_read+10**max(length($prev_read),length($curr_read)))-$prev_read);
185 # return $usage*$self->meter_multiplier;
188 sub getNumberOfDays {
190 return Date::Calc::Delta_Days( time2str('%Y', $self->prev_date),
191 time2str('%L', $self->prev_date),
192 time2str('%e', $self->prev_date),
193 time2str('%Y', $self->curr_date),
194 time2str('%L', $self->curr_date),
195 time2str('%e', $self->curr_date)
200 ### insert into table
208 local $SIG{HUP} = 'IGNORE';
209 local $SIG{INT} = 'IGNORE';
210 local $SIG{QUIT} = 'IGNORE';
211 local $SIG{TERM} = 'IGNORE';
212 local $SIG{TSTP} = 'IGNORE';
213 local $SIG{PIPE} = 'IGNORE';
215 my $oldAutoCommit = $FS::UID::AutoCommit;
216 local $FS::UID::AutoCommit = 0;
219 $error = $self->check;
220 return $error if $error;
222 $error = $self->SUPER::insert;
224 $dbh->rollback if $oldAutoCommit;
225 my $msg = "error: Can't insert data into usage_elec : $error\n"
230 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
235 ### Take in a time and convert it to time string to be entered into usage_elec
236 ### the function use is str2time from module "HTTP::Date qw( str2time )"
237 sub to_usage_elec_time {
240 ### becareful using time2str, year allows are 1970-jan2038
242 return str2time($time);
246 # Get the past 10 usage for a particular svcnum and return the object
248 # array of usages object
254 my ($svcnum, $how_many) = @_;
256 #$how_many = 10 unless $how_many; # default to 10 usages
258 # my @usages = qsearch (
262 # 'svcnum' => $svcnum,
263 # # sort in DESCending order so it easier to splice
264 # # the array in the next step
265 # 'extra_sql' => 'ORDER BY _date DESC'
269 my @usages = qsearch ( {
270 'table' => 'usage_elec',
271 'hashref' => { 'svcnum' => $svcnum },
272 'extra_sql' => 'ORDER BY _date DESC'
275 # shrink the array to $how_many index if it over the requested number
276 $#usages = $how_many - 1 if ( @usages && $how_many && (@usages > $how_many) );
280 # since we query the usage by DESCending order, it a good idea to put it
281 # in ascending order before a return.
282 @usages = reverse @usages;
290 # sub routine that go through the transaction810 and transaction867 table
291 # to put data into usage_elec table
295 # to input data into usage_elect, all condition below must be meet
296 # 1. there is unprocess data from transaction810 table
297 # 2. there is unprocess data from transaction867 table
298 # 3. the unprocess data from transaction867 match transaction810
305 #my @invoices_to_generate; # store usage_elec svcnum
307 # Only send data to usage_elec if a transactin from 810 & 867 match up
310 # first thing first. Let get all edi from transaction_810 table that haven't
312 my @edi_810_processeds = qsearch (
317 unless (@edi_810_processeds) {
318 return "There were no un-process 810 to input into usage_elec.\n"
319 ."Run again when there is 810 data to process\n";
322 # second, let get all edi from transaction_867 table that haven't been
324 my @edi_867_processeds = qsearch (
329 unless (@edi_867_processeds) {
330 return "There were no un-process 867 to match up with 810 data.\n"
331 ."Run again when there is 867 data to process\n";
334 # third, match up the 810 and 867 data. Those data that match up, goes
335 # into usage_elec table.
337 ### for efficientcy we will use the smaller list to traverse
338 if (@edi_810_processeds < @edi_867_processeds) {
340 print "debug: using 810\n" if $debug;
342 foreach my $edi_810 (@edi_810_processeds) {
344 my $ref_identification_810 = $edi_810->ref_identification;
345 my $srv_from_810 = $edi_810->srvc_from_date;
346 my $srv_to_810 = $edi_810->srvc_to_date;
347 ### search for the edi that match exactly with the 810
348 my $edi_867 = qsearchs ( 'transaction867',
349 { 'ref_identification' => $ref_identification_810,
350 'srvc_from_date' => $srv_from_810,
351 'srvc_to_date' => $srv_to_810,
355 ### we have a match, extract the data and put into usage
356 my $usage_elec_obj = extract_data_to_usage_elec ($edi_810, $edi_867);
357 if ($usage_elec_obj) {
359 ### mark the 810 and 867 as already process
360 $edi_810->setfield('processed',1);
361 $edi_867->setfield('processed',1);
363 ### go ahead and billed
364 my $rtnval = billing_call($usage_elec_obj);
366 print "Oh! Oh!.. unable to bill svcnum: $usage_elec_obj->svcnum\n";
368 $edi_810->setfield('processed',0);
369 $edi_867->setfield('processed',0);
370 $usage_elec_obj->delete;
376 print "RED ALERT.. something went wrong when inserting data\n"
377 ."into usage_elec (810)\n";
378 print "ref_identification of 810 : " . $edi_867->ref_identification
389 print "debug: using 867\n" if $debug;
391 foreach my $edi_867 (@edi_867_processeds) {
393 my $ref_identification_867 = $edi_867->ref_identification;
394 my $srv_from_867 = $edi_867->srvc_period_start_date;
395 my $srv_to_867 = $edi_867->srvc_period_end_date;
396 print "(debug) ref_identification: $ref_identification_867\n" if $debug;
397 ### search for the edi that match exactly with the 867
398 my $edi_810 = qsearchs ( 'transaction810',
399 { 'ref_identification' => $ref_identification_867,
400 'srvc_from_date' => $srv_from_867,
401 'srvc_to_date' => $srv_to_867,
406 print "(debug) found an 810 that match the 867: esiid "
407 .$edi_810->esiid."\n" if $debug;
409 ### we have a match, extract the data and put into usage
410 my $usage_elec_obj = extract_data_to_usage_elec($edi_810, $edi_867);
411 if ($usage_elec_obj) {
413 ### mark the 810 and 867 as already process
414 my $edi_810_new = new FS::transaction810( { $edi_810->hash } );
415 $edi_810_new->setfield('processed',1);
416 my $error = $edi_810_new->replace($edi_810);
418 print "there is an error changing column 'processed' of transaction810 table\n";
419 print "error: $error\n";
422 my $edi_867_new = new FS::transaction867( { $edi_867->hash } );
423 $edi_867->setfield('processed',1);
424 $error = $edi_867_new->replace($edi_867);
426 print "there is an error changing column 'processed' of transaction867 table\n";
427 print "error: $error\n";
430 ### go ahead and billed
431 my $rtnval = billing_call($usage_elec_obj);
433 print "Oh! Oh!.. unable to bill svcnum: $usage_elec_obj->svcnum\n";
435 $edi_810->setfield('processed',0);
436 $edi_867->setfield('processed',0);
437 $usage_elec_obj->delete;
442 print "RED ALERT.. something went wrong when inserting data\n"
443 ."into usage_elec (810)\n";
444 print "ref_identification of 810 : " . $edi_810->ref_identification
456 # This subroutine does the physical adding of data into usage_elec
457 # using the transaction810 and transaction867 table
459 sub extract_data_to_usage_elec {
460 my ($edi_810, $edi_867) = @_;
462 ### variables declaration
463 ### following decl are column of usage_elec
464 my ($prev_date, $curr_date, $prev_read, $curr_read, $tdsp, $svcnum, $_date,
465 $meter_multiplier, $total_usage, $measured_demand, $billed_demand,
468 local $SIG{HUP} = 'IGNORE';
469 local $SIG{INT} = 'IGNORE';
470 local $SIG{QUIT} = 'IGNORE';
471 local $SIG{TERM} = 'IGNORE';
472 local $SIG{TSTP} = 'IGNORE';
473 local $SIG{PIPE} = 'IGNORE';
475 my $oldAutoCommit = $FS::UID::AutoCommit;
476 local $FS::UID::AutoCommit = 0;
479 ### this message will print in the year 2038 because their is a limitation
480 # with str2time ( cpan.org package Icwa-1.0.0.tar.gz )
481 if ( int(time2str('%Y',time)) > 2037 ) {
482 print "Bug: Try to use function 'time2str' has generate an error because"
483 ."\n\tit can't handle year greter than 2037.\n";
488 $prev_date = str2time($edi_867->srvc_period_start_date);
489 $curr_date = str2time($edi_867->srvc_period_end_date);
490 $prev_read = $edi_867->prev_read_kwatts;
491 $curr_read = $edi_867->curr_read_kwatts;
492 $meter_multiplier = $edi_867->meter_multiplier;
493 $total_usage = $edi_867->usage_kwatts;
494 $measured_demand = $edi_867->measured_demand;
495 $meter_number = $edi_867->meter_no;
498 $tdsp = sprintf('%.2f',$edi_810->tdsp/100);
499 $billed_demand = $edi_810->billed_demand;
502 ### obtain the svcnum
503 my $esiid = $edi_810->esiid;
504 my $svc_obj = qsearchs ( 'svc_external',
508 return unless ($svc_obj); #debug
509 $svcnum = $svc_obj->svcnum;
515 ### got everything we needed
516 # now let insert it into usage_elec
518 'prev_date' => $prev_date,
519 'curr_date' => $curr_date,
520 'prev_read' => $prev_read,
521 'curr_read' => $curr_read,
525 #'meter_multiplier' => $meter_multiplier,
526 'meter_multiplier' => $meter_multiplier,
527 'total_usage' => $total_usage,
528 'measured_demand' => $measured_demand,
529 'billed_demand' => $billed_demand,
530 'meter_number' => $meter_number,
532 print "usage_elect Dumping". Dumper(\%usage);
534 if ( $edi_810->esiid != '10443720004466311' &&
535 $edi_810->esiid != '10443720004264904') {
539 my $usage_elec_obj = new FS::usage_elec( \%usage );
540 my $error = $usage_elec_obj->insert;
541 print "I'm inserting something into usage_elec\n";
543 $dbh->rollback if $oldAutoCommit;
544 my $msg = "Can't insert data into usage_elec : $error\n"
550 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
552 ### for testing purpose.. put a message into usage_elec_transaction_867
554 my $usage_elec_transaction867_obj = new FS::usage_elec_transaction867(
555 {'usage_elec_id' => $usage_elec_obj->id,
556 'note' => "Attention: a meter change out has occured at"
561 $error = $usage_elec_transaction867_obj->insert;
562 print "Adding note into usage_elec_transaction867\n";
564 $dbh->rollback if $oldAutoCommit;
565 my $msg = "Can't insert data into usage_elec_transaction867 : $error\n";
570 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
574 return $usage_elec_obj;
576 } # end extract_data_to_usage_elec
578 ### do the billing call
579 ### return: if there is an error, returns the error, otherwise
582 my $usage_elec_obj = shift;
586 my $svcnum = $usage_elec_obj->svcnum;
587 print "svcnum = $svcnum\n" if $debug;
589 my $package = qsearchs ( 'cust_svc',
590 { 'svcnum' => $svcnum
594 return "error: sub billing_call: unable to acquire the package\n";
596 my $pkgnum = $package->pkgnum;
597 print "pkgnum = $pkgnum\n" if $debug;
599 my $custpkg = qsearchs ( 'cust_pkg',
600 { 'pkgnum' => $pkgnum
604 return "error: sub billing_call: unable to acquire the custpkg\n";
606 my $custnum = $custpkg->custnum;
607 print "custnum = $custnum\n" if $debug;
609 my $cust_main_obj = qsearchs ( 'cust_main',
610 { 'custnum' => $custnum
613 unless ($cust_main_obj) {
614 return "error: sub billing_call: unable to acquire the cust_main_obj\n";
617 my $rtnval = $cust_main_obj->bill();
619 return "error: calling billing command\n\t$rtnval";
622 ### now let generate the invoice for the customer
624 my $heading = "\tid\tprev_date\tcurr_date\tprev_read\tcurr_read"
625 . "\ttdsp\tsvcnum\t_date\n";
628 print "\t" . $usage_elec_obj->id;
629 print "\t" . $usage_elec_obj->prev_date;
630 print "\t" . $usage_elec_obj->curr_date;
631 print "\t" . $usage_elec_obj->prev_read;
632 print "\t" . $usage_elec_obj->curr_read;
633 print "\t" . $usage_elec_obj->tdsp;
634 print "\t" . $usage_elec_obj->svcnum;
635 print "\t" . $usage_elec_obj->_date;
636 print "\t" . $usage_elec_obj->meter_multiplier;
637 print "\t" . $usage_elec_obj->measured_demand;
638 print "\t" . $usage_elec_obj->billed_demand;