0 && $j > 0 && isset($tmp[$i]) && isset($tmp2[$j]) && $tmp[$i] == $tmp2[$j]) { $i--; $j--; } if (!$res && $i > 0 && file_exists(substr($tmp, 0, ($i + 1))."/main.inc.php")) { $res = @include substr($tmp, 0, ($i + 1))."/main.inc.php"; } if (!$res && $i > 0 && file_exists(dirname(substr($tmp, 0, ($i + 1)))."/main.inc.php")) { $res = @include dirname(substr($tmp, 0, ($i + 1)))."/main.inc.php"; } if (!$res && file_exists("../main.inc.php")) { $res = @include "../main.inc.php"; } if (!$res && file_exists("../../main.inc.php")) { $res = @include "../../main.inc.php"; } if (!$res && file_exists("../../../main.inc.php")) { $res = @include "../../../main.inc.php"; } if (!$res) { die("Include of main fails"); } require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; require_once DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php'; require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php'; require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.facture.class.php'; dol_include_once('/importzugferd/class/zugferdparser.class.php'); dol_include_once('/importzugferd/class/zugferdimport.class.php'); dol_include_once('/importzugferd/class/importline.class.php'); dol_include_once('/importzugferd/class/productmapping.class.php'); dol_include_once('/importzugferd/class/actions_importzugferd.class.php'); dol_include_once('/importzugferd/lib/importzugferd.lib.php'); // Load translation files $langs->loadLangs(array("importzugferd@importzugferd", "bills", "products", "companies")); // Security check if (!$user->hasRight('importzugferd', 'import', 'write')) { accessforbidden(); } // Get parameters $action = GETPOST('action', 'aZ09'); $confirm = GETPOST('confirm', 'alpha'); $id = GETPOST('id', 'int'); // Import ID for editing existing imports $supplier_id = GETPOST('supplier_id', 'int'); $line_id = GETPOST('line_id', 'int'); $product_id = GETPOST('product_id', 'int'); $template_product_id = GETPOST('template_product_id', 'int'); // Initialize objects $form = new Form($db); $formfile = new FormFile($db); $actions = new ActionsImportZugferd($db); $import = new ZugferdImport($db); $importLine = new ImportLine($db); $error = 0; $message = ''; /* * Actions */ // Upload and parse PDF - creates import record immediately if ($action == 'upload') { if (!empty($_FILES['zugferd_file']['tmp_name'])) { $upload_dir = $conf->importzugferd->dir_output.'/temp'; if (!is_dir($upload_dir)) { dol_mkdir($upload_dir); } $filename = dol_sanitizeFileName($_FILES['zugferd_file']['name']); $destfile = $upload_dir.'/'.$filename; if (move_uploaded_file($_FILES['zugferd_file']['tmp_name'], $destfile)) { $force_reimport = GETPOST('force_reimport', 'int'); // Check for duplicate $file_hash = hash_file('sha256', $destfile); $isDuplicate = $import->isDuplicate($file_hash); if ($isDuplicate && !$force_reimport) { $error++; $message = $langs->trans('ErrorDuplicateInvoice'); @unlink($destfile); } else { // If force reimport, delete the old record first if ($isDuplicate && $force_reimport) { $oldImport = new ZugferdImport($db); $oldImport->fetch(0, null, $file_hash); if ($oldImport->id > 0) { // Delete old lines $oldLines = new ImportLine($db); $oldLines->deleteAllByImport($oldImport->id); // Delete old files $old_dir = $conf->importzugferd->dir_output.'/imports/'.$oldImport->id; if (is_dir($old_dir)) { dol_delete_dir_recursive($old_dir); } // Delete old import record $oldImport->delete($user); } } // Parse the file $parser = new ZugferdParser($db); $res = $parser->extractFromPdf($destfile); if ($res > 0) { $res = $parser->parse(); if ($res > 0) { $parsed_data = $parser->getInvoiceData(); // Create import record immediately $import->invoice_number = $parsed_data['invoice_number']; $import->invoice_date = $parsed_data['invoice_date']; $import->seller_name = $parsed_data['seller']['name']; $import->seller_vat = $parsed_data['seller']['vat_id']; $import->buyer_reference = $parsed_data['buyer']['reference'] ?: $parsed_data['buyer']['id']; $import->total_ht = $parsed_data['totals']['net']; $import->total_ttc = $parsed_data['totals']['gross']; $import->currency = $parsed_data['totals']['currency']; $import->xml_content = $parser->getXmlContent(); $import->pdf_filename = $filename; $import->file_hash = $file_hash; // Find supplier $supplier_id = $actions->findSupplier($parsed_data); $import->fk_soc = $supplier_id; // Process line items to find products $processed_lines = $actions->processLineItems($parsed_data['lines'], $supplier_id); // Check if all lines have products $all_have_products = true; foreach ($processed_lines as $line) { if ($line['fk_product'] <= 0) { $all_have_products = false; break; } } // Set status based on product matching if ($all_have_products && $supplier_id > 0) { $import->status = ZugferdImport::STATUS_IMPORTED; } else { $import->status = ZugferdImport::STATUS_PENDING; } $import->date_creation = dol_now(); $result = $import->create($user); if ($result > 0) { // Store line items in database foreach ($processed_lines as $line) { $importLineObj = new ImportLine($db); $importLineObj->fk_import = $import->id; $importLineObj->line_id = $line['line_id']; $importLineObj->supplier_ref = $line['supplier_ref']; $importLineObj->product_name = $line['name']; $importLineObj->description = $line['description']; $importLineObj->quantity = $line['quantity']; $importLineObj->unit_code = $line['unit_code']; $importLineObj->unit_price = $line['unit_price']; $importLineObj->unit_price_raw = $line['unit_price_raw']; $importLineObj->basis_quantity = $line['basis_quantity']; $importLineObj->basis_quantity_unit = $line['basis_quantity_unit']; $importLineObj->line_total = $line['line_total']; $importLineObj->tax_percent = $line['tax_percent']; $importLineObj->ean = $line['ean']; $importLineObj->fk_product = $line['fk_product']; $importLineObj->match_method = $line['match_method']; $importLineObj->create($user); } // Move PDF to permanent storage $final_dir = $conf->importzugferd->dir_output.'/imports/'.$import->id; if (!is_dir($final_dir)) { dol_mkdir($final_dir); } rename($destfile, $final_dir.'/'.$filename); // Redirect to edit page $id = $import->id; $action = 'edit'; setEventMessages($langs->trans('ImportRecordCreated'), null, 'mesgs'); } else { $error++; $message = $import->error; @unlink($destfile); } } else { $error++; $message = $parser->error; @unlink($destfile); } } else { $error++; $message = $parser->error; @unlink($destfile); } } } else { $error++; $message = $langs->trans('ErrorFileUploadFailed'); } } else { $error++; $message = $langs->trans('ErrorNoFileUploaded'); } } // Load existing import for editing if ($id > 0 && empty($action)) { $action = 'edit'; } if ($action == 'edit' && $id > 0) { $result = $import->fetch($id); if ($result <= 0) { $error++; $message = $langs->trans('ErrorRecordNotFound'); $action = ''; } } // Assign product to line if ($action == 'assignproduct' && $line_id > 0 && $product_id > 0) { $lineObj = new ImportLine($db); $result = $lineObj->fetch($line_id); if ($result > 0) { $lineObj->setProduct($product_id, $langs->trans('ManualAssignment'), $user); setEventMessages($langs->trans('ProductAssigned'), null, 'mesgs'); // Get import ID to reload $id = $lineObj->fk_import; // Check if all lines now have products $allHaveProducts = $importLine->allLinesHaveProducts($id); if ($allHaveProducts) { // Update import status $import->fetch($id); if ($import->status == ZugferdImport::STATUS_PENDING) { $import->status = ZugferdImport::STATUS_IMPORTED; $import->update($user); } } } $action = 'edit'; $import->fetch($id); } // Remove product assignment from line if ($action == 'removeproduct' && $line_id > 0) { $lineObj = new ImportLine($db); $result = $lineObj->fetch($line_id); if ($result > 0) { $id = $lineObj->fk_import; $lineObj->setProduct(0, '', $user); setEventMessages($langs->trans('ProductRemoved'), null, 'mesgs'); // Update import status to pending $import->fetch($id); if ($import->status == ZugferdImport::STATUS_IMPORTED) { $import->status = ZugferdImport::STATUS_PENDING; $import->update($user); } } $action = 'edit'; $import->fetch($id); } // Update supplier if ($action == 'setsupplier' && $id > 0) { $import->fetch($id); $import->fk_soc = $supplier_id; $import->update($user); setEventMessages($langs->trans('SupplierUpdated'), null, 'mesgs'); $action = 'edit'; } // Duplicate product from template if ($action == 'duplicateproduct' && $template_product_id > 0 && $line_id > 0) { $lineObj = new ImportLine($db); $result = $lineObj->fetch($line_id); if ($result > 0) { // Load template product $template = new Product($db); if ($template->fetch($template_product_id) > 0) { // Create new product as copy $newproduct = new Product($db); // Copy basic properties from template $newproduct->type = $template->type; $newproduct->status = $template->status; $newproduct->status_buy = $template->status_buy; $newproduct->status_batch = $template->status_batch; $newproduct->fk_product_type = $template->fk_product_type; $newproduct->price = $lineObj->unit_price; $newproduct->price_base_type = 'HT'; $newproduct->tva_tx = $lineObj->tax_percent ?: $template->tva_tx; $newproduct->weight = $template->weight; $newproduct->weight_units = $template->weight_units; $newproduct->fk_unit = $template->fk_unit; // Set label from ZUGFeRD $newproduct->label = $lineObj->product_name; // Generate unique ref $newproduct->ref = 'NEW-'.dol_print_date(dol_now(), '%Y%m%d%H%M%S'); // Build description with ZUGFeRD data $zugferd_info = ''; if (!empty($lineObj->supplier_ref)) { $zugferd_info .= $langs->trans('SupplierRef').': '.$lineObj->supplier_ref."\n"; } if (!empty($lineObj->unit_code)) { $zugferd_info .= $langs->trans('Unit').': '.zugferdGetUnitLabel($lineObj->unit_code)."\n"; } if (!empty($lineObj->ean)) { $zugferd_info .= 'EAN: '.$lineObj->ean."\n"; } $zugferd_info .= "---\n"; $newproduct->description = $zugferd_info . ($template->description ?: ''); // Create the product $result = $newproduct->create($user); if ($result > 0) { setEventMessages($langs->trans('ProductCreated'), null, 'mesgs'); // Redirect to product card for editing header('Location: '.DOL_URL_ROOT.'/product/card.php?id='.$result); exit; } else { setEventMessages($newproduct->error, $newproduct->errors, 'errors'); } } $id = $lineObj->fk_import; } $action = 'edit'; $import->fetch($id); } // Create supplier invoice if ($action == 'createinvoice' && $id > 0) { $import->fetch($id); // Check prerequisites if ($import->fk_soc <= 0) { $error++; setEventMessages($langs->trans('ErrorSupplierRequired'), null, 'errors'); } else { // Check all lines have products $lines = $importLine->fetchAllByImport($id); $allHaveProducts = true; foreach ($lines as $line) { if ($line->fk_product <= 0) { $allHaveProducts = false; break; } } if (!$allHaveProducts) { $error++; setEventMessages($langs->trans('ErrorNotAllProductsAssigned'), null, 'errors'); } else { // Create invoice $invoice = new FactureFournisseur($db); $invoice->socid = $import->fk_soc; $invoice->ref_supplier = $import->invoice_number; $invoice->date = $import->invoice_date; $invoice->note_private = $langs->trans('ImportedFromZugferd').' ('.$import->ref.')'; $invoice->cond_reglement_id = 1; $db->begin(); $result = $invoice->create($user); if ($result > 0) { // Add lines foreach ($lines as $line) { $res = $invoice->addline( $line->product_name, $line->unit_price, $line->tax_percent, 0, 0, $line->quantity, $line->fk_product, 0, '', '', 0, 0, '', 'HT' ); if ($res < 0) { $error++; setEventMessages($invoice->error, $invoice->errors, 'errors'); break; } // Update EAN on product if not set if (!empty($line->ean) && $line->fk_product > 0) { $product = new Product($db); $product->fetch($line->fk_product); if (empty($product->barcode)) { $product->barcode = $line->ean; $product->barcode_type = 2; // EAN13 $product->update($product->id, $user); } } } if (!$error) { // Validate invoice $invoice->validate($user); // Copy PDF to invoice $source_pdf = $conf->importzugferd->dir_output.'/imports/'.$import->id.'/'.$import->pdf_filename; if (file_exists($source_pdf)) { $dest_dir = $conf->fournisseur->facture->dir_output.'/'.get_exdir($invoice->id, 2, 0, 0, $invoice, 'invoice_supplier').$invoice->ref; if (!is_dir($dest_dir)) { dol_mkdir($dest_dir); } copy($source_pdf, $dest_dir.'/'.$import->pdf_filename); } // Update import record $import->fk_facture_fourn = $invoice->id; $import->status = ZugferdImport::STATUS_PROCESSED; $import->date_import = dol_now(); $import->update($user); $db->commit(); setEventMessages($langs->trans('InvoiceCreatedSuccessfully'), null, 'mesgs'); // Redirect to invoice header('Location: '.DOL_URL_ROOT.'/fourn/facture/card.php?facid='.$invoice->id); exit; } else { $db->rollback(); } } else { $error++; setEventMessages($invoice->error, $invoice->errors, 'errors'); $db->rollback(); } } } $action = 'edit'; } // Delete import record if ($action == 'confirm_delete' && $confirm == 'yes' && $id > 0) { $import->fetch($id); // Delete lines first $importLine->deleteAllByImport($id); // Delete files $import_dir = $conf->importzugferd->dir_output.'/imports/'.$import->id; if (is_dir($import_dir)) { dol_delete_dir_recursive($import_dir); } // Delete import record $import->delete($user); setEventMessages($langs->trans('RecordDeleted'), null, 'mesgs'); header('Location: '.$_SERVER['PHP_SELF']); exit; } /* * View */ $title = $langs->trans('ZugferdImport'); llxHeader('', $title, '', '', 0, 0, '', '', '', 'mod-importzugferd page-import'); print load_fiche_titre($title, '', 'fa-file-import'); // Error message if ($error && !empty($message)) { setEventMessages($message, null, 'errors'); } /* * Upload form (shown when no import is being edited) */ if (empty($action) || ($action == 'upload' && $error)) { print '
'; print ''; print ''; print '
'; print '
'; print '
'; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print '
'.$langs->trans('UploadZugferdInvoice').'
'.$langs->trans('File').' (PDF)'; print ''; print '
'.$langs->trans('ForceReimport').''; print ' '; print ''.$langs->trans('ForceReimportHelp').''; print '
'; print '
'; print '
'; print ''; print '
'; print '
'; // Show pending imports print '
'; print '
'; print ''; print ''; print ''; print ''; $sql = "SELECT i.rowid, i.ref, i.invoice_number, i.seller_name, i.total_ttc, i.status, i.date_creation"; $sql .= " FROM ".MAIN_DB_PREFIX."importzugferd_import as i"; $sql .= " WHERE i.entity = ".$conf->entity; $sql .= " AND i.status IN (".ZugferdImport::STATUS_IMPORTED.", ".ZugferdImport::STATUS_PENDING.")"; $sql .= " ORDER BY i.date_creation DESC LIMIT 10"; $resql = $db->query($sql); if ($resql) { $num = $db->num_rows($resql); if ($num > 0) { print ''; print ''; print ''; print ''; print ''; print ''; print ''; while ($obj = $db->fetch_object($resql)) { print ''; print ''; print ''; print ''; print ''; print ''; print ''; } } else { print ''; } } print '
'.$langs->trans('PendingImports').'
'.$langs->trans('Ref').''.$langs->trans('InvoiceNumber').''.$langs->trans('Supplier').''.$langs->trans('TotalTTC').''.$langs->trans('Status').'
'.$obj->ref.''.$obj->invoice_number.''.$obj->seller_name.''.price($obj->total_ttc).''; $tmpimport = new ZugferdImport($db); print $tmpimport->LibStatut($obj->status, 1); print '
'.$langs->trans('NoPendingImports').'
'; print '
'; print '
'; print '
'; print '
'; } /* * Edit/Review import */ if ($action == 'edit' && $import->id > 0) { // Delete confirmation if ($action == 'delete') { $formconfirm = $form->formconfirm( $_SERVER['PHP_SELF'].'?id='.$import->id, $langs->trans('DeleteImportRecord'), $langs->trans('ConfirmDeleteImportRecord', $import->ref), 'confirm_delete' ); print $formconfirm; } // Fetch lines $lines = $importLine->fetchAllByImport($import->id); $missingProducts = $importLine->countLinesWithoutProduct($import->id); $allComplete = ($missingProducts == 0 && $import->fk_soc > 0); // Header info print '
'; // Status banner if ($import->status == ZugferdImport::STATUS_PENDING) { print '
'; print ''; print $langs->trans('ManualInterventionRequired'); if ($missingProducts > 0) { print ' - '.$missingProducts.' '.$langs->trans('ProductsNotAssigned'); } if ($import->fk_soc <= 0) { print ' - '.$langs->trans('SupplierNotAssigned'); } print '

'; } elseif ($allComplete) { print '
'; print ''; print $langs->trans('ReadyToCreateInvoice'); print '
'; } // Invoice data print '
'; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print '
'.$langs->trans('InvoiceData').' - '.$import->ref.'
'.$langs->trans('InvoiceNumber').''.dol_escape_htmltag($import->invoice_number).''.$langs->trans('InvoiceDate').''.dol_print_date($import->invoice_date, 'day').'
'.$langs->trans('Supplier').''.dol_escape_htmltag($import->seller_name).''.$langs->trans('VATIntra').''.dol_escape_htmltag($import->seller_vat).'
'.$langs->trans('BuyerReference').''.dol_escape_htmltag($import->buyer_reference).''.$langs->trans('TotalHT').''.price($import->total_ht).' '.$import->currency.'
'.$langs->trans('Status').''.$import->getLibStatut(1).''.$langs->trans('TotalTTC').''.price($import->total_ttc).' '.$import->currency.'
'; print '
'; // Supplier selection print '
'; print '
'; print ''; print ''; print ''; print '
'; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print '
'.$langs->trans('SupplierAssignment').'
'.$langs->trans('SelectSupplier').' *'; print $form->select_company($import->fk_soc, 'supplier_id', 's.fournisseur = 1', 'SelectThirdParty', 0, 0, null, 0, 'minwidth300'); print ' '; print '
'; print '
'; print '
'; // Line items print '
'; print '
'; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; foreach ($lines as $line) { $hasProduct = ($line->fk_product > 0); $rowClass = $hasProduct ? 'oddeven opacitymedium' : 'oddeven'; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; print ''; } print '
'.$langs->trans('Position').''.$langs->trans('SupplierRef').''.$langs->trans('ProductDescription').''.$langs->trans('Qty').''.$langs->trans('UnitPrice').''.$langs->trans('TotalHT').''.$langs->trans('MatchedProduct').''.$langs->trans('Action').'
'.$line->line_id.''.dol_escape_htmltag($line->supplier_ref).''; print dol_escape_htmltag($line->product_name); if (!empty($line->ean) && !$hasProduct) { print '
EAN: '.$line->ean.''; } print '
'.price2num($line->quantity, 'MS').' '.zugferdGetUnitLabel($line->unit_code).''; print price($line->unit_price); if (!empty($line->basis_quantity) && $line->basis_quantity != 1) { print '
('.price($line->unit_price_raw).'/'.price2num($line->basis_quantity, 'MS').zugferdGetUnitLabel($line->basis_quantity_unit).')'; } print '
'.price($line->line_total).''; if ($hasProduct) { $product = new Product($db); $product->fetch($line->fk_product); print $product->getNomUrl(1); if (!empty($line->match_method)) { print '
'.$langs->trans('MatchMethod').': '.$line->match_method.''; } if (!empty($line->ean)) { print '
'.$line->ean.''; } print ' '; } else { print ''.$langs->trans('NoProductMatch').''; } print '
'; if ($hasProduct) { // Remove assignment button print ''; print ''; print ''; } else { // Product selection form print '
'; print ''; print ''; print ''; print ''; print $form->select_produits('', 'product_id', '', 0, 0, -1, 2, '', 0, array(), 0, '1', 0, 'minwidth150 maxwidth200', 1, '', 0); print ' '; print '
'; // Create new product link $create_url = DOL_URL_ROOT.'/product/card.php?action=create'; $create_url .= '&label='.urlencode($line->product_name); $create_url .= '&price='.urlencode($line->unit_price); $create_desc = ''; if (!empty($line->supplier_ref)) { $create_desc .= $langs->trans('SupplierRef').': '.$line->supplier_ref."\n"; } if (!empty($line->unit_code)) { $create_desc .= $langs->trans('Unit').': '.zugferdGetUnitLabel($line->unit_code)."\n"; } if (!empty($line->ean)) { $create_desc .= 'EAN: '.$line->ean."\n"; } $create_url .= '&description='.urlencode(trim($create_desc)); print '
'; print ' '.$langs->trans('CreateProduct'); print ''; // Product template print '
'; print '
'; print ''; print ''; print ''; print ''; print $form->select_produits('', 'template_product_id', '', 0, 0, -1, 2, '', 0, array(), 0, '1', 0, 'minwidth100 maxwidth150', 1, '', 0); print ' '; print '
'; } print '
'; print '
'; // Action buttons print '
'; if ($allComplete) { print ''; print ''.$langs->trans('CreateSupplierInvoice'); print ''; print '   '; } print ''.$langs->trans('BackToList').''; print '   '; print ''.$langs->trans('Delete').''; print '
'; print '
'; } llxFooter(); $db->close();