summaryrefslogtreecommitdiff
path: root/FS/bin/freeside-bill
blob: 49ad4a76890bb0b660e3caef57134803e0127e1e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#!/usr/bin/perl -w
# don't take any world-facing input
#!/usr/bin/perl -Tw

use strict;
use Fcntl qw(:flock);
use Date::Parse;
use Getopt::Std;
use FS::UID qw(adminsuidsetup);
use FS::Record qw(qsearch qsearchs);
use FS::cust_main;

&untaint_argv;	#what it sounds like  (eww)
use vars qw($opt_a $opt_c $opt_d $opt_p);
getopts("acd:p");
my $user = shift or die &usage;

adminsuidsetup $user;

my %bill_only = map { $_ => 1 } (
  @ARGV ? @ARGV : ( map $_->custnum, qsearch('cust_main', {} ) )
);

#we're at now now (and later).
my($time)= $opt_d ? str2time($opt_d) : $^T;

# find packages w/ bill < time && cancel != '', and create corresponding
# customer objects

my($cust_main,%saw);
foreach $cust_main (
  map {
    unless ( exists $saw{ $_->custnum } && defined $saw{ $_->custnum} ) {
      $saw{ $_->custnum } = 0; # to avoid 'use of uninitialized value' errors
    }
    if (
      ( $opt_a || ( ( $_->getfield('bill') || 0 ) <= $time ) )
      && $bill_only{ $_->custnum }
      && !$saw{ $_->custnum }++
    ) {
      qsearchs('cust_main',{'custnum'=> $_->custnum } );
    } else {
      ();
    }
  } ( qsearch('cust_pkg', { 'cancel' => '' }),
      qsearch('cust_pkg', { 'cancel' => 0  }),
    )
) {

  # and bill them

  print "Billing customer #" . $cust_main->getfield('custnum') . "\n";

  my($error);

  $error=$cust_main->bill('time'=>$time);
  warn "Error billing,  customer #" . $cust_main->getfield('custnum') . 
    ":" . $error if $error;

  if ($opt_p) {
    $cust_main->apply_payments;
    $cust_main->apply_credits;
  }

  if ($opt_c) {
    $error=$cust_main->collect( 'invoice_time' => $time);
    warn "Error collecting from customer #" . $cust_main->custnum.  ":$error"
      if $error;

    #sleep 1;
  }

}

# subroutines

sub untaint_argv {
  foreach $_ ( $[ .. $#ARGV ) { #untaint @ARGV
    #$ARGV[$_] =~ /^([\w\-\/]*)$/ || die "Illegal arguement \"$ARGV[$_]\"";
    # Date::Parse
    $ARGV[$_] =~ /^(.*)$/ || die "Illegal arguement \"$ARGV[$_]\"";
    $ARGV[$_]=$1;
  }
}

sub usage {
  die "Usage:\n\n  freeside-bill [ -c [ -p ] ] [ -d 'date' ] user [ custnum custnum ... ]\n";
}

=head1 NAME

freeside-bill - Command line (crontab, script) interface to customer billing.

=head1 SYNOPSIS

  freeside-bill [ -c [ -p ] [ -a ] ] [ -d 'date' ] user [ custnum custnum ... ]

=head1 DESCRIPTION

This script is deprecated in 1.4.0.  You should use freeside-daily instead.

Bills customers.  Searches for customers who are due for billing and calls
the bill and collect methods of a cust_main object.  See L<FS::cust_main>.

  -c: Turn on collecting (you probably want this).

  -p: Apply unapplied payments and credits before collecting (you probably want
      this too)

  -a: Call collect even if there isn't a new invoice (probably a bad idea for
      daily use)

  -d: Pretend it's 'date'.  Date is in any format Date::Parse is happy with,
      but be careful.

user: From the mapsecrets file - see config.html from the base documentation

custnum: if one or more customer numbers are specified, only bills those
customers.  Otherwise, bills all customers.

=head1 BUGS

=head1 SEE ALSO

L<freeside-daily>, L<FS::cust_main>, config.html from the base documentation

=cut