Biała Lista Ministerstwa Finansów – automatyzacja z pliku

Od 1 września 2019 roku Ministerstwo Finansów udostępnia wykaz podatników VAT, tj. wykaz podmiotów zarejestrowanych, niezarejestrowanych oraz wykreślonych i przywróconych do rejestru VAT. Jest to tzw. biała lista podatników VAT. W wielu firmach pojawi się potrzeba automatycznego sprawdzania kont podatników.

Pisałem już o tym temacie w poście, gdzie skupiłem się na informacjach podstawowych i na API pomagającym w sprawdzaniu podatników.

Przede wszystkim w tym wpisie skupię się na alternatywnej metodzie, polegającej na sprawdzeniu z pliku płaskiego, który będzie udostępniany raz dziennie. Szczegółowe informacje można znaleźć pod linkiem. Jest to metoda, która może posłużyć jako awaryjne rozwiązanie w sytuacji, w której API przestanie działać lub wyczerpiemy limit API.

Biała lista podatników – rozwiązanie

Opracowałem rozwiązanie, które powinno działać w momencie awarii API i strony służącej do ręcznego sprawdzania. Ministerstwo raz dziennie będzie udostępniało plik płaski, który będę pobierał do swojej bazy danych za pomocą skryptu wywoływanego z cron’a. Plik jest parsowany i dane trafiają do bazy. Aktualizacja: plik jest spakowany 7zip’em. Jestem w trakcie opracowania metody rozpakowującej plik za pomocą lokalnej biblioteki albo za pomocą stron rozpakowujących pliki online. Ewentualnie rozważam użycie dodatkowego serwera, żeby wrzucać gotowy plik na serwer.

<?php
  require_once 'db.php';

  $url = 'https://www.gov.pl/attachment/3feb12c9-01b5-4186-b5af-80af9610bd84';
  $whitelist = json_decode(file_get_contents($url));

    try {
      $pdo = new PDO('mysql:host='.$mysql_host.';dbname='.$mysql_database.'', $mysql_user, $mysql_password);
      $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

      $sql = "DELETE FROM wl_masks";
      $sql2 = "DELETE FROM wl_hashes";
      $sql3 = "DELETE FROM wl_naglowek";
      $stmt= $pdo->prepare($sql);
      $stmt->execute();
      $stmt= $pdo->prepare($sql2);
      $stmt->execute();
      $stmt= $pdo->prepare($sql3);
      $stmt->execute();
      $stmt->closeCursor();
      $stmt = null;
      $pdo = null;
    } catch (PDOException $e) {
      print "Error!: " . $e->getMessage() . "<br/>";
      die();
    }
	
    try {
      $pdo = new PDO('mysql:host='.$mysql_host.';dbname='.$mysql_database.'', $mysql_user, $mysql_password);
      $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  
      $data = [
          'liczbatransformacji' => $whitelist->naglowek->liczbatransformacji,
          'datagenerowaniadanych' => $whitelist->naglowek->datagenerowaniadanych,
          'schemat' => $whitelist->naglowek->schemat,
      ];
      $sql = "INSERT INTO wl_naglowek (WL_LICZBATRANSFORMACJI, WL_DATAGENEROWANIADANYCH, WL_SCHEMAT) VALUES (:liczbatransformacji, :datagenerowaniadanych, :schemat)";
      $stmt= $pdo->prepare($sql);
      $stmt->execute($data);
      $stmt->closeCursor();
      $stmt = null;
      $pdo = null;
    } catch (PDOException $e) {
      print "Error!: " . $e->getMessage() . "<br/>";
      die();
    }

  foreach($whitelist->maski as $key => $value){
    try {
      $pdo = new PDO('mysql:host='.$mysql_host.';dbname='.$mysql_database.'', $mysql_user, $mysql_password);
      $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  
      $data = [
          'mask' => $value,
      ];
      $sql = "INSERT INTO wl_masks (WL_MASK, WL_ADD_DATE) VALUES (:mask, SYSDATE())";
      $stmt= $pdo->prepare($sql);
      $stmt->execute($data);
      $stmt->closeCursor();
      $stmt = null;
      $pdo = null;
    } catch (PDOException $e) {
      print "Error!: " . $e->getMessage() . "<br/>";
      die();
    }
  }

  foreach($whitelist->{'skroty podatnikow czynnych'} as $key => $value){
    echo $value."<br>";

    try {
      $pdo = new PDO('mysql:host='.$mysql_host.';dbname='.$mysql_database.'', $mysql_user, $mysql_password);
      $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

      $data = [
          'hash' => $value,
      ];
      $sql = "INSERT INTO wl_hashes (WL_HASH, WL_ADD_DATE, WL_POD_CZYNNY) VALUES (:hash, SYSDATE(), 'T')";
      $stmt= $pdo->prepare($sql);
      $stmt->execute($data);
      $stmt->closeCursor();
      $stmt = null;
      $pdo = null;
    } catch (PDOException $e) {
      print "Error!: " . $e->getMessage() . "<br/>";
      die();
    }
  }

  foreach($whitelist->{'skroty podatnikow zwolnionych'} as $key => $value){
    echo $value."<br>";

    try {
      $pdo = new PDO('mysql:host='.$mysql_host.';dbname='.$mysql_database.'', $mysql_user, $mysql_password);
      $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

      $data = [
          'hash' => $value,
      ];
      $sql = "INSERT INTO wl_hashes (WL_HASH, WL_ADD_DATE, WL_POD_ZWOLNIONY) VALUES (:hash, SYSDATE(), 'T')";
      $stmt= $pdo->prepare($sql);
      $stmt->execute($data);
      $stmt->closeCursor();
      $stmt = null;
      $pdo = null;
    } catch (PDOException $e) {
      print "Error!: " . $e->getMessage() . "<br/>";
      die();
    }
  }
?>

Alternatywnie opracowałem rozwiązanie, które może wczytywać plik strumieniowo. Gdyby ktoś miał dodatkowo problem z ograniczoną ilością zapytań do bazy to w skrypcie jest licznik, dzięki któremu można rozłożyć wczytywanie na kilka etapów. Kod poniżej:

<?php
  require_once 'db.php';

  $url = 'https://automatykanacodzien.pl/bialalista/plikplaski/20191217.json';
  
  $i = 1;
  $jsonParam1 = '"naglowek"';
  $jsonParam2 = '"skrotyPodatnikowCzynnych"';
  $jsonParam3 = '"skrotyPodatnikowZwolnionych"';
  $jsonParam4 = '"maski"';
  $isHeader = false;
  $isSPC = false;
  $isSPZ = false;
  $isMask = false;
  $jsonHeaderParam1 = "dataGenerowaniaDanych";
  $jsonHeaderParam2 = "liczbaTransformacji";
  $jsonHeaderParam3 = "schemat";
  $jsonHeaderValue1 = "";
  $jsonHeaderValue2 = "";
  $jsonHeaderValue3 = "";
  
  if ($handle = fopen($url, "r")) {

    try {
      $pdo = new PDO('mysql:host='.$mysql_host.';dbname='.$mysql_database.'', $mysql_user, $mysql_password);
      $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

      $sql = "DELETE FROM wl_masks";
      $sql2 = "DELETE FROM wl_hashes";
      $sql3 = "DELETE FROM wl_naglowek";
      $stmt= $pdo->prepare($sql);
      $stmt->execute();
      $stmt= $pdo->prepare($sql2);
      $stmt->execute();
      $stmt= $pdo->prepare($sql3);
      $stmt->execute();
      $stmt->closeCursor();
      $stmt = null;
      $pdo = null;
    } catch (PDOException $e) {
      print "Error!: " . $e->getMessage() . "<br/>";
      die();
    }
    
    while (($buffer = fgets($handle, 4096)) !== false) {
      if (strpos($buffer, $jsonParam1) !== false) {
        $isHeader = true;
        $isSPC = false;
        $isSPZ = false;
        $isMask = false;
      }
	  else if (strpos($buffer, $jsonParam2) !== false) {
            //wczytaj naglowek jak przechodzimy do nastepnej sekcji
              echo "<br>";
              echo 'naglowek:'."<br>";
              echo 'liczbatransformacji:'.$jsonHeaderValue1."<br>";
              echo 'datagenerowaniadanych:'.$jsonHeaderValue2."<br>";
              echo 'schemat:'.$jsonHeaderValue3."<br>";
                try {
                  $pdo = new PDO('mysql:host='.$mysql_host.';dbname='.$mysql_database.'', $mysql_user, $mysql_password);
                  $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  
                  $data = [
                      'liczbatransformacji' => $jsonHeaderValue1,
                      'datagenerowaniadanych' => $jsonHeaderValue2,
                      'schemat' => $jsonHeaderValue3,
                  ];
                  $sql = "INSERT INTO wl_naglowek (WL_LICZBATRANSFORMACJI, WL_DATAGENEROWANIADANYCH, WL_SCHEMAT) VALUES (:liczbatransformacji, :datagenerowaniadanych, :schemat)";
                  $stmt= $pdo->prepare($sql);
                  $stmt->execute($data);
                  $stmt->closeCursor();
                  $stmt = null;
                  $pdo = null;
                } catch (PDOException $e) {
                  print "Error!: " . $e->getMessage() . "<br/>";
                  die();
                }

            $isHeader = false;
            $isSPC = true;
            $isSPZ = false;
            $isMask = false;
	  }
	  else if (strpos($buffer, $jsonParam3) !== false) {
            $isHeader = false;
            $isSPC = false;
            $isSPZ = true;
            $isMask = false;
	  }
	  else if (strpos($buffer, $jsonParam4) !== false) {
            $isHeader = false;
            $isSPC = false;
            $isSPZ = false;
            $isMask = true;
	  }
	  
	  if ($isHeader) {
	    $pos = false;
	    $pos = strpos($buffer, $jsonHeaderParam1);
		if ($pos !== false) {
		  $posStart = strpos($buffer, "\"", $pos + strlen($jsonHeaderParam1)+1);
		  $posEnd = strpos($buffer, "\"", $posStart+1);
		  $jsonHeaderValue1 = substr($buffer, $posStart + 1, $posEnd - $posStart-1);
		}
	    $pos = false;
	    $pos = strpos($buffer, $jsonHeaderParam2);
		if ($pos !== false) {
		  $posStart = strpos($buffer, "\"", $pos + strlen($jsonHeaderParam2)+1);
		  $posEnd = strpos($buffer, "\"", $posStart+1);
		  $jsonHeaderValue2 = substr($buffer, $posStart + 1, $posEnd - $posStart-1);
		}
	    $pos = false;
	    $pos = strpos($buffer, $jsonHeaderParam3);
		if ($pos !== false) {
		  $posStart = strpos($buffer, "\"", $pos + strlen($jsonHeaderParam3)+1);
		  $posEnd = strpos($buffer, "\"", $posStart+1);
		  $jsonHeaderValue3 = substr($buffer, $posStart + 1, $posEnd - $posStart-1);
		}
	  }
	  else if ($isSPC) {
		$posStart = strpos($buffer, "\"");
		$posEnd = strpos($buffer, "\"", $posStart+1);
                if ($posStart !== false && $posEnd !== false) {
                  $hashToDB = NULL;
		  $hashToDB = substr($buffer, $posStart + 1, $posEnd - $posStart-1);
                  if (isset($hashToDB)) {

                    try {
                          $pdo = new PDO('mysql:host='.$mysql_host.';dbname='.$mysql_database.'', $mysql_user, $mysql_password);
                          $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
                    
                          $data = [
                              'hash' => $hashToDB,
                          ];
                          $sql = "INSERT INTO wl_hashes (WL_HASH, WL_ADD_DATE, WL_POD_CZYNNY) VALUES (:hash, SYSDATE(), 'T')";
                          $stmt= $pdo->prepare($sql);
                          $stmt->execute($data);
                          $stmt->closeCursor();
                          $stmt = null;
                          $pdo = null;
                        } catch (PDOException $e) {
                          print "Error!: " . $e->getMessage() . "<br/>";
                          die();
                        }

                  }
                }
	  }
	  else if ($isSPZ) {
		$posStart = strpos($buffer, "\"");
		$posEnd = strpos($buffer, "\"", $posStart+1);
                if ($posStart !== false && $posEnd !== false) {
                  $hash2ToDB = NULL;
		  $hash2ToDB = substr($buffer, $posStart + 1, $posEnd - $posStart-1);
                  if (isset($hash2ToDB)) {

                    try {
                      $pdo = new PDO('mysql:host='.$mysql_host.';dbname='.$mysql_database.'', $mysql_user, $mysql_password);
                      $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
                
                      $data = [
                          'hash' => $value,
                      ];
                      $sql = "INSERT INTO wl_hashes (WL_HASH, WL_ADD_DATE, WL_POD_ZWOLNIONY) VALUES (:hash, SYSDATE(), 'T')";
                      $stmt= $pdo->prepare($sql);
                      $stmt->execute($data);
                      $stmt->closeCursor();
                      $stmt = null;
                      $pdo = null;
                    } catch (PDOException $e) {
                      print "Error!: " . $e->getMessage() . "<br/>";
                      die();
                    }

                  }
                }
	  }
	  else if ($isMask) {
	    $posStart = strpos($buffer, "\"");
	    $posEnd = strpos($buffer, "\"", $posStart+1);
            if ($posStart !== false && $posEnd !== false) {
              $maskToDB = NULL;
	      $maskToDB = substr($buffer, $posStart + 1, $posEnd - $posStart-1);
              if (isset($maskToDB)) {

                  try {
                    $pdo = new PDO('mysql:host='.$mysql_host.';dbname='.$mysql_database.'', $mysql_user, $mysql_password);
                    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
                
                    $data = [
                        'mask' => $maskToDB,
                    ];
                    $sql = "INSERT INTO wl_masks (WL_MASK, WL_ADD_DATE) VALUES (:mask, SYSDATE())";
                    $stmt= $pdo->prepare($sql);
                    $stmt->execute($data);
                    $stmt->closeCursor();
                    $stmt = null;
                    $pdo = null;
                  } catch (PDOException $e) {
                    print "Error!: " . $e->getMessage() . "<br/>";
                    die();
                  }

              }
            }
	  }
	  
	  $i++;
	  $pos = false;
	}
	if (!feof($handle)) {
	  echo "Error: unexpected fgets() fail\n";
	}
	fclose($handle);
  }
  
  echo 'koniec odczytu';

?>

Plik zawiera tzw. hashe i maski. Hashe służą do weryfikacji par NIP:Numer konta. Maski pomocne są przy kontach wirtualnych. Przykładowy plik:

{
	"naglowek": {
		"datagenerowaniadanych": "20191018",
		"liczbatransformacji": "5000",
		"schemat": "RRRRMMDDNNNNNNNNNNBBBBBBBBBBBBBBBBBBBBBBBBBB, gdzie R to cyfra roku, M – miesiaca, D - dnia daty generowania pliku, N - cyfra NIPu, a B – cyfra rachunku bankowego"
	},
	"skroty podatnikow czynnych": [
		"e80dad0664355227bb8f28c854467f6ff6eb10d05f7a6d23f3a23788788ba69f87dec801fd897a0f57bc674c87022f73807e48b1947569a36e2034835f02a436",
		"371a4fbf2b393338d0a6c619ec18fe5f636fdc1992c09763acfd5dae0bb97b13359fb091e4a196ba085d1a60a312733d6a384e937e32c9aef7063c7911d46b84",
		"ef23fa00679d0ea058bd08ad8347253b04bb433c7ae251280d50aa25a4ebac5f961e9411035e8b5e1cddd815d21ae15d0ca655177c4e2ee5e1af5171adb1ac3a",
		"140f03c7332d0c55af68e1aa7794b96eb230796cb4ee434831ddd4cba3a635477b47ca1d5ae521a32845686dddca281767eb8ebc0bef260c184c241c583f7e41",
		"3739f6cf2a979cf66f3274ef40115d764e72a8f4efa327196f3b8eed192e118b668c4c2b914e6f45ec19f4ea0a8ed42931f7371b661e02e4536eef9717032d98",
		"518532d7d459a95991bab75ff61d4840ef911d107c3d8c36e470af91e60558daab3a8ec9e8a45f6f92b3da516f6ce6dc503c86fdf5bf1e434fd4e952831afaff",
		"58d93e935797ec83aab8a83ed8087a1c24a6a4a45b20918494f19188f8469b6259898646433764e4c33dfebf5831c277ee21b382ae6d2c091c2a0f32bb643635",
		"1166a098291d26d73894033965dd6099fcb5ee0ae4d924a7a40592f702ca7c6bf419d8ca0c57240de48834024008c081a9a5ce3555cdc2ff504a3f3bba68c1d6"
	],
	"skroty podatnikow zwolnionych": [
		"e1dea3f0c38361ff5da4e495f3a3994c2b35f5bc21d71b617153b072b07300fd2f89a96030741b6bf4399e5c9c65ff296ff85dd007ed13b832c8e5c94702eb66",
		"a7eccea27d100dfe4d9a9665734263fef308ff0c20726e3a3f94e9f37cd287b2857e83465df7ea708d2f3e6c7f93fc73b5c903cc8755760a5bc09d3098c933a2"
	],
	"maski": [
		"XX72123370XXXXXXXYYYXXXXXX",
		"XX54383912XXXXYYYYYXXXXXXX",
		"XX72124011XXXXXXXXXXYYYYYY",
		"XX33401111XXXXXXXXYYYYXXXX"
	]
}

Mając tak zebrane dane mogę na ich podstawie dokonywać weryfikacji, więc stworzę do tego celu prostą stronkę dostępną pod adresem: https://automatykanacodzien.pl/bialalista/

biała lista

Pod przyciskiem „Weryfikuj” kryje się kod napisany w php.

<?php
  if (isset($_POST['data']) && !empty($_POST['data']) &&
       isset($_POST['nip']) && !empty($_POST['nip']) &&
       isset($_POST['numerkonta']) && !empty($_POST['numerkonta']) ){
    $t_data = date('Ymd', strtotime($_POST['data']));
    $t_nip = preg_replace('/[^0-9]/', '',  $_POST['nip']);
    $t_numerkonta = preg_replace('/[^0-9]/', '',  $_POST['numerkonta']);
    
    require_once 'selectHash.php';
  }
?>
<?php
require_once '_db.php';

$liczba_transformacji = 1;
$stmt = $db->prepare('
SELECT WL_LICZBATRANSFORMACJI AS WL_LICZBA_TRANSFORMACJI
  FROM wl_naglowek');
$stmt->execute();
$result = $stmt->fetchAll( PDO::FETCH_ASSOC );
foreach($result as $row) {
  $liczba_transformacji = $row['WL_LICZBA_TRANSFORMACJI'];
}
unset($stmt);


function hashn($algo, $data, $n): string {
  $output = $data;
  for($i = 1; $i <= $n; $i++) {
    $output = hash($algo, $output);
    //$output = str_pad(hash($algo, $output), 128, "0", STR_PAD_LEFT);
  }
  return $output;
}


    $wsad = $t_data.$t_nip.$t_numerkonta;
    $hash = hashn('sha512',$wsad,$liczba_transformacji);
    echo '<br />';
    echo $hash;

$stmt = $db->prepare('
SELECT count(*) as ILOSC
  FROM wl_hashes
 WHERE wl_hash = :hash');

$stmt->bindParam(':hash', $hash);
$stmt->execute();
$result = $stmt->fetchAll( PDO::FETCH_ASSOC );
$check_result = false;

foreach($result as $row) {
  if ($row['ILOSC']==1) {
    $check_result = true;
  }
  else {
    $check_result = false;
  }
}

unset($stmt);

if ($check_result) {
  echo '<br/>jest';
}
else {
  $stmt = $db->prepare('
  SELECT WL_MASK
    FROM wl_masks
   WHERE WL_MASK LIKE :mask_part ');

  $mask_part = '%'.substr($t_numerkonta, 2, 8).'%';
  $stmt->bindParam(':mask_part', $mask_part);
  $stmt->execute();
  $result = $stmt->fetchAll( PDO::FETCH_ASSOC );
  $check_result_mask = false;

  foreach($result as $row) {
    echo '<br/>sprawdzam maske: '.$row['WL_MASK'];

        $t_maska = $row['WL_MASK'];
        
        /*$start = strpos($t_maska, 'Y');
        $ile = preg_match_all('/[Y]/', $t_maska, $t_ile);
        $t_nksubstr = substr($t_numerkonta, $start, $ile);
        $t_nkmaska = preg_replace('/[Y]+/', $t_nksubstr,  $t_maska);*/
        //aktualizacja
        $t_nkmaska = $t_maska;
	$length=strlen($t_maska);
	for ($i=0; $i<$length; $i++){
	  if ($t_maska[$i]=='Y'){
	    $t_nkmaska[$i]=$t_numerkonta[$i];
	  }
	}
        
        $wsad_maska = $t_data.$t_nip.$t_nkmaska;
        $hash_mask = hashn('sha512',$wsad_maska,$liczba_transformacji);

    $stmt = $db->prepare('
    SELECT count(*) as ILOSC
      FROM wl_hashes
     WHERE wl_hash = :hash');

    $stmt->bindParam(':hash', $hash_mask);
    $stmt->execute();
    $result = $stmt->fetchAll( PDO::FETCH_ASSOC );

    foreach($result as $row) {
      if ($row['ILOSC']==1) {
        $check_result_mask = true;
      }
      else {
      }
    }
    if ($check_result_mask) {
      break;
    }
  }

  unset($stmt);

  if ($check_result_mask) {
    echo '<br/>jest';
  }
  else {
    echo '<br/>nie ma';
  }

}
?>

Takie rozwiązanie pozwoli każdemu sprawdzić podatnika i jego numer rachunku w dniu dzisiejszym teoretycznie bez limitów. Plan awaryjny zrealizowany, ale mam nadzieję, że nie trzeba będzie z niego korzystać.

Biała lista podatników – Json Machine

Otrzymałem od Michała dodatkowe rozwiązanie używające Json Machine. Za Jego zgodą zamieszczam kod poniżej. Dzięki Michał.

<?php
//parametry połaczenia z bazą
define('MYSQL_HOST',"localhost");
define('MYSQL_DATABASE',"bialalista");
define('MYSQL_USER',"user");
define('MYSQL_PASSWORD',"pass");
//parametry skryptu
//scieżka do zapisu plików i loga
define('DEST','/home/user/bl/');
/*Logowanie danych
txt - do pliku
scr - na konsole
none - brak logowania*/
define('LOG',"scr");
/* shell do rozpakowania 7 zipa
{dest} - zamieniany na folder docelowy
{json7z} - zamieniany na pełną scieżke do pliku */
define('Z7COMMAND','7zr e -o{dest} {json7z}');
?>
<?php
use Archive7z\Archive7z;
function downloadFile($url, $dest)
{
    logmsg("\t\tPobieranie pliku: $url");
  $options = array(
    CURLOPT_FILE => is_resource($dest) ? $dest : fopen($dest, 'w'),
    CURLOPT_FOLLOWLOCATION => true,
    CURLOPT_URL => $url,
    CURLOPT_FAILONERROR => true, // HTTP code > 400 will throw curl error
    CURLOPT_SSL_VERIFYHOST => 0,
    CURLOPT_SSL_VERIFYPEER => 0
  );

  $ch = curl_init();
  curl_setopt_array($ch, $options);
  $return = curl_exec($ch);

  if ($return === false)
  {
    logmsg("\t\t\tPOBRANIE PLIKU NIE POWIODŁO SIĘ");
    return false;
  }
  else
  {
    return true;
  }
}
function UR_exists($url){
    logmsg("\t\tSprawdzenie czy istnieje plik: $url");
    $headers=get_headers($url);
    return stripos($headers[0],"200 OK")?true:false;
 }
 function checkHash($hash, $filename, $file) {
    logmsg("\t\tSprawdzenie sumy kontrolnej pliku: $filename");
    $line = fgets(fopen($file, 'r'));
    
   if (substr($line,-1*(strlen($filename)+1),-1)  == $filename && substr($line,0, 128) == $hash) {
        return true;
    } else {
        logmsg("\t\t\tNIEZGODNA SUMA KONTROLNA PLIKU");
        return false;
    }
 }
 function extract7z($z, $outfolder) {
    $obj = new Archive7z($z);
    if (!$obj->isValid()) {
        return false;
    } else {
        $obj->setOutputDirectory($outfolder)->extract();
        return true;
    }
 }
 function clearData() {
    //$PDO = new PDO('mysql:host='.MYSQL_HOST.';dbname='.MYSQL_DATABASE, MYSQL_USER, MYSQL_PASSWORD);
    $PDO = new PDO('mysql:host='.MYSQL_HOST.';dbname='.MYSQL_DATABASE.'', MYSQL_USER, MYSQL_PASSWORD);
    $PDO->exec("TRUNCATE TABLE wl_masks");
    $PDO->exec("TRUNCATE TABLE wl_hashes");
    $PDO->exec("TRUNCATE TABLE wl_naglowek");
 }
 function loadNaglowek($jsonfile) {
    $PDO = new PDO('mysql:host='.MYSQL_HOST.';dbname='.MYSQL_DATABASE.'', MYSQL_USER, MYSQL_PASSWORD);
    $whitelist=\JsonMachine\JsonMachine::fromFile($jsonfile, "/naglowek");
    $data=Array();
    foreach($whitelist as $key => $value){
       $data[$key]=$value;
    }
     try{
      $sql = "INSERT INTO wl_naglowek (LICZBATRANSFORMACJI, DATAGENEROWANIADANYCH, SCHEMAT) VALUES (:liczbaTransformacji, :dataGenerowaniaDanych, :schemat)";
      $PDO->prepare($sql)->execute($data);
   }
   catch (Exception $e)
   {      
      $str= filter_var($e->getMessage(), FILTER_SANITIZE_STRING);         
  
      echo $str;           
   }
 }
 function loadMasks($jsonfile) {
    $PDO = new PDO('mysql:host='.MYSQL_HOST.';dbname='.MYSQL_DATABASE.'', MYSQL_USER, MYSQL_PASSWORD);
    $whitelist=\JsonMachine\JsonMachine::fromFile($jsonfile, "/maski");
    $data = array();
    $i=0;
    foreach($whitelist as $key => $value){
        array_push($data,"('".substr($value,2,8)."','$value')");
        if ($i=100) {
            $i=0;
            $sql = "INSERT INTO wl_masks (M, MASK) VALUES " .implode(',',$data);
            $PDO->exec($sql);
            $data = array();
        }
        $i++;
    }
    if (count($data) > 0) {
        $sql = "INSERT INTO wl_masks (M, MASK) VALUES ".implode(',',$data);
        $PDO->exec($sql);
    }
 }
 function loadCZ($jsonfile) {
    $PDO = new PDO('mysql:host='.MYSQL_HOST.';dbname='.MYSQL_DATABASE.'', MYSQL_USER, MYSQL_PASSWORD);
    $whitelist=\JsonMachine\JsonMachine::fromFile($jsonfile, "/skrotyPodatnikowCzynnych");
    $data = array();
    $i=0;
    foreach($whitelist as $key => $value){
        array_push($data,"('$value', 'C', '".substr($value,0,5)."')");
        if ($i=100) {
            $i=0;
            $sql = "INSERT INTO wl_hashes (HASH, RODZAJ, IND) VALUES " .implode(',',$data);
            $PDO->exec($sql);
            $data = array();
        }
        $i++;
    }
    if (count($data) > 0) {
        $sql = "INSERT INTO wl_hashes (HASH, RODZAJ, IND) VALUES ".implode(',',$data);
        $PDO->exec($sql);
    }
 }
 function loadZW($jsonfile) {
    $PDO = new PDO('mysql:host='.MYSQL_HOST.';dbname='.MYSQL_DATABASE.'', MYSQL_USER, MYSQL_PASSWORD);
    $whitelist=\JsonMachine\JsonMachine::fromFile($jsonfile, "/skrotyPodatnikowZwolnionych");
    $data = array();
    $i=0;
    foreach($whitelist as $key => $value){
        array_push($data,"('$value', 'Z', '".substr($value,0,5)."')");
        if ($i=100) {
            $i=0;
            $sql = "INSERT INTO wl_hashes (HASH, RODZAJ, IND) VALUES " .implode(',',$data);
            $PDO->exec($sql);
            $data = array();
        }
        $i++;
    }
    if (count($data) > 0) {
        $sql = "INSERT INTO wl_hashes (HASH, RODZAJ, IND) VALUES ".implode(',',$data);
        $PDO->exec($sql);
    }
 }
 function prepJSON() {
    if (download7z() && unpackJSON()) {
        return true;
    } else {
        return false;
    }
}
function download7z() {
    $dt=date("Ymd");
    $downloadurl="https://plikplaski.mf.gov.pl/pliki/$dt.7z";
    $zip = DEST . $dt . ".7z";
    $zipsum = DEST . $dt . ".7z.sha512sum";
    logMsg("\tSprawdzam czy istnieją pliki:");
    if (UR_exists($downloadurl) 
        && UR_exists($downloadurl. ".sha512sum")
        && downloadFile($downloadurl, $zip)
        && downloadFile($downloadurl. ".sha512sum", $zipsum)) {
        
        $hash = hash_file("sha512",$zip,false);
        if (checkHash($hash, $dt . ".7z", $zipsum)) {
            logmsg("\t\tPliki pobrane");
            return true;
        } else {
            return false;
        }
    } else {
        logmsg("\t\tBRAK PLIKOW DO POBRANIA");
        return false;
    }
    
}
function unpackJSON() {
    $dt=date("Ymd");
    $jsonfile = DEST . $dt . ".json";
    $jsonsum = DEST . $dt . ".json.sha512sum";
    $zip = DEST . $dt . ".7z";
    logMsg("\tRozpoczęcie wypakowywania");
    $cmd = Z7COMMAND;
    $cmd = str_replace("{dest}", DEST, $cmd);
    $cmd = str_replace("{json7z}",$zip, $cmd);
    logmsg("\t\t$cmd");
    shell_exec($cmd);
    //sprawdź czy się wypakował
    if (file_exists($jsonfile) && file_exists($jsonsum)) {
            $hash = hash_file("sha512",$jsonfile,false);
            if (checkHash($hash, $dt . ".json", $jsonsum)) {
                logmsg("\t\tPlik json gotowy");
                return true;
            } else {
                logmsg("\t\tROZPAKOWANIE PLIKU NIE POWIODŁO SIĘ");
                return false;
            }
    } else {
        logmsg("\t\tROZPAKOWANIE PLIKU NIE POWIODŁO SIĘ");
        return false;
    }
}
 function logMsg($msg) {
    $msg  = '['.date("Y-m-d H:i:s"). ']'.$msg."\n";
     switch (LOG) {
         case 'txt':
            file_put_contents(DEST."log.txt",$msg,FILE_APPEND);
            break;
        case 'scr':
            echo $msg;
            break;
     }
 }
 ?>
<?php
include 'config.php';
include 'funkcje.php';
require_once 'vendor/autoload.php';
//wywal starego loga
if (file_exists(DEST."log.txt")) unlink(DEST."log.txt");
logMsg("Rozpoczynam import");
$dt=date("Ymd");
$jsonfile = DEST . $dt . ".json";
$jsonsum = DEST . $dt . ".json.sha512sum";
$zip = DEST . $dt . ".7z";
$zipsum = DEST . $dt . ".7z.sha512sum";
if (prepJSON()) {
    logMsg("\tRozpoczynam import");
    logMsg("\t\tCzyszcze tabele");
    clearData();
    logMsg("\t\tŁaduje nagłówek");
    loadNaglowek($jsonfile);
    logMsg("\t\tŁaduje maski");
    loadMasks($jsonfile);
    logMsg("\t\tŁaduje czynnych");
    loadCZ($jsonfile);
    logMsg("\t\tŁaduje zwolnionych");
    loadZW($jsonfile);
}
logMsg("\tczyszczenie");
if (file_exists($jsonfile)) unlink($jsonfile);
if (file_exists($jsonsum)) unlink($jsonsum);
if (file_exists($zip)) unlink($zip);
if (file_exists($zipsum)) unlink($zipsum);
logMsg("Koniec");
?>

Na koniec dorzucam bezpośredni link do pytań i odpowiedzi na stronie MF – tutaj. Jeśli jesteś zainteresowany podobnymi tematami napisz o tym. Tymczasem, tym wpisem zbieram kilka istotnych informacji i zapraszam do innych artykułów na blogu.

10 komentarzy

    1. Też walczę z tym problemem. Może jakiś konwerter online 7z do zipa lub lokalnie zainstalowany extractor jeśli jest dostęp do serwera.

    2. Załatwiłem to instalując na serwerze linuxowym 7z;
      dalej w PHP, to właściwie 2 linijki kodu, by pobrać i wypakować plik;
      mogę podesłać, gdyby ktoś potrzebował

  1. Trafiłem tu przypadkiem, bo szukałem informacji dlaczego aż 5000 razy trzeba liczyć ten skrót.
    Zastanawiam się dlaczego w dokumentacji nie ma najważniejszej informacji: adresu do pliku.
    Ja je wygooglałem tu: https://plikplaski.mf.gov.pl/pliki/
    Pytanie jak PHP radzi sobie z JSON-em ponad 200MB?

    1. Dzięki. Też już dotarłem do tych plików. To zależy od wersji PHP, serwera/hostingu i pamięci na nim dostępnym. Ja będę testował swoje rozwiązanie wkrótce na dużych plikach. W najbliższym czasie planuję aktualizację posta.

      1. No i też musiałem napisać to w PHP-e, tzn, ładowanie danych do bazy, hosting nic innego nie obsługuje.
        Zadanie w CRON-ie już sie kręci, nie korzystam json_decode tylko czytam za pomocą tego: json-machine (wymogi hostingowego konta) dodatkowo zmodyfikowałem, że wrzucam do bazy w porcjach po 100 hashy (to zawsze 100x zapytan mniej), zmienilem trochę strukturę i nie wykorzystuje prepared statment. Rozpakowywuje za pomocą 7zr i shell_exec. Generalnie nie jestem programistą PHP i jest to rozwiązanie quic&dirty ale działa.
        Jak jest zainteresowanie to mogę wkleić kod, nie robię tego teraz bo nie wiem w jakie znaczniki wstawić, aby to sie nie rozjechało…

        1. Też zrezygnowałem z json_decode, ale kod zostawię w poście jakby ktoś chciał skorzystać z tego rozwiązania. Wykorzystuję fopen i lecę linijka po linijce, to daje większą elastyczność jakby ktoś miał ograniczenia ruchu do bazy. Jest to oszczędność wrzucać po kilkaset rekordów do bazy na raz. Możesz mi podesłać rozwiązanie przez formatkę kontaktową. Zamieszczę jeśli się zgodzisz? Może zrobi się mały zbiór różnych rozwiązań.

Leave a Reply

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *