intracommreport.class.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483
  1. <?php
  2. /* Copyright (C) 2015 ATM Consulting <support@atm-consulting.fr>
  3. * Copyright (C) 2019-2020 Open-DSI <support@open-dsi.fr>
  4. * Copyright (C) 2020 Frédéric France <frederic.france@netlogic.fr>
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. /**
  20. * \file htdocs/intracommreport/class/intracommreport.class.php
  21. * \ingroup Intracomm report
  22. * \brief File of class to manage intracomm report
  23. */
  24. require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
  25. /**
  26. * Class to manage intracomm report
  27. */
  28. class IntracommReport extends CommonObject
  29. {
  30. /**
  31. * @var string ID to identify managed object
  32. */
  33. public $element = 'intracommreport';
  34. /**
  35. * @var string Name of table without prefix where object is stored
  36. */
  37. public $table_element = 'intracommreport';
  38. /**
  39. * @var string Field with ID of parent key if this field has a parent
  40. */
  41. public $fk_element = 'fk_intracommreport';
  42. /**
  43. * @var string declaration number
  44. */
  45. public $declaration_number;
  46. /**
  47. * 0=No test on entity, 1=Test with field entity, 2=Test with link by societe
  48. * @var int
  49. */
  50. public $ismultientitymanaged = 1;
  51. /**
  52. * DEB - Product
  53. */
  54. const TYPE_DEB = 0;
  55. /**
  56. * DES - Service
  57. */
  58. const TYPE_DES = 1;
  59. public static $type = array(
  60. 'introduction'=>'Introduction',
  61. 'expedition'=>'Expédition'
  62. );
  63. /**
  64. * Constructor
  65. *
  66. * @param DoliDB $db Database handle
  67. */
  68. public function __construct(DoliDB $db)
  69. {
  70. $this->db = $db;
  71. $this->exporttype = 'deb';
  72. }
  73. /**
  74. * Fonction create
  75. * @param User $user User
  76. * @param int $notrigger notrigger
  77. * @return int
  78. */
  79. public function create($user, $notrigger = 0)
  80. {
  81. return 1;
  82. }
  83. /**
  84. * Fonction fetch
  85. * @param int $id object ID
  86. * @return int
  87. */
  88. public function fetch($id)
  89. {
  90. return 1;
  91. }
  92. /**
  93. * Fonction delete
  94. * @param int $id object ID
  95. * @param User $user User
  96. * @param int $notrigger notrigger
  97. * @return int
  98. */
  99. public function delete($id, $user, $notrigger = 0)
  100. {
  101. return 1;
  102. }
  103. /**
  104. * Generate XML file
  105. *
  106. * @param int $mode O for create, R for regenerate (Look always 0 ment toujours 0 within the framework of XML exchanges according to documentation)
  107. * @param string $type Declaration type by default - introduction or expedition (always 'expedition' for Des)
  108. * @param string $period_reference Period of reference
  109. * @return SimpleXMLElement|int
  110. */
  111. public function getXML($mode = 'O', $type = 'introduction', $period_reference = '')
  112. {
  113. global $conf, $mysoc;
  114. /**************Construction de quelques variables********************/
  115. $party_id = substr(strtr($mysoc->tva_intra, array(' '=>'')), 0, 4).$mysoc->idprof2;
  116. $declarant = substr($mysoc->managers, 0, 14);
  117. $id_declaration = self::getDeclarationNumber($this->numero_declaration);
  118. /********************************************************************/
  119. /**************Construction du fichier XML***************************/
  120. $e = new SimpleXMLElement('<?xml version="1.0" encoding="utf-8" standalone="yes"?><INSTAT></INSTAT>');
  121. $enveloppe = $e->addChild('Envelope');
  122. $enveloppe->addChild('envelopeId', $conf->global->INTRACOMMREPORT_NUM_AGREMENT);
  123. $date_time = $enveloppe->addChild('DateTime');
  124. $date_time->addChild('date', date('Y-m-d'));
  125. $date_time->addChild('time', date('H:i:s'));
  126. $party = $enveloppe->addChild('Party');
  127. $party->addAttribute('partyType', $conf->global->INTRACOMMREPORT_TYPE_ACTEUR);
  128. $party->addAttribute('partyRole', $conf->global->INTRACOMMREPORT_ROLE_ACTEUR);
  129. $party->addChild('partyId', $party_id);
  130. $party->addChild('partyName', $declarant);
  131. $enveloppe->addChild('softwareUsed', 'Dolibarr');
  132. $declaration = $enveloppe->addChild('Declaration');
  133. $declaration->addChild('declarationId', $id_declaration);
  134. $declaration->addChild('referencePeriod', $period_reference);
  135. if ($conf->global->INTRACOMMREPORT_TYPE_ACTEUR === 'PSI') {
  136. $psiId = $party_id;
  137. } else {
  138. $psiId = 'NA';
  139. }
  140. $declaration->addChild('PSIId', $psiId);
  141. $function = $declaration->addChild('Function');
  142. $functionCode = $function->addChild('functionCode', $mode);
  143. $declaration->addChild('declarationTypeCode', $conf->global->{'INTRACOMMREPORT_NIV_OBLIGATION_'.strtoupper($type)});
  144. $declaration->addChild('flowCode', ($type == 'introduction' ? 'A' : 'D'));
  145. $declaration->addChild('currencyCode', $conf->global->MAIN_MONNAIE);
  146. /********************************************************************/
  147. /**************Ajout des lignes de factures**************************/
  148. $res = $this->addItemsFact($declaration, $type, $period_reference);
  149. /********************************************************************/
  150. $this->errors = array_unique($this->errors);
  151. if (!empty($res)) {
  152. return $e->asXML();
  153. } else {
  154. return 0;
  155. }
  156. }
  157. /**
  158. * Generate XMLDes file
  159. *
  160. * @param int $period_year Year of declaration
  161. * @param int $period_month Month of declaration
  162. * @param string $type_declaration Declaration type by default - introduction or expedition (always 'expedition' for Des)
  163. * @return SimpleXMLElement|int
  164. */
  165. public function getXMLDes($period_year, $period_month, $type_declaration = 'expedition')
  166. {
  167. global $mysoc;
  168. $e = new SimpleXMLElement('<?xml version="1.0" encoding="utf-8" ?><fichier_des></fichier_des>');
  169. $declaration_des = $e->addChild('declaration_des');
  170. $declaration_des->addChild('num_des', self::getDeclarationNumber($this->numero_declaration));
  171. $declaration_des->addChild('num_tvaFr', $mysoc->tva_intra); // /^FR[a-Z0-9]{2}[0-9]{9}$/ // Doit faire 13 caractères
  172. $declaration_des->addChild('mois_des', $period_month);
  173. $declaration_des->addChild('an_des', $period_year);
  174. /**************Ajout des lignes de factures**************************/
  175. $res = $this->addItemsFact($declaration_des, $type_declaration, $period_year.'-'.$period_month, 'des');
  176. /********************************************************************/
  177. $this->errors = array_unique($this->errors);
  178. if (!empty($res)) {
  179. return $e->asXML();
  180. } else {
  181. return 0;
  182. }
  183. }
  184. /**
  185. * Add line from invoice
  186. *
  187. * @param SimpleXMLElement $declaration Reference declaration
  188. * @param string $type Declaration type by default - introduction or expedition (always 'expedition' for Des)
  189. * @param int $period_reference Reference period
  190. * @param string $exporttype deb=DEB, des=DES
  191. * @return int <0 if KO, >0 if OK
  192. */
  193. public function addItemsFact(&$declaration, $type, $period_reference, $exporttype = 'deb')
  194. {
  195. global $conf;
  196. require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
  197. $sql = $this->getSQLFactLines($type, $period_reference, $exporttype);
  198. $resql = $this->db->query($sql);
  199. if ($resql) {
  200. $i = 1;
  201. if ($this->db->num_rows($resql) <= 0) {
  202. $this->errors[] = 'No data for this period';
  203. return 0;
  204. }
  205. if ($exporttype == 'deb' && $conf->global->INTRACOMMREPORT_CATEG_FRAISDEPORT > 0) {
  206. $categ_fraisdeport = new Categorie($this->db);
  207. $categ_fraisdeport->fetch($conf->global->INTRACOMMREPORT_CATEG_FRAISDEPORT);
  208. $TLinesFraisDePort = array();
  209. }
  210. while ($res = $this->db->fetch_object($resql)) {
  211. if ($exporttype == 'des') {
  212. $this->addItemXMlDes($declaration, $res, $i);
  213. } else {
  214. if (empty($res->fk_pays)) {
  215. // We don't stop the loop because we want to know all the third parties who don't have an informed country
  216. $this->errors[] = 'Country not filled in for the third party <a href="'.dol_buildpath('/societe/soc.php', 1).'?socid='.$res->id_client.'">'.$res->nom.'</a>';
  217. } else {
  218. if ($conf->global->INTRACOMMREPORT_CATEG_FRAISDEPORT > 0 && $categ_fraisdeport->containsObject('product', $res->id_prod)) {
  219. $TLinesFraisDePort[] = $res;
  220. } else {
  221. $this->addItemXMl($declaration, $res, $i, '');
  222. }
  223. }
  224. }
  225. $i++;
  226. }
  227. if (!empty($TLinesFraisDePort)) {
  228. $this->addItemFraisDePort($declaration, $TLinesFraisDePort, $type, $categ_fraisdeport, $i);
  229. }
  230. if (count($this->errors) > 0) {
  231. return 0;
  232. }
  233. }
  234. return 1;
  235. }
  236. /**
  237. * Add invoice line
  238. *
  239. * @param string $type Declaration type by default - introduction or expedition (always 'expedition' for Des)
  240. * @param int $period_reference Reference declaration
  241. * @param string $exporttype deb=DEB, des=DES
  242. * @return string <0 if KO, >0 if OK
  243. */
  244. public function getSQLFactLines($type, $period_reference, $exporttype = 'deb')
  245. {
  246. global $mysoc, $conf;
  247. if ($type == 'expedition' || $exporttype == 'des') {
  248. $sql = 'SELECT f.ref as refinvoice, f.total_ht';
  249. $table = 'facture';
  250. $table_extraf = 'facture_extrafields';
  251. $tabledet = 'facturedet';
  252. $field_link = 'fk_facture';
  253. } else { // Introduction
  254. $sql = 'SELECT f.ref_supplier as refinvoice, f.total_ht';
  255. $table = 'facture_fourn';
  256. $table_extraf = 'facture_fourn_extrafields';
  257. $tabledet = 'facture_fourn_det';
  258. $field_link = 'fk_facture_fourn';
  259. }
  260. $sql .= ', l.fk_product, l.qty
  261. , p.weight, p.rowid as id_prod, p.customcode
  262. , s.rowid as id_client, s.nom, s.zip, s.fk_pays, s.tva_intra
  263. , c.code
  264. , ext.mode_transport
  265. FROM '.MAIN_DB_PREFIX.$tabledet.' l
  266. INNER JOIN '.MAIN_DB_PREFIX.$table.' f ON (f.rowid = l.'.$field_link.')
  267. LEFT JOIN '.MAIN_DB_PREFIX.$table_extraf.' ext ON (ext.fk_object = f.rowid)
  268. INNER JOIN '.MAIN_DB_PREFIX.'product p ON (p.rowid = l.fk_product)
  269. INNER JOIN '.MAIN_DB_PREFIX.'societe s ON (s.rowid = f.fk_soc)
  270. LEFT JOIN '.MAIN_DB_PREFIX.'c_country c ON (c.rowid = s.fk_pays)
  271. WHERE f.fk_statut > 0
  272. AND l.product_type = '.($exporttype == 'des' ? 1 : 0).'
  273. AND f.entity = '.$conf->entity.'
  274. AND (s.fk_pays <> '.$mysoc->country_id.' OR s.fk_pays IS NULL)
  275. AND f.datef BETWEEN "'.$period_reference.'-01" AND "'.$period_reference.'-'.date('t').'"';
  276. return $sql;
  277. }
  278. /**
  279. * Add item for DEB
  280. *
  281. * @param SimpleXMLElement $declaration Reference declaration
  282. * @param Resource $res Result of request SQL
  283. * @param int $i Line Id
  284. * @param string $code_douane_spe Specific customs authorities code
  285. * @return void
  286. */
  287. public function addItemXMl(&$declaration, &$res, $i, $code_douane_spe = '')
  288. {
  289. $item = $declaration->addChild('Item');
  290. $item->addChild('itemNumber', $i);
  291. $cn8 = $item->addChild('CN8');
  292. if (empty($code_douane_spe)) {
  293. $code_douane = $res->customcode;
  294. } else {
  295. $code_douane = $code_douane_spe;
  296. }
  297. $cn8->addChild('CN8Code', $code_douane);
  298. $item->addChild('MSConsDestCode', $res->code); // code iso pays client
  299. $item->addChild('countryOfOriginCode', substr($res->zip, 0, 2)); // code iso pays d'origine
  300. $item->addChild('netMass', round($res->weight * $res->qty)); // Poids du produit
  301. $item->addChild('quantityInSU', $res->qty); // Quantité de produit dans la ligne
  302. $item->addChild('invoicedAmount', round($res->total_ht)); // Montant total ht de la facture (entier attendu)
  303. // $item->addChild('invoicedNumber', $res->refinvoice); // Numéro facture
  304. if (!empty($res->tva_intra)) {
  305. $item->addChild('partnerId', $res->tva_intra);
  306. }
  307. $item->addChild('statisticalProcedureCode', '11');
  308. $nature_of_transaction = $item->addChild('NatureOfTransaction');
  309. $nature_of_transaction->addChild('natureOfTransactionACode', 1);
  310. $nature_of_transaction->addChild('natureOfTransactionBCode', 1);
  311. $item->addChild('modeOfTransportCode', $res->mode_transport);
  312. $item->addChild('regionCode', substr($res->zip, 0, 2));
  313. }
  314. /**
  315. * Add item for DES
  316. *
  317. * @param SimpleXMLElement $declaration Reference declaration
  318. * @param Resource $res Result of request SQL
  319. * @param int $i Line Id
  320. * @return void
  321. */
  322. public function addItemXMlDes($declaration, &$res, $i)
  323. {
  324. $item = $declaration->addChild('ligne_des');
  325. $item->addChild('numlin_des', $i);
  326. $item->addChild('valeur', round($res->total_ht)); // Total amount excl. tax of the invoice (whole amount expected)
  327. $item->addChild('partner_des', $res->tva_intra); // Represents the foreign customer's VAT number
  328. }
  329. /**
  330. * This function adds an item by retrieving the customs code of the product with the highest amount in the invoice
  331. *
  332. * @param SimpleXMLElement $declaration Reference declaration
  333. * @param array $TLinesFraisDePort Data of shipping costs line
  334. * @param string $type Declaration type by default - introduction or expedition (always 'expedition' for Des)
  335. * @param Categorie $categ_fraisdeport category of shipping costs
  336. * @param int $i Line Id
  337. * @return void
  338. */
  339. public function addItemFraisDePort(&$declaration, &$TLinesFraisDePort, $type, &$categ_fraisdeport, $i)
  340. {
  341. global $conf;
  342. if ($type == 'expedition') {
  343. $table = 'facture';
  344. $tabledet = 'facturedet';
  345. $field_link = 'fk_facture';
  346. $more_sql = 'f.ref';
  347. } else { // Introduction
  348. $table = 'facture_fourn';
  349. $tabledet = 'facture_fourn_det';
  350. $field_link = 'fk_facture_fourn';
  351. $more_sql = 'f.ref_supplier';
  352. }
  353. foreach ($TLinesFraisDePort as $res) {
  354. $sql = 'SELECT p.customcode
  355. FROM '.MAIN_DB_PREFIX.$tabledet.' d
  356. INNER JOIN '.MAIN_DB_PREFIX.$table.' f ON (f.rowid = d.'.$field_link.')
  357. INNER JOIN '.MAIN_DB_PREFIX.'product p ON (p.rowid = d.fk_product)
  358. WHERE d.fk_product IS NOT NULL
  359. AND f.entity = '.$conf->entity.'
  360. AND '.$more_sql.' = "'.$res->refinvoice.'"
  361. AND d.total_ht =
  362. (
  363. SELECT MAX(d.total_ht)
  364. FROM '.MAIN_DB_PREFIX.$tabledet.' d
  365. INNER JOIN '.MAIN_DB_PREFIX.$table.' f ON (f.rowid = d.'.$field_link.')
  366. WHERE d.fk_product IS NOT NULL
  367. AND '.$more_sql.' = "'.$res->refinvoice.'"
  368. AND d.fk_product NOT IN
  369. (
  370. SELECT fk_product
  371. FROM '.MAIN_DB_PREFIX.'categorie_product
  372. WHERE fk_categorie = '.((int) $categ_fraisdeport->id).'
  373. )
  374. )';
  375. $resql = $this->db->query($sql);
  376. $ress = $this->db->fetch_object($resql);
  377. $this->addItemXMl($declaration, $res, $i, $ress->customcode);
  378. $i++;
  379. }
  380. }
  381. /**
  382. * Return next reference of declaration not already used (or last reference)
  383. *
  384. * @return string free ref or last ref
  385. */
  386. public function getNextDeclarationNumber()
  387. {
  388. $resql = $this->db->query('SELECT MAX(numero_declaration) as max_declaration_number FROM '.MAIN_DB_PREFIX.$this->table_element." WHERE exporttype='".$this->db->escape($this->exporttype)."'");
  389. if ($resql) {
  390. $res = $this->db->fetch_object($resql);
  391. }
  392. return ($res->max_declaration_number + 1);
  393. }
  394. /**
  395. * Verify declaration number. Positive integer of a maximum of 6 characters recommended by the documentation
  396. *
  397. * @param string $number Number to verify / convert
  398. * @return string Number
  399. */
  400. public static function getDeclarationNumber($number)
  401. {
  402. return str_pad($number, 6, 0, STR_PAD_LEFT);
  403. }
  404. /**
  405. * Generate XML file
  406. *
  407. * @return void
  408. */
  409. public function generateXMLFile()
  410. {
  411. $name = $this->periode.'.xml';
  412. $fname = sys_get_temp_dir().'/'.$name;
  413. $f = fopen($fname, 'w+');
  414. fwrite($f, $this->content_xml);
  415. fclose($f);
  416. header('Content-Description: File Transfer');
  417. header('Content-Type: application/xml');
  418. header('Content-Disposition: attachment; filename="'.$name.'"');
  419. header('Expires: 0');
  420. header('Cache-Control: must-revalidate');
  421. header('Pragma: public');
  422. header('Content-Length: '.filesize($fname));
  423. readfile($fname);
  424. exit;
  425. }
  426. }