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 '
'; } /* * 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 '| '.$langs->trans('InvoiceData').' - '.$import->ref.' | '; print '|||
| '.$langs->trans('InvoiceNumber').' | '; print ''.dol_escape_htmltag($import->invoice_number).' | '; print ''.$langs->trans('InvoiceDate').' | '; print ''.dol_print_date($import->invoice_date, 'day').' | '; print '
| '.$langs->trans('Supplier').' | '; print ''.dol_escape_htmltag($import->seller_name).' | '; print ''.$langs->trans('VATIntra').' | '; print ''.dol_escape_htmltag($import->seller_vat).' | '; print '
| '.$langs->trans('BuyerReference').' | '; print ''.dol_escape_htmltag($import->buyer_reference).' | '; print ''.$langs->trans('TotalHT').' | '; print ''.price($import->total_ht).' '.$import->currency.' | '; print '
| '.$langs->trans('Status').' | '; print ''.$import->getLibStatut(1).' | '; print ''.$langs->trans('TotalTTC').' | '; print ''.price($import->total_ttc).' '.$import->currency.' | '; print '
| '.$langs->trans('Position').' | '; print ''.$langs->trans('SupplierRef').' | '; print ''.$langs->trans('ProductDescription').' | '; print ''.$langs->trans('Qty').' | '; print ''.$langs->trans('UnitPrice').' | '; print ''.$langs->trans('TotalHT').' | '; print ''.$langs->trans('MatchedProduct').' | '; print ''.$langs->trans('Action').' | '; print '
| '.$line->line_id.' | '; print ''.dol_escape_htmltag($line->supplier_ref).' | '; print '';
print dol_escape_htmltag($line->product_name);
if (!empty($line->ean) && !$hasProduct) {
print ' EAN: '.$line->ean.''; } print ' | ';
print ''.price2num($line->quantity, 'MS').' '.zugferdGetUnitLabel($line->unit_code).' | '; print '';
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 ' | ';
print ''.price($line->line_total).' | '; print '';
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 ' | ';
print '';
if ($hasProduct) {
// Remove assignment button
print '';
print '';
print '';
} else {
// Product selection form
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 '