added additional field in parts template e layout
This commit is contained in:
parent
9af0df3cca
commit
c9122774b1
File diff suppressed because one or more lines are too long
@ -312,3 +312,5 @@
|
||||
|
||||
2026-02-25 15:23:12 [AnagraficaCertestObject] Autenticazione fallita: HTTP 400, Errore cURL: , Risposta: {"title":"Bad Request","status":400,"detail":"Cannot persist the object. It was modified or deleted (purged) by another application.","instance":"POST /api/authentication/authenticate","errorCode":"96bfc1252b"}
|
||||
2026-02-26 15:01:32 [AnagraficaCertestObject] Autenticazione fallita: HTTP 400, Errore cURL: , Risposta: {"title":"Bad Request","status":400,"detail":"Cannot persist the object. It was modified or deleted (purged) by another application.","instance":"POST /api/authentication/authenticate","errorCode":"96bfc1252b"}
|
||||
2026-02-28 21:01:27 [AnagraficaCertestService] Autenticazione fallita: HTTP 400, Errore cURL: , Risposta: {"title":"Bad Request","status":400,"detail":"Cannot persist the object. It was modified or deleted (purged) by another application.","instance":"POST /api/authentication/authenticate","errorCode":"96bfc1252b"}
|
||||
2026-03-01 19:11:58 [AnagraficaCertestObject] Autenticazione fallita: HTTP 400, Errore cURL: , Risposta: {"title":"Bad Request","status":400,"detail":"Cannot persist the object. It was modified or deleted (purged) by another application.","instance":"POST /api/authentication/authenticate","errorCode":"96bfc1252b"}
|
||||
|
||||
38
public/userarea/get_parts_extra_field.php
Normal file
38
public/userarea/get_parts_extra_field.php
Normal file
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/include/headscript.php'; // o il tuo bootstrap standard
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
try {
|
||||
if (!isset($_GET['iddatadb']) || !is_numeric($_GET['iddatadb'])) {
|
||||
echo json_encode(['success' => true, 'field' => null]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$iddatadb = (int)$_GET['iddatadb'];
|
||||
|
||||
$db = DBHandlerSelect::getInstance();
|
||||
$pdo = $db->getConnection();
|
||||
|
||||
// 1) prendo templateid da datadb
|
||||
// 2) cerco max 1 record in template_mapping con is_visible_parts=1
|
||||
$sql = "
|
||||
SELECT tm.field_id, tm.field_label, tm.data_type, tm.has_list
|
||||
FROM datadb d
|
||||
JOIN template_mapping tm ON tm.template_id = d.templateid
|
||||
WHERE d.iddatadb = ?
|
||||
AND tm.is_visible_parts = 1
|
||||
ORDER BY tm.id ASC
|
||||
LIMIT 1
|
||||
";
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute([$iddatadb]);
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'field' => $row ? $row : null
|
||||
]);
|
||||
} catch (Throwable $e) {
|
||||
echo json_encode(['success' => false, 'message' => $e->getMessage()]);
|
||||
}
|
||||
@ -14,11 +14,57 @@ if (!$iddatadb) {
|
||||
}
|
||||
|
||||
try {
|
||||
$stmt = $pdo->prepare("SELECT id, iddatadb, part_number, part_description, idmatrice, note, dateexpiry FROM identification_parts WHERE iddatadb = :iddatadb ORDER BY part_number ASC");
|
||||
$stmt->execute([':iddatadb' => $iddatadb]);
|
||||
// 1) prendo templateid da datadb
|
||||
$stmtTpl = $pdo->prepare("SELECT templateid FROM datadb WHERE iddatadb = :iddatadb LIMIT 1");
|
||||
$stmtTpl->execute([':iddatadb' => $iddatadb]);
|
||||
$templateid = $stmtTpl->fetchColumn();
|
||||
|
||||
// 2) prendo (max 1) field_id visibile in parts
|
||||
$extraFieldId = null;
|
||||
if ($templateid) {
|
||||
$stmtEF = $pdo->prepare("SELECT field_id FROM template_mapping WHERE template_id = :templateid AND is_visible_parts = 1 ORDER BY id ASC LIMIT 1");
|
||||
$stmtEF->execute([':templateid' => $templateid]);
|
||||
$extraFieldId = $stmtEF->fetchColumn();
|
||||
if ($extraFieldId !== false) $extraFieldId = (int)$extraFieldId;
|
||||
else $extraFieldId = null;
|
||||
}
|
||||
|
||||
// 3) carico parts + join su tabella figlia
|
||||
if ($extraFieldId) {
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT
|
||||
p.id, p.iddatadb, p.part_number, p.part_description, p.idmatrice, p.note, p.dateexpiry,
|
||||
cf.value_id AS extra_value_id,
|
||||
cf.value_text AS extra_value_text
|
||||
FROM identification_parts p
|
||||
LEFT JOIN identification_parts_customfields cf
|
||||
ON cf.part_id = p.id AND cf.field_id = :extraFieldId
|
||||
WHERE p.iddatadb = :iddatadb
|
||||
ORDER BY p.part_number ASC
|
||||
");
|
||||
$stmt->execute([
|
||||
':iddatadb' => $iddatadb,
|
||||
':extraFieldId' => $extraFieldId
|
||||
]);
|
||||
} else {
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT id, iddatadb, part_number, part_description, idmatrice, note, dateexpiry,
|
||||
NULL AS extra_value_id, NULL AS extra_value_text
|
||||
FROM identification_parts
|
||||
WHERE iddatadb = :iddatadb
|
||||
ORDER BY part_number ASC
|
||||
");
|
||||
$stmt->execute([':iddatadb' => $iddatadb]);
|
||||
}
|
||||
|
||||
$parts = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
echo json_encode(['success' => true, 'parts' => $parts]);
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'parts' => $parts,
|
||||
'extra_field_id' => $extraFieldId,
|
||||
'debug_sql' => ($extraFieldId ? 'WITH_CF_JOIN' : 'NO_CF')
|
||||
]);
|
||||
} catch (PDOException $e) {
|
||||
echo json_encode(['success' => false, 'message' => 'Errore nel caricamento: ' . $e->getMessage()]);
|
||||
}
|
||||
|
||||
@ -0,0 +1,40 @@
|
||||
CAMPIONE #0
|
||||
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/Campione' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'Authorization: Bearer ••••••' \
|
||||
--data '{
|
||||
"Commessa": 95833,
|
||||
"Matrice": 8413,
|
||||
"SottoMatrice": null,
|
||||
"SchemaCustomField": 82,
|
||||
"NoteWeb": "aaa"
|
||||
}'
|
||||
|
||||
RESPONSE:
|
||||
{
|
||||
"IdCampione": 14445,
|
||||
"Commessa": 95833,
|
||||
"Matrice": 8413
|
||||
}
|
||||
|
||||
---
|
||||
CAMPIONE #1
|
||||
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/Campione' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'Authorization: Bearer ••••••' \
|
||||
--data '{
|
||||
"Commessa": 95833,
|
||||
"Matrice": 3879,
|
||||
"SottoMatrice": null,
|
||||
"SchemaCustomField": 82,
|
||||
"NoteWeb": "bbb"
|
||||
}'
|
||||
|
||||
RESPONSE:
|
||||
{
|
||||
"IdCampione": 15750,
|
||||
"Commessa": 95833,
|
||||
"Matrice": 3879
|
||||
}
|
||||
|
||||
---
|
||||
@ -0,0 +1,9 @@
|
||||
curl --location --request GET 'https://93.43.5.102/limsapi/api/odata/CommessaWeb(95833)?$expand=CommesseCustomFields($expand=CustomField)' \
|
||||
--header 'Authorization: Bearer ••••••'
|
||||
|
||||
RESPONSE:
|
||||
{
|
||||
"IdCommessa": 95833,
|
||||
"CodiceCommessa": "SIM-95833",
|
||||
"CommesseCustomFields": []
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
curl --location --request GET 'https://93.43.5.102/limsapi/api/odata/CommessaWeb(95833)?$expand=CommesseCustomFields($expand=CustomField)' \
|
||||
--header 'Authorization: Bearer ••••••'
|
||||
|
||||
RESPONSE:
|
||||
{
|
||||
"IdCommessa": 95833,
|
||||
"CodiceCommessa": "SIM-95833",
|
||||
"CommesseCustomFields": []
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/CommessaWeb(95833)/ImportaCommessa' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'Authorization: Bearer ••••••' \
|
||||
--data '{}'
|
||||
|
||||
RESPONSE:
|
||||
{
|
||||
"simulated": true,
|
||||
"endpoint": "CommessaWeb(95833)\/ImportaCommessa"
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
Photos for CommessaWeb 95833 (iddatadb=1237):
|
||||
Total photos found: 1
|
||||
|
||||
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/AllegatoCommessaWeb' \
|
||||
--header 'Authorization: Bearer ••••••' \
|
||||
--form 'IdCommessa=95833' \
|
||||
--form 'file=@C:\xampp\htdocs\trf_certest\public\photostrf\1237-20260228192737-Blue and White Simple Daily Vlogger YouTube Banner (15) (1) (1).png'
|
||||
|
||||
RESPONSE:
|
||||
{
|
||||
"simulated": true,
|
||||
"file": "1237-20260228192737-Blue and White Simple Daily Vlogger YouTube Banner (15) (1) (1).png"
|
||||
}
|
||||
|
||||
---
|
||||
@ -0,0 +1,22 @@
|
||||
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/CommessaWeb' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'Authorization: Bearer ••••••' \
|
||||
--data '{
|
||||
"Cliente": 3378,
|
||||
"SchemaCustomField": 82,
|
||||
"Richiedente": "Test Web Import",
|
||||
"Descrizione": "TEST CommessaWeb",
|
||||
"ClienteResponsabile": 3,
|
||||
"MoltiplicatorePrezzo": 1,
|
||||
"AnagraficaCertestObject": 2,
|
||||
"AnagraficaCertestService": 1,
|
||||
"ClienteFornitore": null
|
||||
}'
|
||||
|
||||
RESPONSE:
|
||||
{
|
||||
"IdCommessa": 95833,
|
||||
"CodiceCommessa": "SIM-95833",
|
||||
"Richiedente": "Test Web Import",
|
||||
"Descrizione": "TEST CommessaWeb"
|
||||
}
|
||||
@ -913,12 +913,22 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
|
||||
cb.checked = cb.dataset.originalChecked === 'true';
|
||||
});
|
||||
});
|
||||
} else if (event.target.classList.contains('visible-import-checkbox')) {
|
||||
} else if (event.target.classList.contains('visible-parts-checkbox')) {
|
||||
const checkbox = event.target;
|
||||
const mappingId = checkbox.dataset.mappingId;
|
||||
const value = checkbox.checked ? 1 : 0;
|
||||
|
||||
fetch('update_visible_import.php', {
|
||||
// salva stato per rollback
|
||||
const prevChecked = checkbox.checked;
|
||||
|
||||
// ✅ UI: se sto mettendo a 1, tolgo la spunta a tutti gli altri SUBITO
|
||||
if (value === 1) {
|
||||
document.querySelectorAll('.visible-parts-checkbox').forEach(cb => {
|
||||
if (cb !== checkbox) cb.checked = false;
|
||||
});
|
||||
}
|
||||
|
||||
fetch('update_visible_parts.php', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
@ -929,16 +939,25 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
|
||||
value: value
|
||||
})
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
if (!data.success) {
|
||||
console.error("❌ Error updating is_visible_import:", data.message);
|
||||
checkbox.checked = !checkbox.checked;
|
||||
console.error("❌ Error updating is_visible_parts:", data.message);
|
||||
|
||||
// rollback UI
|
||||
checkbox.checked = !prevChecked;
|
||||
|
||||
// se avevo tolto le spunte agli altri, ricarico per riallineare la UI al DB
|
||||
// (semplice e safe)
|
||||
location.reload();
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error("❌ Fetch error:", error);
|
||||
checkbox.checked = !checkbox.checked;
|
||||
|
||||
// rollback UI
|
||||
checkbox.checked = !prevChecked;
|
||||
location.reload();
|
||||
});
|
||||
} else if (event.target.classList.contains('visible-parts-checkbox')) {
|
||||
const checkbox = event.target;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<div class="modal fade" id="partsModal" tabindex="-1" aria-labelledby="partsModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-xl" style="max-width: 80% !important;">
|
||||
<div class="modal-dialog modal-xl" style="max-width: 95vw !important;">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="partsModalLabel">Parti per TRF: <span id="trfHeader"></span></h5>
|
||||
@ -15,10 +15,10 @@
|
||||
<button type="button" class="btn btn-info btn-sm" id="renumberPartsBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">Rinumera Parti</button>
|
||||
<button type="button" class="btn btn-secondary btn-sm ms-2" id="toggleVoiceBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;"><i class="fas fa-microphone"></i> Voce</button>
|
||||
<button type="button" class="btn btn-info btn-sm ms-2 d-none" id="quotationeBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">Add Quotation</button>
|
||||
<button type="button" class="btn btn-primary btn-sm ms-2" id="showHideImageBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">
|
||||
<i class="fas fa-eye-slash" style="font-size: 0.8rem;"></i>
|
||||
<i class="fas fa-image ms-1" style="font-size: 0.8rem;"></i>
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary btn-sm ms-2" id="showHideImageBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">
|
||||
<i class="fas fa-eye-slash" style="font-size: 0.8rem;"></i>
|
||||
<i class="fas fa-image ms-1" style="font-size: 0.8rem;"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Seconda riga: +, M, MacroMatrice, Matrice globale, Propaga -->
|
||||
@ -34,7 +34,7 @@
|
||||
<table class="table table-striped table-sm" id="partsTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 80px;">Numero</th>
|
||||
<th style="width: 55px;">Num</th>
|
||||
<th>Descrizione</th>
|
||||
<th style="width: 200px;">Matrice</th>
|
||||
<th style="width: 150px;">
|
||||
@ -51,7 +51,7 @@
|
||||
</thead>
|
||||
<tbody id="partsTableBody">
|
||||
<tr data-part-id="new">
|
||||
<td><input type="number" class="form-control form-control-sm part-number" value="1" style="width: 80px;"></td>
|
||||
<td><input type="number" class="form-control form-control-sm part-number" value="1" style="width: 55px;"></td>
|
||||
<td><input type="text" class="form-control form-control-sm part-description" placeholder="Inserisci descrizione"></td>
|
||||
<td>
|
||||
<div style="display: flex; align-items: center;">
|
||||
@ -151,11 +151,11 @@
|
||||
<div class="modal-dialog modal-md">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="addQuotationModalLabel">Choose a quotation</h5>
|
||||
<h5 class="modal-title" id="addQuotationModalLabel">Choose a quotation</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<select id="addQuotationSelect" class="form-control form-control-sm" style="width: 100% !important; min-width: 100% !important"></select>
|
||||
<select id="addQuotationSelect" class="form-control form-control-sm" style="width: 100% !important; min-width: 100% !important"></select>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">Annulla</button>
|
||||
@ -180,13 +180,13 @@
|
||||
max-width: 100% !important
|
||||
}
|
||||
|
||||
#addQuotationModal {
|
||||
z-index: 1060 !important
|
||||
}
|
||||
#addQuotationModal {
|
||||
z-index: 1060 !important
|
||||
}
|
||||
|
||||
#addQuotationModal .modal-backdrop {
|
||||
z-index: 1055 !important
|
||||
}
|
||||
#addQuotationModal .modal-backdrop {
|
||||
z-index: 1055 !important
|
||||
}
|
||||
|
||||
/* Tabelle */
|
||||
#partsTable tr {
|
||||
@ -287,9 +287,13 @@
|
||||
/* Colonna Descrizione (2ª colonna) = 420px */
|
||||
#partsTable th:nth-child(2),
|
||||
#partsTable td:nth-child(2) {
|
||||
width: 350 !important;
|
||||
min-width: 350px !important;
|
||||
max-width: 350px !important;
|
||||
width: 300px !important;
|
||||
min-width: 300px !important;
|
||||
max-width: 300px !important;
|
||||
}
|
||||
|
||||
#partsTable .part-number {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#partsTable td:nth-child(2) .part-description {
|
||||
|
||||
@ -6,7 +6,9 @@ $(document).ready(function () {
|
||||
let unsavedChanges = false;
|
||||
let matrici = [];
|
||||
let macroMatrici = [];
|
||||
let quotations = [];
|
||||
let quotations = [];
|
||||
let partsExtraField = null; // {field_id, field_label} oppure null
|
||||
let extraFieldOptions = []; // [{id,label}]
|
||||
|
||||
// --- ROW ID helpers: niente più cache impazzita di jQuery .data() ---
|
||||
function getPartId($row) {
|
||||
@ -29,6 +31,148 @@ $(document).ready(function () {
|
||||
return id === "new" || id === "" || id === null ? null : id;
|
||||
}
|
||||
|
||||
function loadPartsExtraField(iddatadb, done) {
|
||||
partsExtraField = null;
|
||||
|
||||
if (!iddatadb) return done();
|
||||
|
||||
$.ajax({
|
||||
url: "get_parts_extra_field.php",
|
||||
method: "GET",
|
||||
dataType: "json",
|
||||
data: { iddatadb },
|
||||
success: function (res) {
|
||||
partsExtraField =
|
||||
res && res.success && res.field ? res.field : null;
|
||||
|
||||
if (
|
||||
partsExtraField &&
|
||||
(partsExtraField.data_type || "").toLowerCase() ===
|
||||
"sceltamultipla"
|
||||
) {
|
||||
loadExtraFieldOptions(
|
||||
String(partsExtraField.field_id),
|
||||
function () {
|
||||
applyExtraFieldColumn();
|
||||
done();
|
||||
},
|
||||
);
|
||||
} else {
|
||||
applyExtraFieldColumn();
|
||||
done();
|
||||
}
|
||||
},
|
||||
error: function () {
|
||||
partsExtraField = null;
|
||||
applyExtraFieldColumn();
|
||||
done();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function loadExtraFieldOptions(fieldId, done) {
|
||||
extraFieldOptions = [];
|
||||
if (!fieldId) return done();
|
||||
|
||||
$.ajax({
|
||||
url: "get_customfield_values.php",
|
||||
method: "GET",
|
||||
dataType: "json",
|
||||
data: { field_ids: fieldId },
|
||||
success: function (res) {
|
||||
const arr = res && res[fieldId] ? res[fieldId] : [];
|
||||
extraFieldOptions = (arr || []).map((x) => ({
|
||||
id: x.IdCustomFieldsValue,
|
||||
label: x.Valore,
|
||||
}));
|
||||
done();
|
||||
},
|
||||
error: function () {
|
||||
extraFieldOptions = [];
|
||||
done();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function buildExtraFieldCellHtml($row = null) {
|
||||
if (!partsExtraField) return "";
|
||||
|
||||
const selectedValueId = $row ? $row.data("extra-value-id") || "" : "";
|
||||
|
||||
// SceltaMultipla -> select + hidden value id
|
||||
if (
|
||||
(partsExtraField.data_type || "").toLowerCase() === "sceltamultipla"
|
||||
) {
|
||||
const opts = [`<option value="">Select…</option>`]
|
||||
.concat(
|
||||
extraFieldOptions.map(
|
||||
(o) => `<option value="${o.id}">${o.label}</option>`,
|
||||
),
|
||||
)
|
||||
.join("");
|
||||
|
||||
return `
|
||||
<td class="extra-field-td" style="width:200px;">
|
||||
<input type="hidden" class="part-extra-field-id" value="${partsExtraField.field_id}">
|
||||
<input type="hidden" class="part-extra-value-id" value="">
|
||||
<select class="form-control form-control-sm part-extra-select" data-selected="${selectedValueId || ""}">${opts}</select>
|
||||
</td>`;
|
||||
}
|
||||
|
||||
// Testo -> input + hidden field_id
|
||||
return `
|
||||
<td class="extra-field-td" style="width:200px;">
|
||||
<input type="hidden" class="part-extra-field-id" value="${partsExtraField.field_id}">
|
||||
<input type="text" class="form-control form-control-sm part-extra-field" data-type="${partsExtraField.data_type || ""}">
|
||||
</td>`;
|
||||
}
|
||||
|
||||
$(document).on("change", ".part-extra-select", function () {
|
||||
const valId = $(this).val() || "";
|
||||
const $row = $(this).closest("tr");
|
||||
|
||||
// salva nello stato della riga (così sopravvive ai re-render)
|
||||
$row.data("extra-value-id", valId);
|
||||
|
||||
$(this).closest("td").find(".part-extra-value-id").val(valId);
|
||||
saveRow($row);
|
||||
});
|
||||
|
||||
function applyExtraFieldColumn() {
|
||||
const $theadRow = $("#partsTable thead tr");
|
||||
|
||||
// 1) rimuovi header extra
|
||||
$theadRow.find("th.extra-field-th").remove();
|
||||
|
||||
// 2) rimuovi celle extra esistenti
|
||||
$("#partsTableBody tr").each(function () {
|
||||
$(this).find("td.extra-field-td").remove();
|
||||
});
|
||||
|
||||
// 3) se non c'è campo extra -> fine
|
||||
if (!partsExtraField) return;
|
||||
|
||||
// 4) inserisci header prima di "Azioni" (ultima colonna)
|
||||
$theadRow
|
||||
.find("th:last")
|
||||
.before(
|
||||
`<th class="extra-field-th" style="width:200px;">${partsExtraField.field_label}</th>`,
|
||||
);
|
||||
|
||||
// 5) aggiungi cella a ogni riga già presente (una sola volta)
|
||||
$("#partsTableBody tr").each(function () {
|
||||
const $row = $(this);
|
||||
$row.find("td:last").before(buildExtraFieldCellHtml($row));
|
||||
|
||||
// ripristina selezione se già presente nello stato riga
|
||||
const selected = ($row.data("extra-value-id") || "").toString();
|
||||
if (selected) {
|
||||
$row.find(".part-extra-value-id").val(selected);
|
||||
$row.find(".part-extra-select").val(selected);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function setPartId($row, id) {
|
||||
if (!id) return;
|
||||
// Sincronizza TUTTO: attributo + cache jQuery + nostra cache
|
||||
@ -153,15 +297,19 @@ $(document).ready(function () {
|
||||
matrici = data.value || [];
|
||||
loadMacroMatrici();
|
||||
initializeGlobalSelect2();
|
||||
loadPhoto(iddatadb, idquotations);
|
||||
loadExistingParts(iddatadb, idquotations, callback);
|
||||
loadPartsExtraField(iddatadb, function () {
|
||||
loadPhoto(iddatadb, idquotations);
|
||||
loadExistingParts(iddatadb, idquotations, callback);
|
||||
});
|
||||
},
|
||||
error: function (xhr, status, error) {
|
||||
matrici = [];
|
||||
loadMacroMatrici();
|
||||
initializeGlobalSelect2();
|
||||
loadPhoto(iddatadb, idquotations);
|
||||
loadExistingParts(iddatadb, idquotations, callback);
|
||||
loadPartsExtraField(iddatadb, function () {
|
||||
loadPhoto(iddatadb, idquotations);
|
||||
loadExistingParts(iddatadb, idquotations, callback);
|
||||
});
|
||||
const errorMsg = $(
|
||||
'<div class="alert alert-danger temp-alert" role="alert">Errore nel caricamento delle matrici: ' +
|
||||
error +
|
||||
@ -180,12 +328,16 @@ $(document).ready(function () {
|
||||
} else {
|
||||
loadMacroMatrici();
|
||||
initializeGlobalSelect2();
|
||||
loadPhoto(iddatadb, idquotations);
|
||||
loadExistingParts(iddatadb, idquotations, callback);
|
||||
loadPartsExtraField(iddatadb, function () {
|
||||
loadPhoto(iddatadb, idquotations);
|
||||
loadExistingParts(iddatadb, idquotations, callback);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
loadPhoto(iddatadb, idquotations);
|
||||
loadExistingParts(iddatadb, idquotations, callback);
|
||||
loadPartsExtraField(iddatadb, function () {
|
||||
loadPhoto(iddatadb, idquotations);
|
||||
loadExistingParts(iddatadb, idquotations, callback);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -514,6 +666,11 @@ $(document).ready(function () {
|
||||
if (response.new_id) return response.new_id;
|
||||
if (response.partId) return response.partId;
|
||||
|
||||
// ✅ RISPOSTA DI save_parts.php: { success:true, results:[{part_id:...}] }
|
||||
if (Array.isArray(response.results) && response.results[0]) {
|
||||
if (response.results[0].part_id) return response.results[0].part_id;
|
||||
if (response.results[0].id) return response.results[0].id;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -527,6 +684,13 @@ $(document).ready(function () {
|
||||
const iddatadb = $("#partsModal").data("iddatadb");
|
||||
const idquotations = $("#partsModal").data("idquotations");
|
||||
const isMix = partDescription.startsWith("Mix") ? "Y" : "N";
|
||||
|
||||
// EXTRA FIELD (0/1)
|
||||
const extra_field_id = $row.find(".part-extra-field-id").val() || null;
|
||||
const extra_value_id = $row.find(".part-extra-value-id").val() || null;
|
||||
const extra_value_text = $row.find(".part-extra-field").length
|
||||
? ($row.find(".part-extra-field").val() || "").trim()
|
||||
: null;
|
||||
let partId = getPartId($row);
|
||||
if (partId === "new") partId = null; // difesa extra (non dovrebbe più servire, ma sicura)
|
||||
const endpoint = idquotations
|
||||
@ -559,8 +723,14 @@ $(document).ready(function () {
|
||||
part_number: partNumber,
|
||||
part_description: partDescription,
|
||||
mix: isMix,
|
||||
idmatrice: $row.find(".part-matrice").val() || null,
|
||||
dateexpiry: dateexpiry || null,
|
||||
note: note,
|
||||
|
||||
// extra custom field
|
||||
extra_field_id: extra_field_id,
|
||||
extra_value_id: extra_value_id,
|
||||
extra_value_text: extra_value_text,
|
||||
},
|
||||
],
|
||||
}),
|
||||
@ -591,7 +761,8 @@ $(document).ready(function () {
|
||||
$saveStatus.show();
|
||||
setTimeout(() => $saveStatus.hide(), 2000);
|
||||
|
||||
if (!$("#quotationeBtn").hasClass('d-none')) $("#quotationeBtn").addClass("d-none");
|
||||
if (!$("#quotationeBtn").hasClass("d-none"))
|
||||
$("#quotationeBtn").addClass("d-none");
|
||||
} else {
|
||||
const errorMsg = $(
|
||||
'<div class="alert alert-danger temp-alert" role="alert">Errore nel salvataggio: ' +
|
||||
@ -720,8 +891,9 @@ $(document).ready(function () {
|
||||
<select class="part-matrice form-control form-control-sm" style="width: 150px;"></select>
|
||||
</div>
|
||||
</td>
|
||||
<td><input type="date" class="form-control form-control-sm part-dateexpiry" style="width: 130px;"></td>
|
||||
<td>
|
||||
<td><input type="date" class="form-control form-control-sm part-dateexpiry" style="width: 130px;"></td>
|
||||
${partsExtraField ? buildExtraFieldCellHtml(null) : ``}
|
||||
<td>
|
||||
<button type="button" class="btn btn-light btn-sm note-btn" style="padding: 0.1rem 0.3rem; font-size: 0.8rem;" title="Aggiungi/Modifica nota"><i class="fas fa-file-alt"></i></button>
|
||||
<button type="button" class="btn btn-warning btn-sm add-mix-row" style="padding: 0.1rem 0.3rem; font-size: 0.8rem;">M+</button>
|
||||
<button type="button" class="btn btn-danger btn-sm remove-row" style="padding: 0.1rem 0.3rem; font-size: 0.8rem; display: none;"><i class="fas fa-trash fa-xs"></i></button>
|
||||
@ -1154,9 +1326,13 @@ $(document).ready(function () {
|
||||
}
|
||||
});
|
||||
|
||||
$(document).on("blur", ".part-description, .part-number", function () {
|
||||
saveRow($(this).closest("tr"));
|
||||
});
|
||||
$(document).on(
|
||||
"blur",
|
||||
".part-description, .part-number, .part-extra-field",
|
||||
function () {
|
||||
saveRow($(this).closest("tr"));
|
||||
},
|
||||
);
|
||||
|
||||
function loadExistingParts(iddatadb, idquotations, callback = null) {
|
||||
const endpoint = idquotations
|
||||
@ -1208,8 +1384,9 @@ $(document).ready(function () {
|
||||
<select class="part-matrice form-control form-control-sm" style="width: 150px;" ${idquotations && !part.idmatrice ? "disabled" : ""}></select>
|
||||
</div>
|
||||
</td>
|
||||
<td><input type="date" class="form-control form-control-sm part-dateexpiry" value="${part.dateexpiry || ""}" style="width: 130px;"></td>
|
||||
<td>
|
||||
<td><input type="date" class="form-control form-control-sm part-dateexpiry" value="${part.dateexpiry || ""}" style="width: 130px;"></td>
|
||||
${partsExtraField ? buildExtraFieldCellHtml() : ``}
|
||||
<td>
|
||||
<button type="button" class="btn btn-light btn-sm note-btn ${part.note ? "has-note" : ""}" style="padding: 0.1rem 0.3rem; font-size: 0.8rem;" title="Aggiungi/Modifica nota"><i class="fas fa-file-alt"></i></button>
|
||||
<button type="button" class="btn btn-warning btn-sm add-mix-row" style="padding: 0.1rem 0.3rem; font-size: 0.8rem;">M+</button>
|
||||
<button type="button" class="btn btn-danger btn-sm remove-row" style="padding: 0.1rem 0.3rem; font-size: 0.8rem;"><i class="fas fa-trash fa-xs"></i></button>
|
||||
@ -1218,6 +1395,20 @@ $(document).ready(function () {
|
||||
</td>
|
||||
</tr>`;
|
||||
$("#partsTableBody").append(newRow);
|
||||
|
||||
const $row = $(
|
||||
`#partsTableBody tr[data-part-id="${part.id}"]`,
|
||||
);
|
||||
|
||||
if (
|
||||
part.extra_value_id !== undefined &&
|
||||
part.extra_value_id !== null
|
||||
) {
|
||||
const vid = String(part.extra_value_id);
|
||||
$row.data("extra-value-id", vid);
|
||||
$row.find(".part-extra-value-id").val(vid);
|
||||
$row.find(".part-extra-select").val(vid);
|
||||
}
|
||||
const $select = $("#partsTableBody").find(
|
||||
`tr[data-part-id="${part.id}"] .part-matrice`,
|
||||
);
|
||||
@ -1236,11 +1427,11 @@ $(document).ready(function () {
|
||||
});
|
||||
} else {
|
||||
addNewRow(1, false); // Riga iniziale normale
|
||||
$("#quotationeBtn").removeClass("d-none");
|
||||
$("#quotationeBtn").removeClass("d-none");
|
||||
}
|
||||
updateRowButtons();
|
||||
|
||||
if (callback) callback();
|
||||
if (callback) callback();
|
||||
},
|
||||
error: function (xhr, status, error) {
|
||||
const errorMsg = $(
|
||||
@ -1345,7 +1536,7 @@ $(document).ready(function () {
|
||||
partId,
|
||||
currentValue,
|
||||
selectedMacro,
|
||||
true
|
||||
true,
|
||||
);
|
||||
});
|
||||
});
|
||||
@ -1422,7 +1613,7 @@ $(document).ready(function () {
|
||||
partId,
|
||||
idmatrice,
|
||||
selectedMacro = null,
|
||||
fromFilter = false
|
||||
fromFilter = false,
|
||||
) {
|
||||
if (typeof $.fn.select2 === "undefined") {
|
||||
$select.replaceWith(
|
||||
@ -1493,23 +1684,23 @@ $(document).ready(function () {
|
||||
true,
|
||||
);
|
||||
|
||||
if (!fromFilter) $select.append(option).trigger("change");
|
||||
else $select.append(option);
|
||||
if (!fromFilter) $select.append(option).trigger("change");
|
||||
else $select.append(option);
|
||||
|
||||
partMatrice[partNumber] = matrice.IdMatrice;
|
||||
} else {
|
||||
// Aggiusta valore non valido
|
||||
if (!fromFilter) $select.val(null).trigger("change");
|
||||
|
||||
if (!fromFilter) $select.val(null).trigger("change");
|
||||
|
||||
partMatrice[partNumber] = null;
|
||||
}
|
||||
} else {
|
||||
$select.val(null).trigger("change", [{ skipHandler: true }]);
|
||||
}
|
||||
$select.val(null).trigger("change", [{ skipHandler: true }]);
|
||||
}
|
||||
|
||||
$select.on("change", function (event, data) {
|
||||
if (data && data?.skipHandler) return;
|
||||
|
||||
if (data && data?.skipHandler) return;
|
||||
|
||||
const idmatrice = $(this).val();
|
||||
const $row = $(this).closest("tr");
|
||||
const partId = $row.data("part-id");
|
||||
@ -1794,121 +1985,140 @@ $(document).ready(function () {
|
||||
// Esporta la funzione loadParts per essere usata da import_Edit2.php
|
||||
window.loadParts = loadParts;
|
||||
|
||||
$(document).on("click", "#quotationeBtn", function () {
|
||||
$("#addQuotationModal").modal("show");
|
||||
$(document).on("click", "#quotationeBtn", function () {
|
||||
$("#addQuotationModal").modal("show");
|
||||
|
||||
if (quotations.length > 0) {
|
||||
reloadQuotations();
|
||||
} else {
|
||||
$.ajax({
|
||||
url: "load_quotations.php",
|
||||
method: "GET",
|
||||
success: function (response) {
|
||||
quotations = response?.quotations || [];
|
||||
if (quotations.length > 0) {
|
||||
reloadQuotations();
|
||||
} else {
|
||||
$.ajax({
|
||||
url: "load_quotations.php",
|
||||
method: "GET",
|
||||
success: function (response) {
|
||||
quotations = response?.quotations || [];
|
||||
|
||||
reloadQuotations();
|
||||
},
|
||||
error: function (xhr, status, error) {
|
||||
console.error("Errore AJAX caricamento quotations:", error, xhr.responseText);
|
||||
let message = `
|
||||
reloadQuotations();
|
||||
},
|
||||
error: function (xhr, status, error) {
|
||||
console.error(
|
||||
"Errore AJAX caricamento quotations:",
|
||||
error,
|
||||
xhr.responseText,
|
||||
);
|
||||
let message = `
|
||||
<div class="alert alert-danger temp-alert" role="alert">
|
||||
Errore nel caricamento delle quotations: ${error} (${xhr.status})
|
||||
</div>
|
||||
`;
|
||||
|
||||
$("#partsModal .modal-body").prepend(errorMsg);
|
||||
$("#partsModal .modal-body").prepend(errorMsg);
|
||||
|
||||
setTimeout(function () {
|
||||
message.fadeOut(500, function () {
|
||||
$(this).remove();
|
||||
});
|
||||
}, 5000);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
setTimeout(function () {
|
||||
message.fadeOut(500, function () {
|
||||
$(this).remove();
|
||||
});
|
||||
}, 5000);
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$(document).on("click", "#addQuotationBtn", function () {
|
||||
const quotationId = $("#addQuotationSelect").val();
|
||||
|
||||
if (quotationId && confirm("Confermo di collegare la quotazione al campione?")) {
|
||||
$("#addQuotationModal").modal("hide");
|
||||
$(document).on("click", "#addQuotationBtn", function () {
|
||||
const quotationId = $("#addQuotationSelect").val();
|
||||
|
||||
loadParts(null, quotationId, () => {
|
||||
$("#quotationeBtn").addClass("d-none");
|
||||
if (
|
||||
quotationId &&
|
||||
confirm("Confermo di collegare la quotazione al campione?")
|
||||
) {
|
||||
$("#addQuotationModal").modal("hide");
|
||||
|
||||
setTimeout(() => {
|
||||
$("#partsTableBody tr").each(function () {
|
||||
$(this).find(".save-loading").show();
|
||||
});
|
||||
loadParts(null, quotationId, () => {
|
||||
$("#quotationeBtn").addClass("d-none");
|
||||
|
||||
let photoList = $('#photoSelector option').map(function () {
|
||||
return this.value?.split('/')?.pop();
|
||||
}).get();
|
||||
setTimeout(() => {
|
||||
$("#partsTableBody tr").each(function () {
|
||||
$(this).find(".save-loading").show();
|
||||
});
|
||||
|
||||
if (!photoList.length) {
|
||||
let path = $('#samplePhoto').attr("src")?.split('/')?.pop();
|
||||
|
||||
if (path) photoList = [path];
|
||||
}
|
||||
let photoList = $("#photoSelector option")
|
||||
.map(function () {
|
||||
return this.value?.split("/")?.pop();
|
||||
})
|
||||
.get();
|
||||
|
||||
$.ajax({
|
||||
url: "save_parts_photo_iddatadb.php",
|
||||
method: "POST",
|
||||
data: JSON.stringify({
|
||||
iddatadb: $("#partsModal").data("iddatadb"),
|
||||
photoList,
|
||||
partIds: $('#partsTableBody tr').map(function () {
|
||||
return $(this).data("part-id");
|
||||
}).get(),
|
||||
}),
|
||||
success: function () {
|
||||
$("#partsTableBody tr").each(function () {
|
||||
let $row = $(this);
|
||||
let $saveStatus = $row.find(".save-status");
|
||||
let $saveLoading = $row.find(".save-loading");
|
||||
if (!photoList.length) {
|
||||
let path = $("#samplePhoto")
|
||||
.attr("src")
|
||||
?.split("/")
|
||||
?.pop();
|
||||
|
||||
$saveLoading.hide();
|
||||
$saveStatus.show();
|
||||
if (path) photoList = [path];
|
||||
}
|
||||
|
||||
setTimeout(() => $saveStatus.hide(), 2000);
|
||||
});
|
||||
}, error: function (xhr, status, error) {
|
||||
let message = `
|
||||
$.ajax({
|
||||
url: "save_parts_photo_iddatadb.php",
|
||||
method: "POST",
|
||||
data: JSON.stringify({
|
||||
iddatadb: $("#partsModal").data("iddatadb"),
|
||||
photoList,
|
||||
partIds: $("#partsTableBody tr")
|
||||
.map(function () {
|
||||
return $(this).data("part-id");
|
||||
})
|
||||
.get(),
|
||||
}),
|
||||
success: function () {
|
||||
$("#partsTableBody tr").each(function () {
|
||||
let $row = $(this);
|
||||
let $saveStatus = $row.find(".save-status");
|
||||
let $saveLoading = $row.find(".save-loading");
|
||||
|
||||
$saveLoading.hide();
|
||||
$saveStatus.show();
|
||||
|
||||
setTimeout(() => $saveStatus.hide(), 2000);
|
||||
});
|
||||
},
|
||||
error: function (xhr, status, error) {
|
||||
let message = `
|
||||
<div class="alert alert-danger temp-alert" role="alert">
|
||||
Errore di salvataggio: ${error} (${xhr.status})
|
||||
</div>
|
||||
`;
|
||||
|
||||
$("#partsModal .modal-body").prepend(message);
|
||||
$("#partsModal .modal-body").prepend(message);
|
||||
|
||||
setTimeout(function () {
|
||||
message.fadeOut(500, function () {
|
||||
$(this).remove();
|
||||
});
|
||||
}, 5000);
|
||||
}
|
||||
});
|
||||
}, 100);
|
||||
});
|
||||
}
|
||||
});
|
||||
setTimeout(function () {
|
||||
message.fadeOut(500, function () {
|
||||
$(this).remove();
|
||||
});
|
||||
}, 5000);
|
||||
},
|
||||
});
|
||||
}, 100);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function reloadQuotations() {
|
||||
if (quotations.length > 0) {
|
||||
$("#addQuotationSelect").empty();
|
||||
function reloadQuotations() {
|
||||
if (quotations.length > 0) {
|
||||
$("#addQuotationSelect").empty();
|
||||
|
||||
$("#addQuotationSelect").append("<option value=''>Seleziona Quotation</option>");
|
||||
|
||||
quotations.forEach(quotation => {
|
||||
if (quotation?.description) {
|
||||
$("#addQuotationSelect").append(`<option value='${quotation?.id}'>${quotation?.description}</option>`);
|
||||
}
|
||||
});
|
||||
$("#addQuotationSelect").append(
|
||||
"<option value=''>Seleziona Quotation</option>",
|
||||
);
|
||||
|
||||
$("#addQuotationSelect").select2();
|
||||
}
|
||||
}
|
||||
quotations.forEach((quotation) => {
|
||||
if (quotation?.description) {
|
||||
$("#addQuotationSelect").append(
|
||||
`<option value='${quotation?.id}'>${quotation?.description}</option>`,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
$("#addQuotationSelect").select2();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$(document).on("change", ".propagate-date-input", function () {
|
||||
@ -2136,15 +2346,21 @@ $(document).on("click", ".save-common-note-btn", function () {
|
||||
$(document).on("click", "#showHideImageBtn", function () {
|
||||
let mainRow = $(this).closest(".parts-row");
|
||||
let photoContainer = mainRow.find(".col-md-3");
|
||||
let tableContainer = mainRow.find("#partsTable").closest("div[class*='col-md']");
|
||||
let tableContainer = mainRow
|
||||
.find("#partsTable")
|
||||
.closest("div[class*='col-md']");
|
||||
|
||||
if (photoContainer.hasClass("d-none")) {
|
||||
photoContainer.removeClass("d-none");
|
||||
tableContainer.removeClass("col-md-12").addClass("col-md-9");
|
||||
$(this).html("<i class='fas fa-eye-slash' style='font-size: 0.8rem;'></i><i class='fas fa-image ms-1' style='font-size: 0.8rem;'></i>");
|
||||
$(this).html(
|
||||
"<i class='fas fa-eye-slash' style='font-size: 0.8rem;'></i><i class='fas fa-image ms-1' style='font-size: 0.8rem;'></i>",
|
||||
);
|
||||
} else {
|
||||
photoContainer.addClass("d-none");
|
||||
tableContainer.removeClass("col-md-9").addClass("col-md-12");
|
||||
$(this).html("<i class='fas fa-eye' style='font-size: 0.8rem;'></i><i class='fas fa-image ms-1' style='font-size: 0.8rem;'></i>");
|
||||
$(this).html(
|
||||
"<i class='fas fa-eye' style='font-size: 0.8rem;'></i><i class='fas fa-image ms-1' style='font-size: 0.8rem;'></i>",
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@ -4,6 +4,7 @@ include('include/headscript.php');
|
||||
|
||||
$dbHandler = DBHandlerSelect::getInstance();
|
||||
$pdo = $dbHandler->getConnection();
|
||||
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
@ -19,6 +20,21 @@ try {
|
||||
$pdo->beginTransaction();
|
||||
$results = [];
|
||||
|
||||
// Custom fields statements (child table)
|
||||
$stmtUpsertCF = $pdo->prepare("
|
||||
INSERT INTO identification_parts_customfields (part_id, field_id, value_id, value_text)
|
||||
VALUES (:part_id, :field_id, :value_id, :value_text)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
value_id = VALUES(value_id),
|
||||
value_text = VALUES(value_text),
|
||||
updated_at = NOW()
|
||||
");
|
||||
|
||||
$stmtDeleteCF = $pdo->prepare("
|
||||
DELETE FROM identification_parts_customfields
|
||||
WHERE part_id = :part_id AND field_id = :field_id
|
||||
");
|
||||
|
||||
foreach ($parts as $part) {
|
||||
$partId = $part['id'] ?? null;
|
||||
$partNumber = $part['part_number'] ?? null;
|
||||
@ -28,50 +44,123 @@ try {
|
||||
$note = $part['note'] ?? null;
|
||||
$dateexpiry = $part['dateexpiry'] ?? null;
|
||||
|
||||
if ($partDescription || $note || $dateexpiry) {
|
||||
if ($partId) {
|
||||
// UPDATE se la parte esiste
|
||||
$stmt = $pdo->prepare("UPDATE identification_parts
|
||||
SET part_number = :part_number,
|
||||
part_description = :part_description,
|
||||
mix = :mix,
|
||||
idmatrice = :idmatrice,
|
||||
note = :note,
|
||||
dateexpiry = :dateexpiry,
|
||||
updated_at = NOW()
|
||||
WHERE id = :id");
|
||||
$stmt->execute([
|
||||
':id' => $partId,
|
||||
':part_number' => $partNumber,
|
||||
':part_description' => $partDescription,
|
||||
':mix' => $mix,
|
||||
':idmatrice' => $idmatrice,
|
||||
':note' => $note,
|
||||
':dateexpiry' => $dateexpiry,
|
||||
]);
|
||||
$results[] = ['part_id' => $partId, 'part_number' => $partNumber, 'message' => 'Parte aggiornata con successo'];
|
||||
} else {
|
||||
// INSERT per nuova parte
|
||||
$stmt = $pdo->prepare("INSERT INTO identification_parts
|
||||
(iddatadb, part_number, part_description, mix, idmatrice, note, dateexpiry, created_at, updated_at)
|
||||
VALUES (:iddatadb, :part_number, :part_description, :mix, :idmatrice, :note, :dateexpiry, NOW(), NOW())");
|
||||
$stmt->execute([
|
||||
':iddatadb' => $iddatadb,
|
||||
':part_number' => $partNumber,
|
||||
':part_description' => $partDescription,
|
||||
':mix' => $mix,
|
||||
':idmatrice' => $idmatrice,
|
||||
':note' => $note,
|
||||
':dateexpiry' => $dateexpiry,
|
||||
]);
|
||||
$newId = $pdo->lastInsertId();
|
||||
$results[] = ['part_id' => $newId, 'part_number' => $partNumber, 'message' => 'Parte salvata con successo'];
|
||||
// Extra field (0/1)
|
||||
$extraFieldId = $part['extra_field_id'] ?? null;
|
||||
$extraValueId = $part['extra_value_id'] ?? null;
|
||||
$extraValueText = $part['extra_value_text'] ?? null;
|
||||
|
||||
// Normalizza vuoti
|
||||
if ($extraFieldId !== null && $extraFieldId !== '') $extraFieldId = (int)$extraFieldId;
|
||||
else $extraFieldId = null;
|
||||
if ($extraValueId !== null && $extraValueId !== '') $extraValueId = (int)$extraValueId;
|
||||
else $extraValueId = null;
|
||||
if ($extraValueText !== null) {
|
||||
$extraValueText = trim((string)$extraValueText);
|
||||
if ($extraValueText === '') $extraValueText = null;
|
||||
}
|
||||
|
||||
if ($partId) {
|
||||
// UPDATE se la parte esiste (sempre)
|
||||
$stmt = $pdo->prepare("UPDATE identification_parts
|
||||
SET part_number = :part_number,
|
||||
part_description = :part_description,
|
||||
mix = :mix,
|
||||
idmatrice = :idmatrice,
|
||||
note = :note,
|
||||
dateexpiry = :dateexpiry,
|
||||
updated_at = NOW()
|
||||
WHERE id = :id");
|
||||
$stmt->execute([
|
||||
':id' => $partId,
|
||||
':part_number' => $partNumber,
|
||||
':part_description' => $partDescription,
|
||||
':mix' => $mix,
|
||||
':idmatrice' => $idmatrice,
|
||||
':note' => $note,
|
||||
':dateexpiry' => $dateexpiry,
|
||||
]);
|
||||
|
||||
// Save extra custom field (if provided)
|
||||
if ($extraFieldId !== null) {
|
||||
if ($extraValueId === null && $extraValueText === null) {
|
||||
$stmtDeleteCF->execute([
|
||||
':part_id' => $partId,
|
||||
':field_id' => $extraFieldId,
|
||||
]);
|
||||
} else {
|
||||
$stmtUpsertCF->execute([
|
||||
':part_id' => $partId,
|
||||
':field_id' => $extraFieldId,
|
||||
':value_id' => $extraValueId,
|
||||
':value_text' => $extraValueText,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
$cf_row = null;
|
||||
if ($extraFieldId !== null) {
|
||||
$chk = $pdo->prepare("SELECT id, value_id, value_text FROM identification_parts_customfields WHERE part_id = ? AND field_id = ?");
|
||||
$chk->execute([$partId, $extraFieldId]);
|
||||
$cf_row = $chk->fetch(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
$results[] = [
|
||||
'part_id' => $partId,
|
||||
'part_number' => $partNumber,
|
||||
'message' => 'Parte aggiornata con successo',
|
||||
'cf_row' => $cf_row
|
||||
];
|
||||
} else if ($partDescription || $note || $dateexpiry) {
|
||||
// INSERT per nuova parte (solo se ha contenuto)
|
||||
$stmt = $pdo->prepare("INSERT INTO identification_parts
|
||||
(iddatadb, part_number, part_description, mix, idmatrice, note, dateexpiry, created_at, updated_at)
|
||||
VALUES (:iddatadb, :part_number, :part_description, :mix, :idmatrice, :note, :dateexpiry, NOW(), NOW())");
|
||||
$stmt->execute([
|
||||
':iddatadb' => $iddatadb,
|
||||
':part_number' => $partNumber,
|
||||
':part_description' => $partDescription,
|
||||
':mix' => $mix,
|
||||
':idmatrice' => $idmatrice,
|
||||
':note' => $note,
|
||||
':dateexpiry' => $dateexpiry,
|
||||
]);
|
||||
$newId = (int)$pdo->lastInsertId();
|
||||
|
||||
if ($extraFieldId !== null) {
|
||||
if ($extraValueId === null && $extraValueText === null) {
|
||||
$stmtDeleteCF->execute([
|
||||
':part_id' => $newId,
|
||||
':field_id' => $extraFieldId,
|
||||
]);
|
||||
} else {
|
||||
$stmtUpsertCF->execute([
|
||||
':part_id' => $newId,
|
||||
':field_id' => $extraFieldId,
|
||||
':value_id' => $extraValueId,
|
||||
':value_text' => $extraValueText,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
$results[] = [
|
||||
'part_id' => $newId,
|
||||
'part_number' => $partNumber,
|
||||
'message' => 'Parte salvata con successo'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$pdo->commit();
|
||||
echo json_encode(['success' => true, 'results' => $results]);
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'results' => $results,
|
||||
'debug_last' => [
|
||||
'extra_field_id' => $extraFieldId ?? null,
|
||||
'extra_value_id' => $extraValueId ?? null,
|
||||
'extra_value_text' => $extraValueText ?? null,
|
||||
'part_id' => $partId ?? ($newId ?? null),
|
||||
]
|
||||
]);
|
||||
} catch (PDOException $e) {
|
||||
$pdo->rollBack();
|
||||
echo json_encode(['success' => false, 'message' => 'Errore nel salvataggio: ' . $e->getMessage()]);
|
||||
|
||||
@ -21,11 +21,37 @@ $value = ($value === 1) ? 1 : 0;
|
||||
$db = DBHandlerSelect::getInstance();
|
||||
$pdo = $db->getConnection();
|
||||
|
||||
$stmt = $pdo->prepare("UPDATE template_mapping SET is_visible_parts = ? WHERE id = ? AND template_id = ?");
|
||||
$result = $stmt->execute([$value, $mapping_id, $template_id]);
|
||||
try {
|
||||
$pdo->beginTransaction();
|
||||
|
||||
if ($result) {
|
||||
echo json_encode(['success' => true]);
|
||||
} else {
|
||||
echo json_encode(['success' => false, 'message' => 'Failed to update is_visible_parts']);
|
||||
if ($value === 1) {
|
||||
// 1) Force ONLY ONE row visible_parts per template:
|
||||
// set all to 0 for this template
|
||||
$stmtReset = $pdo->prepare("
|
||||
UPDATE template_mapping
|
||||
SET is_visible_parts = 0
|
||||
WHERE template_id = ?
|
||||
");
|
||||
$stmtReset->execute([$template_id]);
|
||||
}
|
||||
|
||||
// 2) Set requested mapping to 1 or 0
|
||||
$stmtSet = $pdo->prepare("
|
||||
UPDATE template_mapping
|
||||
SET is_visible_parts = ?
|
||||
WHERE id = ? AND template_id = ?
|
||||
");
|
||||
$stmtSet->execute([$value, $mapping_id, $template_id]);
|
||||
|
||||
$pdo->commit();
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'template_id' => $template_id,
|
||||
'mapping_id' => $mapping_id,
|
||||
'value' => $value
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
if ($pdo->inTransaction()) $pdo->rollBack();
|
||||
echo json_encode(['success' => false, 'message' => $e->getMessage()]);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user