#!/usr/bin/perl -Ilibs

use strict;
use warnings;

use JSON ();

use API::Controller ();

use ECS::DB::Schema;
use ECS::Config;
use ECS::Product::Pricing ();
use ECS::Brand ();
use ECS::LOOPS::ShippingCountryList ();
use ECS::Vendor ();
use ECS::Vendor::Log ();
use ECS::Vendor::Surcharge ();
use ECS::Vendor::AutoPO ();
use ECS::Vendor::TaxMethod ();
use ECS::Purchasing::PaymentTerms;
use ECS::Purchasing::PO::Shipping ();
use ECS::Purchasing::PO ();
use ECS::Stocking ();
use ECS::StockingOld ();
use ECS::DB::LogDbChanges ();
use ECS::Site;
use ECS::GP;
use ECS::Product;
use ECS::Messaging::Queue::AmazonSQS;
use ECS::SparkShipping::Message;

package API::VendorAdmin;

use Moose;
use Carp;

extends 'API::Controller';

__PACKAGE__->meta->make_immutable;

###############################################################################

sub createVendor {
    my $self = (shift)->new({'input' => shift || {}});

    my $duplicateMMVendorID = ECS::Vendor::checkMMVendorID({
        'dbh'           => $self->dbhr,
        'mm_VendorID'   => $self->input->{'mm_VendorID'},
        });

    if($duplicateMMVendorID) {
        $self->error("Duplicate MM Vendor ID!");
        return $self->result;
    }

    if (not $self->input->{'purchasingPaymentTermID'}) {
        $self->input->{'purchasingPaymentTermID'} =
            $ECS::Purchasing::PaymentTerms::DEFAULT_TERMS_ID;
    }
	
    my $vendor = ECS::Vendor->new({
        'DBH'   => $self->dbh,
        'data'  => $self->input,
        });

    # this creates the vendorID
    $vendor->save;
	
	# We need to retrieve the newly inserted record from the database because some of the fields may have been
	# stored with default values.  This could happen if the fields in the object were missing.
	my $insertVendor = ECS::Vendor->load({'DBH'      => $self->dbh,
										  'vendorID' => $vendor->{'vendorID'},
										 });
	
	# Set the changes so that a record for all values is inserted in the log table.
	my @dbFields = $ECS::DB::Class::models->{ ref $insertVendor }{ fields };
	my %changes = ();
	foreach my $field (@{$dbFields[0]}) {
		# Skip the field if it is the vendorID
		if ($field eq 'vendorID') {
			next
		};
		$changes{$field} = $insertVendor->{$field};
	}

	# Insert records into the vendorLog table.
	my $logData = {'dbh'        => $self->dbh,
				   'logSchema'  => 'VendorLog',
				   'keys'       => {'vendorID'   => $insertVendor->{'vendorID'}},
				   'employeeID' => $self->input->{'employeeID'},
				   'ipAddress'  => $self->{'ipAddress'},
				   'mode'       => 'Insert',
				   'changes'    => \%changes,
				  };

	#ECS::Vendor::LogChanges::logVendorChanges($logData);
	ECS::DB::LogDbChanges::logChanges($logData);

    # update the vendor emails
    if($self->input->{'emailArray'}) {
        my $emails = [];
        eval {
            $emails = JSON::from_json($self->input->{'emailArray'});
        };

        ECS::Vendor::updateEmails({
            'dbh'       => $self->dbh,
            'vendorID'  => $vendor->{'vendorID'},
            'emails'    => $emails,
            });
    }

    ECS::Vendor::saveImportNotes({
        'dbh'           => $self->dbh,
        'vendorID'      => $vendor->{'vendorID'},
        'importNotes'   => $self->input->{'importNotes'},
        });

    my $pricingImportBrands = {};
    eval {
        $pricingImportBrands = JSON::from_json(
            $self->input->{'pricingImportVendorBrands'});
    };

    ECS::Vendor::saveImportBrand({
        'dbh'                   => $self->dbh,
        'vendorID'              => $vendor->{'vendorID'},
        'pricingImportBrands'   => $pricingImportBrands,
        });
	
	my $poShippingServices = [];
    eval {
        $poShippingServices = JSON::from_json(
            $self->input->{'poShippingServices'});
    };

    ECS::Vendor::saveShippingServices({
        'dbh'                   => $self->dbh,
        'vendorID'              => $vendor->{'vendorID'},
        'poShippingServices'   => $poShippingServices,
        'shippingType'		=> 'PO',
	});
    
    my $directShippingServices = [];
    eval {
	$directShippingServices = JSON::from_json(
		$self->input->{'directShippingServices'});
    };

    ECS::Vendor::saveShippingServices({
        'dbh'                   => $self->dbh,
        'vendorID'              => $vendor->{'vendorID'},
        'poShippingServices'   => $directShippingServices,
        'shippingType'		=> 'DIRECT',
	});

    $self->data->{'redirect'} = sprintf("?action=getVendor&vendorID=%d",
        $vendor->{'vendorID'});

    return $self->result;
}

###############################################################################
# get all of the data required to display the "Edit Vendor" screen

sub getVendor {
    my $self = (shift)->new({'input' => shift || {}});
	
    $self->data->{'templates'} = [ "edit.html" ];

    $self->data->{'vendor'} = ECS::Vendor::getDetails({
        'dbh'       => $self->dbhr,
        'vendorID'  => $self->input->{'vendorID'},
        });

    unless(defined $self->data->{'vendor'}) {
        $self->error("Vendor Not Found!");
        return $self->result;
    }

		{
			# dropship surcharges
			local $self->data->{vendor}{DBH} = $self->dbhr;
			my $dsSurcharge = ECS::Vendor::surcharge( $self->data->{vendor} );
			if ($dsSurcharge) {
				$self->data->{vendor}{dsSurcharge} = $dsSurcharge->toHash;
			} else {
				$self->data->{vendor}{dsSurcharge} = {};
			}
		}

	$self->data->{vendor}->{sites} = ECS::Site::getAll();
	
	$self->data->{'vendor'}->{'shipFreeSites'} = ECS::Vendor::getShipFreeSites({
        'dbh'       => $self->dbhr,
        'vendorID'  => $self->input->{'vendorID'},
        });
		
		{
			# autoPO options
			local $self->data->{vendor}{DBH} = $self->dbhr;
			my $autoPO = ECS::Vendor::autoPO( $self->data->{vendor} );
			if ($autoPO) {
				$self->data->{vendor}{autoPO} = $autoPO->toHash;
			} else {
				$self->data->{vendor}{autoPO} = {};
			}
		}

    $self->data->{'vendor'}->{'emails'} = ECS::Vendor::getEmails({
        'dbh'       => $self->dbhr,
        'vendorID'  => $self->input->{'vendorID'},
        });
        
    $self->data->{'vendor'}->{'orderRules'} = ECS::Vendor::getOrderReviewRules({
        'dbh'       => $self->dbhr,
        'vendorID'  => $self->input->{'vendorID'},
        });
    
    $self->data->{'vendor'}->{'orderRulesLog'} = ECS::Vendor::getOrderReviewRulesLog({
        'dbh'       => $self->dbhr,
        'vendorID'  => $self->input->{'vendorID'},
        });
    
    $self->data->{'vendor'}->{'pricingImportBrands'}
        = ECS::Vendor::getPricingImportBrands({
            'dbh'       => $self->dbhr,
            'vendorID'  => $self->input->{'vendorID'},
            });

    $self->data->{'vendor'}->{'summary'} = ECS::Vendor::getSummary({
        'dbh'       => $self->dbhr,
        'vendorID'  => $self->input->{'vendorID'},
        })->[0];
     	
    $self->data->{'vendor'}->{'poShippingServices'} = ECS::Vendor::getShippingServices({
        'dbh'       => $self->dbhr,
        'vendor'    => $self->data->{'vendor'},
	'shippingType' => 'PO',
        });
    $self->data->{'vendor'}->{'directShippingServices'} = ECS::Vendor::getShippingServices({
        'dbh'       => $self->dbhr,
        'vendor'    => $self->data->{'vendor'},
		'shippingType' => 'DIRECT',
        });
    $self->data->{'vendor'}->{'wipBufferExclusion'} = ECS::Vendor::getVendorWIPBufferExclusion({
		'dbh' => $self->dbhr,
		'vendorID' => $self->input->{'vendorID'}
	});

    $self->data->{'brandList'} = ECS::Brand::fetchBrandIDs({
        'DBH'   => $self->{'dbhr'},
        });

    $self->data->{'countryList'} = ECS::LOOPS::ShippingCountryList::loop({
        'DBH'   => $self->dbhr,
        });

		# tax methods
		$self->data->{taxMethodList} = ECS::Vendor::TaxMethod->getAll({ 
				DBH		=> $self->dbhr
		});

    $self->data->{'pricingRuleIDs'} = ECS::Vendor::getPricingRuleIDs({
        'dbh'       => $self->dbhr,
        'vendorID'  => $self->input->{'vendorID'},
        });

	$self->data->{'poShippingServices'} = ECS::Purchasing::PO::Shipping::getServices({
        'dbh'       => $self->dbhr,
        });
	$self->data->{'directShippingServices'} = ECS::Purchasing::PO::Shipping::getDirectServices({
        'dbh'       => $self->dbhr,
        });	
    $self->data->{'paymentTerms'} = ECS::Purchasing::PaymentTerms::getActiveTerms({
        'dbh' => $self->dbhr,
        });

    # The Payment Terms saved on the Purchase Receipt could have been deactivated,
    # so retrieve this option and add it to the beginning of the list so 
    # that historical information can be displayed accurately
    unshift(@{ $self->data->{'paymentTerms'} }, values %{
        ECS::Purchasing::PaymentTerms::getTermsByID({
            'dbh' => $self->dbhr,
            'purchasingPaymentTermIDs' =>
                [$self->data->{'vendor'}->{'purchasingPaymentTermID'}],
        })
    });

	$self->data->{'vendorStockingRules'} = ECS::Stocking::getAssociatedStockingRules({
        'dbh'       => $self->dbhr,
        'vendorID'  => $self->input->{'vendorID'}
        });
	$self->data->{'allStockingRules'} = ECS::Stocking::getAllStockingRules({
        'dbh'       => $self->dbhr,
        'vendorID'  => $self->input->{'vendorID'}
        });
	$self->data->{'vendorStockingRulesChangeLog'} = ECS::Stocking::getStockingRuleChangeLog({
        'dbh'       => $self->dbhr,
        'vendorID'  => $self->input->{'vendorID'}
        });
	$self->{'data'}->{'brands'} = ECS::Vendor::getVendorBrands({
		'dbh' => $self->dbhr,
		'vendorID' => $self->input->{'vendorID'}
	});
	$self->data->{'changeLog'} = ECS::Vendor::Log::getLog({
        'dbh'       => $self->dbhr,
        'vendorID'  => $self->input->{'vendorID'}
        });
	
	$self->data->{'allVendors'} = ECS::Vendor::getAllVendorsName({
        'DBH'       => $self->dbhr,
    });

    $self->data->{'currencies'} = $self->_getCurrencies();
    $self->data->{'cancel_schedule'} = $self->_get_cancel_schedule;

    return $self->result;
}

###############################################################################

sub editVendor {
    my $self = (shift)->new({'input' => shift || {}});

	my $prefixName = "shipFree";
	my $sites = ECS::Site::getAll();

    my $vendor = ECS::Vendor->load({
        'DBH'       => $self->dbh,
        'vendorID'  => $self->input->{'vendorID'},
        });

    if (not $self->input->{'purchasingPaymentTermID'}) {
        $self->input->{'purchasingPaymentTermID'} =
            $ECS::Purchasing::PaymentTerms::DEFAULT_TERMS_ID;
    }

	if (not $self->input->{'overrideMarkupMethod'}) {
		 $self->input->{'overrideMarkupMethod'} = 'NO';
	}
	
	if (not $self->input->{'overrideTargetMarkup'}) {
		 $self->input->{'overrideTargetMarkup'} = 'NO';
	}

	if (not $self->input->{'importNewProductVendorID'}) {
		 $self->input->{'importNewProductVendorID'} = undef;
	}

	if (not $self->input->{warehouseHandleTimeCheck}) {
		$self->input->{'warehouseHandleTime'} = undef;
	}

    $self->input->{'etaLead'} ||= undef;
    $self->input->{'acceptsBackOrders'} ||= 0;
    $self->input->{'autocalculate'} ||= 0;
    $self->input->{'warranty'} ||= 0;
    $self->input->{'attemptCancel'} ||= 0;
    $self->input->{'attemptCancelCap'} ||= 0;
    $self->input->{'returnOnReceipt'} ||= 0;
    $self->input->{'networkInventory'} ||= 0;
    $self->input->{'sparkVendorID'} ||= undef;
    $self->input->{'binaryNetworkInventory'} ||= 0;

	#Getting original values for shipFree
	my $shipFreeSites = ECS::Vendor::getShipFreeSites({
        'dbh'       => $self->dbh,
        'vendorID'  => $self->input->{'vendorID'},
        });

    # Figure out if anything changed in the vendor and prepare the data for logging.
	# The logging will be done by Vendor::LogChanges::logVendorChanges, which is looking
	# for orginal and changed values in the record.  It will handle logging the old and new
	# values.
    my %changed = ();
	my %changesForLog = ();
	my %originalValues = ();

    for my $col (keys %{$vendor}) {

        if(exists $self->input->{$col}
            and (defined $vendor->{$col} xor defined $self->input->{$col}
                or $vendor->{$col} ne $self->input->{$col})
            ) {
			# Set the values for logging.
			$originalValues{$col} = $vendor->{$col};
			$changesForLog{$col} = $self->input->{$col};
			
			# Put the updated information in the vendor object.
            $vendor->{$col} = $self->input->{$col};
            $changed{$col} = 1;
        }
    }

	foreach my $col (keys %{$shipFreeSites}) {
		my $siteObject = ECS::Site->fromID( $col );
		
        if(exists $self->input->{$prefixName.$siteObject->name}
            and (defined $shipFreeSites->{$col}->{shipFree} xor defined $self->input->{$prefixName.$siteObject->name}
                or $shipFreeSites->{$col}->{shipFree} ne $self->input->{$prefixName.$siteObject->name})
            ) {
			# Set the values for logging.
			$originalValues{$prefixName.$siteObject->name} = $shipFreeSites->{$col}->{shipFree};
			$changesForLog{$prefixName.$siteObject->name} = $self->input->{$prefixName.$siteObject->name};
			
            $changed{$col} = 1;
        }
    }
	
		# save vendor ds surcharges (if changed)
		my $dsSurcharge = $vendor->surcharge();
		for my $field (keys %{ $dsSurcharge->toHash() }) {
			next if $field eq $ECS::Vendor::Surcharge::PRIMARY_KEY;
			my $inputField = 'dsSurcharge' . ucfirst $field;
			if (exists $self->input->{$inputField}
            and (defined $dsSurcharge->{$field} xor defined $self->input->{$inputField}
                or $dsSurcharge->$field ne $self->input->{ $inputField })) {
				$originalValues{$inputField} = $dsSurcharge->$field;
				$changesForLog{$inputField} = $self->input->{ $inputField };
				$dsSurcharge->$field( $self->input->{ $inputField } || 0 );
				$changed{ surcharge }++;
			}
		}
		if ($changed{ surcharge }) {
			$vendor->surcharge( $dsSurcharge );
		}

		my %shipFreeSites = ();
		foreach my $site (@{$sites}){
			$shipFreeSites{$site->siteID}->{'shipFree'} = $self->{input}->{$prefixName.$site->name};
			$shipFreeSites{$site->siteID}->{'siteID'} = $site->siteID;
		}
		#Update shipFree into vendorSiteShipping table.
		 ECS::Vendor::updateShipFreeBySite({
			'dbh'        => $self->dbh,
			'shipFreeSites' => \%shipFreeSites,
			'vendorID' => $self->{input}->{vendorID}
		 });

		# save vendor autoPO (if changed)
		my $autoPO = $vendor->autoPO();
		for my $field (keys %{ $autoPO->toHash() }) {
			next if $field eq $ECS::Vendor::AutoPO::PRIMARY_KEY;
			my $inputField = 'autoPO' . $field;
			if (exists $self->input->{$inputField}
            and (defined $autoPO->$field xor defined $self->input->{$inputField}
                or $autoPO->$field ne $self->input->{ $inputField })){
				$originalValues{$inputField} = $autoPO->$field;
				$changesForLog{$inputField} = $self->input->{ $inputField };
				$autoPO->$field( $self->input->{ $inputField });
				$changed{ autoPO }++;
			}
		}
		if ($changed{ autoPO }) {
			$vendor->autoPO( $autoPO );
		}

        # save the wipBufferExclusion if changed
        if( exists $self->input->{'wipBufferExclusion'}) {
            my $originalBufferExclusion =  ECS::Vendor::getVendorWIPBufferExclusion({
                'dbh'        => $self->dbh,
                'vendorID'   => $self->{input}->{vendorID},    
            });

            # we didn't actually change anything (even though the data gets serialized in the form) so don't log this as a change
            unless ($self->input->{'wipBufferExclusion'} eq $originalBufferExclusion) {
                $originalValues{wipBufferExclusion} = $originalBufferExclusion;
                $changesForLog{wipBufferExclusion} = $self->input->{'wipBufferExclusion'};
                $changed{wipBufferExclusion} = 1;

                my $saved = ECS::Vendor::updateVendorWIPBufferExclusion({
                    'dbh'        => $self->dbh,
                    'vendorID'   => $self->input->{vendorID},
                    'exclude'    => $self->input->{'wipBufferExclusion'},
                }); 

                croak "Unable to save Vendor WIP Buffer Exclusion" unless $saved;
            }
        }
		
    # Save and log any changes to the vendor.
    if(keys %changed > 0) {
		$vendor->save;
		
		###Ticket: 12167; update productVendorShipping table also for these columns###########
		if(scalar grep {$changed{$_}} qw/dropShip/)
		{
			my $sqlUpdateproductVendorShipping = qq{update productVendorShipping set } . join(', ', map {"$_ = $vendor->{$_}"} grep {$changed{$_}} qw/dropShip/) . qq{ where vendorID = ?};
			my $sthUpdateproductVendorShipping = $self->dbh->prepare($sqlUpdateproductVendorShipping);
			$sthUpdateproductVendorShipping->execute($self->{input}->{vendorID});
		}

		# Insert records into the vendorLog table.
		my $logData = {'dbh'        => $self->dbh,
					   'logSchema'  => 'VendorLog',
					   'keys'       => {'vendorID'   => $vendor->{'vendorID'}},
					   'employeeID' => $self->input->{'employeeID'},
					   'ipAddress'  => $self->{'ipAddress'},
					   'mode'       => 'Update',
					   'original'   => \%originalValues,
					   'changes'    => \%changesForLog,
			  };

		ECS::DB::LogDbChanges::logChanges($logData);
    }

    # update the date modified?
    my $update_date_modified = 0;

    # apply the map pricing if it has changed, and if we have enough data
    if($self->input->{'updateProductPricing'}
        and $vendor->{'markup_method'}
        and $vendor->{'target_markup'}
        ) {
        ECS::Vendor::updateProductPricing({
            'dbh'           => $self->dbh,
            'vendorID'      => $self->input->{'vendorID'},
            'markup_method' => $self->input->{'markup_method'},
            'target_markup' => $self->input->{'target_markup'},
            });
        $update_date_modified = 1;
    }
    if ($changed{dropShip}) {
        $update_date_modified = 1;
    }

    # fields which may result in a changed ETA for products
    my @changeETA = qw(
        lead
        dsLead
        poSameDay
        poNextDay
        dsSameDay
        dsNextDay
		dropShip
        );
    for my $field (@changeETA) {
        if($changed{$field}) {
            ECS::Cache::ProductCache::deleteProductInfoByVendor({
                'DBH'       => $self->dbh,
                'vendorID'  => $self->input->{'vendorID'},
		        update_date_modified => $update_date_modified,
                });
            last;
        }
    }

    #ECS::Product::productModifiedByVendor({
    #        'dbh'       => $self->dbh,
    #        'vendorID'  => $self->input->{'vendorID'},
    #}) if $update_date_modified;

    # run the old RSL
    if($self->input->{'oldRSL'}) {
		ECS::StockingOld::calcStockingByVendor({
			'DBH'       	=> $self->dbh,
			'mm_VendorID'	=> $vendor->{'mm_VendorID'}
		});
    }

    # run the new RSL
    if($self->input->{'newRSL'}) {
		ECS::Stocking::calcStockingByVendor({
            'mm_VendorID'	=>	$vendor->{'mm_VendorID'},
		});
    }

    # run the original RSL (override product-based rules)
    if($self->input->{'RSLOverride'}) {
        ECS::Stocking::calcStockingByVendor({
            'mm_VendorID'                   => $vendor->{'mm_VendorID'},
            'overrideProductStockingRules'   => 1
        });
    }

    # update the vendor emails
    if($self->input->{'emailArray'}) {
        my $emails = [];
        eval {
            $emails = JSON::from_json($self->input->{'emailArray'});
        };

        ECS::Vendor::updateEmails({
            'dbh'       => $self->dbh,
            'vendorID'  => $vendor->{'vendorID'},
            'emails'    => $emails,
            });
    }
    
	# Update all the vendor stocking rules
	if($self->input->{'vendorStockingRules'}) {
	   my $vendorStockingRules = [];
		eval {
            $vendorStockingRules = JSON::from_json($self->input->{'vendorStockingRules'});
        };

		ECS::Stocking::updateAllAssociatedStockingRules({
			'dbh'		=> $self->dbh,
            'vendorID'	=> $vendor->{'vendorID'},
            'rules'    	=> $vendorStockingRules
			});
    }

    # Update all order review rules
    if($self->input->{'arrRules'}) {
        my $rules = [];
        eval {
            $rules = JSON::from_json($self->input->{'arrRules'});
        };
        
        ECS::Vendor::updateOrderReviewRules({
            'dbh'        => $self->dbh,
            'vendorID'   => $vendor->{'vendorID'},
			'employeeID' => $self->input->{'employeeID'},
            'rules'      => $rules,
            });
    }
    
    ECS::Vendor::saveImportNotes({
        'dbh'           => $self->dbh,
        'vendorID'      => $vendor->{'vendorID'},
        'importNotes'   => $self->input->{'importNotes'},
        });

    my $pricingImportBrands = {};
    eval {
        $pricingImportBrands = JSON::from_json(
            $self->input->{'pricingImportVendorBrands'});
    };

    ECS::Vendor::saveImportBrand({
        'dbh'                   => $self->dbh,
        'vendorID'              => $vendor->{'vendorID'},
        'pricingImportBrands'   => $pricingImportBrands,
        });
	
	my $poShippingServices = [];
    eval {
        $poShippingServices = JSON::from_json(
            $self->input->{'poShippingServices'});
    };

    ECS::Vendor::saveShippingServices({
        'dbh'                   => $self->dbh,
        'vendorID'              => $vendor->{'vendorID'},
        'poShippingServices'   => $poShippingServices,
		'shippingType'		=> 'PO',
        });
	
	my $directShippingServices = [];
    eval {
	$directShippingServices = JSON::from_json(
		$self->input->{'directShippingServices'});
    };

    ECS::Vendor::saveShippingServices({
        'dbh'                   => $self->dbh,
        'vendorID'              => $vendor->{'vendorID'},
        'poShippingServices'   => $directShippingServices,
        'shippingType'		=> 'DIRECT',
	});

    $self->_save_cancel_schedule;

	# enqueue a vendor action in the GP Queue (for vendor information and/or LC vendor
	# details
	ECS::GP::enqueue {
		action		=> 'vendor',
		vendorID	=> $vendor->{vendorID},
	};

    $self->data->{'redirect'} = sprintf(
        "?action=getVendor&vendorID=%d&message=%s",
        $vendor->{'vendorID'},
        "Your changes have been saved.",
        );

    return $self->result;
}

###############################################################################

sub _get_cancel_schedule {
    my ( $self ) = @_;
    
    my $sql = q{
        SELECT *
        FROM vendorCancelSchedule
        WHERE vendorID = ?
    };
    my $sth = $self->dbhr->prepare( $sql );
    $sth->execute( $self->input->{ 'vendorID' } );
    return $sth->fetchall_arrayref( {} );
}

###############################################################################

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

    return unless $self->input->{ 'new_cancel_schedule_days' };
    return unless $self->input->{ 'new_cancel_schedule_time' };

    my $days_string = $self->input->{ 'new_cancel_schedule_days' };
    my @enabled_days = grep { m/^[a-z]+$/ } split "\0", $days_string;
    my $sql_enabled_days = join( ",", map { "$_ = 1" } @enabled_days );

    my $sql = qq{
        INSERT INTO vendorCancelSchedule
        SET vendorID    = ?,
            minuteToRun = ?,
            $sql_enabled_days
    };
    my $sth = $self->dbh->prepare( $sql );
    $sth->execute(
        $self->input->{ 'vendorID' },
        $self->input->{ 'new_cancel_schedule_time' },
    );

    return;
}

###############################################################################

sub deleteVendorCancelSchedule {
    my $self = (shift)->new({'input' => shift || {}});

    my $sql = q{
        DELETE FROM vendorCancelSchedule
        WHERE vendorCancelScheduleID = ?
    };
    my $sth = $self->dbh->prepare( $sql );
    $sth->execute( $self->input->{ 'vendorCancelScheduleID' } );

    $self->data->{ data }{ success } = 1;
    $self->data->{dataType} = 'json';
    return $self->result;
}

###############################################################################

sub newVendor {
    my $self = (shift)->new({'input' => shift || {}});

	$self->data->{'vendor'}->{'poSameDayCutOffTime'} = "16:30:00";
	$self->data->{'vendor'}->{'poNextDayCutOffTime'} = "16:30:00";
	$self->data->{'vendor'}->{'poCutOffTime'}        = "16:30:00";
	
	$self->data->{'vendor'}->{'dsSameDayCutOffTime'} = "16:30:00";
	$self->data->{'vendor'}->{'dsNextDayCutOffTime'} = "16:30:00";
	$self->data->{'vendor'}->{'dsCutOffTime'}        = "16:30:00";
	
	# Ensure that the country list is available for the location dropdowns.
	$self->data->{'countryList'} = ECS::LOOPS::ShippingCountryList::loop({
        'DBH'   => $self->dbh,
    });

    $self->data->{'templates'} = [ "edit.html" ];

    return $self->result;
}

###############################################################################

sub vendorIndex {
    my $self = (shift)->new({'input' => shift || {}});

    $self->data->{'templates'} = [ "index.html" ];

    $self->data->{'vendors'} = ECS::Vendor::getSummary({
        'dbh'   => $self->dbhr,
        });

    return $self->result;
}

###############################################################################

sub editTaxMethod {
    my $self = (shift)->new({'input' => shift || {}});

		$self->data->{templates} = [ "taxMethods.html" ];
		my $taxMethods = ECS::Vendor::TaxMethod->getAll({ DBH => $self->dbh });
		my $ID = ($self->input->{taxMethodID}||0);
		if (@$taxMethods && !$ID) {
			$ID = $taxMethods->[0]{taxMethodID};
		}
		$_->{selected} = !!( $_->{taxMethodID} == $ID ) for @$taxMethods;
				
				
		$self->data->{ taxMethods } = $taxMethods;
		$self->data->{ _no_header } = 1;
		
		return $self->result;	

}

###############################################################################
sub listTaxMethods {
    my $self = (shift)->new({'input' => shift || {}});

		# return all the methods
		my $taxMethods = ECS::Vendor::TaxMethod->getAll({ DBH => $self->dbhr });
		my $ID = 0;
		if (@$taxMethods && !$ID) {
			$ID = $taxMethods->[0]{taxMethodID};
		}
		$_->{selected} = !!( $_->{taxMethodID} == $ID ) for @$taxMethods;
				
		$self->data->{ data }{ taxMethods } = $taxMethods;
		$self->data->{ data }{ success } = 1;
		$self->data->{ _no_header } = 1;
		$self->data->{dataType} = 'json';
		
		return $self->result;	

}
sub updateTaxMethod {
    my $self = (shift)->new({'input' => shift || {}});

		my $id = $self->input->{taxMethodID};
		unless ($id) {
        $self->error("taxMethodID must be a positive integer");
        return $self->result;
		}
		my $record = eval { 
        JSON::from_json($self->input->{'method'})
		};
		unless ($record) {
        $self->error("Invalid update data");
        return $self->result;
		}
		
		my ($tm) = ECS::Vendor::TaxMethod->search({
			DBH	=> $self->dbh,
			search => {
				taxMethodID => $id 
			}
		});
		unless ($tm) {
        $self->error("Can't update a non-existent Tax Method");
        return $self->result;
		}

		if ($record->{method}) {
			$record->{method} = eval { 
				JSON::to_json( $record->{method} );
			};
		}
		unless ($record->{method}) {
        $self->error("Invalid update data");
        return $self->result;
		}
		$tm->$_( $record->{$_} ) for qw/name description method/;
		eval { 
			$tm->save;
			1;
		} or do {
        $self->error($@);
        return $self->result;
		};

		# return all the methods
		my $taxMethods = ECS::Vendor::TaxMethod->getAll({ DBH => $self->dbh });
		my $ID = ($self->input->{taxMethodID}||0);
		if (@$taxMethods && !$ID) {
			$ID = $taxMethods->[0]{taxMethodID};
		}
		$_->{selected} = !!( $_->{taxMethodID} == $ID ) for @$taxMethods;
				
		$self->data->{ data }{ taxMethods } = $taxMethods;
		$self->data->{ data }{ selectedID } = $ID if $ID;
		$self->data->{ data }{ success } = 1;
		$self->data->{ _no_header } = 1;
		$self->data->{dataType} = 'json';
		
		
		
		return $self->result;	

}

sub addTaxMethod {
    my $self = (shift)->new({'input' => shift || {}});

		my $record = eval { 
        JSON::from_json($self->input->{'method'})
		};
		unless ($record) {
        $self->error("Invalid insert data");
        return $self->result;
		}
		
		my $tm = ECS::Vendor::TaxMethod->new({ 
			dbh	=> $self->dbh,
		});
		unless ($tm) {
        $self->error("Can't create new method");
        return $self->result;
		}

		if ($record->{method}) {
			$record->{method} = eval { 
				JSON::to_json( $record->{method} );
			};
			unless ($record->{method}) {
				$self->error("Can't create new method") unless $record->{method};
				return $self->result;
			}
		} else {
			$record->{ method } = JSON::to_json( [{ percent => "" }] );
		}
	
		if ($record->{methodName}) {
			@$record{ qw/name description/} = delete @$record{ qw/methodName methodDescription/ };
		}
		$tm->$_( $record->{$_} ) for qw/name description method/;
		eval { 
			$tm->save;
			1;
		} or do {
        $self->error($@);
        return $self->result;
		};

		my $ID = $tm->taxMethodID;
		# return all the methods
		my $taxMethods = ECS::Vendor::TaxMethod->getAll({ DBH => $self->dbh });
		if (@$taxMethods && !$ID) {
			$ID = $taxMethods->[0]{taxMethodID};
		}
		$_->{selected} = !!( $_->{taxMethodID} == $ID ) for @$taxMethods;
				
		$self->data->{ data }{ taxMethods } = $taxMethods;
		$self->data->{ data }{ selectedID } = $ID;
		$self->data->{ data }{ success } = 1;
		$self->data->{ _no_header } = 1;
		$self->data->{dataType} = 'json';
		
		return $self->result;	

}

sub deleteTaxMethod {
    my $self = (shift)->new({'input' => shift || {}});
		my $id = $self->input->{taxMethodID};
		unless ($id) {
        $self->error("taxMethodID must be a positive integer");
        return $self->result;
		}

		my ($tm) = ECS::Vendor::TaxMethod->search({
			DBH	=> $self->dbh,
			search => {
				taxMethodID => $id 
			}
		});
		unless ($tm) {
        $self->error("Can't delete a non-existent Tax Method");
        return $self->result;
		}

		eval { 
			$tm->delete;
			1;
		} or do {
        $self->error($@);
        return $self->result;
		};

		my $ID = 0;
		# return all the methods
		my $taxMethods = ECS::Vendor::TaxMethod->getAll({ DBH => $self->dbh });
		if (@$taxMethods && !$ID) {
			$ID = $taxMethods->[0]{taxMethodID};
		}
		$_->{selected} = !!( $_->{taxMethodID} == $ID ) for @$taxMethods;
				
		$self->data->{ data }{ taxMethods } = $taxMethods;
		$self->data->{ data }{ selectedID } = $ID;
		$self->data->{ data }{ success } = 1;
		$self->data->{ _no_header } = 1;
		$self->data->{dataType} = 'json';
		
		return $self->result;	

}

sub runNetworkInventory {
    my $self = (shift)->new({ 'input' => shift || {} });
    $self->data->{'dataType'} = "json";

    my $vendorID = $self->input->{'vendorID'};

    if ( not $vendorID ) {
        $self->data->{'data'}->{'success'} = 0;
        $self->data->{'data'}->{'message'} = "vendorID required";
        return $self->result;
    }

    my $syncQueueEnv = "SPARK_SHIPPING_SYNC_SQS_QUEUE_URL";
    my $syncQueueURI = $ECS::Config::c { $syncQueueEnv }; #$ENV{ $syncQueueEnv };

    if ( not $syncQueueURI ) {
        $self->data->{'data'}->{'success'} = 0;
        $self->data->{'data'}->{'message'} = "Missing ENV config: ${syncQueueEnv}";
        return $self->result;
    }

    my $syncQueue = ECS::Messaging::Queue::AmazonSQS->new({
        'accessKey' => $ECS::Config::c{'sqsAccessKey'},
        'secretKey' => $ECS::Config::c{'sqsSecretKey'},
        'queueUri' => $syncQueueURI,
        'serializer' => "JSON",
    });

    my $message = ECS::SparkShipping::Message->new({
        'vendorID' => $vendorID,
    });

    $syncQueue->push( $message );

    $self->data->{'data'}->{'success'} = 1;

    return $self->result;
}

sub getNetworkInventoryLog {
    my $self = (shift)->new({ 'input' => shift || {} });
    $self->data->{'dataType'} = "json";

    my $vendorID = $self->input->{'vendorID'};
    if ( not $vendorID ) {
        $self->data->{'data'}->{'success'} = 0;
        $self->data->{'data'}->{'message'} = "vendorID required";
        return $self->result;
    }

    my $limit = $self->input->{'limit'};
    if ( not $limit ) {
        $self->data->{'data'}->{'success'} = 0;
        $self->data->{'data'}->{'message'} = "limit required";
        return $self->result;
    }

    my $afterDateTime = $self->input->{'afterDateTime'}; # optional

    my $having = "";
    if ( $afterDateTime ) {
        $having = "HAVING dtStart < ?";
    }

    my $sthTasks = $self->dbhr->prepare(
        "SELECT
            taskID,
            MIN( tsLogged ) AS dtStart,
            MIN( UNIX_TIMESTAMP( tsLogged ) ) AS dtStartEpoch
        FROM
            vendorInventoryLog
        WHERE
            vendorID = ?
            AND statType = 'dtStart'
        GROUP BY
            taskID
        ${having}
        ORDER BY
            tsLogged DESC
        LIMIT ?
        "
    );

    $sthTasks->execute(
        $vendorID,
        $afterDateTime ? $afterDateTime : (),
        $limit,
    );

    if ( $sthTasks->rows == 0 ) {
        $self->data->{'data'}->{'success'} = 1;
        $self->data->{'data'}->{'tasks'} = [];
        return $self->result;
    }

    my %tasksByID;
    while ( my $task = $sthTasks->fetchrow_hashref ) {
        $task->{'stats'} = {
            'SyncSpark' => {},
            'Apply' => {},
        };
        $tasksByID{ $task->{'taskID'} } = $task;
    }

    my $sthStats = $self->dbhr->prepare(
        "SELECT
            taskID,
            action,
            UNIX_TIMESTAMP( tsLogged ) AS tsLoggedEpoch,
            statType,
            statValue
        FROM
            vendorInventoryLog
        WHERE
            vendorID = ?
            AND taskID IN ("
                .join( ",", ( "?" ) x keys %tasksByID )
            .")
        "
    );

    $sthStats->execute(
        $vendorID,
        keys %tasksByID,
    );

    while ( my $stat = $sthStats->fetchrow_hashref ) {
        my $taskID = delete( $stat->{'taskID'} );
        my $task = $tasksByID{ $taskID };
        $task->{'stats'}
            ->{ $stat->{'action'} }
            ->{ $stat->{'statType' } }
                = $stat;
    }

    my @tasks = sort {
        $b->{'dtStartEpoch'} <=> $a->{'dtStartEpoch'}
    } values %tasksByID;

    $self->data->{'data'}->{'success'} = 1;
    $self->data->{'data'}->{'tasks'} = \@tasks;

    return $self->result;
}

sub _getCurrencies {
    my $self = shift;

    my $schema = ECS::DB::Schema::getSchema({
        'dbh' => $self->dbhr,
        'schema' => "CurrencyExchange",
    });

    my $resultSet = $schema->search({
        'me.base' => "USD",
    });

    my @currencies;
    while ( my $row = $resultSet->next ) {
        push( @currencies, {
            'currency' => $row->currency,
        } );
    }

    return \@currencies;
}

1;
