use strict;
use warnings;
use English qw( -no_match_vars );
use version; my $VERSION = qv('0.01');
use WWW::Salesforce::Simple;
use Getopt::Long;
use Text::CSV_XS;
use Scalar::Util qw( looks_like_number );
my %cli_options = command_line();
my %quantity_of = get_products_from( $cli_options{csv} );
my $sf = {};
eval {
$sf = WWW::Salesforce::Simple->new(
'username' => $cli_options{ user },
'password' => $cli_options{ pass },
);
};
die $EVAL_ERROR if ( $EVAL_ERROR );
die "Error: user $cli_options{user} could not login to Salesforce.com"
if( !$sf );
if ( $cli_options{ delete } ) {
salesforce_delete_products_from( $sf, $cli_options{ opportunity } );
}
for my $product_code ( keys %quantity_of ) {
my $salesforce_product_ref =
salesforce_get_product_wth_code($sf, $product_code);
next if ( !$salesforce_product_ref->{ IsActive } );
if ( !$salesforce_product_ref->{ Pricebook2Id } ) {
print "Product $product_code not found in Salesforce.com. Skipping \n";
next;
}
print "Adding - SKU: $salesforce_product_ref->{ProductCode}"
. ", Quantity: $quantity_of{$product_code}\n";
eval {
$sf->create(
type=> "OpportunityLineItem",
OpportunityId => $cli_options{ opportunity },
Quantity => $quantity_of{ $product_code },
Description => $salesforce_product_ref->{ Name },
UnitPrice => $salesforce_product_ref->{ UnitPrice },
PricebookEntryId => $salesforce_product_ref->{ Id }->[0],
);
};
die $EVAL_ERROR if( $EVAL_ERROR );
}
print "Done\n";
sub get_products_from {
my ($file_name ) = @_;
my @rows;
my %products;
my $csv = Text::CSV_XS->new ({ binary => 1 }) or
die "Cannot use CSV: ".Text::CSV->error_diag ();
open my $fh, "<:encoding(utf8)", $file_name
or die "$file_name: $!";
while (my $row = $csv->getline ($fh)) {
if ( looks_like_number $row->[1] ) {
$products{ $row->[0] } = $row->[1];
}
else {
die "$file_name: Bad quantity $row->[1]\n";
}
}
$csv->eof or $csv->error_diag ();
close $fh;
return %products;
}
sub salesforce_get_product_wth_code {
my ($sforce, $product_sku) = @_;
my $query =
qq{SELECT Id, Name, Pricebook2Id, Product2Id, UnitPrice, IsActive, }
. qq{UseStandardPrice, CreatedDate, CreatedById, LastModifiedDate, }
. qq{LastModifiedById, SystemModstamp, ProductCode, IsDeleted }
. qq{FROM PricebookEntry WHERE ProductCode = '$product_sku'};
my $query_result_ref;
eval {
$query_result_ref = $sforce->do_query($query, 1);
};
die $EVAL_ERROR if( $EVAL_ERROR );
return {} if( !$query_result_ref);
return $query_result_ref->[0];
}
sub salesforce_delete_products_from {
my ($sforce, $opportunity_id) = @_;
my $query =
qq{SELECT Id FROM OpportunityLineItem }
. qq{WHERE OpportunityId = '$opportunity_id'};
my $query_result_ref;
eval {
$query_result_ref = $sforce->do_query($query, 1);
};
die $EVAL_ERROR if( $EVAL_ERROR );
return if( !$query_result_ref);
my @products_to_delete = ();
for my $result_ref ( @{ $query_result_ref } ) {
push @products_to_delete, $result_ref->{Id}->[0];
}
eval {
$sforce->delete( @products_to_delete );
};
die $EVAL_ERROR if( $EVAL_ERROR );
}
sub command_line {
my %cli;
print "\nimport-products $VERSION";
print "\nAuthor: BigDiver (bigdiver.wordpress.com)\n\n";
GetOptions(\%cli,
"help",
"version",
"quiet",
"csv=s",
"opportunity=s",
"user=s",
"pass=s",
"delete",
);
if ( $cli{ help } || $cli{ version }) {
usage();
exit;
}
if ( !$cli{ csv }
|| !$cli{ opportunity }
|| !$cli{ user }
|| !$cli{ pass } ) {
usage(); exit(0);
}
return %cli;
}
sub usage {
print <<"HERE";
Import products from CSV formated file into Salesforce.com Opportunity
import-products --csv=<product-file.csv> --opportunity=<opportunityID>
--user=<user name> --pass=<password>
[--delete]
product-file.csv
MUST be a Comma Separated file with two columns (SKU, Quantity).
No header line.
opportunityID
MUST be an existant Salesforce Opportunity to which you want to add
the products from the <products-file.csv> to.
user, pass
Salesforce.com user name and password.
delete
Delete all of the opportunities products before adding any new ones.
HERE
}