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.