1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* LibreDTE |
5
|
|
|
* Copyright (C) SASCO SpA (https://sasco.cl) |
6
|
|
|
* |
7
|
|
|
* Este programa es software libre: usted puede redistribuirlo y/o |
8
|
|
|
* modificarlo bajo los términos de la Licencia Pública General Affero de GNU |
9
|
|
|
* publicada por la Fundación para el Software Libre, ya sea la versión |
10
|
|
|
* 3 de la Licencia, o (a su elección) cualquier versión posterior de la |
11
|
|
|
* misma. |
12
|
|
|
* |
13
|
|
|
* Este programa se distribuye con la esperanza de que sea útil, pero |
14
|
|
|
* SIN GARANTÍA ALGUNA; ni siquiera la garantía implícita |
15
|
|
|
* MERCANTIL o de APTITUD PARA UN PROPÓSITO DETERMINADO. |
16
|
|
|
* Consulte los detalles de la Licencia Pública General Affero de GNU para |
17
|
|
|
* obtener una información más detallada. |
18
|
|
|
* |
19
|
|
|
* Debería haber recibido una copia de la Licencia Pública General Affero de GNU |
20
|
|
|
* junto a este programa. |
21
|
|
|
* En caso contrario, consulte <http://www.gnu.org/licenses/agpl.html>. |
22
|
|
|
*/ |
23
|
|
|
|
24
|
|
|
namespace sasco\LibreDTE; |
25
|
|
|
|
26
|
|
|
/** |
27
|
|
|
* Clase para acciones genéricas asociadas al SII de Chile |
28
|
|
|
* @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl) |
29
|
|
|
* @version 2017-09-11 |
30
|
|
|
*/ |
31
|
|
|
class Sii |
32
|
|
|
{ |
33
|
|
|
|
34
|
|
|
private static $config = [ |
35
|
|
|
'wsdl' => [ |
36
|
|
|
'*' => 'https://{servidor}.sii.cl/DTEWS/{servicio}.jws?WSDL', |
37
|
|
|
'QueryEstDteAv' => 'https://{servidor}.sii.cl/DTEWS/services/{servicio}?WSDL', |
38
|
|
|
'wsDTECorreo' => 'https://{servidor}.sii.cl/DTEWS/services/{servicio}?WSDL', |
39
|
|
|
], |
40
|
|
|
'servidor' => ['palena', 'maullin'], ///< servidores 0: producción, 1: certificación |
41
|
|
|
'certs' => [300, 100], ///< certificados 0: producción, 1: certificación |
42
|
|
|
]; |
43
|
|
|
|
44
|
|
|
const PRODUCCION = 0; ///< Constante para indicar ambiente de producción |
45
|
|
|
const CERTIFICACION = 1; ///< Constante para indicar ambiente de desarrollo |
46
|
|
|
|
47
|
|
|
const IVA = 19; ///< Tasa de IVA |
48
|
|
|
|
49
|
|
|
private static $retry = 10; ///< Veces que se reintentará conectar a SII al usar el servicio web |
50
|
|
|
private static $verificar_ssl = true; ///< Indica si se deberá verificar o no el certificado SSL del SII |
51
|
|
|
private static $ambiente = self::PRODUCCION; ///< Ambiente que se utilizará |
52
|
|
|
|
53
|
|
|
private static $direcciones_regionales = [ |
54
|
|
|
'CHILLÁN VIEJO' => 'CHILLÁN', |
55
|
|
|
'HUECHURABA' => 'SANTIAGO NORTE', |
56
|
|
|
'LA CISTERNA' => 'SANTIAGO SUR', |
57
|
|
|
'LAS CONDES' => 'SANTIAGO ORIENTE', |
58
|
|
|
'LO ESPEJO' => 'SANTIAGO SUR', |
59
|
|
|
'PEÑALOLÉN' => 'ÑUÑOA', |
60
|
|
|
'PUDAHUEL' => 'SANTIAGO PONIENTE', |
61
|
|
|
'RECOLETA' => 'SANTIAGO NORTE', |
62
|
|
|
'SANTIAGO' => 'SANTIAGO CENTRO', |
63
|
|
|
'SAN MIGUEL' => 'SANTIAGO SUR', |
64
|
|
|
'SAN VICENTE' => 'SAN VICENTE TAGUA TAGUA', |
65
|
|
|
'TALTAL' => 'ANTOFAGASTA', |
66
|
|
|
'VITACURA' => 'SANTIAGO ORIENTE', |
67
|
|
|
'VICHUQUÉN' => 'CURICÓ', |
68
|
|
|
]; /// Direcciones regionales del SII según la comuna |
69
|
|
|
|
70
|
|
|
/** |
71
|
|
|
* Método que permite asignar el nombre del servidor del SII que se |
72
|
|
|
* usará para las consultas al SII |
73
|
|
|
* @param servidor Servidor que se usará, si es https://maullin2.sii.cl, entonces se debe pasar como valor maullin2 |
74
|
|
|
* @param certificacion Permite definir si se está cambiando el servidor de certificación o el de producción |
75
|
|
|
* @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl) |
76
|
|
|
* @version 2015-09-14 |
77
|
|
|
*/ |
78
|
|
|
public static function setServidor($servidor = 'maullin', $certificacion = Sii::CERTIFICACION) |
79
|
|
|
{ |
80
|
|
|
self::$config['servidor'][$certificacion] = $servidor; |
81
|
|
|
} |
82
|
|
|
|
83
|
|
|
/** |
84
|
|
|
* Método que entrega el nombre del servidor a usar según el ambiente |
85
|
|
|
* @param ambiente Ambiente que se desea obtener el servidor, si es null se autodetectará |
86
|
|
|
* @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl) |
87
|
|
|
* @version 2016-08-01 |
88
|
|
|
*/ |
89
|
|
|
public static function getServidor($ambiente = null) |
90
|
|
|
{ |
91
|
|
|
return self::$config['servidor'][self::getAmbiente($ambiente)]; |
92
|
|
|
} |
93
|
|
|
|
94
|
|
|
/** |
95
|
|
|
* Método que entrega la URL de un recurso en el SII según el ambiente que se esté usando |
96
|
|
|
* @param recurso Recurso del sitio del SII que se desea obtener la URL |
97
|
|
|
* @param ambiente Ambiente que se desea obtener el servidor, si es null se autodetectará |
98
|
|
|
* @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl) |
99
|
|
|
* @version 2017-09-11 |
100
|
|
|
*/ |
101
|
|
|
public static function getURL($recurso, $ambiente = null) |
102
|
|
|
{ |
103
|
|
|
return 'https://'.self::getServidor($ambiente).'.sii.cl'.$recurso; |
104
|
|
|
} |
105
|
|
|
|
106
|
|
|
/** |
107
|
|
|
* Método para obtener el WSDL |
108
|
|
|
* |
109
|
|
|
* \code{.php} |
110
|
|
|
* $wsdl = \sasco\LibreDTE\Sii::wsdl('CrSeed'); // WSDL para pedir semilla |
111
|
|
|
* \endcode |
112
|
|
|
* |
113
|
|
|
* Para forzar el uso del WSDL de certificación hay dos maneras, una es |
114
|
|
|
* pasando un segundo parámetro al método get con valor Sii::CERTIFICACION: |
115
|
|
|
* |
116
|
|
|
* \code{.php} |
117
|
|
|
* $wsdl = \sasco\LibreDTE\Sii::wsdl('CrSeed', \sasco\LibreDTE\Sii::CERTIFICACION); |
118
|
|
|
* \endcode |
119
|
|
|
* |
120
|
|
|
* La otra manera, para evitar este segundo parámetro, es asignar el valor a |
121
|
|
|
* través de la configuración: |
122
|
|
|
* |
123
|
|
|
* \code{.php} |
124
|
|
|
* \sasco\LibreDTE\Sii::setAmbiente(\sasco\LibreDTE\Sii::CERTIFICACION); |
125
|
|
|
* \endcode |
126
|
|
|
* |
127
|
|
|
* @param servicio Servicio por el cual se está solicitando su WSDL |
128
|
|
|
* @param ambiente Ambiente a usar: Sii::PRODUCCION o Sii::CERTIFICACION o null (para detección automática) |
129
|
|
|
* @return URL del WSDL del servicio según ambiente solicitado |
130
|
|
|
* @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl) |
131
|
|
|
* @version 2016-06-11 |
132
|
|
|
*/ |
133
|
|
|
public static function wsdl($servicio, $ambiente = null) |
134
|
|
|
{ |
135
|
|
|
// determinar ambiente que se debe usar |
136
|
|
|
$ambiente = self::getAmbiente($ambiente); |
137
|
|
|
// entregar WSDL local (modificados para ambiente de certificación) |
138
|
|
|
if ($ambiente==self::CERTIFICACION) { |
139
|
|
|
$wsdl = dirname(dirname(__FILE__)).'/wsdl/'.self::$config['servidor'][$ambiente].'/'.$servicio.'.jws'; |
140
|
|
|
if (is_readable($wsdl)) |
141
|
|
|
return $wsdl; |
|
|
|
|
142
|
|
|
} |
143
|
|
|
// entregar WSDL oficial desde SII |
144
|
|
|
$location = isset(self::$config['wsdl'][$servicio]) ? self::$config['wsdl'][$servicio] : self::$config['wsdl']['*']; |
145
|
|
|
$wsdl = str_replace( |
146
|
|
|
['{servidor}', '{servicio}'], |
147
|
|
|
[self::$config['servidor'][$ambiente], $servicio], |
148
|
|
|
$location |
149
|
|
|
); |
150
|
|
|
// entregar wsdl |
151
|
|
|
return $wsdl; |
152
|
|
|
} |
153
|
|
|
|
154
|
|
|
/** |
155
|
|
|
* Método para realizar una solicitud al servicio web del SII |
156
|
|
|
* @param wsdl Nombre del WSDL que se usará |
157
|
|
|
* @param request Nombre de la función que se ejecutará en el servicio web |
158
|
|
|
* @param args Argumentos que se pasarán al servicio web |
159
|
|
|
* @param retry Intentos que se realizarán como máximo para obtener respuesta |
160
|
|
|
* @return Objeto SimpleXMLElement con la espuesta del servicio web consultado |
161
|
|
|
* @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl) |
162
|
|
|
* @version 2016-08-28 |
163
|
|
|
*/ |
164
|
|
|
public static function request($wsdl, $request, $args = null, $retry = null) |
165
|
|
|
{ |
166
|
|
|
if (is_numeric($args)) { |
167
|
|
|
$retry = (int)$args; |
168
|
|
|
$args = null; |
169
|
|
|
} |
170
|
|
|
if (!$retry) |
171
|
|
|
$retry = self::$retry; |
172
|
|
|
if ($args and !is_array($args)) |
|
|
|
|
173
|
|
|
$args = [$args]; |
174
|
|
View Code Duplication |
if (!self::$verificar_ssl) { |
|
|
|
|
175
|
|
|
if (self::getAmbiente()==self::PRODUCCION) { |
176
|
|
|
$msg = Estado::get(Estado::ENVIO_SSL_SIN_VERIFICAR); |
177
|
|
|
\sasco\LibreDTE\Log::write(Estado::ENVIO_SSL_SIN_VERIFICAR, $msg, LOG_WARNING); |
|
|
|
|
178
|
|
|
} |
179
|
|
|
$options = ['stream_context' => stream_context_create([ |
180
|
|
|
'ssl' => [ |
181
|
|
|
'verify_peer' => false, |
182
|
|
|
'verify_peer_name' => false, |
183
|
|
|
'allow_self_signed' => true |
184
|
|
|
] |
185
|
|
|
])]; |
186
|
|
|
} else { |
187
|
|
|
$options = []; |
188
|
|
|
} |
189
|
|
|
try { |
190
|
|
|
$soap = new \SoapClient(self::wsdl($wsdl), $options); |
191
|
|
|
} catch (\Exception $e) { |
192
|
|
|
$msg = $e->getMessage(); |
193
|
|
View Code Duplication |
if (isset($e->getTrace()[0]['args'][1]) and is_string($e->getTrace()[0]['args'][1])) { |
|
|
|
|
194
|
|
|
$msg .= ': '.$e->getTrace()[0]['args'][1]; |
195
|
|
|
} |
196
|
|
|
\sasco\LibreDTE\Log::write(Estado::REQUEST_ERROR_SOAP, Estado::get(Estado::REQUEST_ERROR_SOAP, $msg)); |
|
|
|
|
197
|
|
|
return false; |
198
|
|
|
} |
199
|
|
|
for ($i=0; $i<$retry; $i++) { |
200
|
|
|
try { |
201
|
|
|
if ($args) { |
202
|
|
|
$body = call_user_func_array([$soap, $request], $args); |
203
|
|
|
} else { |
204
|
|
|
$body = $soap->$request(); |
205
|
|
|
} |
206
|
|
|
break; |
207
|
|
|
} catch (\Exception $e) { |
208
|
|
|
$msg = $e->getMessage(); |
209
|
|
View Code Duplication |
if (isset($e->getTrace()[0]['args'][1]) and is_string($e->getTrace()[0]['args'][1])) { |
|
|
|
|
210
|
|
|
$msg .= ': '.$e->getTrace()[0]['args'][1]; |
211
|
|
|
} |
212
|
|
|
\sasco\LibreDTE\Log::write(Estado::REQUEST_ERROR_SOAP, Estado::get(Estado::REQUEST_ERROR_SOAP, $msg)); |
|
|
|
|
213
|
|
|
$body = null; |
214
|
|
|
} |
215
|
|
|
} |
216
|
|
View Code Duplication |
if ($body===null) { |
|
|
|
|
217
|
|
|
\sasco\LibreDTE\Log::write(Estado::REQUEST_ERROR_BODY, Estado::get(Estado::REQUEST_ERROR_BODY, $wsdl, $retry)); |
|
|
|
|
218
|
|
|
return false; |
219
|
|
|
} |
220
|
|
|
return new \SimpleXMLElement($body, LIBXML_COMPACT); |
221
|
|
|
} |
222
|
|
|
|
223
|
|
|
/** |
224
|
|
|
* Método que permite indicar si se debe o no verificar el certificado SSL |
225
|
|
|
* del SII |
226
|
|
|
* @param verificar =true si se quiere verificar certificado, =false en caso que no (por defecto se verifica) |
227
|
|
|
* @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl) |
228
|
|
|
* @version 2015-09-16 |
229
|
|
|
*/ |
230
|
|
|
public static function setVerificarSSL($verificar = true) |
231
|
|
|
{ |
232
|
|
|
self::$verificar_ssl = $verificar; |
233
|
|
|
} |
234
|
|
|
|
235
|
|
|
/** |
236
|
|
|
* Método que indica si se está o no verificando el SSL en las conexiones al SII |
237
|
|
|
* @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl) |
238
|
|
|
* @version 2017-05-11 |
239
|
|
|
*/ |
240
|
|
|
public static function getVerificarSSL() |
241
|
|
|
{ |
242
|
|
|
return self::$verificar_ssl; |
243
|
|
|
} |
244
|
|
|
|
245
|
|
|
/** |
246
|
|
|
* Método que realiza el envío de un DTE al SII |
247
|
|
|
* Referencia: http://www.sii.cl/factura_electronica/factura_mercado/envio.pdf |
248
|
|
|
* @param usuario RUN del usuario que envía el DTE |
249
|
|
|
* @param empresa RUT de la empresa emisora del DTE |
250
|
|
|
* @param dte Documento XML con el DTE que se desea enviar a SII |
251
|
|
|
* @param token Token de autenticación automática ante el SII |
252
|
|
|
* @param retry Intentos que se realizarán como máximo para obtener respuesta |
253
|
|
|
* @return Respuesta XML desde SII o bien null si no se pudo obtener respuesta |
254
|
|
|
* @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl) |
255
|
|
|
* @version 2016-08-06 |
256
|
|
|
*/ |
257
|
|
|
public static function enviar($usuario, $empresa, $dte, $token, $retry = null) |
258
|
|
|
{ |
259
|
|
|
// definir datos que se usarán en el envío |
260
|
|
|
list($rutSender, $dvSender) = explode('-', str_replace('.', '', $usuario)); |
261
|
|
|
list($rutCompany, $dvCompany) = explode('-', str_replace('.', '', $empresa)); |
262
|
|
|
if (strpos($dte, '<?xml')===false) { |
263
|
|
|
$dte = '<?xml version="1.0" encoding="ISO-8859-1"?>'."\n".$dte; |
264
|
|
|
} |
265
|
|
View Code Duplication |
do { |
|
|
|
|
266
|
|
|
$file = sys_get_temp_dir().'/dte_'.md5(microtime().$token.$dte).'.xml'; |
267
|
|
|
} while (file_exists($file)); |
268
|
|
|
file_put_contents($file, $dte); |
269
|
|
|
$data = [ |
270
|
|
|
'rutSender' => $rutSender, |
271
|
|
|
'dvSender' => $dvSender, |
272
|
|
|
'rutCompany' => $rutCompany, |
273
|
|
|
'dvCompany' => $dvCompany, |
274
|
|
|
'archivo' => curl_file_create( |
275
|
|
|
$file, |
276
|
|
|
'application/xml', |
277
|
|
|
basename($file) |
278
|
|
|
), |
279
|
|
|
]; |
280
|
|
|
// definir reintentos si no se pasaron |
281
|
|
|
if (!$retry) |
282
|
|
|
$retry = self::$retry; |
283
|
|
|
// crear sesión curl con sus opciones |
284
|
|
|
$curl = curl_init(); |
285
|
|
|
$header = [ |
286
|
|
|
'User-Agent: Mozilla/4.0 (compatible; PROG 1.0; LibreDTE)', |
287
|
|
|
'Referer: https://libredte.cl', |
288
|
|
|
'Cookie: TOKEN='.$token, |
289
|
|
|
]; |
290
|
|
|
$url = 'https://'.self::$config['servidor'][self::getAmbiente()].'.sii.cl/cgi_dte/UPL/DTEUpload'; |
291
|
|
|
curl_setopt($curl, CURLOPT_POST, true); |
292
|
|
|
curl_setopt($curl, CURLOPT_POSTFIELDS, $data); |
293
|
|
|
curl_setopt($curl, CURLOPT_HTTPHEADER, $header); |
294
|
|
|
curl_setopt($curl, CURLOPT_URL, $url); |
295
|
|
|
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); |
296
|
|
|
// si no se debe verificar el SSL se asigna opción a curl, además si |
297
|
|
|
// se está en el ambiente de producción y no se verifica SSL se |
298
|
|
|
// generará una entrada en el log |
299
|
|
View Code Duplication |
if (!self::$verificar_ssl) { |
|
|
|
|
300
|
|
|
if (self::getAmbiente()==self::PRODUCCION) { |
301
|
|
|
$msg = Estado::get(Estado::ENVIO_SSL_SIN_VERIFICAR); |
302
|
|
|
\sasco\LibreDTE\Log::write(Estado::ENVIO_SSL_SIN_VERIFICAR, $msg, LOG_WARNING); |
|
|
|
|
303
|
|
|
} |
304
|
|
|
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); |
305
|
|
|
} |
306
|
|
|
// enviar XML al SII |
307
|
|
View Code Duplication |
for ($i=0; $i<$retry; $i++) { |
|
|
|
|
308
|
|
|
$response = curl_exec($curl); |
309
|
|
|
if ($response and $response!='Error 500') |
|
|
|
|
310
|
|
|
break; |
311
|
|
|
} |
312
|
|
|
unlink($file); |
313
|
|
|
// verificar respuesta del envío y entregar error en caso que haya uno |
314
|
|
|
if (!$response or $response=='Error 500') { |
|
|
|
|
315
|
|
|
if (!$response) |
|
|
|
|
316
|
|
|
\sasco\LibreDTE\Log::write(Estado::ENVIO_ERROR_CURL, Estado::get(Estado::ENVIO_ERROR_CURL, curl_error($curl))); |
|
|
|
|
317
|
|
|
if ($response=='Error 500') |
318
|
|
|
\sasco\LibreDTE\Log::write(Estado::ENVIO_ERROR_500, Estado::get(Estado::ENVIO_ERROR_500)); |
|
|
|
|
319
|
|
|
return false; |
320
|
|
|
} |
321
|
|
|
// cerrar sesión curl |
322
|
|
|
curl_close($curl); |
323
|
|
|
// crear XML con la respuesta y retornar |
324
|
|
|
try { |
325
|
|
|
$xml = new \SimpleXMLElement($response, LIBXML_COMPACT); |
326
|
|
|
} catch (Exception $e) { |
|
|
|
|
327
|
|
|
\sasco\LibreDTE\Log::write(Estado::ENVIO_ERROR_XML, Estado::get(Estado::ENVIO_ERROR_XML, $e->getMessage())); |
|
|
|
|
328
|
|
|
return false; |
329
|
|
|
} |
330
|
|
|
if ($xml->STATUS!=0) { |
331
|
|
|
\sasco\LibreDTE\Log::write( |
332
|
|
|
$xml->STATUS, |
333
|
|
|
Estado::get($xml->STATUS).(isset($xml->DETAIL)?'. '.implode("\n", (array)$xml->DETAIL->ERROR):'') |
|
|
|
|
334
|
|
|
); |
335
|
|
|
} |
336
|
|
|
return $xml; |
337
|
|
|
} |
338
|
|
|
|
339
|
|
|
/** |
340
|
|
|
* Método para obtener la clave pública (certificado X.509) del SII |
341
|
|
|
* |
342
|
|
|
* \code{.php} |
343
|
|
|
* $pub_key = \sasco\LibreDTE\Sii::cert(100); // Certificado IDK 100 (certificación) |
344
|
|
|
* \endcode |
345
|
|
|
* |
346
|
|
|
* @param idk IDK de la clave pública del SII. Si no se indica se tratará de determinar con el ambiente que se esté usando |
347
|
|
|
* @return Contenido del certificado |
348
|
|
|
* @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl) |
349
|
|
|
* @version 2015-09-16 |
350
|
|
|
*/ |
351
|
|
|
public static function cert($idk = null) |
352
|
|
|
{ |
353
|
|
|
// si se pasó un idk y existe el archivo asociado se entrega |
354
|
|
|
if ($idk) { |
355
|
|
|
$cert = dirname(dirname(__FILE__)).'/certs/'.$idk.'.cer'; |
356
|
|
|
if (is_readable($cert)) |
357
|
|
|
return file_get_contents($cert); |
358
|
|
|
} |
359
|
|
|
// buscar certificado y entregar si existe o =false si no |
360
|
|
|
$ambiente = self::getAmbiente(); |
361
|
|
|
$cert = dirname(dirname(__FILE__)).'/certs/'.self::$config['certs'][$ambiente].'.cer'; |
362
|
|
|
if (!is_readable($cert)) { |
363
|
|
|
\sasco\LibreDTE\Log::write(Estado::SII_ERROR_CERTIFICADO, Estado::get(Estado::SII_ERROR_CERTIFICADO, self::$config['certs'][$ambiente])); |
|
|
|
|
364
|
|
|
return false; |
365
|
|
|
} |
366
|
|
|
return file_get_contents($cert); |
367
|
|
|
} |
368
|
|
|
|
369
|
|
|
/** |
370
|
|
|
* Método que asigna el ambiente que se usará por defecto (si no está |
371
|
|
|
* asignado con la constante _LibreDTE_CERTIFICACION_) |
372
|
|
|
* @param ambiente Ambiente a usar: Sii::PRODUCCION o Sii::CERTIFICACION |
373
|
|
|
* @warning No se está verificando SSL en ambiente de certificación |
374
|
|
|
* @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl) |
375
|
|
|
* @version 2016-08-28 |
376
|
|
|
*/ |
377
|
|
|
public static function setAmbiente($ambiente = self::PRODUCCION) |
378
|
|
|
{ |
379
|
|
|
$ambiente = $ambiente ? self::CERTIFICACION : self::PRODUCCION; |
380
|
|
|
if ($ambiente==self::CERTIFICACION) { |
381
|
|
|
self::setVerificarSSL(false); |
382
|
|
|
} |
383
|
|
|
self::$ambiente = $ambiente; |
384
|
|
|
} |
385
|
|
|
|
386
|
|
|
/** |
387
|
|
|
* Método que determina el ambiente que se debe utilizar: producción o |
388
|
|
|
* certificación |
389
|
|
|
* @param ambiente Ambiente a usar: Sii::PRODUCCION o Sii::CERTIFICACION o null (para detección automática) |
390
|
|
|
* @return Ambiente que se debe utilizar |
391
|
|
|
* @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl) |
392
|
|
|
* @version 2015-09-07 |
393
|
|
|
*/ |
394
|
|
|
public static function getAmbiente($ambiente = null) |
395
|
|
|
{ |
396
|
|
|
if ($ambiente===null) { |
397
|
|
|
if (defined('_LibreDTE_CERTIFICACION_')) |
398
|
|
|
$ambiente = (int)_LibreDTE_CERTIFICACION_; |
|
|
|
|
399
|
|
|
else |
400
|
|
|
$ambiente = self::$ambiente; |
|
|
|
|
401
|
|
|
} |
402
|
|
|
return $ambiente; |
403
|
|
|
} |
404
|
|
|
|
405
|
|
|
/** |
406
|
|
|
* Método que entrega la tasa de IVA vigente |
407
|
|
|
* @return Tasa de IVA vigente |
408
|
|
|
* @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl) |
409
|
|
|
* @version 2015-09-03 |
410
|
|
|
*/ |
411
|
|
|
public static function getIVA() |
412
|
|
|
{ |
413
|
|
|
return self::IVA; |
414
|
|
|
} |
415
|
|
|
|
416
|
|
|
/** |
417
|
|
|
* Método que entrega un arreglo con todos los datos de los contribuyentes |
418
|
|
|
* que operan con factura electrónica descargados desde el SII |
419
|
|
|
* @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl) |
420
|
|
|
* @version 2017-07-07 |
421
|
|
|
*/ |
422
|
|
|
public static function getContribuyentes(\sasco\LibreDTE\FirmaElectronica $Firma, $ambiente = null, $dia = null) |
423
|
|
|
{ |
424
|
|
|
// solicitar token |
425
|
|
|
$token = \sasco\LibreDTE\Sii\Autenticacion::getToken($Firma); |
|
|
|
|
426
|
|
|
if (!$token) |
|
|
|
|
427
|
|
|
return false; |
428
|
|
|
// definir ambiente y servidor |
429
|
|
|
$ambiente = self::getAmbiente($ambiente); |
430
|
|
|
$servidor = self::$config['servidor'][$ambiente]; |
431
|
|
|
// preparar consulta curl |
432
|
|
|
$curl = curl_init(); |
433
|
|
|
$header = [ |
434
|
|
|
'User-Agent: Mozilla/4.0 (compatible; PROG 1.0; LibreDTE)', |
435
|
|
|
'Referer: https://'.$servidor.'.sii.cl/cvc/dte/ee_empresas_dte.html', |
436
|
|
|
'Cookie: TOKEN='.$token, |
437
|
|
|
'Accept-Encoding' => 'gzip, deflate, sdch', |
438
|
|
|
]; |
439
|
|
|
$dia = $dia===null ? date('Ymd') : str_replace('-', '', $dia); |
440
|
|
|
$url = 'https://'.$servidor.'.sii.cl/cvc_cgi/dte/ce_empresas_dwnld?NOMBRE_ARCHIVO=ce_empresas_dwnld_'.$dia.'.csv'; |
441
|
|
|
curl_setopt($curl, CURLOPT_HTTPHEADER, $header); |
442
|
|
|
curl_setopt($curl, CURLOPT_URL, $url); |
443
|
|
|
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); |
444
|
|
|
// si no se debe verificar el SSL se asigna opción a curl, además si |
445
|
|
|
// se está en el ambiente de producción y no se verifica SSL se |
446
|
|
|
// generará un error de nivel E_USER_NOTICE |
447
|
|
View Code Duplication |
if (!self::$verificar_ssl) { |
|
|
|
|
448
|
|
|
if ($ambiente==self::PRODUCCION) { |
449
|
|
|
$msg = Estado::get(Estado::ENVIO_SSL_SIN_VERIFICAR); |
450
|
|
|
\sasco\LibreDTE\Log::write(Estado::ENVIO_SSL_SIN_VERIFICAR, $msg, LOG_WARNING); |
|
|
|
|
451
|
|
|
} |
452
|
|
|
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); |
453
|
|
|
} |
454
|
|
|
// realizar consulta curl |
455
|
|
|
$response = curl_exec($curl); |
456
|
|
|
if (!$response) |
457
|
|
|
return false; |
458
|
|
|
// cerrar sesión curl |
459
|
|
|
curl_close($curl); |
460
|
|
|
// entregar datos del archivo CSV |
461
|
|
|
ini_set('memory_limit', '1024M'); |
462
|
|
|
$lines = explode("\n", $response); |
463
|
|
|
$n_lines = count($lines); |
464
|
|
|
$data = []; |
465
|
|
View Code Duplication |
for ($i=1; $i<$n_lines; $i++) { |
|
|
|
|
466
|
|
|
$row = str_getcsv($lines[$i], ';', ''); |
467
|
|
|
unset($lines[$i]); |
468
|
|
|
if (!isset($row[5])) |
469
|
|
|
continue; |
470
|
|
|
for ($j=0; $j<6; $j++) |
471
|
|
|
$row[$j] = trim($row[$j]); |
472
|
|
|
$row[1] = utf8_decode($row[1]); |
473
|
|
|
$row[4] = strtolower($row[4]); |
474
|
|
|
$row[5] = strtolower($row[5]); |
475
|
|
|
$data[] = $row; |
476
|
|
|
} |
477
|
|
|
return $data; |
478
|
|
|
} |
479
|
|
|
|
480
|
|
|
|
481
|
|
|
/** |
482
|
|
|
* Método que entrega un arreglo con todas los documentos tributarios electrónicos |
483
|
|
|
* recepcionados en el SII |
484
|
|
|
* @param Firma digital del certificado |
485
|
|
|
* @param Ambiente de prueba |
486
|
|
|
* @param Rut empresa a consultar |
487
|
|
|
* @param Desde fecha a consultar. Formato d-m-Y |
488
|
|
|
* @param Hasta fecha a consultar. Formato d-m-Y |
489
|
|
|
* @return Dirección regional del SII |
490
|
|
|
* [0] Lin. |
491
|
|
|
* [1] Rut del Emisor |
492
|
|
|
* [2] Razón Social Emisor |
493
|
|
|
* [3] Tipo Dte |
494
|
|
|
* [4] Folio Dte |
495
|
|
|
* [5] Fecha Emisión |
496
|
|
|
* [6] Monto Total |
497
|
|
|
* [7] Fecha Recepción |
498
|
|
|
* [8] Número Envío |
499
|
|
|
* @author Adonias Vasquez (adonias.vasquez[at]epys.cl) |
500
|
|
|
* @version 2017-09-30 |
501
|
|
|
*/ |
502
|
|
|
public static function getDTERecibidos(\sasco\LibreDTE\FirmaElectronica $Firma, $ambiente = null, $rut = null, $desde = null, $hasta = null) |
503
|
|
|
{ |
504
|
|
|
// solicitar token |
505
|
|
|
$token = \sasco\LibreDTE\Sii\Autenticacion::getToken($Firma); |
|
|
|
|
506
|
|
|
if (!$token) |
|
|
|
|
507
|
|
|
return false; |
508
|
|
|
|
509
|
|
|
// RUT_NS Y DV_NS |
510
|
|
|
list($rut_ns, $dv_ns) = explode('-', $rut); |
511
|
|
|
|
512
|
|
|
// definir ambiente y servidor |
513
|
|
|
$ambiente = self::getAmbiente($ambiente); |
514
|
|
|
$servidor = self::$config['servidor'][$ambiente]; |
515
|
|
|
|
516
|
|
|
// preparar consulta curl |
517
|
|
|
$curl = curl_init(); |
518
|
|
|
$header = [ |
519
|
|
|
'User-Agent: User-Agent: Mozilla/4.0 (compatible; PROG 1.0; LibreDTE)', |
520
|
|
|
'Referer: https://' . $servidor . '.sii.cl', |
521
|
|
|
'Cookie: TOKEN=' . $token . '; RUT_NS='.$rut_ns.'; DV_NS='.$dv_ns, |
522
|
|
|
'Accept-Encoding' => 'gzip, deflate, sdch', |
523
|
|
|
]; |
524
|
|
|
$desde = $desde === null ? date('01-m-Y', strtotime('-60 days')) : $desde; // Consulto últimos 2 meses |
525
|
|
|
$hasta = $hasta === null ? date('d-m-Y') : $hasta; //Hasta fecha actual |
526
|
|
|
|
527
|
|
|
$fields = [ |
528
|
|
|
'DESDE' => urlencode($desde), |
529
|
|
|
'HASTA' => urlencode($hasta), |
530
|
|
|
'RUT' => urlencode($rut), |
531
|
|
|
'TIPO_CONSULTA' => '' // En blancopara que genere un CSV |
532
|
|
|
]; |
533
|
|
|
|
534
|
|
|
$url = 'https://' . $servidor . '.sii.cl/cgi_dte/consultaDTE/wsDTEConsRecCont.sh'; |
535
|
|
|
curl_setopt($curl, CURLOPT_HTTPHEADER, $header); |
536
|
|
|
curl_setopt($curl, CURLOPT_URL, $url); |
537
|
|
|
curl_setopt($curl, CURLOPT_POST, 1); |
538
|
|
|
curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($fields)); |
539
|
|
|
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); |
540
|
|
|
curl_setopt($curl, CURLINFO_HEADER_OUT, true); // capture the header info |
541
|
|
|
|
542
|
|
|
|
543
|
|
|
// si no se debe verificar el SSL se asigna opción a curl, además si |
544
|
|
|
// se está en el ambiente de producción y no se verifica SSL se |
545
|
|
|
// generará un error de nivel E_USER_NOTICE |
546
|
|
View Code Duplication |
if (!self::$verificar_ssl) { |
|
|
|
|
547
|
|
|
if ($ambiente == self::PRODUCCION) { |
548
|
|
|
$msg = Estado::get(Estado::ENVIO_SSL_SIN_VERIFICAR); |
549
|
|
|
\sasco\LibreDTE\Log::write(Estado::ENVIO_SSL_SIN_VERIFICAR, $msg, LOG_WARNING); |
|
|
|
|
550
|
|
|
} |
551
|
|
|
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); |
552
|
|
|
} |
553
|
|
|
|
554
|
|
|
// realizar consulta curl |
555
|
|
|
$response = curl_exec($curl); |
556
|
|
|
if (!$response) |
557
|
|
|
return false; |
558
|
|
|
// cerrar sesión curl |
559
|
|
|
curl_close($curl); |
560
|
|
|
// entregar datos del archivo CSV |
561
|
|
|
ini_set('memory_limit', '1024M'); |
562
|
|
|
$lines = explode("\n", $response); |
563
|
|
|
$n_lines = count($lines); |
564
|
|
|
$data = []; |
565
|
|
View Code Duplication |
for ($i = 1; $i < $n_lines; $i++) { |
|
|
|
|
566
|
|
|
$row = str_getcsv($lines[$i], ';', ''); |
567
|
|
|
unset($lines[$i]); |
568
|
|
|
for ($j = 0; $j < 6; $j++) |
569
|
|
|
$row[$j] = trim($row[$j]); |
570
|
|
|
$row[1] = utf8_decode($row[1]); |
571
|
|
|
$row[2] = utf8_decode($row[2]); |
572
|
|
|
$row[3] = utf8_decode($row[3]); |
573
|
|
|
|
574
|
|
|
$data[] = $row; |
575
|
|
|
} |
576
|
|
|
|
577
|
|
|
return $data; |
578
|
|
|
} |
579
|
|
|
/** |
580
|
|
|
* Método que entrega la dirección regional según la comuna que se esté |
581
|
|
|
* consultando |
582
|
|
|
* @param comuna de la sucursal del emior o bien código de la sucursal del SII |
583
|
|
|
* @return Dirección regional del SII |
584
|
|
|
* @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl) |
585
|
|
|
* @version 2016-06-03 |
586
|
|
|
*/ |
587
|
|
|
public static function getDireccionRegional($comuna) |
588
|
|
|
{ |
589
|
|
|
if (!is_numeric($comuna)) { |
590
|
|
|
$direccion = mb_strtoupper($comuna, 'UTF-8'); |
591
|
|
|
return isset(self::$direcciones_regionales[$direccion]) ? self::$direcciones_regionales[$direccion] : $direccion; |
592
|
|
|
} |
593
|
|
|
return 'SUC '.$comuna; |
|
|
|
|
594
|
|
|
} |
595
|
|
|
|
596
|
|
|
} |
597
|
|
|
|
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.
Let’s take a look at an example:
Our function
my_function
expects aPost
object, and outputs the author of the post. The base classPost
returns a simple string and outputting a simple string will work just fine. However, the child classBlogPost
which is a sub-type ofPost
instead decided to return anobject
, and is therefore violating the SOLID principles. If aBlogPost
were passed tomy_function
, PHP would not complain, but ultimately fail when executing thestrtoupper
call in its body.