瀏覽代碼

FIX ZATCA Encoding

Laurent Destailleur 3 年之前
父節點
當前提交
592a277091

+ 6 - 2
dev/resources/iso-normes/QR code for invoices.txt

@@ -8,6 +8,10 @@ https://en.wikipedia.org/wiki/EPC_QR_code#Generators
 
 
 
-* For ZATCA QR Code format (Saudi Arabia)
------------------------------------------
+* For ZATCA QR Code format (Saudi Arabia). Used when INVOICE_ADD_ZATCA_QR_CODE is set
+-------------------------------------------------------------------------------------
 https://www.pwc.com/m1/en/services/tax/me-tax-legal-news/2021/saudi-arabia-guide-to-develop-compliant-qr-code-for-simplified-einvoices.html
+
+https://www.tecklenborgh.com/post/ksa-zatca-publishes-guide-on-how-to-develop-a-fatoora-compliant-qr-code
+
+Method to encode/decode ZATCA string is available in test/phpunit/BarcodeTest.php 

+ 5 - 3
htdocs/core/class/commoninvoice.class.php

@@ -827,8 +827,10 @@ abstract class CommonInvoice extends CommonObject
 		$tmplang->load("main");
 
 		$datestring = dol_print_date($this->date, 'dayhourrfc');
-		$pricewithtaxstring = price($this->total_ttc, 0, $tmplang, 0, -1, 2);
-		$pricetaxstring = price($this->total_tva, 0, $tmplang, 0, -1, 2);
+		//$pricewithtaxstring = price($this->total_ttc, 0, $tmplang, 0, -1, 2);
+		//$pricetaxstring = price($this->total_tva, 0, $tmplang, 0, -1, 2);
+		$pricewithtaxstring = price2num($this->total_ttc, 2, 1);
+		$pricetaxstring = price2num($this->total_tva, 2, 1);
 
 		/*
 		$name = implode(unpack("H*", $this->thirdparty->name));
@@ -857,7 +859,7 @@ abstract class CommonInvoice extends CommonObject
 		// Using TLV format
 		$s = pack('C1', 1).pack('C1', strlen($this->thirdparty->name)).$this->thirdparty->name;
 		$s .= pack('C1', 2).pack('C1', strlen($this->thirdparty->tva_intra)).$this->thirdparty->tva_intra;
-		$s .= pack('C1', 3).pack('C1', strlen($datestring)).$this->date;
+		$s .= pack('C1', 3).pack('C1', strlen($datestring)).$datestring;
 		$s .= pack('C1', 4).pack('C1', strlen($pricewithtaxstring)).$pricewithtaxstring;
 		$s .= pack('C1', 5).pack('C1', strlen($pricetaxstring)).$pricetaxstring;
 		$s .= '';					// Hash of xml invoice

+ 203 - 0
test/phpunit/BarcodeTest.php

@@ -0,0 +1,203 @@
+<?php
+/* Copyright (C) 2010 Laurent Destailleur  <eldy@users.sourceforge.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ * or see https://www.gnu.org/
+ */
+
+/**
+ *      \file       test/phpunit/BarcodeTest.php
+ *		\ingroup    test
+ *      \brief      PHPUnit test
+ *      \remarks    To run this script as CLI:  phpunit filename.php
+ */
+
+global $conf,$user,$langs,$db;
+//define('TEST_DB_FORCE_TYPE','mysql');	// This is to force using mysql driver
+//require_once 'PHPUnit/Autoload.php';
+require_once dirname(__FILE__).'/../../htdocs/master.inc.php';
+require_once dirname(__FILE__).'/../../htdocs/compta/facture/class/facture.class.php';
+
+
+if (empty($user->id)) {
+	print "Load permissions for admin user nb 1\n";
+	$user->fetch(1);
+	$user->getrights();
+}
+$conf->global->MAIN_DISABLE_ALL_MAILS=1;
+
+$langs->load("main");
+
+
+/**
+ * Class for PHPUnit tests
+ *
+ * @backupGlobals disabled
+ * @backupStaticAttributes enabled
+ * @remarks	backupGlobals must be disabled to have db,conf,user and lang not erased.
+ */
+class BarcodeTest extends PHPUnit\Framework\TestCase
+{
+	protected $savconf;
+	protected $savuser;
+	protected $savlangs;
+	protected $savdb;
+
+	/**
+	 * Constructor
+	 * We save global variables into local variables
+	 *
+	 * @return BarcodeTest
+	 */
+	public function __construct()
+	{
+		parent::__construct();
+
+		//$this->sharedFixture
+		global $conf,$user,$langs,$db;
+		$this->savconf=$conf;
+		$this->savuser=$user;
+		$this->savlangs=$langs;
+		$this->savdb=$db;
+
+		print __METHOD__." db->type=".$db->type." user->id=".$user->id;
+		//print " - db ".$db->db;
+		print "\n";
+	}
+
+	/**
+	 * setUpBeforeClass
+	 *
+	 * @return void
+	 */
+	public static function setUpBeforeClass()
+	{
+		global $conf,$user,$langs,$db;
+		$db->begin(); // This is to have all actions inside a transaction even if test launched without suite.
+
+		print __METHOD__."\n";
+	}
+
+	/**
+	 * tearDownAfterClass
+	 *
+	 * @return	void
+	 */
+	public static function tearDownAfterClass()
+	{
+		global $conf,$user,$langs,$db;
+		$db->rollback();
+
+		print __METHOD__."\n";
+	}
+
+	/**
+	 * Init phpunit tests
+	 *
+	 * @return  void
+	 */
+	protected function setUp()
+	{
+		global $conf,$user,$langs,$db;
+		$conf=$this->savconf;
+		$user=$this->savuser;
+		$langs=$this->savlangs;
+		$db=$this->savdb;
+
+		print __METHOD__."\n";
+	}
+
+	/**
+	 * End phpunit tests
+	 *
+	 * @return  void
+	 */
+	protected function tearDown()
+	{
+		print __METHOD__."\n";
+	}
+
+
+	/**
+	 * testBarcodeZATCAEncode
+	 *
+	 * @return  int
+	 */
+	public function testBarcodeZATCAEncode()
+	{
+		global $conf,$user,$langs,$db;
+		$conf=$this->savconf;
+		$user=$this->savuser;
+		$langs=$this->savlangs;
+		$db=$this->savdb;
+
+		$company = new Societe($db);
+		$company->name = 'Specimen company';
+		$company->tva_intra = '123456789';
+
+		$tmpinvoice = new Facture($db);
+
+		$tmpinvoice->thirdparty = $company;
+		$tmpinvoice->total_ht = 100;
+		$tmpinvoice->total_tva = 20;
+		$tmpinvoice->total_ttc = $tmpinvoice->total_ht + $tmpinvoice->total_tva;
+		$tmpinvoice->date = dol_mktime(12, 34, 56, 1, 1, 2020, 'gmt');
+
+		$string_zatca = $tmpinvoice->buildZATCAQRString();
+
+		$this->assertEquals($string_zatca, "ARBTcGVjaW1lbiBjb21wYW55AgkxMjM0NTY3ODkDFDIwMjAtMDEtMDFUMDk6MzQ6NTZaBAMxMjAFAjIw");
+
+		return 1;
+	}
+
+
+
+	/**
+	 * testBarcodeZATCADecode
+	 *
+	 * @return  int
+	 */
+	public function testBarcodeZATCADecode()
+	{
+		global $conf,$user,$langs,$db;
+		$conf=$this->savconf;
+		$user=$this->savuser;
+		$langs=$this->savlangs;
+		$db=$this->savdb;
+
+		//$string_zatca_base64 = "AQZSYWZlZXECDTEyMzQ1Njc4OVQxMjUDFDIwMjEtMDctMTJUMTQ6MjU6MDlaBAM3ODYFAjI1";
+		$string_zatca_base64 = "ARBTcGVjaW1lbiBjb21wYW55AgkxMjM0NTY3ODkDFDIwMjAtMDEtMDFUMDk6MzQ6NTZaBAMxMjAFAjIw";
+
+		$decoded = base64_decode($string_zatca_base64);
+
+		//print_r($decoded)
+		//raw data
+		//\u0001\u0006Rafeeq\u0002\t123456789\u0003\u00142021-07-12T14:25:09Z\u0004\u0003786\u0005\u000225
+
+		$result_data = preg_replace('/[\x00-\x1F\x80-\xFF]/', ',', $decoded);
+
+		$arrayOfData = explode(',,', $result_data);
+
+
+		print __METHOD__." result=".var_export($arrayOfData, true)."\n";
+		$this->assertEquals("", $arrayOfData[0]);
+		$this->assertEquals("Specimen company", $arrayOfData[1]);
+		$this->assertEquals("123456789", $arrayOfData[2]);
+		$this->assertEquals("2020-01-01T09:34:56Z", $arrayOfData[3]);
+		$this->assertEquals("120", $arrayOfData[4]);
+		$this->assertEquals("20", $arrayOfData[5]);
+
+		return 1;
+	}
+}