intracommreport.class.php 16 KB

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