dolibarr 25.0.0-alpha
upload_page.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2005-2017 Laurent Destailleur <eldy@users.sourceforge.net>
3 * Copyright (C) 2024-2025 Frédéric France <frederic.france@free.fr>
4 * Copyright (C) 2024-2026 MDW <mdeweerd@users.noreply.github.com>
5 *
6 * This file is a modified version of datepicker.php from phpBSM to fix some
7 * bugs, to add new features and to dramatically increase speed.
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 */
22
28require_once '../main.inc.php';
36require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
37require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
38require_once DOL_DOCUMENT_ROOT.'/ai/class/ai.class.php';
39require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
40require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
41require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.facture.class.php';
42
43$langs->loadLangs(array("main", "exports", "other"));
44
45$action = GETPOST('action', 'aZ09');
46$modulepart = GETPOST('modulepart', 'aZ09');
47$socid = GETPOSTINT('socid');
48$prodid = GETPOSTINT('prodid');
49
50
51// users/temp/import
52$upload_dir = $conf->user->dir_temp.'/import';
53dol_mkdir($upload_dir);
54
55$file = GETPOST('file');
56
57$originalfilename = $file;
58$uid = $thiid = $pid = $erid = $salid = 0;
59$reg = array();
60if (preg_match('/-uid([\d+])/', $file, $reg)) {
61 $uid = $reg[1];
62 $originalfilename = preg_replace('/-uid\d+/', '', $originalfilename);
63}
64if (preg_match('/-thiid([\d+])/', $file, $reg)) {
65 $thiid = $reg[1];
66 $originalfilename = preg_replace('/-thiid\d+/', '', $originalfilename);
67}
68if (preg_match('/-pid([\d+])/', $file, $reg)) {
69 $pid = $reg[1];
70 $originalfilename = preg_replace('/-pid\d+/', '', $originalfilename);
71}
72if (preg_match('/-erid([\d+])/', $file, $reg)) {
73 $erid = $reg[1];
74 $originalfilename = preg_replace('/-erid\d+/', '', $originalfilename);
75}
76if (preg_match('/-salid([\d+])/', $file, $reg)) {
77 $salid = $reg[1];
78 $originalfilename = preg_replace('/-salid\d+/', '', $originalfilename);
79}
80$originalfilename = preg_replace('/^upload_page-[a-z_]+-/', '', $originalfilename);
81
82$error = 0;
83
84$ai = new Ai($db);
85$ajaxFileUrl = '';
86$fullnewname = '';
87
88
89/*
90 * Actions
91 */
92
93if (getDolGlobalString('MAIN_USE_TOP_MENU_IMPORT_FILE') && !is_numeric(getDolGlobalString('MAIN_USE_TOP_MENU_IMPORT_FILE'))) {
94 $urlforuploadpage = getDolGlobalString('MAIN_USE_TOP_MENU_IMPORT_FILE');
95
96 header("Location: ".$urlforuploadpage);
97 exit(1);
98}
99
100if ($action == 'uploadfile') { // Test on permission not required here. Done later
101 if (!$modulepart) { // Should not happen
102 print 'Error, modulepart param is empty';
103 exit(1);
104 }
105
106 // $modulepart can be 'invoice_supplier', ...
107 $arrayobject = getElementProperties($modulepart);
108
109 $module = $arrayobject['module'];
110 $element = $arrayobject['element'];
111 $dir_output = $arrayobject['dir_output'];
112 $dir_temp = $arrayobject['dir_temp'];
113
114 $permlevel1 = 'read';
115 $permlevel2 = '';
116 $fileprefix = 'unknown';
117 if (in_array($modulepart, array('fournisseur', 'invoice_supplier'))) {
118 $permlevel1 = 'facture';
119 $permlevel2 = 'read';
120 $fileprefix = 'upload_page-'.$modulepart.'-uid'.$user->id.'-thiid'.(GETPOSTINT('socid') > 0 ? GETPOSTINT('socid') : 0).'-pid'.(GETPOSTINT('search_prodid') > 0 ? GETPOSTINT('search_prodid') : 0);
121 } elseif ($modulepart == 'expensereport') {
122 $fileprefix = 'upload_page-'.$modulepart.'-uid'.$user->id.'-erid'.(GETPOSTINT('userexpensereportid') > 0 ? GETPOSTINT('userexpensereportid') : 0).'-pid'.(GETPOSTINT('search_prodid') > 0 ? GETPOSTINT('search_prodid') : 0);
123 } elseif ($modulepart == 'salaries') {
124 $fileprefix = 'upload_page-'.$modulepart.'-uid'.$user->id.'-salid'.(GETPOSTINT('usersalaryid') > 0 ? GETPOSTINT('usersalaryid') : 0);
125 }
126
127 if ($permlevel2) {
128 $permissiontoadd = $user->hasRight($module, $permlevel1, $permlevel2); // Used by actions_linkedfiles
129 } else {
130 $permissiontoadd = $user->hasRight($module, $permlevel1); // Used by actions_linkedfiles
131 }
132 $forceFullTextIndexation = '0'; // Used by actions_linkedfiles
133
134
135 if (!empty($_FILES['userfile']['name'])) {
136 $fullnewname = $fileprefix.'-'.$_FILES['userfile']['name'];
137 $_FILES['userfile']['name'] = $fullnewname;
138
139 // $dir_output = output dir of object
140 // $dir_temp = temp dir of object
141 // $upload_dir is "users/temp/import"
142 include DOL_DOCUMENT_ROOT.'/core/actions_linkedfiles.inc.php'; // Save uploaded file into $upload_dir (with overwrite if file already exists)
143
144 // Call AI to decrypt the file
145 // Use AJAX call instead of redirect
146 // @phpstan-ignore-next-line $error may have been modified by actions_linkedfiles.inc.php
147 if (!$error) {
148 // We will make the AJAX call from the bottom of the page
149 $ajaxFileUrl = DOL_URL_ROOT.'/core/ajax/ajaxuploadpage.php?file='.urlencode($fullnewname);
150 }
151 }
152} else {
153 // Delete the temporary files that are used when uploading files
154 dol_delete_file($upload_dir.'/upload_page-by'.$user->id.'-*');
155}
156
157
158/*
159 * View
160 */
161
162$form = new Form($db);
163
164// Important: Following code is to avoid page request by browser and PHP CPU at each Dolibarr page access.
165/*
166if (GETPOSTINT('cache')) {
167 header('Cache-Control: max-age='.GETPOSTINT('cache').', public');
168 // For a .php, we must set an Expires to avoid to have it forced to an expired value by the web server
169 header('Expires: '.gmdate('D, d M Y H:i:s', dol_now('gmt') + GETPOSTINT('cache')).' GMT');
170 // HTTP/1.0
171 header('Pragma: token=public');
172} else {
173 // HTTP/1.0
174 header('Cache-Control: no-cache');
175}
176*/
177
178$title = $langs->trans("UploadFile");
179$help_url = '';
180
181$arrayofjs = array();
182$arrayofcss = array();
183
184llxHeader('', $title, $help_url, '', 0, 0, $arrayofjs, $arrayofcss, '', 'mod-upload page-card');
185
186print load_fiche_titre('', '', '', 0, '', '', '<h2>'.img_picto('', 'upload').' '.$title.'</h2>');
187
188
189// Instantiate hooks of thirdparty module
190$hookmanager->initHooks(array('uploadform'));
191
192// Define $uploadform
193$uploadform = '';
194
195
196// Form to upload a supplier invoice
197if (empty($action)) {
198 $uploadform = '<div class="display-flex">';
199
200 if (isModEnabled('supplier_invoice')) {
201 $prodtext = $langs->trans("RefOrLabel");
202
203 $langs->load("bills");
204 $uploadform .= '
205 <div id="supplierinvoice" class="flex-item flex-item-uploadfile">'.img_picto('', 'bill', 'class="fa-2x"').'<br>
206 <div>'.$langs->trans("SupplierInvoice").'<br><br>';
207
208 //$uploadform .= '<div class="disableautoopen paddingbottom">';
209 //$uploadform .= '<input type="checkbox" name="newsupplierinvoice" id="newsupplierinvoice" value="1" checked="checked" class="disableautoopen"><label for="newsupplierinvoice" class="disableautoopen">'.$langs->trans("NewInvoice").'</label><br>';
210 //$uploadform .= '</div>';
211
212 $uploadform .= img_picto('', 'company', 'class="pictofixedwidth"');
213 $uploadform .= $form->select_company($socid, 'socid', '(statut:=:0)', $langs->transnoentitiesnoconv("Supplier"), 0, 0, array(), 0, 'maxwidth200 disableautoopen');
214
215 $uploadform .= '<br>';
216
217 //$uploadform .= $form->select_produits_fournisseurs(0, $prodid, 'prodid', '', '', 0, 1, 2, $prodtext, 0, array(), GETPOSTINT('socid'), '1', 0, 'maxwidth200 disableautoopen', 0, '', null, 1);
218 $uploadform .= img_picto('', 'product', 'class="pictofixedwidth"');
219 $uploadform .= $form->select_produits_fournisseurs(0, $prodid, 'prodid', '', '', array(), 1, 1, 'maxwidth200 disableautoopen', $prodtext, 1);
220
221 $uploadform .= '<br>';
222
223 $uploadform .= '<br>
224 <small class="opacitymedium">'.$langs->trans("OrClickToSelectAFile").'...</small>
225 </div>
226 </div>';
227 }
228
229 // Form to upload an expense report
230 if (isModEnabled('expensereport')) {
231 $langs->load("expensereport");
232 $uploadform .= '
233 <div id="userexpensereport" class="flex-item flex-item-uploadfile">'.img_picto('', 'expensereport', 'class="fa-2x"').'<br>
234 <div>'.$langs->trans("ExpenseReport").'<br><br>';
235
236 $uploadform .= img_picto('', 'user', 'class="pictofixedwidth"');
237 //$uploadform .= '<span class="disableautoopen">';
238 $uploadform .= $form->select_dolusers(GETPOSTINT('userexpensereportid') > 0 ? GETPOSTINT('userexpensereportid') : $user->id, 'userexpensereportid', $langs->transnoentitiesnoconv("User"), null, 0, 'hierarchyme', '', '', 0, 0, '', 0, '', 'maxwidth200 disableautoopen', 1);
239 //$uploadform .= '</span>';
240
241 $uploadform .= '<br>';
242
243 $uploadform .= img_picto('', 'invoice', 'class="pictofixedwidth"');
244 $uploadform .= $form->select_type_fees(GETPOSTINT('feetypeid') > 0 ? GETPOSTINT('feetypeid') : -1, 'feetypeid', $langs->transnoentitiesnoconv("TypeFees"), 'maxwidth200 disableautoopen', 1, 1);
245
246 $uploadform .= '<br>';
247
248 $uploadform .= '<br>
249 <small class="opacitymedium">'.$langs->trans("OrClickToSelectAFile").'...</small>
250 </div>
251 </div>';
252 }
253
254
255 // Form to upload a salary document
256 if (isModEnabled('salaries')) {
257 $langs->load("salaries");
258 $uploadform .= '
259 <div id="userpayroll" class="flex-item flex-item-uploadfile">'.img_picto('', 'salary', 'class="fa-2x"').'<br>
260 <div>'.$langs->trans("UserPaySlip").'<br><br>';
261
262 $uploadform .= img_picto('', 'user', 'class="pictofixedwidth"');
263 $uploadform .= $form->select_dolusers(GETPOSTINT('usersalaryid') > 0 ? GETPOSTINT('usersalaryid') : $user->id, 'usersalaryid', $langs->transnoentitiesnoconv("Employee"), null, 0, 'hierarchyme', '', '', 0, 0, '', 0, '', 'maxwidth200 disableautoopen', 1);
264
265 $uploadform .= '<br>';
266
267 $uploadform .= '<br>
268 <small class="opacitymedium">'.$langs->trans("OrClickToSelectAFile").'...</small>
269 </div>
270 </div>';
271 }
272
273 // Form to upload a salary document
274 if (isModEnabled('tax')) {
275 $langs->load("taxes");
276 $uploadform .= '
277 <div id="userpayroll" class="flex-item flex-item-uploadfile">'.img_picto('', 'invoice', 'class="fa-2x"').'<br>
278 <div>'.$langs->trans("SocialContribution").'<br><br>';
279
280 include_once DOL_DOCUMENT_ROOT.'/core/class/html.formsocialcontrib.class.php';
281 $formsocialcontrib = new FormSocialContrib($db);
282
283 $uploadform .= img_picto('', 'invoice', 'class="pictofixedwidth"');
284 $uploadform .= $formsocialcontrib->select_type_socialcontrib(GETPOST('socialcontrib', 'alpha') ? GETPOSTINT('socialcontrib') : -1, 'socialcontrib', $langs->transnoentities("TypeContrib"), 0, 0, 'minwidth150 maxwidth200 disableautoopen', 0, 1);
285
286 $uploadform .= '<br>';
287
288 $uploadform .= img_picto('', 'user', 'class="pictofixedwidth"');
289 $uploadform .= $form->select_dolusers(GETPOSTINT('usertaxid') > 0 ? GETPOSTINT('usertaxid') : -1, 'usertaxid', $langs->transnoentitiesnoconv("Employee"), null, 0, 'hierarchyme', '', '', 0, 0, '', 0, '', 'maxwidth200 disableautoopen', 1);
290
291 $uploadform .= '<br>
292 <small class="opacitymedium">'.$langs->trans("OrClickToSelectAFile").'...</small>
293 </div>
294 </div>';
295 }
296
297 $uploadform .= '</div>';
298}
299
300
301
302// Execute hook printSearchForm
303$parameters = array('uploadform' => $uploadform);
304$reshook = $hookmanager->executeHooks('printUploadForm', $parameters); // Note that $action and $object may have been modified by some hooks
305if (empty($reshook)) {
306 $uploadform .= $hookmanager->resPrint;
307} else {
308 $uploadform = $hookmanager->resPrint;
309}
310
311$uploadform .= '<br>';
312
313
314if ($action == 'uploadfile' || $action == 'showsummary') {
315 print '<b>'.$langs->trans("ImportingFile", $originalfilename).'</b><br>';
316 print '<br>';
317
318 print '<div class="'.($action == 'showsummary' ? 'green' : 'neutral').'" style="padding-left: 20px; padding-right: 20px;">';
319
320 print '<div class="marginbottom">';
321 if ($action == 'showsummary') {
322 print $langs->trans("AIProcessingDone", $ai->getApiService());
323 } else {
324 print $langs->trans("AIProcessingPleaseWait", $ai->getApiService()).'...';
325 }
326 print '</div>';
327 print '<br>';
328
329 print '<div class="progress" title="80%">
330 <div class="progress-bar" role="progressbar" style="width: '.($action == 'showsummary' ? '100' : '0').'%" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div>
331 </div>';
332
333 print '</div>';
334
335 // Add AJAX call script
336 if (!empty($ajaxFileUrl)) {
337 print '<script>
338 $(document).ready(function() {
339 console.log(\'Call ajaxFileUrl: '.dol_escape_js($ajaxFileUrl).'\');
340 $.ajax({
341 url: \''.dol_escape_js($ajaxFileUrl).'\',
342 type: "GET",
343 dataType: "json",
344 beforeSend: function() {
345 $(".progress-bar").css("width", "25%");
346 },
347 success: function(data, textStatus, jqXHR) {
348 if (jqXHR.status === 200) {
349 // Display the JSON content
350 console.log("Fill the #ajax-result with json result");
351 console.log(data);
352 $("#ajax-result").val(JSON.stringify(data, null, 2));
353 $(".progress-bar").css("width", "100%");
354
355 // Submit the form
356 //$("#form-result").submit();
357
358 // or show the button Next
359 $("#ajax-result-message").html("<div class=\"ok\">Your file has been analyzed by the AI service.</div>");
360 $("#form-result-submit").show();
361 } else {
362 console.log("We received an error");
363
364 // Display error if status is not 200
365 $("#ajax-result-message").html("<div class=\"error\">Error: HTTP status " + jqXHR.status + "</div>");
366 }
367 },
368 error: function(jqXHR, textStatus, errorThrown) {
369 // Display error
370 console.log("We received an error");
371 $("#ajax-result-message").html("<div class=\"error\">Error: " + (jqXHR.responseText || errorThrown || "Unknown error") + "</div>");
372 $(".progress-bar").css("width", "100%");
373 }
374 });
375 });
376 </script>';
377 }
378
379 // Example of JSON result when importing a supplier PDF invoice
380 /* {
381 "document_info": {
382 "type": "Facture",
383 "invoice_number": "MSTRL-API-1234-001",
384 "invoice_reference": "Pro - Annual",
385 "issue_date": "2026-08-21",
386 "due_date": "2026-08-21",
387 "payment_terms": {
388 "days": 0,
389 "description": "0 jours"
390 },
391 "organization": "MyBigCompany"
392 },
393 "vendor": {
394 "name": "Mistral AI SAS",
395 "address": {
396 "street": "15, rue des Halles",
397 "postal_code": "75001",
398 "city": "Paris",
399 "country": "France"
400 },
401 "vat_number": "FR95952418325",
402 "professional_id": {
403 "siren": "952 418 325",
404 "capital": "19,189.04 €",
405 "rcs": "Paris"
406 },
407 "email": "no-reply@mistral.ai",
408 "contact_url": "https://help.mistral.ai/en"
409 },
410 "recipient": {
411 "name": "MyBigCompany",
412 "address": {
413 "street": "1 rue de la paix",
414 "postal_code": "75000",
415 "city": "Paris",
416 "country": "France"
417 },
418 "vat_number": "FR012345678901",
419 "email": "contact@mybigcompany.com"
420 },
421 "items": [
422 {
423 "description": "Fee for use- August",
424 "service": "Pro - Annual",
425 "quantity": 1,
426 "unit_price": 143.9,
427 "vat_rate": 20,
428 "total_excluding_tax": 143.9,
429 "total_including_tax": 172.68,
430 "period_start": "2026-08-21",
431 "period_end": "2027-08-20"
432 }
433 ],
434 "summary": {
435 "subtotal_excluding_tax": 143.9,
436 "tax": {
437 "rate": 20,
438 "amount": 28.78,
439 "description": "EU FR Standard (20.0% sur 143,90 €)"
440 },
441 "total_including_tax": 172.68
442 },
443 "payment_info": {
444 "due_date": "2026-08-21",
445 "amount_due": 172.68,
446 "currency": "EUR"
447 },
448 "other": {
449 "notes": [
450 {
451 "type": "legal",
452 "content": "Mistral AI - SAS au capital de 19,189.04€ - 952 418 325 R.C.S. Paris"
453 },
454 {
455 "type": "contact",
456 "content": "For any inquiry, please refer to https://help.mistral.ai/en"
457 }
458 ]
459 }
460 }
461 */
462
463 print '<form class="" id="form-result" method="POST">';
464 print '<input type="hidden" name="token" value="'.newToken().'">';
465 print '<input type="hidden" name="action" value="showsummary">';
466 print '<input type="hidden" name="modulepart" value="'.$modulepart.'">';
467 print '<input type="hidden" name="socid" value="'.$socid.'">';
468 print '<input type="hidden" name="prodid" value="'.$prodid.'">';
469 print '<input type="hidden" name="originalfilename" value="'.$fullnewname.'">';
470 print '<input type="hidden" name="jsonstring" id="ajax-result" value="jsonstringtoreplace">'; // Fill by the ajax answer
471 print '<div id="ajax-result-message"></div>';
472 if ($action == 'uploadfile') {
473 print '<br>';
474 print '<input type="submit" name="form-result-submit" id="form-result-submit" class="button" value="'.$langs->trans("Next").'">'; // TODO Hide.
475 }
476 print '</form>'."\n";
477 print "\n";
478}
479
480if ($action == 'showsummary') {
481 print '<form class="" id="summary-result" method="POST">';
482 print '<input type="hidden" name="token" value="'.newToken().'">';
483 print '<input type="hidden" name="action" value="addobject">';
484 print '<input type="hidden" name="modulepart" value="'.$modulepart.'">';
485 print '<input type="hidden" name="originalfilename" value="'.$fullnewname.'">';
486 print '<input type="hidden" name="jsonstring" id="ajax-result" value="'.GETPOST('jsonstring', 'restricthtml').'">';
487 print '<div id="ajax-result-message"></div>';
488 //print '<br>';
489
490 print load_fiche_titre('Summary', '', 'upload');
491
492 print '<div class="neutral">';
493
494 print '<input type="hidden" name="token" value="'.newToken().'">';
495
496 $json = null;
497 try {
498 $json = json_decode(GETPOST('jsonstring', 'restricthtml'), true);
499 } catch (Exception $e) {
500 print 'Failed to decode the result of the AI service. Error: '.$e->getMessage();
501 print '<br>';
502 print 'Result: '.GETPOST('jsonstring', 'restricthtml');
503 print '<br>';
504 print '<br>';
505 }
506
507 if ($modulepart == 'invoice_supplier') {
508 // Parse JSON to get information for an invoice
509 $invoiceindoc = $ai->decodeJsonIntoArray($json, 'supplier_invoice');
510
511 //var_dump($json, $invoiceindoc);
512
513 print '<div id="supplierinvoice" class="">';
514
515 //print '<div class="inline-block">'.img_picto('', 'bill', '').' '.$langs->trans("SupplierInvoice").'</div>';
516 //print '<br><br>';
517
518 $nameindoc = (string) $invoiceindoc['vendor_name'];
519 $addressindoc = (string) $invoiceindoc['vendor_address'];
520 $idprof1indoc = (string) $invoiceindoc['vendor_profid1'];
521 $idprof2indoc = (string) $invoiceindoc['vendor_profid2'];
522 $emailindoc = (string) $invoiceindoc['vendor_email'];
523 $vatnumberindoc = (string) $invoiceindoc['vendor_vat_number'];
524
525 // Thirdparty
526 $tmpthirdparty = new Societe($db);
527 print img_picto('', 'company', 'class="pictofixedwidth"').$langs->trans("ThirdParty").'<br>';
528 if (GETPOSTINT('socid')) {
529 $tmpthirdparty->fetch(GETPOSTINT('socid'));
530 // TODO We can show a warning if we find a diff with autodetected value $nameindoc
531 } else {
532 // Try to find thirdparty
533 $result = $tmpthirdparty->findNearest(0, $nameindoc, '', '', $idprof1indoc, $idprof2indoc, '', '', '', '', $emailindoc, $vatnumberindoc, 0, 0, '1');
534
535 if ($result > 0) {
536 $tmpthirdparty->fetch($result);
537 }
538 }
539
540 print '<span class="opacitymedium">'.$langs->trans("KeyDataFoundInDocument").' :</span> ';
541 $s = $nameindoc;
542 if (!empty($addressindoc)) {
543 $s .= ', &nbsp;'.$addressindoc;
544 }
545 if (!$s) {
546 print '<span class="warning">'.$langs->trans("NoDataRelatedTo").'</span>';
547 }
548 print $s;
549 print '<br>';
550 if (empty($tmpthirdparty->id)) {
551 if ($s) {
552 print '<span class="warning">'.$langs->trans("NotFoundInDatabase").'</span><br>';
553 }
554 print $langs->trans("ChooseTheThirdPartyTouse").' ';
555 print $form->select_company($socid, 'socid', '(statut:=:0)', $langs->trans("Name"), 0, 0, array(), 0, 'maxwidth200 disableautoopen');
556 //print ' &nbsp; '.$langs->trans("or").' &nbsp; <input type="checkbox" name="createthirdparty" id="createthirdparty" value="1" checked><label for="createthirdparty"> '.$langs->trans("CreateIt").'</label>';
557 } else {
558 print $langs->trans("WillUse").' ';
559 print $tmpthirdparty->getNomUrl(1);
560 print '<input type="hidden" name="socid" value="'.$tmpthirdparty->id.'">';
561 }
562
563
564 print '<br><br><hr><br>';
565
566
567 // @phan-suppress-next-line PhanTypeMismatchDimFetch
568 $nameprodindoc = $invoiceindoc['items'][0]['description'];
569
570
571 // Product
572 $prodid = GETPOSTINT('prodid');
573 $prodtext = $langs->trans("RefOrLabel");
574
575 $tmpproduct = new Product($db);
576 print img_picto('', 'product', 'class="pictofixedwidth"').$langs->trans("Product").'<br>';
577
578 if ($prodid) {
579 $tmpproduct->fetch($prodid);
580 // TODO We can show a warning if we find a diff with autodetected value $json['items']['service']
581 } else {
582 //$tmpproduct->ref = $json['ref'];
583 }
584
585 print '<span class="opacitymedium">'.$langs->trans("KeyDataFoundInDocument").' :</span> ';
586 $s = $nameprodindoc;
587 if (!$s) {
588 print '<span class="warning">'.$langs->trans("NoDataRelatedTo").'</span>';
589 }
590 print $s;
591 print '<br>';
592 if (empty($tmpproduct->id)) {
593 print $langs->trans("ChooseTheProductTouse").' ';
594 print $form->select_produits_fournisseurs(0, $prodid, 'prodid', '', '', array(), 1, 1, 'maxwidth200 disableautoopen', $prodtext, 1);
595
596 print '<br>';
597 print $langs->trans("TotalTTC").' ';
598 print '<input type="text" class="width100 right" name="amount_ttc" id="amount_ttc" value="'.(GETPOSTFLOAT('amount_ttc', '', 2) ? price(GETPOSTFLOAT('amount_ttc', '', 2)) : '').'">';
599
600 // TODO On selection of product, refresh next section with the product price ref
601 } else {
602 print $langs->trans("WillUse").' ';
603 print $tmpproduct->getNomUrl(1);
604 print '<input type="hidden" name="prodid" value="'.$tmpproduct->id.'">';
605 }
606
607
608 print '<br><br><hr><br>';
609
610
611 // Supplier invoice
612 $tmpinvoice = new FactureFournisseur($db);
613
614 print img_picto('', 'supplier_invoice', 'class="pictofixedwidth"').$langs->trans("SupplierInvoice").'<br>';
615
616 print '<span class="opacitymedium">'.$langs->trans("KeyDataFoundInDocument").' :</span> ';
617 $tmpvalue = array();
618 $invoiceindoc['supplierref'] ? $tmpvalue[] = $invoiceindoc['supplierref'] : '';
619 $invoiceindoc['due_date'] ? $tmpvalue[] = dol_print_date($invoiceindoc['due_date'], 'day', 'tzuserrel') : '';
620 $invoiceindoc['issue_date'] ? $tmpvalue[] = dol_print_date($invoiceindoc['issue_date'], 'day', 'tzuserrel') : '';
621 $invoiceindoc['currency_code'] ? $tmpvalue[] = $invoiceindoc['currency_code'] : '';
622 $invoiceindoc['invoice_label'] ? $tmpvalue[] = $invoiceindoc['invoice_label'] : '';
623
624 $s = implode(', ', $tmpvalue);
625 if (!$s) {
626 print '<span class="warning">'.$langs->trans("NoDataRelatedTo").'</span>';
627 }
628 print $s;
629 print '<br>';
630 if (!empty($invoiceindoc['supplierref'])) {
631 // Try to find supplier invoice
632
633 // Exact match. Always tried first, and always enough on its own.
634 $sql = "SELECT rowid, total_ttc FROM " . $db->prefix() . "facture_fourn";
635 $sql .= " WHERE ref_supplier = '" . $db->escape($invoiceindoc['supplierref']) . "'";
636 $sql .= " AND fk_soc = " . ((int) $tmpthirdparty->id);
637 $sql .= " AND entity IN (" . getEntity('facture_fourn') . ")";
638 $sql .= " LIMIT 1";
639 $resql = $db->query($sql);
640 if (!$resql) {
641 dol_syslog("SQL Error " . $db->lasterror(), LOG_ERR);
642 return -1;
643 }
644 $obj = $db->fetch_object($resql);
645 if ($obj) {
646 $tmpinvoice->fetch($obj->rowid);
647 }
648 }
649
650 if (empty($tmpinvoice->id)) {
651 print $langs->trans("InvoiceWillBeCreated");
652 print '<input type="hidden" name="supplierref" value="'.$invoiceindoc['supplierref'].'">';
653 print '<input type="hidden" name="due_date" value="'.$invoiceindoc['due_date'].'">';
654 print '<input type="hidden" name="issue_date" value="'.$invoiceindoc['issue_date'].'">';
655 print '<input type="hidden" name="currency_code" value="'.$invoiceindoc['currency_code'].'">';
656 print '<input type="hidden" name="currency_code" value="'.$invoiceindoc['currency_code'].'">';
657 print '<input type="hidden" name="invoice_label" value="'.$invoiceindoc['invoice_label'].'">';
658 } else {
659 print $tmpinvoice->getNomUrl(1);
660 print '<input type="hidden" name="invoiceid" value="'.$tmpinvoice->id.'">';
661 }
662
663 print '<br>
664 </div>
665 </div>';
666
667 print '<center><input type="submit" name="submit" class="button" value="'.$langs->trans("Create").'"></center>';
668 }
669
670 print '<input type="hidden" name="jsonstring" value="'.$json.'">';
671
672 print '</div>';
673 print '</form>';
674} else {
675 // Show all forms
676 print "\n";
677 print "<!-- Begin UploadForm -->\n";
678 print '<form id="uploadform" enctype="multipart/form-data" method="POST" action="'.dolBuildUrl($_SERVER["PHP_SELF"]).'">';
679 print '<input type="hidden" name="token" value="'.newToken().'">';
680 print '<input type="hidden" name="action" value="uploadfile">';
681 print '<input type="hidden" name="sendit" value="1">';
682 print '<input type="hidden" name="modulepart" id="modulepart" value="">';
683 print '<input type="hidden" name="overwritefile" value="1">';
684
685 print '<div class="center"><div class="center" style="padding: 10px;">';
686 print '<style>.menu_titre { padding-top: 7px; }</style>';
687 print '<div id="blockupload" class="center">'."\n";
688 //print '<input name="filenamePDF" id="filenamePDF" type="hideobject">';
689 print $uploadform;
690
691
692 $accept = '.pdf,image/*';
693 $disablemulti = 1;
694 $perm = 1;
695 $capture = 1;
696
697 $maxfilesizearray = getMaxFileSizeArray();
698 $max = $maxfilesizearray['max'];
699 $maxmin = $maxfilesizearray['maxmin'];
700 $maxphptoshow = $maxfilesizearray['maxphptoshow'];
701 $maxphptoshowparam = $maxfilesizearray['maxphptoshowparam'];
702 $out = '';
703 if ($maxmin > 0) {
704 $out .= '<input type="hidden" name="MAX_FILE_SIZE" value="'.($maxmin * 1024).'">'; // MAX_FILE_SIZE must precede the field type=file
705 }
706 $out .= '<input class="hideobject" type="file" id="fileInput" value=""';
707 // @phpstan-ignore-next-line
708 $out .= ((getDolGlobalString('MAIN_DISABLE_MULTIPLE_FILEUPLOAD') || $disablemulti) ? ' name="userfile"' : ' name="userfile[]" multiple');
709 // @phpstan-ignore-next-line
710 $out .= (!getDolGlobalString('MAIN_UPLOAD_DOC') || empty($perm) ? ' disabled' : '');
711 // @phpstan-ignore-next-line
712 $out .= (!empty($accept) ? ' accept="'.$accept.'"' : ' accept=""');
713 // @phpstan-ignore-next-line
714 $out .= (!empty($capture) ? ' capture="capture"' : '');
715 $out .= '>';
716
717 print $out;
718
719
720 print "<script>
721 $(document).ready(function() {
722 jQuery('#supplierinvoice:not(.disableautoopen)').on('click', function(event) {
723 console.log('Click on link supplierinvoice to open input file');
724 console.log(event);
725 if (!event.target.closest('.disableautoopen')) {
726 $('#modulepart').val('invoice_supplier');
727 $('#fileInput').click();
728 }
729 });
730
731 jQuery('#userexpensereport:not(.disableautoopen)').on('click', function(event) {
732 console.log('Click on link userexpensereport to open input file');
733 console.log(event);
734 if (!event.target.closest('.disableautoopen')) {
735 $('#modulepart').val('expensereport');
736 $('#fileInput').click();
737 }
738 });
739
740 jQuery('#userpayroll:not(.disableautoopen)').on('click', function(event) {
741 console.log('Click on link userpayroll to open input file');
742 console.log(event);
743 if (!event.target.closest('.disableautoopen')) {
744 $('#modulepart').val('salaries');
745 $('#fileInput').click();
746 }
747 });
748
749 jQuery('#fileInput').on('change', function(event) {
750 console.log(event);
751 console.log('A file was selected, we submit the form');
752 $('#uploadform').submit();
753 });
754 });
755 </script>";
756
757 print '</div>'."\n";
758 print '</div></div>';
759
760 print '</form>';
761 print "\n<!-- End UploadForm -->\n";
762}
763
764
765// End of page
766llxFooter();
767$db->close();
$propal type
'integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortfield]]]',...
Definition propal.php:280
if(!defined('NOREQUIRESOC')) if(!defined( 'NOREQUIRETRAN')) if(!defined('NOTOKENRENEWAL')) if(!defined( 'NOREQUIREMENU')) if(!defined('NOREQUIREHTML')) if(!defined( 'NOREQUIREAJAX')) llxHeader($head='', $title='', $help_url='', $target='', $disablejs=0, $disablehead=0, $arrayofjs='', $arrayofcss='', $morequerystring='', $morecssonbody='', $replacemainareaby='', $disablenofollow=0, $disablenoindex=0)
Empty header.
Definition wrapper.php:73
$object ref
Definition info.php:90
Class for AI feature.
Definition ai.class.php:36
Class to manage generation of HTML components Only common components must be here.
Class to manage generation of HTML components for social contributions management.
if(! $sortfield) if(! $sortorder) $module
Definition list.php:193
print $script_file $mode $langs defaultlang(is_numeric($duration_value) ? " delay=". $duration_value :"").(is_numeric($duration_value2) ? " after cd cd cd description as description
Only used if Module[ID]Desc translation string is not found.
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $conf
The main.inc.php has been included so the following variable are now defined:
dol_delete_file($file, $disableglob=0, $nophperrors=0, $nohook=0, $object=null, $allowdotdot=false, $indexdatabase=1, $nolog=0)
Remove a file or several files with a mask.
getElementProperties($elementType)
Get an array with properties of an element.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0, $nodefault=0)
Return value of a param into GET or POST supervariable.
GETPOSTINT($paramname, $method=0, $nodefault=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
isModEnabled($module)
Is Dolibarr module enabled.
dol_mkdir($dir, $dataroot='', $newmask='')
Creation of a directory (this can create recursive subdir)
load_fiche_titre($title, $morehtmlright='', $picto='generic', $pictoisfullpath=0, $id='', $morecssontable='', $morehtmlcenter='', $morecssonpicto='widthpictotitle')
Load a title with picto.
$conf db name
Only used if Module[ID]Name translation string is not found.
Definition repair.php:140