(function () {
'use strict';
function qs(sel, root) { return (root || document).querySelector(sel); }
function qsa(sel, root) { return Array.prototype.slice.call((root || document).querySelectorAll(sel)); }
function resolveUrl() { return window.DEVIS_AJAX_URL || window.ajaxUrl || ''; }
/* ─────────────────────────────────────────────
Helpers affichage client
───────────────────────────────────────────── */
function asText(value) {
if (value === null || value === undefined) return '';
return String(value).trim();
}
function buildCustomerDisplayLabel(row) {
if (!row) return '';
var firstname = asText(row.firstname);
var lastname = asText(row.lastname);
var email = asText(row.email);
var name = (firstname + ' ' + lastname).trim();
if (name && email) return name + ' (' + email + ')';
if (name) return name;
if (email) return '(' + email + ')';
return '';
}
function getCustomerShopLabel(row) {
if (!row) return '';
var shopLabel = asText(row.shop_label);
if (shopLabel) return shopLabel;
var idShop = parseInt(row.id_shop, 10);
var shopName = asText(row.shop_name);
if (idShop > 0 && shopName) {
return 'Shop ' + idShop + ' — ' + shopName;
}
if (idShop > 0) {
return 'Shop ' + idShop;
}
return '';
}
function buildCustomerSearchResultText(row) {
var base = buildCustomerDisplayLabel(row);
var shop = getCustomerShopLabel(row);
if (base && shop) return base + ' — ' + shop;
return base;
}
function updateSelectedCustomerLabel(row) {
var lbl = qs('#devisCustomerSelectedLabel');
if (!lbl) return;
if (!row) {
lbl.textContent = '';
return;
}
var base = buildCustomerDisplayLabel(row);
if (!base) {
base = asText(row.label);
}
var shop = getCustomerShopLabel(row);
if (base && shop) {
lbl.textContent = base + ' — ' + shop;
} else {
lbl.textContent = base;
}
}
/* ── Champs cachés client ── */
function setCustomerFields(idCust, idAddrInv, idAddrDel) {
var elC = qs('input[name="id_customer"]');
var elI = qs('input[name="id_address_invoice"]');
var elD = qs('input[name="id_address_delivery"]');
if (elC) elC.value = idCust || 0;
if (elI) elI.value = idAddrInv || 0;
if (elD) elD.value = idAddrDel || 0;
}
/* ── Recherche client ── */
function bindCustomerSearch() {
var input = qs('#devisCustomerSearch');
var results = qs('#devisCustomerResults');
if (!input || !results) return;
var timer = null;
input.addEventListener('input', function () {
var v = input.value.trim();
if (timer) clearTimeout(timer);
if (v.length < 2) { results.style.display = 'none'; return; }
timer = setTimeout(function () {
var p = new URLSearchParams();
p.append('ajax', '1');
p.append('action', 'SearchCustomers');
p.append('q', v);
fetch(resolveUrl(), { method: 'POST', body: p })
.then(function (r) { return r.json(); })
.then(function (rows) {
results.innerHTML = '';
if (!rows || !rows.length) { results.style.display = 'none'; return; }
rows.forEach(function (r) {
var a = document.createElement('a');
a.className = 'list-group-item';
a.style.cursor = 'pointer';
a.textContent = buildCustomerSearchResultText(r);
a.addEventListener('click', function (e) {
e.preventDefault();
setCustomerFields(
r.id_customer,
r.id_address_invoice || 0,
r.id_address_delivery || 0
);
updateSelectedCustomerLabel(r);
var msg = qs('#devisCustomerMsg');
if (msg) msg.innerHTML = ' Client lié. ';
results.style.display = 'none';
recalcTotals();
});
results.appendChild(a);
});
results.style.display = 'block';
})
.catch(function () { results.style.display = 'none'; });
}, 250);
});
}
/* ── Créer / Lier client ── */
function bindCreateCustomer() {
var btn = qs('#devisCreateCustomerBtn');
if (!btn) return;
btn.addEventListener('click', function (e) {
e.preventDefault();
var email = ((qs('#devisC_email') || {}).value || '').trim();
var firstname = ((qs('#devisC_firstname') || {}).value || '').trim();
var lastname = ((qs('#devisC_lastname') || {}).value || '').trim();
var company = ((qs('#devisC_company') || {}).value || '').trim();
var address1 = ((qs('#devisC_address1') || {}).value || '').trim();
var postcode = ((qs('#devisC_postcode') || {}).value || '').trim();
var city = ((qs('#devisC_city') || {}).value || '').trim();
var id_country = ((qs('#devisC_id_country') || {}).value || '0');
var msg = qs('#devisCustomerMsg');
if (!email || !firstname || !lastname) {
if (msg) msg.innerHTML = ' Email, prénom et nom sont obligatoires. ';
return;
}
if (msg) msg.innerHTML = ' Traitement… ';
var p = new URLSearchParams();
p.append('ajax', '1');
p.append('action', 'CreateCustomer');
p.append('email', email);
p.append('firstname', firstname);
p.append('lastname', lastname);
p.append('company', company);
p.append('address1', address1);
p.append('postcode', postcode);
p.append('city', city);
p.append('id_country', id_country);
fetch(resolveUrl(), { method: 'POST', body: p })
.then(function (r) { return r.json(); })
.then(function (res) {
if (res && res.ok) {
setCustomerFields(
res.id_customer,
res.id_address_invoice,
res.id_address_delivery
);
var displayData = {
firstname: asText(res.firstname) || firstname,
lastname: asText(res.lastname) || lastname,
email: asText(res.email) || email,
id_shop: res.id_shop,
shop_name: res.shop_name,
shop_label: res.shop_label,
};
updateSelectedCustomerLabel(displayData);
if (msg) msg.innerHTML = res.linked
? ' Client existant lié au devis. '
: ' Client créé et lié ! ';
recalcTotals();
} else {
var err = (res && res.errors) ? res.errors.join(', ') : 'Erreur inconnue';
if (msg) msg.innerHTML = ' ' + err + ' ';
}
})
.catch(function () {
if (msg) msg.innerHTML = 'Erreur réseau. ';
});
});
}
/* ── Lire le JSON "live" ── */
function getLiveProductsJson() {
var products = [];
var container = qs('#products-tbody') || qs('#devis-products-panel');
if (!container) return null;
qsa('tr[data-id], tr[data-product-id]', container).forEach(function (tr) {
var idP = tr.getAttribute('data-id') || tr.getAttribute('data-product-id');
var qtyEl = qs('.qty-input', tr) || qs('input.qty', tr);
if (!idP || !qtyEl) return;
var puEl = qs('.price-input', tr) || qs('input.unit_price_ht', tr);
var fixedEl = qs('.fixed-price-input', tr);
var idAttr = tr.getAttribute('data-attr') || tr.getAttribute('data-id-product-attribute') || 0;
var q = parseInt(qtyEl.value, 10) || 0;
var pu = puEl ? (parseFloat(String(puEl.value).replace(',', '.')) || 0) : 0;
var fixed = fixedEl ? (parseFloat(String(fixedEl.value).replace(',', '.')) || 0) : 0;
/* ── Lecture du taux de TVA ── */
var taxRateRaw = tr.getAttribute('data-tax-rate');
var taxRate = 0;
if (taxRateRaw !== null && taxRateRaw !== undefined) {
taxRate = parseFloat(String(taxRateRaw).replace(',', '.'));
if (!isFinite(taxRate)) taxRate = 0;
}
/* ── Lecture du flag marquage ── */
var isMarkingRaw = tr.getAttribute('data-is-marking');
var isMarkingProduct = 0;
if (isMarkingRaw !== null && isMarkingRaw !== undefined) {
isMarkingProduct = parseInt(isMarkingRaw, 10) === 1 ? 1 : 0;
}
if (q > 0) {
products.push({
id_product: parseInt(idP, 10),
id_product_attribute: parseInt(idAttr, 10) || 0,
quantity: q,
unit_price_ht: pu,
fixed_unit_price_ht: fixed,
tax_rate: taxRate,
is_marking_product: isMarkingProduct,
});
}
});
return products.length > 0 ? JSON.stringify(products) : null;
}
/* ── UI helpers ── */
function setSpan(id, val) {
var el = document.getElementById(id);
if (!el) return;
var n = parseFloat(val);
if (!isFinite(n)) n = 0;
el.innerText = n.toFixed(2).replace('.', ',') + '\u00a0€';
}
function showRow(rowId, condition) {
var row = document.getElementById(rowId);
if (row) row.style.display = condition ? '' : 'none';
}
/* ── Bloc Remises ── */
function updateCartRules(cartRules) {
var panel = document.getElementById('devis-totals-panel');
if (!panel) return;
var body = qs('.panel-body', panel);
if (!body) return;
qsa('.devis-tier-dynamic', body).forEach(function (e) { e.remove(); });
var tierBlock = qs('#devis_tot_cart_rules_box', body);
if (!cartRules || !cartRules.length) {
if (tierBlock) tierBlock.style.display = 'none';
return;
}
if (!tierBlock) {
tierBlock = document.createElement('div');
tierBlock.id = 'devis_tot_cart_rules_box';
tierBlock.style.cssText = 'margin-top:12px;padding:8px 12px;background:#fff8e1;border-left:3px solid #f9a825;border-radius:3px;';
body.appendChild(tierBlock);
}
tierBlock.style.display = '';
tierBlock.innerHTML = ' Remise(s) appliquée(s) : ';
cartRules.forEach(function (rule) {
var div = document.createElement('div');
div.className = 'devis-tier-dynamic devis_tot_rule_line';
div.style.cssText = 'margin-top:4px;color:#8a6d3b;font-size:12px;';
var name = rule.name || '';
if (rule.reduction_percent > 0) {
div.innerHTML = ' ' + parseFloat(rule.reduction_percent).toFixed(1) + '% — ' + name;
} else if (rule.reduction_amount > 0) {
div.innerHTML = ' ' + parseFloat(rule.reduction_amount).toFixed(2).replace('.', ',') + ' € — ' + name;
} else {
div.innerHTML = ' ' + name;
}
tierBlock.appendChild(div);
});
}
/* ─────────────────────────────────────────────
Bloc vérification quantités marquage
Source unique : data-is-marking sur chaque tr
───────────────────────────────────────────── */
function refreshQuantityControlTotals() {
var totalProducts = 0;
var totalMarking = 0;
var totalNonMarking = 0;
var tbody = document.getElementById('products-tbody');
if (!tbody) return;
var rows = tbody.querySelectorAll('tr');
for (var i = 0; i < rows.length; i++) {
var tr = rows[i];
// Lecture quantité – valeur invalide => 0
var qtyInput = tr.querySelector('.qty-input');
var qty = 0;
if (qtyInput) {
var rawQty = parseInt(qtyInput.value, 10);
qty = (isFinite(rawQty) && rawQty > 0) ? rawQty : 0;
}
// Lecture du flag marquage uniquement via data-is-marking
var isMarkingAttr = tr.getAttribute('data-is-marking');
var isMarking = (isMarkingAttr !== null && parseInt(isMarkingAttr, 10) === 1) ? 1 : 0;
totalProducts += qty;
if (isMarking === 1) {
totalMarking += qty;
} else {
totalNonMarking += qty;
}
}
// Mise à jour DOM
var elTP = document.getElementById('js-total-products-qty');
var elTM = document.getElementById('js-total-marking-qty');
var elTNM = document.getElementById('js-total-non-marking-qty');
if (elTP) elTP.textContent = totalProducts;
if (elTM) elTM.textContent = totalMarking;
if (elTNM) elTNM.textContent = totalNonMarking;
}
/* ─────────────────────────────────────────────
Recalcul des totaux financiers
───────────────────────────────────────────── */
var _recalcTimer = null;
function recalcTotalsThrottled() {
if (_recalcTimer) clearTimeout(_recalcTimer);
_recalcTimer = setTimeout(function () {
_recalcTimer = null;
recalcTotals();
}, 200);
}
function recalcTotals() {
var url = resolveUrl();
if (!url) return;
var live = getLiveProductsJson();
var fd = new FormData();
fd.append('ajax', '1');
fd.append('action', 'RecalcTotals');
fd.append('id_customer', (qs('input[name="id_customer"]') || { value: '0' }).value || '0');
fd.append('id_address_invoice', (qs('input[name="id_address_invoice"]') || { value: '0' }).value || '0');
fd.append('id_address_delivery', (qs('input[name="id_address_delivery"]') || { value: '0' }).value || '0');
fd.append('products_json', live || JSON.stringify([]));
fetch(url, { method: 'POST', body: fd })
.then(function (r) { return r.json(); })
.then(function (data) {
if (!data || !data.ok || !data.totals) return;
var t = data.totals;
setSpan('devis_tot_subtotal_products_ht', t.subtotal_products_ht);
setSpan('devis_tot_discounts_ht', t.discounts_ht);
setSpan('devis_tot_total_products_ht', t.total_products_ht);
setSpan('devis_tot_shipping_ht', t.shipping_ht);
setSpan('devis_tot_preparation_ht', t.preparation_ht);
showRow('devis_tot_preparation_row', (parseFloat(t.preparation_ht) || 0) > 0.01);
setSpan('devis_tot_total_ht', t.total_ht);
setSpan('devis_tot_tax_amount', t.tax_amount);
setSpan('devis_tot_total_ttc', t.total_ttc);
showRow('devis_tot_discounts_row', (parseFloat(t.discounts_ht) || 0) > 0.01);
showRow('devis_tot_remise_row', (parseFloat(t.discounts_ht) || 0) > 0.01);
showRow('devis_tot_shipping_row', (parseFloat(t.shipping_ht) || 0) > 0.01);
updateCartRules(data.cart_rules || []);
refreshQuantityControlTotals();
})
.catch(function () {});
}
/* ── Initialisation ── */
document.addEventListener('DOMContentLoaded', function () {
bindCustomerSearch();
bindCreateCustomer();
document.addEventListener('input', function (e) {
if (!e || !e.target || !e.target.classList) return;
if (
e.target.classList.contains('qty-input') ||
e.target.classList.contains('price-input') ||
e.target.classList.contains('fixed-price-input')
) {
refreshQuantityControlTotals();
recalcTotalsThrottled();
}
});
if (window.DEVIS_PREFILL && parseInt(window.DEVIS_PREFILL.id_customer, 10) > 0) {
setCustomerFields(
window.DEVIS_PREFILL.id_customer,
window.DEVIS_PREFILL.id_address_invoice,
window.DEVIS_PREFILL.id_address_delivery
);
updateSelectedCustomerLabel(window.DEVIS_PREFILL);
}
recalcTotals();
refreshQuantityControlTotals();
var form = qs('#devis_form') || qs('form[name="devis_form"]');
if (form) {
form.addEventListener('submit', function () {
var hidden = qs('#products_json');
if (!hidden) return;
var live = getLiveProductsJson();
if (live !== null) {
hidden.value = live;
}
});
}
});
/* ── Exposition globale ── */
window.DevisRecalcTotals = recalcTotals;
window.DevisRefreshQtyControl = refreshQuantityControlTotals;
})();
Tee-shirts Homme manches courtes vierges ou à personnaliser | T-Shirts Nice
Accueil
T-shirts
Tee-shirts Homme manches courtes
Filtres
keyboard_arrow_down
Affichage 1-5 sur 5 article(s)
Supprimer les filtres
Marque :
Kariban
Certificats :
OCS 100 Organic
check_circle
check_circle
Service Fermé. On répond demain.
keyboard_arrow_up