Saltar a contenido

Uso de Greenter

Más opciones que tenemos al utilizar Greenter.

Generar XML firmado

El ejemplo básico nos mostró como realizar todo el proceso de facturacion con un solo método send(), pero si necesitamos generar solo el XML firmado, lo cual seria útil para Boletas de Venta ya que estas no se envían a SUNAT individualmente.

<?php

use Greenter\Model\Sale\Invoice;
use Greenter\See;

$boleta = (new Invoice())
    ->setUblVersion('2.1')
    ->setTipoOperacion('0101')
    ->setTipoDoc('03') // Código para Boletas, ver Catalog. 51  
    ->setSerie('B001')
    ->setCorrelativo('1')
    // ...
    ;

$see = new See();
$see->setCertificate(file_get_contents(__DIR__.'/certificate.pem'));

$xml = $see->getXmlSigned($boleta);

file_put_contents('20000000001-03-B001-1.xml', $xml);

Enviar XML generado

Si necesitas enviar un XML previamente generado, debes seguir el siguiente ejemplo.

<?php

require __DIR__.'/vendor/autoload.php';

$see = require __DIR__.'/config.php';

$xmlSigned = file_get_contents('20000000001-01-F001-1.xml');

$result = $see->sendXmlFile($xmlSigned);
// $result se maneja del mismo modo que con el metodo send()

Resumen diario

Para comunicar las boletas de ventas emitidas o anuladas, así como las notas de crédito/débito releacionadas, necesita hacerlo mediante un resumen diario. A diferencia del envío de una factura, donde la respuesta es inmediata, en este documento debemos hacer un consulta adicional para conocer su estado utilizando el numero de ticket.

<?php

use Greenter\Model\Company\Company;
use Greenter\Model\Company\Address;
use Greenter\Model\Sale\Document;
use Greenter\Model\Summary\Summary;
use Greenter\Model\Summary\SummaryDetail;

$company = new Company();
$company->setRuc('20000000001')
    ->setRazonSocial('EMPRESA SAC')
    ->setNombreComercial('EMPRESA')
    ->setAddress((new Address())
        ->setUbigueo('150101')
        ->setDepartamento('LIMA')
        ->setProvincia('LIMA')
        ->setDistrito('LIMA')
        ->setUrbanizacion('Albar')
        ->setDireccion('AV ROSALES'));

$detail = new SummaryDetail();
$detail->setTipoDoc('07') // Nota de Credito
    ->setSerieNro('B001-4')
    ->setDocReferencia((new Document()) // Documento relacionado (Boleta)
        ->setTipoDoc('03')
        ->setNroDoc('B001-1'))
    ->setEstado('1') // Emisión
    ->setClienteTipo('1') // Tipo documento identidad: DNI
    ->setClienteNro('00000000') // Nro de documento identidad
    ->setTotal(200)
    ->setMtoOperGravadas(40)
    ->setMtoOperExoneradas(50)
    ->setMtoOperInafectas(100)
    ->setMtoIGV(7.2)
    ->setMtoISC(2.8);

$detail2 = new SummaryDetail();
$detail2->setTipoDoc('03') // Boleta
    ->setSerieNro('B001-2')
    ->setEstado('3') // Anulación
    ->setClienteTipo('1')
    ->setClienteNro('00000000')
    ->setTotal(119)
    ->setMtoOperGravadas(20)
    ->setMtoOperInafectas(24.4)
    ->setMtoOperExoneradas(50)
    ->setMtoOtrosCargos(21)
    ->setMtoIGV(3.6);

$resumen = new Summary();
$resumen->setFecGeneracion(new \DateTime('2020-08-01')) // Fecha de emisión de las boletas.
    ->setFecResumen(new \DateTime('2020-08-02')) // Fecha de envío del resumen diario.
    ->setCorrelativo('001') // Correlativo, necesario para diferenciar de otros Resumen diario del mismo día.
    ->setCompany($company)
    ->setDetails([$detail, $detail2]);

$result = $see->send($resumen);
// Guardar XML
file_put_contents($resumen->getName().'.xml',
                  $see->getFactory()->getLastXml());

if (!$result->isSuccess()) {
    // Si hubo error al conectarse al servicio de SUNAT.
    var_dump($result->getError());
    exit();
}

$ticket = $result->getTicket();
echo 'Ticket : '.$ticket.PHP_EOL;

$statusResult = $see->getStatus($ticket);
if (!$statusResult->isSuccess()) {
    // Si hubo error al conectarse al servicio de SUNAT.
    var_dump($statusResult->getError());
    return;
}

echo $statusResult->getCdrResponse()->getDescription();
// Guardar CDR
file_put_contents('R-'.$resumen->getName().'.zip', $statusResult->getCdrZip());

Comunicacion de Baja

Para comunicar a SUNAT las anulaciones de facturas y sus notas de crédito/débito releacionadas, necesita hacerlo mediante el documento de comunicación de baja.

El envío a los servicios de SUNAT se maneja de la misma forma que el resumen diario.

<?php

use Greenter\Model\Voided\Voided;
use Greenter\Model\Voided\VoidedDetail;

$detail1 = new VoidedDetail();
$detail1->setTipoDoc('01') // Factura
    ->setSerie('F001')
    ->setCorrelativo('1')
    ->setDesMotivoBaja('ERROR EN CÁLCULOS'); // Motivo por el cual se da de baja.

$detail2 = new VoidedDetail();
$detail2->setTipoDoc('07') // Nota de Crédito
    ->setSerie('FC01')
    ->setCorrelativo('2')
    ->setDesMotivoBaja('ERROR DE RUC');

$cDeBaja = new Voided();
$cDeBaja->setCorrelativo('00001') // Correlativo, necesario para diferenciar c. de baja de en un mismo día.
    ->setFecGeneracion(new \DateTime('2020-08-01')) // Fecha de emisión de los comprobantes a dar de baja
    ->setFecComunicacion(new \DateTime('2020-08-02')) // Fecha de envio de la C. de baja
    ->setCompany($company)
    ->setDetails([$detail1, $detail2]);

$result = $see->send($cDeBaja);
// Guardar XML
file_put_contents($cDeBaja->getName().'.xml',
                  $see->getFactory()->getLastXml());

if (!$result->isSuccess()) {
    // Si hubo error al conectarse al servicio de SUNAT.
    var_dump($result->getError());
    exit();
}

$ticket = $result->getTicket();
echo 'Ticket : '.$ticket.PHP_EOL;

$statusResult = $see->getStatus($ticket);
if (!$statusResult->isSuccess()) {
    // Si hubo error al conectarse al servicio de SUNAT.
    var_dump($statusResult->getError());
    return;
}

echo $statusResult->getCdrResponse()->getDescription();
// Guardar CDR
file_put_contents('R-'.$cDeBaja->getName().'.zip', $statusResult->getCdrZip());

Anulación de Boletas

Para anular boletas y NCR, NDB relacionadas, se utiliza el resumen diario indicando el campo estado=3