App-GnuCash-MembershipUtils
view release on metacpan or search on metacpan
bin/gc-members-generate-invoices view on Meta::CPAN
#!/usr/bin/env perl
##----------------------------------------------------------------------------
## :mode=perl:indentSize=2:tabSize=2:noTabs=true:
##****************************************************************************
## NOTES:
## * Before comitting this file to the repository, ensure Perl Critic can be
## invoked at the HARSH [3] level with no errors
##****************************************************************************
use strict;
use warnings;
use feature qw( say state );
## Cannot use Find::Bin because script may be invoked as an
## argument to another script, so instead we use __FILE__
use File::Basename qw(dirname fileparse basename);
use File::Spec;
## Add script directory
use lib File::Spec->catdir(File::Spec->splitdir(dirname(__FILE__)));
## Add script directory/lib
use lib File::Spec->catdir(File::Spec->splitdir(dirname(__FILE__)), qq{lib});
## Add script directory/../lib
use lib File::Spec->catdir(
File::Spec->splitdir(dirname(__FILE__)),
qq{..},
qq{lib});
use App::GnuCash::MembershipUtils qw( :all );
use Getopt::Long qw( :config no_ignore_case bundling );
use Pod::Usage;
use Cwd qw(abs_path);
use Readonly;
use Data::Dump qw( pp );
use Text::CSV;
use DateTime;
use DateTime::Format::DateParse;
Readonly::Hash my %DATE_FORMATS => (
us => "%m/%d/%Y", # MM/DD/YYYY
uk => "%d/%m/%Y", # DD/MM/YYYY
iso => "%Y-%m-%d", # YYYY-MM-DD
europe => "%d.%m.%Y", # DD.MM.YYYY
);
## Used for the version string
Readonly::Scalar my $VERSION => qq{0.01};
Readonly::Scalar my $DEFAULT_TITLE => qq{GnuCash Members Generate Invoices};
##--------------------------------------------------------
## Return codes for booleans
##--------------------------------------------------------
Readonly::Scalar my $FALSE => 0;
Readonly::Scalar my $TRUE => 1;
##--------------------------------------------------------
## A list of all command line options
## For GetOptions the following parameter indicaters are used
## = Required parameter
## : Optional parameter
## s String parameter
## i Integer parameter
## f Real number (float)
## + Parameter is inremented for each occurrence on
## command line
## ! boolean parameter that also allows "no-xxx"
## If a parameter is not indicated, then a value of 1
## indicates the parameter was found on the command line
##--------------------------------------------------------
#<<< begin perltidy exclusion zone
my @CommandLineOptions = (
"config|c:s",
"debug|d+",
"due|D:s",
"file|f:s",
"format|F:s",
"help|h|?",
"man|M",
"memo|m:s",
"note|n:s",
"opened|O:s",
"outfile|o:s",
"version|v",
);
#>>> end perltidy exclusion zone
##--------------------------------------------------------
## A hash to hold all default values for command line
## options
##--------------------------------------------------------
#<<< begin perltidy exclusion zone
my %gOptions = (
"config" => "GnuCashMembershipUtils.yml",
"debug" => 0,
"due" => undef,
"file" => undef,
"format" => undef,
bin/gc-members-generate-invoices view on Meta::CPAN
## Non-windows OSes don't care about short names
$gOptions{ScriptPathShort} = $gOptions{ScriptPath};
}
## See if there were any unknown parameters on the command line
if (@ARGV && !$allow_extra_args)
{
display_usage_and_exit("\n\nERROR: Invalid "
. (scalar(@ARGV) > 1 ? "arguments" : "argument") . ":\n "
. join("\n ", @ARGV)
. "\n\n");
}
return ($TRUE);
}
##----------------------------------------------------------------------------
## @fn display_usage_and_exit($message, $filenameexitval)
## @brief Display the usage with the given message and exit with the given
## value
## @param $message - Message to display. DEFAULT: ""
## @param $exitval - Exit vaule DEFAULT: 1
## @return NONE
## @note
##----------------------------------------------------------------------------
sub display_usage_and_exit
{
my $message = shift // qq{};
my $exitval = shift // 1;
pod2usage(
-input => \*DATA,
-message => $message,
-exitval => $exitval,
-verbose => 1,
);
return;
}
##----------------------------------------------------------------------------
##
##----------------------------------------------------------------------------
sub format_date {
my $dt = shift;
state $format = $DATE_FORMATS{lc($gOptions{format})};
return $dt->strftime($format);
}
##----------------------------------------------------------------------------
## MAIN
##----------------------------------------------------------------------------
## Set STDOUT to autoflush
$| = 1; ## no critic (RequireLocalizedPunctuationVars)
## Parse the command line
process_commandline();
my $dt_tz = DateTime::TimeZone->new( name => 'local' );
my $inv_open_dt;
if ($gOptions{open}) {
$inv_open_dt = DateTime::Format::DateParse->parse_datetime($gOptions{open});
} else {
# Default is first of the current month
$inv_open_dt = DateTime->today( time_zone => $dt_tz, )->set_day(1);
}
my $inv_due_dt;
if ($gOptions{due}) {
$inv_due_dt = DateTime::Format::DateParse->parse_datetime($gOptions{due});
} else {
# Default is last of current month.
# This is done by getting the 1st day of next month and subtrating 1 day
$inv_due_dt = DateTime->today( time_zone => $dt_tz, )->set_day(1)->add(months => 1)->subtract(days => 1);
}
if ($inv_open_dt > $inv_due_dt) {
printf STDERR "ERROR: The opening date cannot be after the due date!\n";
exit(1);
}
my ($error, $config) = get_config($gOptions{config}, $gOptions{debug});
if ($error) {
printf STDERR "Error getting config: %s\n", $error;
exit(1);
}
$gOptions{format} //= $config->{GnuCash}{format};
unless ($DATE_FORMATS{lc($gOptions{format})}) {
printf STDERR "Invalid date format option '%s'. Must be one of '%s'\n",
$gOptions{format},
join("', '", keys(%DATE_FORMATS));
exit(1);
}
$gOptions{memo} //= $config->{GnuCash}{memo};
$gOptions{note} //= $inv_open_dt->strftime("%B membership dues");
unless ($gOptions{outfile}) {
$gOptions{outfile} = DateTime->today( time_zone => $dt_tz, )->strftime("%Y-%m-%d_gnucash_customer_invoices.csv");
}
my $schema;
($error, $schema) = open_gnucash(get_gnucash_filename($gOptions{file}, $config));
if ($error) {
printf STDERR "Error opening GnuCash file: %s\n", $error;
exit(1);
}
my $warning;
($error, $warning) = validate_accounts_in_config({
schema => $schema,
config => $config,
});
say STDERR $warning if ($warning);
if ($error) {
say STDERR $error;
exit(1);
}
my @members = get_all_members({
active_only => 1,
schema => $schema,
config => $config,
debug => $gOptions{debug},
});
my $last_inv_id = $schema->resultset('Invoice')->last_invoice_id;
my $next_inv_id = $last_inv_id + 1;
printf(
join(
"\n",
"Output File: '%s'",
"Opening Date: %s",
"Due Date: %s",
"Item Memo: '%s'",
"Invoice Memo: '%s'",
"First Invoice: '%06d'",
"",
),
$gOptions{outfile},
format_date($inv_open_dt),
format_date($inv_due_dt),
$gOptions{memo},
$gOptions{note},
$next_inv_id,
);
my $csv = Text::CSV->new ({
# Cannot quote spaces, or GnuCash won't find your accounts
quote_space => 0,
sep_char => ';',
binary => 1,
auto_diag => 1,
});
my %inv_template = (
date_opened => format_date($inv_open_dt),
date => format_date($inv_open_dt),
date_posted => format_date($inv_open_dt),
( run in 3.128 seconds using v1.01-cache-2.11-cpan-5c0b1e786e0 )