#------------------------------------------------------------
# Copyright (C) 2009 RoyaltyShare, Inc.   All Rights Reserved
#------------------------------------------------------------
package RPS::Mechanical::CA::Process::RunController;
use strict;

use Data::Dumper;
use File::Path;

use lib '/app/tools/common/lib';
use Common::Assert;
use Common::RSApp;
use Common::Log;

use lib '/app/tools/rps/lib';
use lib '/app/tools/job/lib';
use lib '/app/tools/raptor/lib';

use RPS::Mechanical::CA::MechanicalRun;
use RPS::Mechanical::Process::Exception;
use RPS::DB::Item::StatRate;
use RPS::DB::Item::CALicenseReserve;
use RPS::DB::Item::CALicenseReserveRun;
use RPS::DB::Item::CAMechanicalRun;
use RPS::DB::Item::CAMechanicalRunUnlicensedTrackLog;
use RPS::DB::Item::CAMechanicalStatement;
use RPS::DB::Item::CAMechanicalStatementAdjustmentItem;
use RPS::DB::Item::CAMechanicalStatementItem;
use RPS::DB::Item::CAMechanicalStatementLicense;
use RPS::DB::Item::CAMechanicalStatementLicenseTransaction;
use RPS::DB::Item::CAMechanicalStatementTrack;
use RPS::DB::Item::CAMechanicalStatementTransaction;
use RPS::DB::Item::CAPublisher;
use RPS::DB::Item::CATrackLicense;
use RPS::DB::Item::SaleCAPublisherMap;
use RPS::DB::Item::CAPublisherAccount;
use RPS::Mechanical::CA::Job::CreateStatement;
use RPS::Mechanical::CA::Job::CreatePDFStatements;
use RPS::Mechanical::CA::Job::CreateTextStatements;
use RPS::Mechanical::CA::Job::CreateCMRRAStatements;
use RPS::Mechanical::CA::Job::RunMechanicalRoyalties;
use RPS::Mechanical::CA::Process::CreateStatement;
use RPS::Statement::Mechanical::CA::PDF;
use RPS::Statement::Mechanical::CA::Text;
use RPS::Statement::Mechanical::CA::CMRRA;

use Raptor::DB::Item::Sale;
use RPS::Mechanical::Process::RunController::Complex;
use RPS::Mechanical::CA::Job::CreateReservePipelineReport;
use RPS::DB::Item::ReportQueries::CAMechanicalReservePipeline;
use RPS::Mechanical::CA::Job::CreateRoyaltyExportReport;
use RPS::Mechanical::CA::Job::CreateRoyaltyExportReportExcel;
use RPS::Mechanical::CA::Job::CreateCMRRAAgentReportExcel;
use RPS::Mechanical::CA::Job::CreateCMRRASummaryReportExcel;

use base 'RPS::Mechanical::Process::RunController::Complex';

use constant kStatementVersion => 4;

sub _runType {
    return RPS::DB::Item::SaleRunMap::kRunTypeCAMechanical;
}

sub run {
    my ($self) = @_;

    # Call the inherited method
    #
    my $rval = $self->SUPER::run();

    # We don't want the data in the SalePublisherMap table to persist.
    # It takes up a lot of space...
    #
    RPS::DB::Item::SaleCAPublisherMap->DeleteAllForRun( $self->{_runID}, RPS::DB::Item::SaleRunMap::kRunTypeMechanical() );

    # Create the unlicensed accrual report
    #
    $self->_createUnlicensedAccrualReport();

    # Create the reserve pipeline report
    #
    $self->_createReservePipelineReport();

    # Create the royalty data export report
    #
    $self->_createRoyaltyDataExportReport();

    # Create the CMRRA Agent and Summary reports
    #
    $self->_createCMRRAAgentReports();

    return $rval;
}

sub _createSalePublisherMapEntry {
    my ( $self, %args ) = @_;
    assert( $args{run_id} );
    assert( $args{sale_id} );
    assert( $args{publisher_id} );

    my $newMapItem = RPS::DB::Item::SaleCAPublisherMap->Create(
        run_id          => $args{run_id},
        sale_id         => $args{sale_id},
        ca_publisher_id => $args{publisher_id},
    );

    $newMapItem->save();

}

sub _trackIsMechanicalExempt {
    my ( $self, $track ) = @_;
    return $track->ca_mechanical_exempt();
}

sub _nativeCountryCode {
    my ($self) = @_;
    return 'CA';
}

sub _logPublicDomainShare {
    my ( $self, $sale, $trackID, $trackLicense, $share ) = @_;

    return unless ( 'CA' eq $sale->country_code );

    $self->SUPER::_logPublicDomainShare( $sale, $trackID, $trackLicense, $share );
}

sub _getAllUnprocessedSales {
    my ( $self, $endDate ) = @_;

    my $allSales = Raptor::DB::Item::Sale->GetUnprocessedCAMechanicalSales( ending_sale_date => $endDate );
    return $allSales;
}

sub _getAllPublishers {
    my ($self) = @_;

    my $allPublishers = RPS::DB::Item::CAPublisher->GetAll();
    return $allPublishers;
}

sub _getMechanicalRunTrackLicenseItems {
    my ($self) = @_;

    my $allItems = RPS::DB::Item::MechanicalRunTrackLicense->GetByRunIDAndTypeAndCountryCode( $self->{_runID}, $self->_runType, 'CA' );
    return $allItems;
}

sub _deleteMechanicalRunTrackLicenseItems {
    my ($self) = @_;

    my $allItems = RPS::DB::Item::MechanicalRunTrackLicense->DeleteByRunIDAndType( $self->{_runID}, $self->_runType );
    return $allItems;
}

sub _publisherIDFromTrackLicense {
    my ( $self, $tl ) = @_;
    return $tl->ca_publisher_id;
}

sub _getReserveErrors {
    my $self = shift;

    return RPS::DB::Item::CATrackLicense->GetAllWithoutReserveLiquidation();
}

sub _getPennyRateErrors {
    my $self = shift;

    return RPS::DB::Item::CATrackLicense->GetAllWithInvalidPennyRate();
}

sub _getLicenseShareErrors {
    my $self = shift;

    return RPS::DB::Item::CATrackLicense->GetAllWithZeroShare();
}

sub _getAllTrackLicenses {
    my $self = shift;

    return RPS::DB::Item::CATrackLicense->GetAll();
}

sub _publisherIDFromPublisher {
    my ( $self, $publisher ) = @_;

    return $publisher->ca_publisher_id();
}

sub _getAllActivePublisherIDs {
    my ($self) = @_;

    my %idHash;
    my $payorID    = $self->{_payorID};
    my $collection = RPS::DB::Item::CAPublisher->GetAllActiveOrOnHoldWithLicensesForPayor($payorID);
    while ( my $publisher = $collection->next() ) {
        $self->_report( "_getAllActivePublisherIDs : publisher:", 4 );
        $self->_report( $publisher,                               4 );

        # Add this publisher to the hash
        #
        $idHash{ $publisher->ca_publisher_id } = 1;

        # Also add this guys admin or agent id..
        # agent trumps admin
        #
        if ( $publisher->agent_id ) {
            $idHash{ $publisher->agent_id } = 1;
        } elsif ( $publisher->admin_id ) {
            $idHash{ $publisher->admin_id } = 1;
        }
    }

    # publishers with pending transactions including publishers without licenses
    $collection = RPS::DB::Item::CAPublisherAccount->GetAllActiveOrOnHoldPublisherByPayorID($payorID);
    while ( my $publisher = $collection->next() ) {
        $self->_report( "_getAllActivePublisherIDsWithPendingTransactions : publisher:", 4 );
        $self->_report( $publisher,                                                      4 );
        $idHash{ $publisher->ca_publisher_id } = 1;
    }

    # publishers with balance
    $collection = RPS::DB::Item::CAPublisher->GetAllActiveOrOnHoldWithBalance($payorID);
    while ( my $publisher = $collection->next() ) {
        $idHash{ $publisher->ca_publisher_id } = 1;
    }

    return keys %idHash;
}

sub _getRunDBItem {
    my ($self) = @_;
    assert( $self->{_runID} );

    my $dbItem = RPS::DB::Item::CAMechanicalRun->Lookup( ca_mechanical_run_id => $self->{_runID} );
    return $dbItem;
}

sub _preCommitSanityCheck {
    my ($self) = @_;

    $self->_testPendingTransactionsAreValid();

    # See if all the reserves are present and accounted for...
    #
    if ( RPS::DB::Item::CALicenseReserveRun->CountMissingRunReserves( $self->{_runID} ) > 0 ) {
        die RPS::Mechanical::Process::Exception->new("ERROR - Not all run reserves are accounted for");
    }

    return 1;
}

sub _testPendingTransactionsAreValid {
    my ($self) = @_;

    my $runID = $self->{_runID};

    # Make sure nobody deleted the pending transactions that appear on this run!
    #
    my $statements = RPS::DB::Item::CAMechanicalStatement->GetByMechanicalRunID($runID);
    while ( my $statement = $statements->next() ) {
        my $statementID         = $statement->ca_mechanical_statement_id;
        my $statementTransItems = RPS::DB::Item::CAMechanicalStatementTransaction->GetByMechanicalStatementID($statementID);
        my $transItem;
        while ( $transItem = $statementTransItems->next() ) {
            my $pendingTransactionID = $transItem->pending_transaction_id;
            my $pending = RPS::DB::Item::PendingTransaction->Lookup( pending_transaction_id => $pendingTransactionID );
            if ( !$pending ) {
                die RPS::Mechanical::Process::Exception->new( "ERROR - original pending transaction was deleted! : ", $transItem );
            }
        }

        my $licenseTransItems = RPS::DB::Item::CAMechanicalStatementLicenseTransaction->GetByMechanicalStatementID($statementID);
        while ( $transItem = $licenseTransItems->next() ) {
            my $pendingTransactionID = $transItem->pending_transaction_id;
            my $pending = RPS::DB::Item::PendingTransaction->Lookup( pending_transaction_id => $pendingTransactionID );
            if ( !$pending ) {
                die RPS::Mechanical::Process::Exception->new( "ERROR - original pending transaction was deleted! : ", $transItem );
            }
        }
    }

    return 1;
}

sub _commitCarryover {
    my ($self) = @_;

    # !!! We don't DO carryover anymore.

    # What we're really doing here is clearing out the _old_ carryovers.
    # Each 'statement' will then create entries for the new carryover (later).
    #

    # Truncate the carryover table.
    #
    #    my $runCarryoverIDs = RPS::DB::Item::MechanicalRunCarryover->GetByMechanicalRunID($self->{_runID});
    #    while (my $carryoverMapItem = $runCarryoverIDs->next())
    #    {
    #        my $carryoverID = $carryoverMapItem->mechanical_carryover_id;
    #        my $carryover = RPS::DB::Item::MechanicalCarryover->Lookup(mechanical_carryover_id => $carryoverID);
    #        $carryover->delete();
    #    }

    # Get rid of entries in the carryover run map
    #
    # !!! KEEP FOR DEBUGGING
    #    RPS::DB::Item::MechanicalRunCarryover->DeleteByMechanicalRunID($self->{_runID});
}

sub _commitReserves {
    my ($self) = @_;

    my $runID = $self->{_runID};

    # 'turn the crank' on the reserves table.
    #
    my $runReserves = RPS::DB::Item::CALicenseReserveRun->GetByMechanicalRunID($runID);
    while ( my $runReserveItem = $runReserves->next() ) {
        my $reserve = RPS::DB::Item::CALicenseReserve->Lookup( ca_license_reserve_id => $runReserveItem->ca_license_reserve_id );

        if ( !$reserve ) {

            # This should never happen!
            #
            $self->_report( "ERROR!  reserve id " . $runReserveItem->ca_license_reserve_id . " is MISSING" );
            next;
        }
        my $period = $reserve->periods_remaining();

        if ( 1 == $period ) {

            # This reserve has been liquidated;
            # So we'll make note of the run id.
            #
            $reserve->liquidated_run_id($runID);
        }

        # Just decrement the periods_remaining column.
        # Liquidated reserves will end up with '0' periods_remaining.
        #
        $reserve->periods_remaining( $period - 1 );
        $reserve->save();
    }

}

sub _commitStatement {
    my ( $self, $statement ) = @_;

    # Instantiate the proper CreatePublisherStatement entity, and let that handle the details.
    #
    my $statementProcessor = RPS::Mechanical::CA::Process::CreateStatement->new( statementID => $statement->ca_mechanical_statement_id );
    $statementProcessor->commit();
}

sub _getAllStatements {
    my ($self) = @_;

    my $statements = RPS::DB::Item::CAMechanicalStatement->GetByMechanicalRunID( $self->{_runID} );
    return $statements;
}

sub _deleteFromSaleRunMap {
    my ($self) = @_;

    RPS::DB::Item::SaleRunMap->DeleteByCAMechanicalRunID( $self->{_runID} );
}

sub _getLogFilePath {
    my ($self) = @_;

    my $logBasePath = RPS::Mechanical::CA::Job::RunMechanicalRoyalties->LogFilePath( Common::RSApp::GetClientID(), $self->{_runID} );
    return $logBasePath;
}

sub delete {
    my ($self) = @_;

    RPS::DB::Item::CALicenseReserveRun->DeleteByMechanicalRunID( $self->{_runID} );

    #    RPS::DB::Item::CAMechanicalRunCarryover->DeleteByMechanicalRunID($self->{_runID});

    $self->SUPER::delete();

    # MAKE SURE this is emptied out.
    # We clear this table when a run exits normally, but if something went haywire we might need to try again.
    #
    RPS::DB::Item::SaleCAPublisherMap->DeleteAllForRun( $self->{_runID} );

    # Clear out any associated pipeline report
    #
    RPS::DB::Item::ReportQueries::CAMechanicalReservePipeline->DeleteByMechanicalRunID( $self->{_runID} );

    return 0;
}

sub _deleteStatement {
    my ( $self, $statement ) = @_;

    my $statementID = $statement->ca_mechanical_statement_id;

    $self->_report("deleting statement $statementID");
    $statement->delete();

    $self->_report( "  ... deleting reserve items", 2 );
    RPS::DB::Item::CALicenseReserve->DeleteByStatementID($statementID);

    $self->_report( "  ... deleting track items", 2 );
    RPS::DB::Item::CAMechanicalStatementTrack->DeleteByMechanicalStatementID($statementID);

    $self->_report( "  ... deleting license items", 2 );
    RPS::DB::Item::CAMechanicalStatementLicense->DeleteByMechanicalStatementID($statementID);

    $self->_report( "  ... deleting adjustment items", 2 );
    RPS::DB::Item::CAMechanicalStatementAdjustmentItem->DeleteByMechanicalStatementID($statementID);

    $self->_report( "  ... deleting statement items", 2 );
    RPS::DB::Item::CAMechanicalStatementItem->DeleteByMechanicalStatementID($statementID);

    return 0;
}

sub _checkForFeatureFlags {
    my $self = shift;
    my $err  = 0;  # default to no error
    return $err;
}

sub _createSalePublisherLicenseEntry {
    my ( $self, $saleID, $publisherID, $trackLicenses ) = @_;
    $self->_report( " >> _createSalePublisherLicenseEntry not implemented for CA mechanicals", 2 );
}


sub _initializeSalePublisherLicenseMap {
    my ( $self ) = shift;

    $self->_report( " >> _initializeSalePublisherLicenseMap not implemented for CA mechanicals", 2 );
}

sub _clearLicenseCache {
    my ( $self ) = shift;
    $self->_report( " >> _clearLicenseCache not implemented for CA mechanicals", 2 );
}

sub _createSaleLicenseMapping {
    my ($self) = @_;
    $self->_report( " >> _createSaleLicenseMapping not implemented for CA mechanicals", 2 );
}

sub _createStatementsAndJobs {
    my ($self) = @_;

    # Before we kick off the individual publisher jobs, we
    # need to 'pre-scan' the sale table.
    # For huge customers, I don't want to have every job looking at every sale.
    # So we will do a quick (15 minutes or so for our biggest clients) sorting process.
    #
    $self->_createSalePublisherMapping();

#    $self->_createSaleLicenseMapping() if ( $self-{_RSD7859} );

    $self->SUPER::_createStatementsAndJobs();
}

sub _getStatement {
    my ( $self, $statementID ) = @_;

    my $statement = RPS::DB::Item::CAMechanicalStatement->Lookup( ca_mechanical_statement_id => $statementID );
    return $statement;
}

sub _createDirectPublisherStatementJob {
    my ( $self, $publisherID ) = @_;
    assert($publisherID);

    # First, create an 'empty' statement database record.
    #
    my $statementObj = RPS::DB::Item::CAMechanicalStatement->Create(
        ca_mechanical_run_id => $self->{_runID},
        payor_id             => $self->{_payorID},
        ca_publisher_id      => $publisherID,
        status               => RPS::Statement::Status::kNotQueued(),
        version              => kStatementVersion,
    );
    $statementObj->save();

    my $statementID = $statementObj->ca_mechanical_statement_id();

    # Create the Job object
    #
    my $job = RPS::Mechanical::CA::Job::CreateStatement->new(
        runID       => $self->{_runID},
        statementID => $statementID,
        logLevel    => $self->{_logLevel}
    );
    $job->enqueue();

    # Update the statement record with the job id, and
    # update the status to 'in queue'
    #
    $statementObj->job_id( $job->id() );
    $statementObj->status( RPS::Statement::Status::kInQueue() );
    $statementObj->save();

    return $job;
}

sub _createPDFJobs {
    my ($self) = @_;

    # Make sure the path exists
    #
    my $filePath = RPS::Statement::Mechanical::CA::PDF->StatementPathFromRunID( $self->{_runID} );
    if ( !-d $filePath ) {
        mkpath($filePath) or die RPS::Mechanical::Process::Exception->new("Unable to create path $filePath: $!");
    }

    # Create a job for all statements, and put it in the queue.
    #
    my $clientID = Common::RSApp::GetClientID();

    my $jobArgs = RPS::Mechanical::CA::Job::CreatePDFStatements->new(
        runID    => $self->{_runID},
        clientID => $clientID,
        filePath => $filePath,
        mode     => 'b',
    );
    my $job = $jobArgs->enqueue();

}

sub _createRoyaltyDataExportReport {
    my ($self) = @_;

    # This report will be stored in the same directory as the mechanical statements.
    #
    my $filePath = RPS::Statement::Mechanical::CA::Text->StatementPathFromRunID( $self->{_runID} );
    if ( !-d $filePath ) {
        mkpath($filePath) or die RPS::Mechanical::Process::Exception->new("ERROR: Unable to create path $filePath: $!");
    }

    my $clientID = Common::RSApp::GetClientID();

    # Queue the text report
    #
    my $jobArgs = RPS::Mechanical::CA::Job::CreateRoyaltyExportReport->new(
        runID    => $self->{_runID},
        clientID => $clientID,
        filePath => $filePath,
    );
    my $job = $jobArgs->enqueue();

    # Queue the Excel report
    #
    $jobArgs = RPS::Mechanical::CA::Job::CreateRoyaltyExportReportExcel->new(
        runID    => $self->{_runID},
        clientID => $clientID,
        filePath => $filePath,
    );
    $job = $jobArgs->enqueue();

    $self->_report( "_createRoyaltyDataExportReport - filePath=$filePath", 2 );
}

sub _createCMRRAAgentReports {
    my ($self) = @_;

    # This report will be stored in the same directory as the mechanical statements.
    #
    my $filePath = RPS::Statement::Mechanical::CA::Text->StatementPathFromRunID( $self->{_runID} );
    if ( !-d $filePath ) {
        mkpath($filePath) or die RPS::Mechanical::Process::Exception->new("ERROR: Unable to create path $filePath: $!");
    }

    my $clientID = Common::RSApp::GetClientID();

    # Queue the CMRRA Agent report
    #
    my $jobArgs = RPS::Mechanical::CA::Job::CreateCMRRAAgentReportExcel->new(
        runID    => $self->{_runID},
        clientID => $clientID,
        filePath => $filePath,
    );
    my $job = $jobArgs->enqueue();

    # Queue the CMRRA summary report
    #
    $jobArgs = RPS::Mechanical::CA::Job::CreateCMRRASummaryReportExcel->new(
        runID    => $self->{_runID},
        clientID => $clientID,
        filePath => $filePath,
    );
    $job = $jobArgs->enqueue();

    $self->_report( "_createAgentReports - filePath=$filePath", 2 );
}

sub _createTextJobs {
    my ($self) = @_;

    my $filePath = RPS::Statement::Mechanical::CA::Text->StatementPathFromRunID( $self->{_runID} );
    if ( !-d $filePath ) {
        mkpath($filePath) or die RPS::Mechanical::Process::Exception->new("ERROR: Unable to create path $filePath: $!");
    }

    # Create a job for all text statements, and put it in the queue.
    #
    my $clientID = Common::RSApp::GetClientID();

    my $jobArgs = RPS::Mechanical::CA::Job::CreateTextStatements->new(
        runID    => $self->{_runID},
        clientID => $clientID,
        filePath => $filePath,
    );

    my $job = $jobArgs->enqueue();

    # We also need to create the CMRRA formatted text statements
    #
    # Create a job for all statements, and put it in the queue.
    #

    $jobArgs = RPS::Mechanical::CA::Job::CreateCMRRAStatements->new(
        runID    => $self->{_runID},
        clientID => $clientID,
        filePath => $filePath,
    );

    $job = $jobArgs->enqueue();

}

sub _createUnlicensedTrackLogEntry {
    my (
        $self, $track, $product, $upc, $productType,
        $missingShare, $totalOwed, $units, $year, $rate
    ) = @_;

    # Skip tracks that are mechanical exempt
    #
    return if ( $track->ca_mechanical_exempt );

    # Before we add this entry to the log, let's see if the track is covered
    # by any other licenses.
    my $regionsWithCA = $self->_findRegionsForCountryCode( 'CA' );
    my $productTypeID = $product->product_type_id;

    # Need to make sure we are only checking for ringtone license for ringtone sales.
    if ($productType eq 'RING') {
        $productTypeID = RPS::DB::Item::Product::kProductTypeRingtone;
    }

    # I guess we'll just ignore the $missingShare that is passed in, since it
    # doesn't factor in expired licenses.
    my $allPayorsShare = RPS::DB::Item::CATrackLicense->GetAllPayorsShare($track->track_id, $productTypeID, $regionsWithCA);

    # If the missing share is covered by other payors,
    # do not include this in the report.
    return if ($allPayorsShare >= 100);

    $missingShare = 100 - $allPayorsShare;

    # Apply the missing share percentage to the total owed.
    my $accrual  = Common::RSMath::round( $totalOwed * ( $missingShare / 100 ), 4 );

    my $album = $self->_getAlbum( $track->album_id );

    # Write the data to a tab-delimited file.
    # We'll then use mysql_import to load the table data at the end.
    my $staticDataPath = "/app/data/ca_mechanical_run/" . Common::RSApp::GetClientID() . "/" . $self->{_runID};
    my $outputDataPath = $staticDataPath . "/output";
    if ( !-d $outputDataPath ) {
        mkpath($outputDataPath) or die("Unable to create path $outputDataPath $!");
    }

    my $filename = $outputDataPath . '/ca_mechanical_run_unlicensed_track_log';
    open DATAFILE, ">> $filename" or die "ERROR: Unable to open $filename for writing: $!";
    print DATAFILE join("\t",
        $self->{_runID},
        $album->label_id,
        $track->track_id,
        $album->album_id,
        $product->product_id,
        $upc,
        $productType,
        $missingShare,
        $accrual,
        $year,
        $rate,
        $units
    ) . "\n";

    return;
}

sub _getAllOtherCommittedRuns {
    my ($self) = @_;

    my $runID = $self->{_runID};
    return RPS::DB::Item::CAMechanicalRun->GetOtherCommittedRuns($runID);
}

sub _getStatRates {
    my ($self) = @_;
    return RPS::DB::Item::StatRate->GetRatesCA();
}

sub _ringtoneStatRateType {
    my ($self) = @_;
    return RPS::DB::Item::StatRate::kCARingStatRateType;
}

sub _idFromTrackLicense {
    my ( $self, $trackLicense ) = @_;
    return $trackLicense->ca_track_license_id();
}

sub _trackLicenseFromID {
    my ( $self, $trackLicenseID ) = @_;

    return RPS::DB::Item::CATrackLicense->Lookup( ca_track_license_id => $trackLicenseID );
}

sub _defaultIssueStatRateID {

    my ( $self, $licenseType ) = @_;

    # This is the only possible value for CA (for now).
    #
    return 26;
}

sub _createReservePipelineReport {
    my ($self) = @_;

    $self->_report("*** adding reserve pipeline job to queue");
    my $clientID = Common::RSApp::GetClientID();

    my $jobArgs = RPS::Mechanical::CA::Job::CreateReservePipelineReport->new(
        runID    => $self->{_runID},
        clientID => $clientID,
    );
    my $job = $jobArgs->enqueue();
    $self->_report("*** done adding reserve pipeline job to queue!");
}

sub _createUnlicensedAccrualReport {
    my ($self) = @_;

    $self->_report("*** importing unlicensed accrual data");
    my $clientID = Common::RSApp::GetClientID();

    my $staticDataPath = "/app/data/ca_mechanical_run/" . Common::RSApp::GetClientID() . "/" . $self->{_runID};
    my $outputDataPath = $staticDataPath . "/output";
    my $filename = $outputDataPath . '/ca_mechanical_run_unlicensed_track_log';

    ## Let's make sure file exists before attempting to import it
    if ( -e $filename ) {
        my $fields = "ca_mechanical_run_id,label_id,track_id,album_id,product_id,upc_ean,product_type,unlicensed_share,accrual,year,rate,units";
        my @command = (
            'mysqlimport',                                               '--local',
            '--fields-optionally-enclosed-by=\"',                        "--columns=$fields",
            "--user=" . $Common::RSDB::CLIENT_DB{$clientID}->{username}, "--password=" . $Common::RSDB::CLIENT_DB{$clientID}->{password},
            "--host=" . $Common::RSDB::CLIENT_DB{$clientID}->{server},   $Common::RSDB::CLIENT_DB{$clientID}->{db_name},
            $filename,
        );
        $self->_report("COMMAND: " . join( ' ', @command ), 2 );

        # Catch the bloody error code!
        #
        my $status = system(@command);
        die "ERROR EXECUTING " . join( ' ', @command ) . " : ($?) $!" unless 0 == $status;

        $self->_report("*** done importing unlicensed accrual data!");
    }
}

1;
