package SUNAT::SEE;

=head1 NAME

SUNAT::SEE - Capa para la interacción para envio de documentos a SUNAT 

=head1 VERSION

Version 1.44

=cut 

use File::Path qw( make_path rmtree );
use IO::Uncompress::Unzip qw(unzip $UnzipError) ;
use Cwd;
use MIME::Base64;
use LWP::UserAgent;
use XML::Simple;
use XML::XPath;
use XML::XPath::XMLParser;
use Digest::SHA1 qw(sha1 sha1_base64);
use MIME::Base64;

sub new
{
my $class = shift;  
my ($env,$ruc,$user,$pass) = (@_);

my $self = {};
bless( $self, $class ); 
$self->{env} = $env; 

#Directorio que se almacenara los ficheros de recepcion y de envio 
$self->{dirname} = '/devel/yupay/files/';

 #fuente de WS: http://orientacion.sunat.gob.pe/index.php/empresas-menu/comprobantes-de-pago-empresas/comprobantes-de-pago-electronicos-empresas/see-desde-los-sistemas-del-contribuyente/guias-manuales-y-servicios-web
 #uri_cpfeg WebService de Facturacion electronica
 #uri_guia WebService de Guias de Remision
 #uri_ep Otros comprobantes Recepcion Percepcion etc 

 if($env eq 'PRO')
 {
 	$self->{uri_cpfeg} = q[https://e-factura.sunat.gob.pe/ol-ti-itcpfegem/billService];
 	$self->{uri_guia} = q[https://e-guiaremision.sunat.gob.pe/ol-ti-itemision-guia-gem/billService];
 	$self->{uri_otroscpe} = q[https://e-factura.sunat.gob.pe/ol-ti-itemision-otroscpe-gem/billService];
 	$self->{uri_conscpe} = q[https://e-factura.sunat.gob.pe/ol-it-wsconscpegem/billConsultService];

 }
 elsif($env eq 'DEV')
 {
	$self->{uri_cpfeg} = q[https://e-beta.sunat.gob.pe/ol-ti-itcpfegem-beta/billService];
 	$self->{uri_guia} = q[https://e-beta.sunat.gob.pe/ol-ti-itemision-guia-gem-beta/billService];
 	$self->{uri_otroscpe} = q[https://e-beta.sunat.gob.pe/ol-ti-itemision-otroscpe-gem-beta/billService];
 	$self->{uri_conscpe} = q[https://e-beta.sunat.gob.pe/ol-it-wsconscpegem/billConsultService];

 }
 elsif($env eq 'HOMOLOGACION')
 {
	$self->{uri_cpfeg} = q[https://www.sunat.gob.pe/ol-ti-itcpgem-sqa/billService];
 	$self->{uri_guia} = q[];
 	$self->{uri_conscpe} = q[];
 	$self->{uri_conscpe} = q[https://e-beta.sunat.gob.pe/ol-it-wsconscpegem/billConsultService];

 }
 else
 {
 	return 0;
 }

 #WS de consulta de validez y verificacion de facturas electornicas
 #$self->{uri_ve} =q[https://e-factura.sunat.gob.pe/ol-it-wsconsvalidcpe/billValidService?wsdl];
 
 #WS de consulta de CDR y estado de envio 
 #$self->{uri_co} =q[https://e-factura.sunat.gob.pe/ol-it-wsconscpegem/billConsultService?wsdl];

 $self->{env} = $env;
 $self->{ruc} = $ruc;
 $self->{user} = $user;
 $self->{pass} = $pass;

 $self->{xml} =q[<?xml version="1.0" encoding="utf-8" ?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ser="http://service.sunat.gob.pe" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
   <soapenv:Header>
		<wsse:Security>
			<wsse:UsernameToken>
			<wsse:Username>%RUC%%USER%</wsse:Username>
			<wsse:Password>%PASS%</wsse:Password>
			</wsse:UsernameToken>
		</wsse:Security>
   </soapenv:Header>
   <soapenv:Body>
   %BODY%
   </soapenv:Body>
</soapenv:Envelope>];

 $self->{xml} =~s/%RUC%/$self->{ruc}/ig;
 $self->{xml} =~s/%USER%/$self->{user}/ig;
 $self->{xml} =~s/%PASS%/$self->{pass}/ig;


return $self; 
}


=head2 setDirname($dirname)

Setea el directorio de trabajo a:

=cut


sub setDirname
{
  my $self = shift;
  my ($dirname) = (@_);
  $self->{dirname} = $dirname;

}
=head2 cons_getStatus($ruc,$tipo,$serie,$numero)

Obtiene el status de un comprobante de pago 

=cut


sub cons_getStatus
{
 my $self = shift;
 my ($ruc,$tipo,$serie,$numero) = (@_);
 my $xml_body = q[     <ser:getStatus>
         <rucComprobante>%RUC%</rucComprobante>
         <tipoComprobante>%TIPO%</tipoComprobante>
         <serieComprobante>%SERIE%</serieComprobante>
         <numeroComprobante>%NUMERO%</numeroComprobante>
      </ser:getStatus>];

 my $xml = $self->{xml};

 $xml_body =~s/%RUC%/$ruc/ig;
 $xml_body =~s/%TIPO%/$tipo/ig;
 $xml_body =~s/%SERIE%/$serie/ig;
 $xml_body =~s/%NUMERO%/$numero/ig;

 $xml =~s/%BODY%/$xml_body/ig;

 #envio de la información a SUNAT
 my $SOAPans = HTTPconectar($self->{uri_conscpe},$xml,'urn:getStatus');

 my $resp = {};

#print $SOAPans;
 my $xpSOAP = XML::XPath->new( xml => $SOAPans );
 $resp->{'statusCode'} = $xpSOAP->getNodeText('//statusCode')->value();
 $resp->{'statusMessage'} = $xpSOAP->getNodeText('//statusMessage')->value();
 
 return $resp;
}
=head2 cons_getStatusCdr($ruc,$tipo,$serie,$numero)

Obtiene el fichero CDR de un comprobante de pago 

=cut
sub cons_getStatusCdr    
{
 #obtiene el CDR de un CPE
 my $self = shift;
 my ($ruc,$tipo,$serie,$numero) = (@_);

 my $xml_body = q[  <ser:getStatusCdr>
         <rucComprobante>%RUC%</rucComprobante>
         <tipoComprobante>%TIPO%</tipoComprobante>
         <serieComprobante>%SERIE%</serieComprobante>
         <numeroComprobante>%NUMERO%</numeroComprobante>
      </ser:getStatusCdr>];    

 my $xml = $self->{xml};

 $xml_body =~s/%RUC%/$ruc/ig;
 $xml_body =~s/%TIPO%/$tipo/ig;
 $xml_body =~s/%SERIE%/$serie/ig;
 $xml_body =~s/%NUMERO%/$numero/ig;

 $xml =~s/%BODY%/$xml_body/ig;

 #envio de la información a SUNAT
 my $SOAPans = $self->HTTPconectar($self->{uri_conscpe},$xml,'urn:getStatusCdr');

 my $resp = {};

 my $xpSOAP = XML::XPath->new( xml => $SOAPans );
 $resp->{'statusCode'} = $xpSOAP->getNodeText('//statusCode')->value();
 $resp->{'statusMessage'} = $xpSOAP->getNodeText('//statusMessage')->value();
 $resp->{'content'} = $xpSOAP->getNodeText('//content')->value();

 #decodifica a xml
 $resp->{'content'} = $self->decode64($self->{dirname}.$ruc.'/',"R-$ruc-$tipo-$serie-$numero.ZIP",$resp->{'content'});

 my $xpUBL = XML::XPath->new( xml => $resp->{'content'} );
 $resp->{'ResponseCode'} = $xpUBL->getNodeText('//cbc:ResponseCode')->value();
 $resp->{'Description'} = $xpUBL->getNodeText('//cbc:Description')->value();
 $resp->{'Note'} 		= $xpUBL->getNodeText('//cbc:Note')->value();

 return $resp;
}

=head2 encode64($filename)

Codifica en base64 un fichero ZIP

=cut

sub encode64
{
  my $self = shift;
  my ($filename) = (@_);
  my $content;
  open my $fh, '<', $filename.'.ZIP';
    binmode $fh;
    $/ = undef;
    $content = <$fh>;
  close $fh;


  my $encoded= MIME::Base64::encode_base64($content);
  $encoded =~s/\n//ig;
  return $encoded;
}

=head2 decode($filename,$content)

devuelve un 

=cut

sub decode64
{
 my $self = shift;
 my ($dirname,$filename,$content) = (@_);
 my $decoded= MIME::Base64::decode_base64($content);
 my $xml;


 if($content ne '')
 {
  if(!(-e $dirname))
  {
  	make_path $dirname;
  }
  open my $fh, '>', $dirname.$filename or die $!;
  binmode $fh;
  print $fh $decoded;
  close $fh;

  if($filename =~m/(.*)ZIP$/)
  {
    my $cmd = '/usr/bin/unzip -o -d '.$dirname.' '.$dirname.$filename;
    system ($cmd);

    #Abre el fichero de respuesta 
    my $xml_file = $filename;
    $xml_file =~s/ZIP/XML/ig;

    if(-e $dirname.$xml_file)
    {
    open my $fh, '<', $dirname.$xml_file;
    binmode $fh;
    $/ = undef;
    $xml = <$fh>;
    close $fh;
    }
    else
    {
      $xml_file =~s/XML/xml/ig;
      if(-e $dirname.$xml_file)
      {
        open my $fh, '<', $dirname.$xml_file;
        binmode $fh;
        $/ = undef;
        $xml = <$fh>;
        close $fh;
      }
      else
      {
        print "ERROR DECODE64: fichero $dirname.$xml_file no existe\n";
      }
    }
  }

 }
 return $xml; 
}

=head2 sendBill($dirname, $filename)

Envia un Invoice a sunat 

=cut

sub sendBill
{
 my $self = shift;
 my ($dirname,$filename) = (@_);

my $content =  $self->encode64($dirname.$filename);


 my $xml_body = q[      <ser:sendBill>
         <fileName>%FILE%.ZIP</fileName>
         <contentFile>%CONTENT%</contentFile>
      </ser:sendBill>];

 my $xml = $self->{xml};

 $xml_body =~s/%FILE%/$filename/ig;
 $xml_body =~s/%CONTENT%/$content/ig;
 

 $xml =~s/%BODY%/$xml_body/ig;

 #print "\n\n$xml\n\n\n";
 #envio de la información a SUNAT
 my $SOAPans = $self->HTTPconectar($self->{uri_cpfeg},$xml,'urn:sendBill');


 my $resp = {};

my $xpSOAP = XML::XPath->new( xml => $SOAPans );
 


$resp->{'faultcode'} = $xpSOAP->getNodeText('//faultcode')->value();
$resp->{'faultstring'} = $xpSOAP->getNodeText('//faultstring')->value();

$resp->{'content'} = $self->decode64($dirname,"R-$filename.ZIP",$xpSOAP->getNodeText('//applicationResponse')->value());

if($resp->{'content'} ne '')
{
 my $xpUBL = XML::XPath->new( xml => $resp->{'content'} );
 $resp->{'ResponseDate'} = $xpUBL->getNodeText('//cbc:ResponseDate')->value();
 $resp->{'ResponseTime'} = $xpUBL->getNodeText('//cbc:ResponseTime')->value();
 $resp->{'ResponseCode'} = $xpUBL->getNodeText('//cbc:ResponseCode')->value();
 $resp->{'Description'} = $xpUBL->getNodeText('//cbc:Description')->value();
 $resp->{'Note'}    = $xpUBL->getNodeText('//cbc:Note')->value();
}

return $resp;
}

=head2 sendSumary($dirname, $filename)

Envia un resumen a sunat 

=cut

sub sendSumary
{
 my $self = shift;
 my ($dirname,$filename) = (@_);

my $content =  $self->encode64($dirname.$filename);


 my $xml_body = q[    <ser:sendSummary>
         <fileName>%FILE%.ZIP</fileName>
         <contentFile>%CONTENT%</contentFile>
      </ser:sendSummary>
      ];

 my $xml = $self->{xml};

 $xml_body =~s/%FILE%/$filename/ig;
 $xml_body =~s/%CONTENT%/$content/ig;
 

 $xml =~s/%BODY%/$xml_body/ig;


 #print "\n\n$xml\n\n\n";
 #envio de la información a SUNAT
 my $SOAPans = $self->HTTPconectar($self->{uri_cpfeg},$xml,'urn:sendSummary');


 my $resp = {};

my $xpSOAP = XML::XPath->new( xml => $SOAPans );
 

$resp->{'faultcode'} = $xpSOAP->getNodeText('//faultcode')->value();
$resp->{'faultstring'} = $xpSOAP->getNodeText('//faultstring')->value();
$resp->{'ticket'} = $xpSOAP->getNodeText('//ticket')->value();

return $resp;
}


=head2 getStatus($dirname, $filename)

Obtiene el status de un Ticket 

=cut

sub getStatus
{
 my $self = shift;
 my ($dirname,$filename,$ticket) = (@_);

my $content =  $self->encode64($dirname.$filename);


 my $xml_body = q[      <ser:getStatus>
         <ticket>%TICKET%</ticket>
      </ser:getStatus>
      ];

 my $xml = $self->{xml};

 $xml_body =~s/%TICKET%/$ticket/ig;
 

 $xml =~s/%BODY%/$xml_body/ig;


 #print "\n\n$xml\n\n\n";
 #envio de la información a SUNAT
 my $SOAPans = $self->HTTPconectar($self->{uri_cpfeg},$xml,'urn:sendSummary');

#print $SOAPans;


 my $resp = {};

my $xpSOAP = XML::XPath->new( xml => $SOAPans );
 

$resp->{'faultcode'} = $xpSOAP->getNodeText('//faultcode')->value();
$resp->{'faultstring'} = $xpSOAP->getNodeText('//faultstring')->value();

$resp->{'content'} = $self->decode64($dirname,"R-$filename.ZIP",$xpSOAP->getNodeText('//content')->value());

if($resp->{'content'} ne '')
{
 my $xpUBL = XML::XPath->new( xml => $resp->{'content'} );
 $resp->{'ResponseDate'} = $xpUBL->getNodeText('//cbc:ResponseDate')->value();
 $resp->{'ResponseTime'} = $xpUBL->getNodeText('//cbc:ResponseTime')->value();
 $resp->{'ResponseCode'} = $xpUBL->getNodeText('//cbc:ResponseCode')->value();
 $resp->{'Description'} = $xpUBL->getNodeText('//cbc:Description')->value();
 $resp->{'Note'}    = $xpUBL->getNodeText('//cbc:Note')->value();
}


return $resp;
}

=head2 Sign($dirname, $filename)

Firma un documento XML con el binario sign externo

=cut

sub Sign
{
  my $self = shift;
  my ($dirname,$filename) = (@_);

  my $path = getcwd;

  $path =~m/(.*)ws-project(.*)/;
  $path = $1;

  my $execute = $path.'ws-project/bin/sign '.$dirname.' '.$filename; 

#print "\n$execute\n\n";
  return system($execute);

}

=head2 getDigest($xml)

obtiene el Digest de un XML

=cut

sub getDigest
{
  my $self = shift;
  my ($xml) =(@_);

  require XML::CanonicalizeXML;
  my $xpath = '<XPath>(//. | //@* | //namespace::*)</XPath>';
  $canonical =  XML::CanonicalizeXML::canonicalize( $xml, $xpath, '', 0, 0);

  my $bin_digest    = sha1( $canonical );
  my $digest        = encode_base64( $bin_digest, '' );

  return $digest;
}


sub HTTPconectar 
{
  my $self = shift;
  my ($endpoint,$content,$SOAPAction) = (@_);

  my $ua = LWP::UserAgent->new;

  $ua->send_te(0);
  
  $ua->agent("Apache-HttpClient/4.1.1 (java 1.5)");
  $ua->default_headers->header('SOAPAction' => $SOAPAction);
  $ua->timeout(120);
  $ua->show_progress(1);

  # Create a request
  my $req = HTTP::Request->new(POST => $endpoint);
  $req->content_type('text/xml; charset=utf-8');
  $req->content($content);
  #print $content;
  # Pass request to the user agent and get a response back
  my $res = $ua->request($req);



  # Check the outcome of the response
  if ($res->is_success) 
  {
    return $res->decoded_content;
  }
  else 
  {
    return $res->decoded_content;
  }
}



return 1;
