retrieve($record); $pl = $inv->getPositionList(); $response = array( 'document_no' => $inv->document_no, 'register_date' => $inv->register_date, 'payment_date' => $inv->payment_date, 'total_netto' => $inv->total_netto, 'total_brutto' => $inv->total_brutto, 'currency_id' => $inv->currency_id, 'currency_value' => $inv->currency_value, 'vats_summary' => $inv->vats_summary, 'position_list' => $pl ); echo json_encode($response); } function sendProduct($record) { require_once('modules/EcmProducts/EcmProduct.php'); $prod = new EcmProduct(); $prod->retrieve($record); $response = array( 'name' => $prod->name, 'code' => $prod->code, 'ean' => $prod->ean, 'vat' => $prod->vat_value, 'unit_id' => $prod->unit_id, 'product_category_id' => $prod->product_category_id, 'product_category_name' => $prod->product_category_name ); echo json_encode($response); } function copySaleFromTwinpol($record) { $db = $GLOBALS['db']; // check if sale exists $exists = $db->fetchByAssoc($db->query("SELECT id, document_no FROM ecmsales WHERE edi_zs_id='$record'")); if (isset($exists)) { echo "ZS istnieje: "; echo '' . $exists['document_no'] . ''; die(); } $res = makeCUrlRequest("https://crm.twinpol.com/REST/index.php?key=d68dac4c-f784-4e1b-8267-9ffcfa0eda4c&action=getSale&record=" . $record); $sale = json_decode($res); $sale->position_list = json_decode($sale->position_list); echo "Numer dokumentu TwinPol: " . $sale->document_no . "
"; $gotAllProducts = true; // hope :) $newPositionList = array(); $total_netto = 0; $total_brutto = 0; $total_discount = 0; $vats = array(); foreach ($sale->position_list as $product) { echo 'Produkt: ' . $product->product_code; $p = getProduct(trim($product->product_code)); if (!$p) { echo '  (produkt nie istnieje w bazie E5)
'; $gotAllProducts = false; continue; } else { // pricebook price? $pricebookPrice = $db->fetchByAssoc($db->query(" SELECT price FROM ecmpricebooks_ecmproducts WHERE ecmpricebook_id = '3e78ac33-7c46-1b94-0a67-653a17c06f9e' AND ecmproduct_id='$p->id' AND deleted=0 LIMIT 0,1")); if (isset($pricebookPrice)) { $price_start = $pricebookPrice['price']; echo " Cena: " . $price_start . " (Amazon_2023)
"; } else { // invoice price? $fvPrice = $db->fetchByAssoc($db->query(" SELECT i.id, i.document_no, ii.price_start, ii.ecmvat_value, ii.ecmvat_name, ii.ecmvat_id FROM ecminvoiceoutitems AS ii INNER JOIN ecminvoiceouts AS i ON i.id = ii.ecminvoiceout_id WHERE i.parent_id= '1b9643ca-5b1a-8f9b-b809-586b5619b068' AND i.currency_id = 'PLN' AND ii.ecmproduct_id = '$p->id' ORDER BY i.register_date DESC LIMIT 0,1 ")); if (!isset($fvPrice)) { $price_start = 0; echo " Brak ceny
"; $vat_id = $product->ecmvat_id; $vat_name = $product->ecmvat_name; $vat_value = $product->ecmvat_value; } else { $price_start = $fvPrice['price_start']; $vat_id = $fvPrice['ecmvat_id']; $vat_name = $fvPrice['ecmvat_name']; $vat_value = $fvPrice['ecmvat_value']; echo " Cena: " . $price_start . " (" . $fvPrice['document_no'] . ")
"; } } $prod = array(); if (!isset($product->discount)) { $product->discount = 0; } $prod['quantity'] = $product->quantity; $prod['discount'] = $product->discount; $prod['price_start'] = $price_start; $prod['price_netto'] = round($price_start - ($price_start * ($product->discount / 100)), 2); $prod['ecmvat_id'] = $vat_id; $prod['ecmvat_value'] = $vat_value; $prod['ecmvat_name'] = $vat_name; $prod['price_netto'] = round((float)$price_start - ((float)$price_start * ((float)$product->discount / 100)), 2); $prod['total_netto'] = round($prod['price_netto'] * $prod['quantity'], 2); $prod['total_brutto'] = round($prod['total_netto'] + ($prod['total_netto'] * ($prod['ecmvat_value'] / 100)), 2); $total_netto += $prod['total_netto']; $total_brutto += $prod['total_brutto']; $total_discount = ($prod['price_start'] - $prod['price_netto']) * $prod['quantity']; if (isset($vats[$prod['ecmvat_value']])) { $vats[$prod['ecmvat_value']] += $prod['total_netto']; } else { $vats[$prod['ecmvat_value']] = $prod['total_netto']; } $prod['date_entered'] = date('Y-m-d H:i:s'); $prod['date_modified'] = date('Y-m-d H:i:s'); $prod['modified_user_id'] = null; $prod['assigned_user_id'] = null; $prod['created_by'] = null; $prod['deleted'] = '0'; $prod['product_id'] = $p->id; $prod['position'] = $product->position; $prod['product_code'] = $p->code; $prod['name'] = $product->name; $prod['unit_id'] = $product->unit_id; $prod['unit_name'] = $product->unit_name; $prod['category_id'] = $p->product_category_id; $prod['recipient_code'] = $product->recipient_code; $prod['ean'] = $p->ean; array_push($newPositionList, $prod); } } $vats_summary = ''; foreach ($vats as $k => $v) { $vats_summary .= $k . '%:' . $v . ','; } if (!$gotAllProducts) { echo 'Brak produktow w bazie E5. Dokument nie zostal skopiowany.'; die(); } require_once('modules/EcmSales/EcmSale.php'); $new = new EcmSale(); $new->position_list = $newPositionList; $new->parent_id = "1b9643ca-5b1a-8f9b-b809-586b5619b068"; $new->parent_name = "Twinpol Sp. z o.o."; $new->parent_address_street = "al. Lipowa 48"; $new->parent_address_city = "Obrowo"; $new->parent_address_postalcode = "87-126"; $new->parent_address_country = "Polska"; $new->parent_nip = "8792676609"; $new->parent_iln = "5909000837119"; $new->pricebook_id = "13b29aa1-48f0-de58-7630-59c22756c5e4"; $new->status = "s30"; $new->type = "sales_order"; $new->name = $sale->name; $new->ecmpaymentcondition_name = "90 dni"; $new->ecmpaymentcondition_id = "28de9f83-abd3-9a5d-4df9-4df9b7e0031f"; $new->parent_document_no = $sale->parent_document_no; $new->register_date = $sale->register_date; $new->send_date = $sale->send_date; $new->delivery_date = $sale->delivery_date; $new->ecmlanguage = "pl_pl"; $new->currency_id = 'PLN'; $new->total_netto = $total_netto; $new->total_brutto = $total_brutto; $new->discount = $total_discount; $new->vats_summary = $vats_summary; $new->shipping_address_name = $sale->parent_name; $new->shipping_address_street = $sale->parent_address_street; $new->shipping_address_postalcode = $sale->parent_address_postalcode; $new->shipping_address_city = $sale->parent_address_city; $new->shipping_address_country = $sale->parent_address_country; $new->shipping_iln = $sale->parent_iln; $new->shipping_nip = $sale->parent_nip; $new->edi_zs_id = $sale->id; $new->edi_zs_document_no = $sale->document_no; $new->pdf_text = "Dostawa " . $sale->shipping_address_name . " " . $sale->shipping_address_street . " " . $sale->shipping_address_postalcode . " " . $sale->shipping_address_city . " " . $sale->shipping_address_country . " NIP: " . $sale->shipping_nip . " ILN: " . $sale->shipping_iln; $newId = $new->save(true); //update E5 number in Twinpol $res = $db->fetchByAssoc($db->query("SELECT document_no FROM ecmsales WHERE id='$newId'")); $document_no = str_replace(" ", "%20", $res['document_no']); makeCUrlRequest("https://crm.twinpol.com/REST/index.php?key=d68dac4c-f784-4e1b-8267-9ffcfa0eda4c&action=updateSaleE5Number&record=$record&e5_record=$newId&e5_document_no=$document_no"); echo 'Utworzono ZS.'; die(); } // local helpers function getProduct($code) { $db = $GLOBALS['db']; $res = $db->fetchByAssoc($db->query("SELECT * FROM ecmproducts WHERE code='$code' AND deleted=0")); if (!$res) { return null; } else { require_once('modules/EcmProducts/EcmProduct.php'); $prod = new EcmProduct(); $prod->retrieve($res['id']); return $prod; } } function makeCUrlRequest($url) { $curl = curl_init(); curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'GET'); curl_setopt($curl, CURLOPT_VERBOSE, 1); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0); curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0); curl_setopt($curl, CURLOPT_URL, $url); return curl_exec($curl); } function brecho($msg) { echo '

'; var_dump($msg); echo '

'; } function createCSVReports() { // generateProductComponentsCSV(); $db = $GLOBALS['db']; $exportDir = __DIR__ . "/export"; $jobs = [ [ 'sql' => " SELECT i.document_no, i.register_date, i.parent_name, p.code, p.name, CASE p.group_ks WHEN '1' THEN 'Towar handlowy' WHEN '2' THEN 'Wyrób gotowy' WHEN '3' THEN 'Surowiec' WHEN '4' THEN 'Usługa' ELSE 'Nieznane' END AS group_ks_name, GROUP_CONCAT(DISTINCT c.name ORDER BY cb.position SEPARATOR ' | ') AS category, ii.quantity, ii.price_netto, cur.name AS currency_name FROM ecminvoiceouts AS i INNER JOIN ecminvoiceoutitems AS ii ON i.id = ii.ecminvoiceout_id AND ii.deleted = 0 INNER JOIN ecmproducts AS p ON ii.ecmproduct_id = p.id AND p.deleted = 0 INNER JOIN currencies AS cur ON cur.id = i.currency_id AND cur.deleted = 0 LEFT JOIN ecmproductcategories_bean AS cb ON cb.bean_id = p.id AND cb.bean_name = 'EcmProducts' AND cb.deleted = 0 LEFT JOIN ecmproductcategories AS c ON c.id = cb.ecmproductcategory_id AND c.deleted = 0 WHERE i.type = 'normal' AND i.register_date BETWEEN '2024-01-01' AND '2024-12-31' AND i.deleted = 0 GROUP BY i.id, ii.id ORDER BY i.register_date DESC; ", 'filename' => 'invoices_2024.csv', ], // invoices 2024 [ 'sql' => " SELECT i.document_no, i.register_date, i.parent_name, p.code, p.name, CASE p.group_ks WHEN '1' THEN 'Towar handlowy' WHEN '2' THEN 'Wyrób gotowy' WHEN '3' THEN 'Surowiec' WHEN '4' THEN 'Usługa' ELSE 'Nieznane' END AS group_ks_name, GROUP_CONCAT(DISTINCT c.name ORDER BY cb.position SEPARATOR ' | ') AS category, ii.quantity, ii.price_netto, cur.name AS currency_name FROM ecminvoiceouts AS i INNER JOIN ecminvoiceoutitems AS ii ON i.id = ii.ecminvoiceout_id AND ii.deleted = 0 INNER JOIN ecmproducts AS p ON ii.ecmproduct_id = p.id AND p.deleted = 0 INNER JOIN currencies AS cur ON cur.id = i.currency_id AND cur.deleted = 0 LEFT JOIN ecmproductcategories_bean AS cb ON cb.bean_id = p.id AND cb.bean_name = 'EcmProducts' AND cb.deleted = 0 LEFT JOIN ecmproductcategories AS c ON c.id = cb.ecmproductcategory_id AND c.deleted = 0 WHERE i.type = 'normal' AND i.register_date BETWEEN '2025-01-01' AND '2025-12-31' AND i.deleted = 0 GROUP BY i.id, ii.id ORDER BY i.register_date DESC; ", 'filename' => 'invoices_2025.csv', ], // invoices 2025 [ 'sql' => " SELECT i.document_no, i.register_date, oi.document_no AS FV, oi.register_date AS FV_date, i.parent_name, p.code, p.name, CASE p.group_ks WHEN '1' THEN 'Towar handlowy' WHEN '2' THEN 'Wyrób gotowy' WHEN '3' THEN 'Surowiec' WHEN '4' THEN 'Usługa' ELSE 'Nieznane' END AS group_ks_name, GROUP_CONCAT(DISTINCT c.name ORDER BY cb.position SEPARATOR ' | ') AS category, ii.quantity_corrected AS quantity_corrected, ii.total_netto_corrected AS total_netto_corrected, cur.name AS currency_name FROM ecminvoiceouts AS i INNER JOIN ecminvoiceoutitems AS ii ON i.id = ii.ecminvoiceout_id AND ii.deleted = 0 INNER JOIN ecmproducts AS p ON ii.ecmproduct_id = p.id AND p.deleted = 0 INNER JOIN ecminvoiceouts AS oi ON oi.id = i.ecminvoiceout_id AND oi.deleted = 0 INNER JOIN currencies AS cur ON cur.id = i.currency_id AND cur.deleted = 0 LEFT JOIN ecmproductcategories_bean AS cb ON cb.bean_id = p.id AND cb.bean_name = 'EcmProducts' AND cb.deleted = 0 LEFT JOIN ecmproductcategories AS c ON c.id = cb.ecmproductcategory_id AND c.deleted = 0 WHERE i.type = 'correct' AND i.register_date BETWEEN '2024-01-01' AND '2024-12-31' AND i.deleted = 0 GROUP BY i.id, ii.id, oi.id ORDER BY i.register_date DESC; ", 'filename' => 'correct_invoices_2024.csv', ], // correct invoices 2024 [ 'sql' => " SELECT i.document_no, i.register_date, oi.document_no AS FV, oi.register_date AS FV_date, i.parent_name, p.code, p.name, CASE p.group_ks WHEN '1' THEN 'Towar handlowy' WHEN '2' THEN 'Wyrób gotowy' WHEN '3' THEN 'Surowiec' WHEN '4' THEN 'Usługa' ELSE 'Nieznane' END AS group_ks_name, GROUP_CONCAT(DISTINCT c.name ORDER BY cb.position SEPARATOR ' | ') AS category, ii.quantity_corrected AS quantity_corrected, ii.total_netto_corrected AS total_netto_corrected, cur.name AS currency_name FROM ecminvoiceouts AS i INNER JOIN ecminvoiceoutitems AS ii ON i.id = ii.ecminvoiceout_id AND ii.deleted = 0 INNER JOIN ecmproducts AS p ON ii.ecmproduct_id = p.id AND p.deleted = 0 INNER JOIN ecminvoiceouts AS oi ON oi.id = i.ecminvoiceout_id AND oi.deleted = 0 INNER JOIN currencies AS cur ON cur.id = i.currency_id AND cur.deleted = 0 LEFT JOIN ecmproductcategories_bean AS cb ON cb.bean_id = p.id AND cb.bean_name = 'EcmProducts' AND cb.deleted = 0 LEFT JOIN ecmproductcategories AS c ON c.id = cb.ecmproductcategory_id AND c.deleted = 0 WHERE i.type = 'correct' AND i.register_date BETWEEN '2025-01-01' AND '2025-12-31' AND i.deleted = 0 GROUP BY i.id, ii.id, oi.id ORDER BY i.register_date DESC; ", 'filename' => 'correct_invoices_2025.csv', ], // correct invoices 2025 [ 'sql' => " SELECT i.document_no, i.register_date, p.code, p.name, CASE p.group_ks WHEN '1' THEN 'Towar handlowy' WHEN '2' THEN 'Wyrób gotowy' WHEN '3' THEN 'Surowiec' WHEN '4' THEN 'Usługa' ELSE 'Nieznane' END AS group_ks_name, pc.category, s.name AS stock, ii.quantity FROM ecmstockdocinsideouts AS i INNER JOIN ecmstockdocinsideoutitems AS ii ON i.id = ii.ecmstockdocinsideout_id AND ii.deleted = 0 INNER JOIN ecmproducts AS p ON ii.ecmproduct_id = p.id AND p.deleted = 0 INNER JOIN ecmstocks AS s ON i.stock_id = s.id AND s.deleted = 0 LEFT JOIN ( SELECT cb.bean_id AS product_id, GROUP_CONCAT(DISTINCT c.name ORDER BY cb.position SEPARATOR ' | ') AS category FROM ecmproductcategories_bean AS cb INNER JOIN ecmproductcategories AS c ON c.id = cb.ecmproductcategory_id AND c.deleted = 0 WHERE cb.bean_name = 'EcmProducts' AND cb.deleted = 0 GROUP BY cb.bean_id ) AS pc ON pc.product_id = p.id WHERE i.deleted = 0 AND i.register_date BETWEEN '2025-01-01' AND '2025-12-31' ORDER BY i.register_date DESC; ", 'filename' => 'rw_2025.csv', ], // rw 2025 [ 'sql' => " SELECT i.document_no, i.register_date, p.code, p.name, CASE p.group_ks WHEN '1' THEN 'Towar handlowy' WHEN '2' THEN 'Wyrób gotowy' WHEN '3' THEN 'Surowiec' WHEN '4' THEN 'Usługa' ELSE 'Nieznane' END AS group_ks_name, pc.category, s.name AS stock, ii.quantity FROM ecmstockdocinsideouts AS i INNER JOIN ecmstockdocinsideoutitems AS ii ON i.id = ii.ecmstockdocinsideout_id AND ii.deleted = 0 INNER JOIN ecmproducts AS p ON ii.ecmproduct_id = p.id AND p.deleted = 0 INNER JOIN ecmstocks AS s ON i.stock_id = s.id AND s.deleted = 0 LEFT JOIN ( SELECT cb.bean_id AS product_id, GROUP_CONCAT(DISTINCT c.name ORDER BY cb.position SEPARATOR ' | ') AS category FROM ecmproductcategories_bean AS cb INNER JOIN ecmproductcategories AS c ON c.id = cb.ecmproductcategory_id AND c.deleted = 0 WHERE cb.bean_name = 'EcmProducts' AND cb.deleted = 0 GROUP BY cb.bean_id ) AS pc ON pc.product_id = p.id WHERE i.deleted = 0 AND i.register_date BETWEEN '2024-01-01' AND '2024-12-31' ORDER BY i.register_date DESC; ", 'filename' => 'rw_2024.csv', ], // rw 2024 [ 'sql' => " SELECT ss.product_code, ss.product_name, ss.product_id, COALESCE(NULLIF(ss.quantity, ''), 0) AS quantity, s.name, CASE WHEN s.id = 'c7afd71a-4c3a-bde4-138d-4acaee1644e4' THEN COALESCE(si.ordered_quantity, 0) WHEN s.id = '368479db-22c5-0220-3a14-4bc426b1c709' THEN COALESCE(poi.ordered_quantity, 0) ELSE 0 END AS ordered_quantity FROM ecmstockstates AS ss JOIN ecmstocks AS s ON ss.stock_id = s.id LEFT JOIN ( SELECT i.ecmproduct_id, SUM(i.quantity) AS ordered_quantity FROM ecmsaleitems AS i JOIN ecmsales AS es ON es.id = i.ecmsale_id WHERE es.status IN ('s20', 's30') AND es.deleted = 0 AND i.deleted = 0 AND es.register_date >= '2025-06-01' GROUP BY i.ecmproduct_id ) AS si ON si.ecmproduct_id = ss.product_id LEFT JOIN ( SELECT poi.ecmproduct_id, SUM(poi.quantity) AS ordered_quantity FROM ecmpurchaseorderitems AS poi JOIN ecmpurchaseorders AS po ON po.id = poi.ecmpurchaseorder_id WHERE po.status IN ('accepted', 'registered') AND po.deleted = 0 AND poi.deleted = 0 AND po.register_date >= '2025-06-01' GROUP BY poi.ecmproduct_id ) AS poi ON poi.ecmproduct_id = ss.product_id WHERE ss.stock_id IN ('c7afd71a-4c3a-bde4-138d-4acaee1644e4', '368479db-22c5-0220-3a14-4bc426b1c709') ORDER BY quantity + 0 DESC; ", 'filename' => 'stocks.csv', ],// stocks [ 'sql' => " SELECT i.code AS product_code, i.name AS product_name, i.quantity, i.price_netto, es.document_no, es.parent_name, CASE es.status WHEN 's20' THEN 'Oczekujący' WHEN 's30' THEN 'Zaakceptowany' ELSE 'Nieznane' END AS status, es.register_date, es.delivery_date, es.send_date FROM ecmsaleitems AS i JOIN ecmsales AS es ON es.id = i.ecmsale_id WHERE es.status IN ('s20','s30') AND es.deleted = 0 AND i.deleted = 0 AND es.register_date >= '2025-06-01' ORDER BY es.register_date DESC, es.document_no DESC, i.position; ", 'filename' => 'zs.csv', ], //sales [ 'sql' => " SELECT i.code AS product_code, i.name AS product_name, i.quantity, i.price_start, es.document_no, es.parent_name, CASE es.status WHEN 'accepted' THEN 'Zaakceptowany' WHEN 'registered' THEN 'Zarejestrowany' ELSE 'Nieznane' END AS status, es.register_date, es.delivery_date FROM ecmpurchaseorderitems AS i JOIN ecmpurchaseorders AS es ON es.id = i.ecmpurchaseorder_id WHERE es.status IN ('accepted','registered') AND es.deleted = 0 AND i.deleted = 0 AND es.register_date >= '2025-06-01' ORDER BY es.register_date DESC, es.document_no DESC, i.position; ", 'filename' => 'zz.csv', ], //orders ]; $report = []; foreach ($jobs as $job) { $sql = $job['sql']; $filename = $job['filename']; $headers = isset($job['headers']) ? $job['headers'] : null; $res = $db->query($sql); $fullpath = rtrim($exportDir, "/") . "/" . $filename; $result = exportToCSVFile($res, $fullpath, $headers, ';', true); if ($result['ok']) { $report[] = "OK → {$result['path']} (wiersze: {$result['rows']})" . PHP_EOL; } else { $report[] = "ERR → {$result['path']} ({$result['error']})" . PHP_EOL; } } echo implode("\n", $report); exit; } function exportToCSVFile($res, $fullpath, array $headers = null, $delimiter = ';', $withBom = true) { $db = $GLOBALS['db']; $dir = dirname($fullpath); if (!is_dir($dir)) { if (!@mkdir($dir, 0775, true)) { return ['ok' => false, 'path' => $fullpath, 'rows' => 0, 'error' => "Nie mogę utworzyć katalogu: $dir"]; } } if (!is_writable($dir)) { return ['ok' => false, 'path' => $fullpath, 'rows' => 0, 'error' => "Katalog nie jest zapisywalny: $dir"]; } $fp = @fopen($fullpath, 'w'); if ($fp === false) { return ['ok' => false, 'path' => $fullpath, 'rows' => 0, 'error' => "Nie mogę otworzyć pliku do zapisu: $fullpath"]; } // BOM dla Excel PL if ($withBom) { fwrite($fp, "\xEF\xBB\xBF"); } // pobierz pierwszy wiersz, by ewentualnie zbudować nagłówki $first = $db->fetchByAssoc($res); // brak danych → pusty plik z ewentualnym nagłówkiem (jeśli podany ręcznie) if (!$first) { if ($headers !== null) { fputcsv($fp, $headers, $delimiter); } fclose($fp); return ['ok' => true, 'path' => $fullpath, 'rows' => 0, 'error' => null]; } // dynamiczne nagłówki, jeśli nie podano if ($headers === null) { $headers = array_keys($first); } // wypisz nagłówki fputcsv($fp, $headers, $delimiter); // zapisz pierwszy wiersz w kolejności nagłówków $line = []; foreach ($headers as $h) { $line[] = isset($first[$h]) ? $first[$h] : ''; } fputcsv($fp, $line, $delimiter); $count = 1; // pozostałe wiersze while ($row = $db->fetchByAssoc($res)) { $line = []; foreach ($headers as $h) { $line[] = isset($row[$h]) ? $row[$h] : ''; } fputcsv($fp, $line, $delimiter); $count++; } fclose($fp); $chmod_ok = @chmod($fullpath, 0664); return ['ok' => true, 'path' => $fullpath, 'rows' => $count, 'chmod' => $chmod_ok, 'error' => null]; } function generateProductComponentsCSV() { $db = $GLOBALS['db']; $exportDir = __DIR__ . "/export"; $filename = 'product_components.csv'; $fullpath = rtrim($exportDir, "/") . "/" . $filename; if (!is_dir($exportDir)) { if (!@mkdir($exportDir, 0775, true)) { echo "Błąd: Nie mogę utworzyć katalogu: $exportDir"; return; } } $productsWithComponents = $db->query(" SELECT DISTINCT p.id, p.code, p.name FROM ecmproducts p INNER JOIN ecmproductcomponents c ON p.id = c.ecmproduct_id WHERE p.deleted = '0' AND c.deleted = '0' AND p.active = '1' ORDER BY p.code "); $fp = @fopen($fullpath, 'w'); if ($fp === false) { echo "Błąd: Nie mogę otworzyć pliku do zapisu: $fullpath"; return; } fwrite($fp, "\xEF\xBB\xBF"); $headers = [ 'Poziom_1_Kod', 'Poziom_1_Nazwa', 'Poziom_1_Ilosc', 'Poziom_2_Kod', 'Poziom_2_Nazwa', 'Poziom_2_Ilosc', 'Poziom_3_Kod', 'Poziom_3_Nazwa', 'Poziom_3_Ilosc', 'Poziom_4_Kod', 'Poziom_4_Nazwa', 'Poziom_4_Ilosc', 'Poziom_5_Kod', 'Poziom_5_Nazwa', 'Poziom_5_Ilosc', 'Poziom_6_Kod', 'Poziom_6_Nazwa', 'Poziom_6_Ilosc', 'Poziom_7_Kod', 'Poziom_7_Nazwa', 'Poziom_7_Ilosc', 'Poziom_8_Kod', 'Poziom_8_Nazwa', 'Poziom_8_Ilosc', 'Poziom_9_Kod', 'Poziom_9_Nazwa', 'Poziom_9_Ilosc', 'Poziom_10_Kod', 'Poziom_10_Nazwa', 'Poziom_10_Ilosc' ]; fputcsv($fp, $headers, ';'); $totalRows = 0; while ($product = $db->fetchByAssoc($productsWithComponents)) { $rows = generateComponentRows($product['id'], $product['code'], $product['name'], 1.0, [], $db); foreach ($rows as $row) { fputcsv($fp, $row, ';'); $totalRows++; } } fclose($fp); echo "Wygenerowano plik CSV: $fullpath (wiersze: $totalRows)"; } function generateComponentRows($productId, $productCode, $productName, $quantity, $path, $db, $level = 1) { if (in_array($productId, array_column($path, 'id'))) { return []; } if ($level > 10) { return []; } $rows = []; $currentPath = $path; $currentPath[] = [ 'id' => $productId, 'code' => $productCode, 'name' => $productName, 'quantity' => $quantity ]; $componentsQuery = $db->query(" SELECT c.ecmcomponent_id, c.quantity, p.code, p.name FROM ecmproductcomponents c INNER JOIN ecmproducts p ON c.ecmcomponent_id = p.id WHERE c.ecmproduct_id = '$productId' AND c.deleted = '0' AND p.deleted = '0' ORDER BY c.position, p.code "); $hasComponents = false; while ($component = $db->fetchByAssoc($componentsQuery)) { $hasComponents = true; // Oblicz rzeczywistą ilość komponentu (ilość z hierarchii wyżej * ilość komponentu) $totalQuantity = bcmul($quantity, $component['quantity'], 6); // Sprawdź czy komponent ma swoje komponenty (rekurencja) $subRows = generateComponentRows( $component['ecmcomponent_id'], $component['code'], $component['name'], $totalQuantity, $currentPath, $db, $level + 1 ); if (empty($subRows)) { // Komponent nie ma pod-komponentów, dodaj wiersz z kompletną ścieżką $row = createCSVRow($currentPath, $component['ecmcomponent_id'], $component['code'], $component['name'], $totalQuantity); $rows[] = $row; } else { // Komponent ma pod-komponenty, dodaj wszystkie zwrócone wiersze $rows = array_merge($rows, $subRows); } } // Jeśli produkt nie ma komponentów i jesteśmy na poziomie > 1, zwróć pusty array // (główny produkt zawsze musi być pokazany) if (!$hasComponents && $level > 1) { $row = createCSVRow($currentPath); $rows[] = $row; } return $rows; } function createCSVRow($path, $lastComponentId = null, $lastComponentCode = null, $lastComponentName = null, $lastQuantity = null) { // Utwórz tablicę z 30 elementami (10 poziomów * 3 kolumny: kod, nazwa, ilość) $row = array_fill(0, 30, ''); // Wypełnij ścieżkę foreach ($path as $index => $item) { if ($index < 10) { // Maksymalnie 10 poziomów $row[$index * 3] = $item['code']; // Kod $row[$index * 3 + 1] = $item['name']; // Nazwa $row[$index * 3 + 2] = $item['quantity']; // Ilość } } // Dodaj ostatni komponent jeśli został przekazany if ($lastComponentId !== null && count($path) < 10) { $lastIndex = count($path); $row[$lastIndex * 3] = $lastComponentCode; $row[$lastIndex * 3 + 1] = $lastComponentName; $row[$lastIndex * 3 + 2] = $lastQuantity; } return $row; }