El día de hoy les compartimos nuestro desarrollo para la integración de vTiger con el Timbrado de CFDI.

Paso 1:

Crear los campos adicionales solicitados por el SAT para el timbrado de facturas.

1.1 – Pantalla Contactos:

1.2 – Pantalla Invoice:


Paso 2:

El timbrado de la factura se realizará por medio de un flujo de trabajo, para lograr esto, es necesario crear un Handler y su clase asociada que ejecuta la acción, recibiendo la entidad Invoice como parámetro. 

2.1 – Crear el archivo include/InvoiceHandler.php

<? php
function handleInvoice($entity) {
	require_once("include/utils/InventoryUtils.php");
	createCFDI($entity);
}
?>

2.2 – Modificar el archivo InventoryUtils.php:

<? php
function createCFDI($entity) {
    global $log, $adb;
    $entity_id = vtws_getIdComponents($entity->getId());
    $entity_id = $entity_id[1];
    $query="select contactid, subtotal, total from vtiger_invoice where invoiceid=?";
    $resultInvoice=$adb->pquery($query, array($entity_id));
    $contactid=$adb->query_result($resultInvoice,0,'contactid');
    $subTotal = $adb->query_result($resultInvoice,0,'subtotal');
    $total = $adb->query_result($resultInvoice,0,'total');
    $rfc = 'RFC DE LA EMPRESA A TIMBRAR';
    $folio = $entity_id;
    $serie = 'A';
    $lugarExpedicion =CP DE LA EMPRESA  A TIMBRAR;
    $fecha = time();
    $fechaOperacion =  date("Y-m-d", $fecha);
    //Modificar los campos por los identificadores de los campos creados en la nueva instalación de vTiger 
    $query = "select cf_1262, cf_1264, cf_1266, cf_1260, cf_1268 from vtiger_invoicecf where invoiceid=?";
    $resultInvoiceDetail = $adb->pquery($query, array($entity_id));
    $formaPago = substr($adb->query_result($resultInvoiceDetail, 0, 'cf_1262'), 0, 2)  ;
    $metodoPago =  substr($adb->query_result($resultInvoiceDetail, 0, 'cf_1264'),0,3);     
    $moneda =  $adb->query_result($resultInvoiceDetail, 0, 'cf_1260');
    $tipoCambio = $adb->query_result($resultInvoiceDetail, 0, 'cf_1268');
    $tipoDeComprobante = $adb->query_result($resultInvoiceDetail, 0, 'cf_1266');
    //El ejemplo actual solo timbra comprobantes de ingreso
    if ($tipoDeComprobante == 'Ingreso') {
        $tipoDeComprobante ='I';
         //Modificar los campos por los identificadores de los campos creados en la nueva instalación de vTiger 
        $query = "select cf_1284,cf_894  from vtiger_contactscf where contactid=?";
        $resultContact = $adb->pquery($query, array($contactid));
        $receptorNombre =  $adb->query_result($resultContact, 0, 'cf_1284');
        $receptorRFC =  $adb->query_result($resultContact, 0, 'cf_894');
        $usoCFDI = "G03"; //En caso de no querer tener un tipo de gasto por default, agregarlo a los datos del contacto
        $query = "select productid,quantity,listprice, tax1 from vtiger_inventoryproductrel where id=?";
        $resultProduct = $adb->pquery($query, array($entity_id));
        $params = array (
                    'EmpresaRfc' => $rfc,
                    'CondicionesDePago' => $condicionsPago,
                    'Folio' => $folio,
                    'Serie' => $serie,
                    'FormaPago' => $formaPago,
                    'LugarExpedicion' => $lugarExpedicion,
                    'MetodoPago' => $metodoPago,
                    'Moneda' => $moneda,
                    'TipoDeComprobante' => $tipoDeComprobante,
                    'TipoCambio' => $tipoCambio,
                    'FechaOperacion' => $fechaOperacion,
                    'SubTotal' => $subTotal,
                    'Total' => $total,
                    'Receptor' => json_encode(array(array(
                        'Nombre' => $receptorNombre,
                        'Rfc' => $receptorRFC,
                        'UsoCFDI' => $usoCFDI
                        ))),
                    'Comentario' => 'FACTURA DE VTIGER'
                    );
$conceptos = array();
        if ($adb->num_rows($resultProduct) > 0) {
            for($j=0; $j<$adb->num_rows($resultProduct); $j++){
                $productId =  $adb->query_result($resultProduct,0,'productid');
                $cantidad =  $adb->query_result($resultProduct,0,'quantity');
                $listprice =  $adb->query_result($resultProduct,0,'listprice');
                $noIdentificacion = $productId;
                $valorUnitario = $listprice;
                $tax1 =  $adb->query_result($resultProduct,0,'tax1');
                $importe = $cantidad * $listprice;
                $query = "select product_no,productname,productcode, productsheet from vtiger_products where productid=?";
                $resultProductDesc = $adb->pquery($query, array($productId));
                $product_no =  $adb->query_result($resultProductDesc,0,'product_no');
                $descripcion =  $adb->query_result($resultProductDesc,0,'productname');
                $claveProdServ =  $adb->query_result($resultProductDesc,0,'productcode');
                $claveUnidad =  $adb->query_result($resultProductDesc,0,'productsheet');
                $impuestosBase = $importe;
                $impuestosImporte = $importe * ($tax1/100);
                $tipoFactor = "Tasa";
                $tasaOCuota = $tax1;
                $impuestoId = "002";
                $concepto = array(
                    "Cantidad" => $cantidad,
                    "ClaveProdServ"=>$claveProdServ,
                    "Descripcion" => $descripcion,
                    "Importe" =>$importe,
                    "ClaveUnidad" => $claveUnidad,
                    "Unidad" => $claveUnidad,
                    "ValorUnitario" => $valorUnitario,
                    "NoIdentificacion" => $noIdentificacion,
                    "Impuestos" => array(
                        "Base" =>  $impuestosBase,
                        "Importe" => $impuestosImporte,
                        "TipoFactor" => $tipoFactor,
                        "TasaOCuota" => $tasaOCuota,
                        "Impuesto" => $impuestoId,
                        )
                    );
                    array_push($conceptos,$concepto);
            }
            $params['Conceptos'] = json_encode($conceptos);
        }
        $contrasena = CONTRASEÑA DEL CLIENTE
        $defaults = array(
            CURLOPT_URL => 'https://app.gestioncorporativa.net/service/timbradoComprobante.php/timbradocomprobante/USUARIO/'.$contrasena,
            CURLOPT_POST => true,
            CURLOPT_POSTFIELDS => $params,
            CURLOPT_VERBOSE => 0,
            CURLOPT_RETURNTRANSFER => 1
        );

  $curl = curl_init();
        curl_setopt_array($curl,$defaults);
        $contenido = curl_exec($curl);
        curl_close($curl);
        $response = json_decode($contenido, true);

        $result = $response['result'];
        if ($result == 'OK') {

            $idComprobante = $response['idComprobante'];
            $message = $response['message'];
            $UUID = $response['documentos']['UUID'];
            $XML = $response['documentos']['XML'];
            $PDF = $response['documentos']['PDF'];
            $files = array(
                    "name" => $UUID.'.PDF',
                    "type" => "application/pdf",
                    "error" => 0,
                    "size" => strlen(base64_decode($PDF)),
                    "original_name" => $UUID.'.PDF');
                    uploadAndSaveFile($entity_id,"Documents",$files,"PDF",$PDF);
            $files = array(
                    "name" => $UUID.'.XML',
                    "type" => "application/xml",
                    "error" => 0,
                    "size" => strlen(base64_decode($PDF)),
                    "original_name" => $UUID.'.XML');
                    uploadAndSaveFile($entity_id,"Documents",$files,"XML",$XML);
        }
        else {
            print_R($response);
        }

    }
}
function uploadAndSaveFile($id,$module,$file_details,$description,$filebase64) {
    global $log, $adb;

    $log->debug("Entering into uploadAndSaveFile($id,$module,$file_details) method.");

    global $current_user;
    global $upload_badext;

    $date_var = date('Y-m-d H:i:s');

    //to get the owner id
    $ownerid = $current_user->id;
    $save_file = 'true';

    $file = $file_details['name'];

    $binFile = sanitizeUploadFileName($file, $upload_badext);

    $filename = ltrim(basename(" ".$binFile)); //allowed filename like UTF-8 characters
    $filetype= $file_details['type'];
    $filesize = $file_details['size'];

    $current_id = $adb->getUniqueID("vtiger_crmentity");

    //get the file path inwhich folder we want to upload the file
    $upload_file_path = decideFilePath();
    //upload the file in server
    file_put_contents($upload_file_path.$current_id."_".$binFile, base64_decode($filebase64));


    if($save_file == 'true') {

        $sql1 = "insert into vtiger_crmentity (crmid,smcreatorid,smownerid,setype,description,createdtime,modifiedtime) values(?,?,?,?,?,?,?)";
        $params1 = array($current_id, $current_user->id, $ownerid, $module, $description, $adb->formatString("vtiger_crmentity","createdtime",$date_var), $adb->formatDate($date_var, true));
        $adb->pquery($sql1, $params1);

        $query = "INSERT INTO vtiger_notes (notesid, title, filename,folderid,filetype,filelocationtype,filedownloadcount,filestatus,filesize,notecontent) values(?,?,?,?,?,?,?,?,?,?)";
                $re=$adb->pquery($query,array($current_id,$filename,$$filesize,$upload_file_path.$current_id."_".$binFile,1,$filetype,'',0,1,$filesize,$upload_file_path.$current_id."_".$binFile));

        $query = "INSERT INTO vtiger_senotesrel (crmid, notesid) values(?,?)";
                $re=$adb->pquery($query,array($id,$current_id));
    }
    else {
        $log->debug("Skip the save attachment process.");
    }
    $log->debug("Exiting from uploadAndSaveFile($id,$module,$file_details) method.");
    return;
}

?>


Paso 3:

Ingresar a Valores de lista de selección en la Configuración de vTiger para crear el Estatus de Invoice CFDI que disparará la factura.

3.1 – Creación del Estatus CFDI


Paso 4:

Registrar el Procedimiento custom de flujo de trabajo en la base de datos.

Código:

INSERT INTO `com_vtiger_workflowtasks_entitymethod` (`workflowtasks_entitymethod_id`, `module_name`, `method_name`, `function_path`, `function_name`)
VALUES
	(11, 'Invoice', 'InvoiceCFDI', 'include/InvoiceHandler.php', 'handleInvoice');

Incrementar la secuencia:

UPDATE com_vtiger_workflowtasks_entitymethod_seq SET id = id + 1 


Paso 5:

Para la creación del flujo de trabajo, se debe ingresar en la sección de flujo de trabajo y crear uno nuevo para Invoice o Facturas.

Finalmente, la factura aparecerá como parte de los documentos visibles del Invoice.