'Matrice', 'D' => 'Diametro', 'R' => 'Rettangolo', 'T' => 'Tubo', 'Q' => 'Quadrato', 'S' => 'Sezione', '?' => 'Altro', ]; /* ===================== MATRICE / MISURA ===================== */ /** * Scompone il valore "Matrice/Misura" di Mago. * M00786S → tipo M, codice 00786S * M00407P-1 → tipo M, codice 00407P-1 * Q8X8M00936P → tipo M, codice 00936P, misura 8X8 (profilo Q) * D 3,3 → tipo D, misura 3,3 * ZB00193P → tipo ?, codice ZB00193P (si cerca il valore intero tra le matrici) */ public static function parse($raw) { $v = strtoupper(trim(preg_replace('/\s+/', ' ', (string)$raw))); $out = ['raw' => $v, 'tipo' => '', 'codice' => '', 'misura' => '']; if ($v === '') return $out; if (preg_match('/M(\d{4,6}[A-Z]?(?:-\d+)?)/', $v, $m, PREG_OFFSET_CAPTURE)) { $out['tipo'] = 'M'; $out['codice'] = $m[1][0]; $before = trim(substr($v, 0, $m[0][1])); if (preg_match('/^[DRTQS]\s*([0-9][0-9.,X ]*)$/', $before, $mm)) $out['misura'] = trim($mm[1]); return $out; } if (preg_match('/^([DRTQS])\s*([0-9][0-9.,X ]*)$/', $v, $m)) { $out['tipo'] = $m[1]; $out['misura'] = trim($m[2]); return $out; } $out['tipo'] = '?'; $out['codice'] = $v; return $out; } /** Indice delle matrici del portale per il collegamento (caricato una volta). */ private static function matriceIndex(PDO $pdo) { $idx = ['exact' => [], 'base' => [], 'num' => []]; foreach ($pdo->query("SELECT id, nome FROM matrice WHERE TRIM(nome) <> ''") as $r) { $tok = strtoupper(strtok(trim($r['nome']), ' ')); if ($tok === '' || $tok === false) continue; $idx['exact'][$tok] = ['id' => (int)$r['id'], 'nome' => trim($r['nome'])]; $base = preg_replace('/-\d+$/', '', $tok); $idx['base'][$base][] = ['id' => (int)$r['id'], 'nome' => trim($r['nome']), 'tok' => $tok]; if (preg_match('/^(\d+)/', $tok, $n)) $idx['num'][(int)$n[1]][] = trim($r['nome']); } foreach ($idx['base'] as &$list) { usort($list, function ($a, $b) { return strnatcmp($b['tok'], $a['tok']); // revisione più alta per prima }); } unset($list); $idx['map'] = []; try { foreach ($pdo->query("SELECT codice_mago, idmatrice FROM matrice_mago_map") as $r) { $idx['map'][strtoupper(trim($r['codice_mago']))] = (int)$r['idmatrice']; } } catch (Throwable $e) { } return $idx; } /** * Trova la matrice del portale per un valore Matrice/Misura. * Ritorna [idmatrice|null, match_status, match_info, parse] */ public static function resolve(array $idx, $raw) { $p = self::parse($raw); if ($p['raw'] === '') return [null, 'anomalia', 'Matrice/Misura vuoto in Mago', $p]; // 0) corrispondenza manuale if (isset($idx['map'][$p['raw']])) return [$idx['map'][$p['raw']], 'mappa', 'Corrispondenza salvata', $p]; if ($p['codice'] !== '') { $c = $p['codice']; // 1) esatta if (isset($idx['exact'][$c])) return [$idx['exact'][$c]['id'], 'esatta', '', $p]; // 2) stessa matrice, altra revisione $base = preg_replace('/-\d+$/', '', $c); if (!empty($idx['base'][$base])) { $best = $idx['base'][$base][0]; return [$best['id'], 'revisione', "Revisione diversa: Mago $c, anagrafica {$best['nome']}", $p]; } // 3) stesso numero, lettera diversa: solo suggerimento $sim = ''; if (preg_match('/^(\d+)/', $c, $n) && !empty($idx['num'][(int)$n[1]])) { $sim = ' · simili: ' . implode(', ', array_slice($idx['num'][(int)$n[1]], 0, 3)); } if ($p['tipo'] === 'M') return [null, 'da_creare', "Matrice $c non presente in anagrafica" . $sim, $p]; return [null, 'anomalia', "Codice $c non riconosciuto" . $sim, $p]; } // profilo standard (D/R/T/Q/S + misura) $label = self::TIPI[$p['tipo']] ?? $p['tipo']; return [null, 'profilo', "$label {$p['misura']}: scegliere la matrice", $p]; } /* ===================== CODA "NUOVI DA MAGO" ===================== */ /** * Allinea la coda con le righe d'ordine Mago locali. * Ritorna un piccolo report (nuove, aggiornate, chiuse, modificate dopo l'import, importate in automatico). */ public static function refreshQueue(PDO $pdo) { $rep = ['candidati' => 0, 'nuove' => 0, 'aggiornate' => 0, 'chiuse' => 0, 'modificate' => 0, 'auto_importate' => 0]; $idx = self::matriceIndex($pdo); $excl = ''; foreach (self::EXCLUDED_ITEM_PREFIXES as $i => $pfx) { $excl .= " AND d.item NOT LIKE " . $pdo->quote($pfx . '%'); } $rows = MagoSalesStats::fetchAllQ($pdo, " SELECT d.sale_ord_id, d.sub_id, d.line, d.item, d.description, d.uom, d.qty, d.delivered_qty, d.notes, d.matrice_misura, d.cod_art_cli, " . MagoSalesStats::ziboDate('d') . " AS zibo_date, " . MagoSalesStats::clientDate('d') . " AS client_date, x.internal_ord_no, x.order_date, x.customer, x.company_name FROM mago_sale_ord_detail d JOIN " . MagoSalesStats::baseFrom() . " ON x.sale_ord_id = d.sale_ord_id WHERE x.order_date >= :from AND x.status IN ('aperto', 'bloccato') AND d.delivered = 0 AND d.cancelled = 0 AND d.item <> '' AND d.qty > :minq $excl ", ['from' => self::QUEUE_FROM_DATE, 'minq' => self::MIN_QTY]); $rep['candidati'] = count($rows); $existing = []; foreach ($pdo->query("SELECT * FROM mago_prod_queue") as $q) { $existing[$q['sale_ord_id'] . '-' . $q['sub_id']] = $q; } $ins = $pdo->prepare(" INSERT INTO mago_prod_queue ( sale_ord_id, sub_id, internal_ord_no, line_no, order_date, customer, company_name, item, description, cod_art_cli, uom, qty, delivered_qty, residual_qty, zibo_date, client_date, mago_notes, matrice_misura, mm_tipo, mm_codice, mm_misura, idmatrice, match_status, match_info, queue_status, first_seen_at, updated_at ) VALUES ( :sale_ord_id, :sub_id, :internal_ord_no, :line_no, :order_date, :customer, :company_name, :item, :description, :cod_art_cli, :uom, :qty, :delivered_qty, :residual_qty, :zibo_date, :client_date, :mago_notes, :matrice_misura, :mm_tipo, :mm_codice, :mm_misura, :idmatrice, :match_status, :match_info, 'nuovo', NOW(), NOW() )"); $upd = $pdo->prepare(" UPDATE mago_prod_queue SET internal_ord_no = :internal_ord_no, line_no = :line_no, order_date = :order_date, customer = :customer, company_name = :company_name, item = :item, description = :description, cod_art_cli = :cod_art_cli, uom = :uom, qty = :qty, delivered_qty = :delivered_qty, residual_qty = :residual_qty, zibo_date = :zibo_date, client_date = :client_date, mago_notes = :mago_notes, matrice_misura = :matrice_misura, mm_tipo = :mm_tipo, mm_codice = :mm_codice, mm_misura = :mm_misura, idmatrice = :idmatrice, match_status = :match_status, match_info = :match_info, queue_status = :queue_status, mago_changed = :mago_changed, mago_changes = :mago_changes, updated_at = NOW() WHERE id = :id"); $seen = []; $pdo->beginTransaction(); try { foreach ($rows as $r) { $key = $r['sale_ord_id'] . '-' . $r['sub_id']; $seen[$key] = true; [$idm, $ms, $mi, $p] = self::resolve($idx, $r['matrice_misura']); $residual = max(0, (float)$r['qty'] - (float)$r['delivered_qty']); $data = [ 'internal_ord_no' => $r['internal_ord_no'], 'line_no' => (int)$r['line'], 'order_date' => $r['order_date'], 'customer' => $r['customer'], 'company_name' => mb_substr((string)$r['company_name'], 0, 200), 'item' => $r['item'], 'description' => mb_substr((string)$r['description'], 0, 255), 'cod_art_cli' => (string)$r['cod_art_cli'], 'uom' => $r['uom'], 'qty' => $r['qty'], 'delivered_qty' => $r['delivered_qty'], 'residual_qty' => $residual, 'zibo_date' => $r['zibo_date'], 'client_date' => $r['client_date'], 'mago_notes' => $r['notes'], 'matrice_misura' => (string)$r['matrice_misura'], 'mm_tipo' => $p['tipo'], 'mm_codice' => mb_substr($p['codice'], 0, 40), 'mm_misura' => mb_substr($p['misura'], 0, 40), 'idmatrice' => $idm, 'match_status' => $ms, 'match_info' => mb_substr($mi, 0, 255), ]; if (!isset($existing[$key])) { $ins->execute($data + ['sale_ord_id' => $r['sale_ord_id'], 'sub_id' => $r['sub_id']]); $rep['nuove']++; continue; } $q = $existing[$key]; $status = $q['queue_status']; $changed = (int)$q['mago_changed']; $changes = $q['mago_changes']; // scelta manuale della matrice: non la tocco if ($q['match_status'] === 'manuale' && $q['idmatrice']) { $data['idmatrice'] = $q['idmatrice']; $data['match_status'] = 'manuale'; $data['match_info'] = $q['match_info']; } if ($status === 'importato') { $diff = self::diff($q, $data); if ($diff) { $changed = 1; $changes = mb_substr(trim(($changes ? $changes . ' | ' : '') . date('d/m') . ': ' . implode(', ', $diff)), -500); $rep['modificate']++; } // dopo l'import la matrice resta quella scelta in produzione $data['idmatrice'] = $q['idmatrice']; $data['match_status'] = $q['match_status']; $data['match_info'] = $q['match_info']; } elseif ($status === 'chiuso_mago') { $status = 'nuovo'; // riaperta in Mago } $upd->execute($data + ['queue_status' => $status, 'mago_changed' => $changed, 'mago_changes' => $changes, 'id' => $q['id']]); $rep['aggiornate']++; } // righe non più "da produrre" in Mago (consegnate, annullate, cancellate, quantità ≤ 1...) $why = $pdo->prepare("SELECT delivered, cancelled FROM mago_sale_ord_detail WHERE sale_ord_id = ? AND sub_id = ?"); $close = $pdo->prepare("UPDATE mago_prod_queue SET queue_status = :s, mago_changed = :c, mago_changes = :t, updated_at = NOW() WHERE id = :id"); foreach ($existing as $key => $q) { if (isset($seen[$key]) || in_array($q['queue_status'], ['chiuso_mago'], true)) continue; $why->execute([$q['sale_ord_id'], $q['sub_id']]); $d = $why->fetch(PDO::FETCH_ASSOC); $reason = !$d ? 'riga cancellata in Mago' : ((int)$d['cancelled'] ? 'riga annullata in Mago' : ((int)$d['delivered'] ? 'riga consegnata in Mago' : 'riga non più da produrre in Mago')); if ($q['queue_status'] === 'importato') { // già in produzione: segnalo solo se è un annullamento/cancellazione (la consegna è normale) if (strpos($reason, 'consegnata') === false && strpos((string)$q['mago_changes'], $reason) === false) { $close->execute(['s' => 'importato', 'c' => 1, 't' => mb_substr(trim(($q['mago_changes'] ? $q['mago_changes'] . ' | ' : '') . date('d/m') . ': ' . $reason), -500), 'id' => $q['id']]); $rep['modificate']++; } continue; } if ($q['queue_status'] === 'nuovo' || $q['queue_status'] === 'scartato') { $close->execute(['s' => 'chiuso_mago', 'c' => 0, 't' => $reason, 'id' => $q['id']]); $rep['chiuse']++; } } $pdo->commit(); } catch (Throwable $e) { if ($pdo->inTransaction()) $pdo->rollBack(); throw $e; } // import automatico (spento finché AUTO_IMPORT = false) if (self::AUTO_IMPORT) { $ids = $pdo->query("SELECT id FROM mago_prod_queue WHERE queue_status = 'nuovo' AND match_status IN ('esatta','mappa') AND idmatrice IS NOT NULL")->fetchAll(PDO::FETCH_COLUMN); foreach ($ids as $id) { $pre = self::prefill($pdo, (int)$id); if (!$pre || !$pre['id_linea']) continue; // linea non univoca: resta da completare a mano try { self::import($pdo, (int)$id, $pre, 'automatico'); $rep['auto_importate']++; } catch (Throwable $e) { } } } return $rep; } /** Differenze rilevanti tra la riga in coda e la riga Mago aggiornata. */ private static function diff(array $q, array $d) { $out = []; if (abs((float)$q['residual_qty'] - (float)$d['residual_qty']) > 0.0005) { $out[] = 'quantità ' . self::num($q['residual_qty']) . ' → ' . self::num($d['residual_qty']) . ' ' . $d['uom']; } if ((string)$q['zibo_date'] !== (string)$d['zibo_date']) $out[] = 'data ZIBO ' . self::dt($q['zibo_date']) . ' → ' . self::dt($d['zibo_date']); if ((string)$q['client_date'] !== (string)$d['client_date']) $out[] = 'data cliente ' . self::dt($q['client_date']) . ' → ' . self::dt($d['client_date']); if (strtoupper(trim($q['matrice_misura'])) !== strtoupper(trim($d['matrice_misura']))) $out[] = 'matrice ' . $q['matrice_misura'] . ' → ' . $d['matrice_misura']; return $out; } private static function num($v) { return rtrim(rtrim(number_format((float)$v, 3, ',', '.'), '0'), ','); } /** Riconosce la matrice di un valore Matrice/Misura (indice caricato una volta per richiesta). [idmatrice|null, match_status, match_info] */ public static function lookup(PDO $pdo, $raw) { static $idx = null; if ($idx === null) { try { $idx = self::matriceIndex($pdo); } catch (Throwable $e) { $idx = ['exact' => [], 'base' => [], 'num' => [], 'map' => []]; } } [$id, $ms, $mi] = self::resolve($idx, $raw); return [$id, $ms, $mi]; } private static function dt($v) { return $v ? date('d/m/Y', strtotime($v)) : '—'; } /* ===================== PRECOMPILAZIONE ===================== */ /** Dati tecnici di una matrice: linee, mescole e velocità dalla scheda di lavoro. */ public static function matriceInfo(PDO $pdo, $idmatrice) { $idmatrice = (int)$idmatrice; $info = ['lines' => [], 'mescole' => [], 'speed_m_h' => null, 'speed_kg_h' => null, 'g_m' => null, 'worksheet' => null]; if (!$idmatrice) return $info; $st = $pdo->prepare("SELECT idlinea FROM matrici_lines WHERE idmatrice = ? ORDER BY idlinea"); $st->execute([$idmatrice]); $info['lines'] = array_map('intval', $st->fetchAll(PDO::FETCH_COLUMN)); $st = $pdo->prepare("SELECT id, worksheet_number, speed_expected_m_h, speed_expected_kg_h FROM work_sheets WHERE idmatrice = ? AND worksheet_status = 'active' ORDER BY worksheet_date DESC, id DESC LIMIT 1"); $st->execute([$idmatrice]); if ($ws = $st->fetch(PDO::FETCH_ASSOC)) { $info['worksheet'] = (int)$ws['worksheet_number']; $info['speed_m_h'] = $ws['speed_expected_m_h'] !== null ? (float)$ws['speed_expected_m_h'] : null; $info['speed_kg_h'] = $ws['speed_expected_kg_h'] !== null ? (float)$ws['speed_expected_kg_h'] : null; $st = $pdo->prepare("SELECT idmescola, mix_weight_g_m FROM work_sheet_mescole WHERE worksheet_id = ? ORDER BY mix_position"); $st->execute([$ws['id']]); $g = 0; foreach ($st->fetchAll(PDO::FETCH_ASSOC) as $m) { $info['mescole'][] = (int)$m['idmescola']; $g += (float)$m['mix_weight_g_m']; } $info['g_m'] = $g > 0 ? $g : null; } if (!$info['mescole']) { $st = $pdo->prepare("SELECT idmescola FROM matrici_mescole WHERE idmatrice = ?"); $st->execute([$idmatrice]); $info['mescole'] = array_map('intval', $st->fetchAll(PDO::FETCH_COLUMN)); } return $info; } /** Proposta dei campi di productiondata per una riga della coda. */ public static function prefill(PDO $pdo, $queueId, $idmatrice = null) { $st = $pdo->prepare("SELECT * FROM mago_prod_queue WHERE id = ?"); $st->execute([(int)$queueId]); $q = $st->fetch(PDO::FETCH_ASSOC); if (!$q) return null; $idm = $idmatrice !== null ? (int)$idmatrice : (int)$q['idmatrice']; $info = self::matriceInfo($pdo, $idm); $uom = strtoupper(trim($q['uom'])); $qty = (float)$q['residual_qty']; $metri = in_array($uom, ['MT', 'M', 'ML'], true) ? $qty : 0; $ore = 0; if ($metri > 0 && $info['speed_m_h'] > 0) $ore = $metri / $info['speed_m_h']; elseif ($uom === 'KG' && $info['speed_kg_h'] > 0) $ore = $qty / $info['speed_kg_h']; $note = []; $note[] = "Da Mago: ordine {$q['internal_ord_no']} riga {$q['line_no']} · {$q['item']}" . ($q['cod_art_cli'] ? " · cod. cliente {$q['cod_art_cli']}" : ''); $note[] = 'Matrice/Misura Mago: ' . $q['matrice_misura'] . ($q['mm_misura'] !== '' ? ' (' . (self::TIPI[$q['mm_tipo']] ?? '') . ' ' . $q['mm_misura'] . ')' : ''); if ($metri <= 0) $note[] = 'Quantità ordinata: ' . self::num($qty) . ' ' . $q['uom']; if ($metri > 0 && $info['g_m']) $note[] = 'Kg stimati: ' . self::num(round($metri * $info['g_m'] / 1000, 1)) . ' (scheda di lavoro ' . $info['worksheet'] . ')'; if (trim((string)$q['mago_notes']) !== '') $note[] = trim($q['mago_notes']); return [ 'queue' => $q, 'idmatrice' => $idm ?: null, 'id_linea' => count($info['lines']) === 1 ? $info['lines'][0] : null, 'lines' => $info['lines'], 'mescole' => $info['mescole'], 'metri' => round($metri, 2), 'kg_sp' => 0, 'kg_p' => 0, 'ore_previste' => round($ore, 2), 'data_zibo' => $q['zibo_date'], 'data_cliente' => $q['client_date'], 'conferma_ordine' => $q['internal_ord_no'], 'note_extra' => implode("\n", $note), 'id_status' => self::STATUS_DA_PROGRAMMARE, 'worksheet' => $info['worksheet'], ]; } /* ===================== IMPORT IN PRODUZIONE ===================== */ /** Cliente del portale per un codice cliente Mago (lo crea se non esiste). */ public static function clientId(PDO $pdo, $code, $name) { $code = trim((string)$code); $name = trim((string)$name) ?: $code; if ($code === '') return null; $st = $pdo->prepare("SELECT id FROM clients WHERE mago_code = ? LIMIT 1"); $st->execute([$code]); if ($id = $st->fetchColumn()) return (int)$id; $st = $pdo->prepare("SELECT id FROM clients WHERE (mago_code IS NULL OR mago_code = '') AND UPPER(TRIM(nome)) = UPPER(TRIM(?)) LIMIT 1"); $st->execute([$name]); if ($id = $st->fetchColumn()) { $pdo->prepare("UPDATE clients SET mago_code = ? WHERE id = ?")->execute([$code, $id]); return (int)$id; } // nuovo cliente (il nome è unico: se esiste già con un altro codice aggiungo il codice) $st = $pdo->prepare("SELECT COUNT(*) FROM clients WHERE nome = ?"); $st->execute([$name]); $nome = $st->fetchColumn() ? "$name ($code)" : $name; $pdo->prepare("INSERT INTO clients (nome, mago_code) VALUES (?, ?)")->execute([mb_substr($nome, 0, 255), $code]); return (int)$pdo->lastInsertId(); } /** * Prossima posizione libera (in fondo) per uno stato della programmazione. * La pagina numera le posizioni di tutte le linee in un'unica sequenza per stato, * quindi MAX + 1 mette il lavoro in fondo anche alla sua linea. */ public static function nextPriority(PDO $pdo, $status) { $st = $pdo->prepare("SELECT COALESCE(MAX(priority), 0) + 1 FROM productiondata WHERE id_status = ?"); $st->execute([(int)$status]); return (int)$st->fetchColumn(); } /** * Crea il lavoro in productiondata e collega la riga della coda. * $d: idmatrice, id_linea, mescole[], metri, kg_sp, kg_p, ore_previste, data_zibo, data_cliente, * note_extra, id_status, remember (salva la corrispondenza codice Mago → matrice) */ public static function import(PDO $pdo, $queueId, array $d, $user = '') { $st = $pdo->prepare("SELECT * FROM mago_prod_queue WHERE id = ? FOR UPDATE"); $own = !$pdo->inTransaction(); if ($own) $pdo->beginTransaction(); try { $st->execute([(int)$queueId]); $q = $st->fetch(PDO::FETCH_ASSOC); if (!$q) throw new RuntimeException('Riga non trovata'); if ($q['queue_status'] === 'importato') throw new RuntimeException("Ordine {$q['internal_ord_no']} riga {$q['line_no']}: già in programmazione"); $idm = (int)($d['idmatrice'] ?? 0); $lin = (int)($d['id_linea'] ?? 0); if (!$idm) throw new RuntimeException("Ordine {$q['internal_ord_no']} riga {$q['line_no']}: manca la matrice"); if (!$lin) throw new RuntimeException("Ordine {$q['internal_ord_no']} riga {$q['line_no']}: manca la linea"); $status = (int)($d['id_status'] ?? self::STATUS_DA_PROGRAMMARE); if (!in_array($status, [self::STATUS_DA_PROGRAMMARE, self::STATUS_PROGRAMMATO], true)) $status = self::STATUS_DA_PROGRAMMARE; $idCliente = self::clientId($pdo, $q['customer'], $q['company_name']); $date = function ($v) { return MagoSalesStats::isValidDate((string)$v) ? $v : null; }; $priority = self::nextPriority($pdo, $status); $pdo->prepare(" INSERT INTO productiondata (Data, data_produzione, conferma_ordine, idmatrice, id_linea, id_cliente, data_zibo, data_cliente, metri, kg_sp, kg_p, ore_previste, note_extra, id_status, priority, fermomacchina, `hour`, hourprod, kgprod, mtprod, scarto) VALUES (CURDATE(), NULL, :conferma, :idmatrice, :id_linea, :id_cliente, :data_zibo, :data_cliente, :metri, :kg_sp, :kg_p, :ore, :note, :status, :priority, 0, '00:00:00', '00:00:00', 0, 0, 0) ")->execute([ 'priority' => $priority, 'conferma' => mb_substr($q['internal_ord_no'], 0, 50), 'idmatrice' => $idm, 'id_linea' => $lin, 'id_cliente' => $idCliente, 'data_zibo' => $date($d['data_zibo'] ?? $q['zibo_date']), 'data_cliente' => $date($d['data_cliente'] ?? $q['client_date']), 'metri' => round((float)($d['metri'] ?? 0), 2), 'kg_sp' => round((float)($d['kg_sp'] ?? 0), 2), 'kg_p' => round((float)($d['kg_p'] ?? 0), 2), 'ore' => min(999.99, round((float)($d['ore_previste'] ?? 0), 2)), 'note' => (string)($d['note_extra'] ?? '') ?: null, 'status' => $status, ]); $pid = (int)$pdo->lastInsertId(); $mescole = array_values(array_unique(array_filter(array_map('intval', (array)($d['mescole'] ?? []))))); if ($mescole) { $insM = $pdo->prepare("INSERT INTO productiondata_mescole (id_productiondata, id_mescola) VALUES (?, ?)"); foreach ($mescole as $m) $insM->execute([$pid, $m]); } // la scelta della matrice fatta a mano diventa la regola per le prossime righe con lo stesso codice $matchStatus = $q['match_status']; if ((int)$q['idmatrice'] !== $idm) $matchStatus = 'manuale'; if (!empty($d['remember']) && trim($q['matrice_misura']) !== '') { $pdo->prepare("INSERT INTO matrice_mago_map (codice_mago, idmatrice, note, created_by, created_at) VALUES (?, ?, ?, ?, NOW()) ON DUPLICATE KEY UPDATE idmatrice = VALUES(idmatrice), created_by = VALUES(created_by), created_at = NOW()") ->execute([strtoupper(trim($q['matrice_misura'])), $idm, 'da import ordine ' . $q['internal_ord_no'], mb_substr((string)$user, 0, 100)]); $matchStatus = 'mappa'; } $pdo->prepare("UPDATE mago_prod_queue SET queue_status = 'importato', productiondata_id = ?, idmatrice = ?, match_status = ?, imported_at = NOW(), imported_by = ?, mago_changed = 0, mago_changes = '', updated_at = NOW() WHERE id = ?") ->execute([$pid, $idm, $matchStatus, mb_substr((string)$user, 0, 100), $q['id']]); if ($own) $pdo->commit(); return $pid; } catch (Throwable $e) { if ($own && $pdo->inTransaction()) $pdo->rollBack(); throw $e; } } }