Add multiple connections per customer, contract period, and layout redesign
- Refactor data model: each customer now has a liittymat array (auto-migration from old format) - Add sopimuskausi (1/12/24/36 kk) and alkupvm fields per connection - Form supports adding/removing multiple connection rows per company - Add "use same as installation address" checkbox for billing address - Move stat cards to compact sidebar on the right - Place search bar above customer table Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
369
script.js
369
script.js
@@ -35,9 +35,7 @@ async function apiCall(action, method = 'GET', body = null) {
|
||||
async function checkAuth() {
|
||||
try {
|
||||
const data = await apiCall('check_auth');
|
||||
if (data.authenticated) {
|
||||
showDashboard();
|
||||
}
|
||||
if (data.authenticated) showDashboard();
|
||||
} catch (e) { /* not logged in */ }
|
||||
}
|
||||
|
||||
@@ -73,24 +71,52 @@ async function loadCustomers() {
|
||||
renderTable();
|
||||
}
|
||||
|
||||
// Helper: flatten customers into rows (one row per liittymä)
|
||||
function flattenRows(customerList) {
|
||||
const rows = [];
|
||||
customerList.forEach(c => {
|
||||
const liittymat = c.liittymat || [];
|
||||
if (liittymat.length === 0) {
|
||||
rows.push({ customer: c, liittyma: { asennusosoite: '', postinumero: '', kaupunki: '', liittymanopeus: '', hinta: 0, sopimuskausi: '', alkupvm: '' }, index: 0 });
|
||||
} else {
|
||||
liittymat.forEach((l, i) => {
|
||||
rows.push({ customer: c, liittyma: l, index: i });
|
||||
});
|
||||
}
|
||||
});
|
||||
return rows;
|
||||
}
|
||||
|
||||
function renderTable() {
|
||||
const query = searchInput.value.toLowerCase().trim();
|
||||
let filtered = customers;
|
||||
if (query) {
|
||||
filtered = customers.filter(c =>
|
||||
c.yritys.toLowerCase().includes(query) ||
|
||||
(c.asennusosoite || '').toLowerCase().includes(query) ||
|
||||
(c.postinumero || '').toLowerCase().includes(query) ||
|
||||
(c.kaupunki || '').toLowerCase().includes(query) ||
|
||||
(c.yhteyshenkilö || '').toLowerCase().includes(query) ||
|
||||
(c.liittymanopeus || '').toLowerCase().includes(query)
|
||||
);
|
||||
filtered = customers.filter(c => {
|
||||
const liittymat = c.liittymat || [];
|
||||
const inLiittymat = liittymat.some(l =>
|
||||
(l.asennusosoite || '').toLowerCase().includes(query) ||
|
||||
(l.postinumero || '').toLowerCase().includes(query) ||
|
||||
(l.kaupunki || '').toLowerCase().includes(query) ||
|
||||
(l.liittymanopeus || '').toLowerCase().includes(query)
|
||||
);
|
||||
return c.yritys.toLowerCase().includes(query) ||
|
||||
(c.yhteyshenkilö || '').toLowerCase().includes(query) ||
|
||||
inLiittymat;
|
||||
});
|
||||
}
|
||||
|
||||
const rows = flattenRows(filtered);
|
||||
|
||||
// Sort
|
||||
filtered.sort((a, b) => {
|
||||
let va = a[sortField] ?? '';
|
||||
let vb = b[sortField] ?? '';
|
||||
rows.sort((a, b) => {
|
||||
let va, vb;
|
||||
if (['asennusosoite', 'postinumero', 'kaupunki', 'liittymanopeus', 'hinta', 'sopimuskausi'].includes(sortField)) {
|
||||
va = a.liittyma[sortField] ?? '';
|
||||
vb = b.liittyma[sortField] ?? '';
|
||||
} else {
|
||||
va = a.customer[sortField] ?? '';
|
||||
vb = b.customer[sortField] ?? '';
|
||||
}
|
||||
if (sortField === 'hinta') {
|
||||
va = parseFloat(va) || 0;
|
||||
vb = parseFloat(vb) || 0;
|
||||
@@ -103,77 +129,92 @@ function renderTable() {
|
||||
return 0;
|
||||
});
|
||||
|
||||
if (filtered.length === 0) {
|
||||
if (rows.length === 0) {
|
||||
tbody.innerHTML = '';
|
||||
noCustomers.style.display = 'block';
|
||||
document.getElementById('customer-table').style.display = 'none';
|
||||
} else {
|
||||
noCustomers.style.display = 'none';
|
||||
document.getElementById('customer-table').style.display = 'table';
|
||||
tbody.innerHTML = filtered.map(c => `
|
||||
<tr data-id="${c.id}">
|
||||
<td><strong>${esc(c.yritys)}</strong></td>
|
||||
<td>${esc(c.asennusosoite)}</td>
|
||||
<td>${esc(c.postinumero)}</td>
|
||||
<td>${esc(c.kaupunki)}</td>
|
||||
<td>${esc(c.liittymanopeus)}</td>
|
||||
<td class="price-cell">${formatPrice(c.hinta)}</td>
|
||||
let prevCustomerId = null;
|
||||
tbody.innerHTML = rows.map(r => {
|
||||
const c = r.customer;
|
||||
const l = r.liittyma;
|
||||
const isFirst = c.id !== prevCustomerId;
|
||||
prevCustomerId = c.id;
|
||||
const sopimus = l.sopimuskausi ? l.sopimuskausi + ' kk' : '';
|
||||
const alkupvm = l.alkupvm ? ' (' + esc(l.alkupvm) + ')' : '';
|
||||
return `
|
||||
<tr data-id="${c.id}" class="${isFirst ? '' : 'sub-row'}">
|
||||
<td>${isFirst ? '<strong>' + esc(c.yritys) + '</strong>' : '<span class="sub-marker">↳</span>'}</td>
|
||||
<td>${esc(l.asennusosoite)}${l.postinumero ? ', ' + esc(l.postinumero) : ''}</td>
|
||||
<td>${esc(l.kaupunki)}</td>
|
||||
<td>${esc(l.liittymanopeus)}</td>
|
||||
<td class="price-cell">${formatPrice(l.hinta)}</td>
|
||||
<td>${sopimus}${alkupvm}</td>
|
||||
<td class="actions-cell">
|
||||
<button onclick="event.stopPropagation(); editCustomer('${c.id}')" title="Muokkaa">✏️</button>
|
||||
<button onclick="event.stopPropagation(); deleteCustomer('${c.id}', '${esc(c.yritys)}')" title="Poista">🗑️</button>
|
||||
${isFirst ? `
|
||||
<button onclick="event.stopPropagation(); editCustomer('${c.id}')" title="Muokkaa">✎</button>
|
||||
<button onclick="event.stopPropagation(); deleteCustomer('${c.id}', '${esc(c.yritys)}')" title="Poista">🗑</button>
|
||||
` : ''}
|
||||
</td>
|
||||
</tr>
|
||||
`).join('');
|
||||
</tr>`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
updateSummary(filtered);
|
||||
updateSummary();
|
||||
}
|
||||
|
||||
function updateSummary(filtered) {
|
||||
function getAllLiittymat() {
|
||||
const all = [];
|
||||
customers.forEach(c => (c.liittymat || []).forEach(l => all.push(l)));
|
||||
return all;
|
||||
}
|
||||
|
||||
function updateSummary() {
|
||||
const liittymat = getAllLiittymat();
|
||||
const count = customers.length;
|
||||
const total = customers.reduce((sum, c) => sum + (parseFloat(c.hinta) || 0), 0);
|
||||
customerCount.textContent = `${count} asiakasta`;
|
||||
const connCount = liittymat.length;
|
||||
const total = liittymat.reduce((sum, l) => sum + (parseFloat(l.hinta) || 0), 0);
|
||||
|
||||
customerCount.textContent = `${count} asiakasta, ${connCount} liittymää`;
|
||||
totalBilling.textContent = `Laskutus yhteensä: ${formatPrice(total)}/kk`;
|
||||
|
||||
// Stat-kortit
|
||||
const statCount = document.getElementById('stat-count');
|
||||
const statBilling = document.getElementById('stat-billing');
|
||||
const statYearly = document.getElementById('stat-yearly');
|
||||
if (statCount) statCount.textContent = count;
|
||||
if (statBilling) statBilling.textContent = formatPrice(total);
|
||||
if (statYearly) statYearly.textContent = formatPrice(total * 12);
|
||||
setText('stat-count', count);
|
||||
setText('stat-connections', connCount);
|
||||
setText('stat-billing', formatPrice(total));
|
||||
setText('stat-yearly', formatPrice(total * 12));
|
||||
|
||||
// Nippelitilastot
|
||||
updateTrivia();
|
||||
updateTrivia(liittymat, connCount);
|
||||
}
|
||||
|
||||
function updateTrivia() {
|
||||
const count = customers.length;
|
||||
if (count === 0) {
|
||||
function updateTrivia(liittymat, connCount) {
|
||||
if (connCount === 0) {
|
||||
setTrivia('stat-top-zip', '-', '');
|
||||
setTrivia('stat-top-speed', '-', '');
|
||||
setText('stat-avg-price', '-');
|
||||
const st = document.getElementById('stat-speed-table');
|
||||
if (st) st.innerHTML = '<span style="color:#aaa;font-size:0.85rem;">-</span>';
|
||||
return;
|
||||
}
|
||||
|
||||
// Suosituin postinumero
|
||||
const zipCounts = {};
|
||||
customers.forEach(c => {
|
||||
const zip = (c.postinumero || '').trim();
|
||||
liittymat.forEach(l => {
|
||||
const zip = (l.postinumero || '').trim();
|
||||
if (zip) zipCounts[zip] = (zipCounts[zip] || 0) + 1;
|
||||
});
|
||||
const topZip = Object.entries(zipCounts).sort((a, b) => b[1] - a[1])[0];
|
||||
if (topZip) {
|
||||
const city = customers.find(c => (c.postinumero || '').trim() === topZip[0]);
|
||||
const city = liittymat.find(l => (l.postinumero || '').trim() === topZip[0]);
|
||||
setTrivia('stat-top-zip', topZip[0], `${topZip[1]} liittymää` + (city && city.kaupunki ? ` (${city.kaupunki})` : ''));
|
||||
} else {
|
||||
setTrivia('stat-top-zip', '-', 'ei postinumeroita');
|
||||
setTrivia('stat-top-zip', '-', '');
|
||||
}
|
||||
|
||||
// Nopeus-jakauma
|
||||
const speedCounts = {};
|
||||
customers.forEach(c => {
|
||||
const speed = (c.liittymanopeus || '').trim();
|
||||
liittymat.forEach(l => {
|
||||
const speed = (l.liittymanopeus || '').trim();
|
||||
if (speed) speedCounts[speed] = (speedCounts[speed] || 0) + 1;
|
||||
});
|
||||
const speedTable = document.getElementById('stat-speed-table');
|
||||
@@ -181,19 +222,19 @@ function updateTrivia() {
|
||||
const sorted = Object.entries(speedCounts).sort((a, b) => b[1] - a[1]);
|
||||
const maxCount = sorted.length > 0 ? sorted[0][1] : 0;
|
||||
if (sorted.length === 0) {
|
||||
speedTable.innerHTML = '<span style="color:#aaa; font-size:0.85rem;">-</span>';
|
||||
speedTable.innerHTML = '<span style="color:#aaa;font-size:0.85rem;">-</span>';
|
||||
} else {
|
||||
speedTable.innerHTML = sorted.map(([speed, cnt]) => {
|
||||
const isTop = cnt === maxCount;
|
||||
const barWidth = Math.max(20, (cnt / maxCount) * 60);
|
||||
const barWidth = Math.max(15, (cnt / maxCount) * 50);
|
||||
return `<span class="speed-item ${isTop ? 'top' : ''}">${esc(speed)} (${cnt})<span class="speed-bar" style="width:${barWidth}px"></span></span>`;
|
||||
}).join('');
|
||||
}
|
||||
}
|
||||
|
||||
// Keskihinta
|
||||
const total = customers.reduce((sum, c) => sum + (parseFloat(c.hinta) || 0), 0);
|
||||
setText('stat-avg-price', formatPrice(total / count));
|
||||
const total = liittymat.reduce((sum, l) => sum + (parseFloat(l.hinta) || 0), 0);
|
||||
setText('stat-avg-price', formatPrice(total / connCount));
|
||||
}
|
||||
|
||||
function setTrivia(id, value, sub) {
|
||||
@@ -240,8 +281,7 @@ document.querySelectorAll('th[data-sort]').forEach(th => {
|
||||
tbody.addEventListener('click', (e) => {
|
||||
const row = e.target.closest('tr');
|
||||
if (!row) return;
|
||||
const id = row.dataset.id;
|
||||
showDetail(id);
|
||||
showDetail(row.dataset.id);
|
||||
});
|
||||
|
||||
function detailVal(val) {
|
||||
@@ -260,13 +300,47 @@ function showDetail(id) {
|
||||
if (!c) return;
|
||||
currentDetailId = id;
|
||||
|
||||
const fullAddress = [c.asennusosoite, c.postinumero, c.kaupunki].filter(Boolean).join(', ');
|
||||
const liittymat = c.liittymat || [];
|
||||
const fullBillingAddress = [c.laskutusosoite, c.laskutuspostinumero, c.laskutuskaupunki].filter(Boolean).join(', ');
|
||||
|
||||
const liittymatHtml = liittymat.map((l, i) => {
|
||||
const fullAddr = [l.asennusosoite, l.postinumero, l.kaupunki].filter(Boolean).join(', ');
|
||||
const sopimus = l.sopimuskausi ? l.sopimuskausi + ' kk' : '-';
|
||||
const alku = l.alkupvm || '-';
|
||||
return `
|
||||
<div class="liittyma-card">
|
||||
${liittymat.length > 1 ? `<div class="liittyma-num">Liittymä ${i + 1}</div>` : ''}
|
||||
<div class="detail-grid">
|
||||
<div class="detail-item">
|
||||
<div class="detail-label">Osoite</div>
|
||||
<div class="detail-value">${detailVal(fullAddr)}</div>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<div class="detail-label">Nopeus</div>
|
||||
<div class="detail-value">${detailVal(l.liittymanopeus)}</div>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<div class="detail-label">Hinta / kk</div>
|
||||
<div class="detail-value price-cell">${formatPrice(l.hinta)}</div>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<div class="detail-label">Sopimuskausi</div>
|
||||
<div class="detail-value">${sopimus}</div>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<div class="detail-label">Alkaen</div>
|
||||
<div class="detail-value">${detailVal(alku)}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
}).join('');
|
||||
|
||||
const totalHinta = liittymat.reduce((s, l) => s + (parseFloat(l.hinta) || 0), 0);
|
||||
|
||||
document.getElementById('detail-title').textContent = c.yritys;
|
||||
document.getElementById('detail-body').innerHTML = `
|
||||
<div class="detail-section">
|
||||
<h3>Yritys ja liittymä</h3>
|
||||
<h3>Perustiedot</h3>
|
||||
<div class="detail-grid">
|
||||
<div class="detail-item">
|
||||
<div class="detail-label">Yritys</div>
|
||||
@@ -276,20 +350,13 @@ function showDetail(id) {
|
||||
<div class="detail-label">Y-tunnus</div>
|
||||
<div class="detail-value">${detailVal(c.ytunnus)}</div>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<div class="detail-label">Asennusosoite</div>
|
||||
<div class="detail-value">${detailVal(fullAddress)}</div>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<div class="detail-label">Nopeus</div>
|
||||
<div class="detail-value">${detailVal(c.liittymanopeus)}</div>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<div class="detail-label">Hinta / kk</div>
|
||||
<div class="detail-value price-cell">${formatPrice(c.hinta)}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="detail-section">
|
||||
<h3>Liittymät (${liittymat.length})</h3>
|
||||
${liittymatHtml}
|
||||
${liittymat.length > 1 ? `<div class="liittyma-total">Yhteensä: ${formatPrice(totalHinta)}/kk</div>` : ''}
|
||||
</div>
|
||||
<div class="detail-section">
|
||||
<h3>Yhteystiedot</h3>
|
||||
<div class="detail-grid">
|
||||
@@ -331,27 +398,24 @@ function showDetail(id) {
|
||||
${c.lisatiedot ? `
|
||||
<div class="detail-section">
|
||||
<h3>Lisätiedot</h3>
|
||||
<p style="white-space:pre-wrap; color:#555;">${esc(c.lisatiedot)}</p>
|
||||
<p style="white-space:pre-wrap;color:#555;">${esc(c.lisatiedot)}</p>
|
||||
</div>` : ''}
|
||||
<div class="detail-section">
|
||||
<h3>Tiedostot</h3>
|
||||
<div class="file-upload-area">
|
||||
<label class="file-upload-btn btn-primary" style="display:inline-block; cursor:pointer; font-size:0.85rem; padding:8px 16px;">
|
||||
<label class="file-upload-btn btn-primary" style="display:inline-block;cursor:pointer;font-size:0.85rem;padding:8px 16px;">
|
||||
+ Lisää tiedosto
|
||||
<input type="file" id="file-upload-input" style="display:none" multiple>
|
||||
</label>
|
||||
<span class="file-upload-hint" style="font-size:0.8rem; color:#999; margin-left:8px;">Max 20 MB / tiedosto</span>
|
||||
<span class="file-upload-hint" style="font-size:0.8rem;color:#999;margin-left:8px;">Max 20 MB / tiedosto</span>
|
||||
</div>
|
||||
<div id="file-list" class="file-list" style="margin-top:0.75rem;"></div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
detailModal.style.display = 'flex';
|
||||
|
||||
// Lataa tiedostolista
|
||||
loadFiles(id);
|
||||
|
||||
// Upload handler
|
||||
const fileInput = document.getElementById('file-upload-input');
|
||||
fileInput.addEventListener('change', async () => {
|
||||
for (const file of fileInput.files) {
|
||||
@@ -381,7 +445,7 @@ async function loadFiles(customerId) {
|
||||
try {
|
||||
const files = await apiCall(`file_list&customer_id=${customerId}`);
|
||||
if (files.length === 0) {
|
||||
fileList.innerHTML = '<p style="color:#aaa; font-size:0.85rem;">Ei tiedostoja.</p>';
|
||||
fileList.innerHTML = '<p style="color:#aaa;font-size:0.85rem;">Ei tiedostoja.</p>';
|
||||
return;
|
||||
}
|
||||
fileList.innerHTML = files.map(f => `
|
||||
@@ -395,7 +459,7 @@ async function loadFiles(customerId) {
|
||||
</div>
|
||||
`).join('');
|
||||
} catch (e) {
|
||||
fileList.innerHTML = '<p style="color:#e74c3c; font-size:0.85rem;">Virhe ladattaessa tiedostoja.</p>';
|
||||
fileList.innerHTML = '<p style="color:#e74c3c;font-size:0.85rem;">Virhe ladattaessa tiedostoja.</p>';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -426,6 +490,108 @@ document.getElementById('detail-delete').addEventListener('click', () => {
|
||||
}
|
||||
});
|
||||
|
||||
// ============ FORM: Liittymät (add/remove rows) ============
|
||||
|
||||
let formLiittymat = [];
|
||||
|
||||
function createLiittymaRow(data = {}, index = 0) {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'liittyma-row';
|
||||
div.dataset.index = index;
|
||||
div.innerHTML = `
|
||||
<div class="liittyma-row-header">
|
||||
<span class="liittyma-row-title">Liittymä ${index + 1}</span>
|
||||
<button type="button" class="btn-remove-row" title="Poista liittymä">✕</button>
|
||||
</div>
|
||||
<div class="form-grid form-grid-liittyma">
|
||||
<div class="form-group">
|
||||
<label>Osoite</label>
|
||||
<input type="text" class="l-asennusosoite" value="${esc(data.asennusosoite || '')}" placeholder="esim. Kauppakatu 5">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Postinumero</label>
|
||||
<input type="text" class="l-postinumero" value="${esc(data.postinumero || '')}" placeholder="20100">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Kaupunki</label>
|
||||
<input type="text" class="l-kaupunki" value="${esc(data.kaupunki || '')}" placeholder="Turku">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Nopeus</label>
|
||||
<input type="text" class="l-liittymanopeus" value="${esc(data.liittymanopeus || '')}" placeholder="esim. 100/100">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Hinta €/kk</label>
|
||||
<input type="number" class="l-hinta" step="0.01" min="0" value="${data.hinta || ''}">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Sopimuskausi</label>
|
||||
<select class="l-sopimuskausi">
|
||||
<option value="">- Valitse -</option>
|
||||
<option value="1" ${data.sopimuskausi === '1' ? 'selected' : ''}>1 kk</option>
|
||||
<option value="12" ${data.sopimuskausi === '12' ? 'selected' : ''}>12 kk</option>
|
||||
<option value="24" ${data.sopimuskausi === '24' ? 'selected' : ''}>24 kk</option>
|
||||
<option value="36" ${data.sopimuskausi === '36' ? 'selected' : ''}>36 kk</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Alkaen</label>
|
||||
<input type="date" class="l-alkupvm" value="${esc(data.alkupvm || '')}">
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
div.querySelector('.btn-remove-row').addEventListener('click', () => {
|
||||
div.remove();
|
||||
renumberLiittymaRows();
|
||||
});
|
||||
return div;
|
||||
}
|
||||
|
||||
function renumberLiittymaRows() {
|
||||
const container = document.getElementById('liittymat-container');
|
||||
container.querySelectorAll('.liittyma-row').forEach((row, i) => {
|
||||
row.dataset.index = i;
|
||||
row.querySelector('.liittyma-row-title').textContent = `Liittymä ${i + 1}`;
|
||||
});
|
||||
}
|
||||
|
||||
function collectLiittymatFromForm() {
|
||||
const container = document.getElementById('liittymat-container');
|
||||
const rows = container.querySelectorAll('.liittyma-row');
|
||||
return Array.from(rows).map(row => ({
|
||||
asennusosoite: row.querySelector('.l-asennusosoite').value,
|
||||
postinumero: row.querySelector('.l-postinumero').value,
|
||||
kaupunki: row.querySelector('.l-kaupunki').value,
|
||||
liittymanopeus: row.querySelector('.l-liittymanopeus').value,
|
||||
hinta: row.querySelector('.l-hinta').value,
|
||||
sopimuskausi: row.querySelector('.l-sopimuskausi').value,
|
||||
alkupvm: row.querySelector('.l-alkupvm').value,
|
||||
}));
|
||||
}
|
||||
|
||||
document.getElementById('btn-add-liittyma').addEventListener('click', () => {
|
||||
const container = document.getElementById('liittymat-container');
|
||||
const count = container.querySelectorAll('.liittyma-row').length;
|
||||
container.appendChild(createLiittymaRow({}, count));
|
||||
});
|
||||
|
||||
// Billing "same as" checkbox
|
||||
document.getElementById('form-billing-same').addEventListener('change', function () {
|
||||
const billingFields = document.getElementById('billing-fields');
|
||||
if (this.checked) {
|
||||
billingFields.style.display = 'none';
|
||||
// Copy first liittymä address into billing fields
|
||||
const firstRow = document.querySelector('.liittyma-row');
|
||||
if (firstRow) {
|
||||
document.getElementById('form-laskutusosoite').value = firstRow.querySelector('.l-asennusosoite').value;
|
||||
document.getElementById('form-laskutuspostinumero').value = firstRow.querySelector('.l-postinumero').value;
|
||||
document.getElementById('form-laskutuskaupunki').value = firstRow.querySelector('.l-kaupunki').value;
|
||||
}
|
||||
} else {
|
||||
billingFields.style.display = 'block';
|
||||
}
|
||||
});
|
||||
|
||||
// Add/Edit modal
|
||||
document.getElementById('btn-add').addEventListener('click', () => openCustomerForm());
|
||||
document.getElementById('modal-close').addEventListener('click', () => customerModal.style.display = 'none');
|
||||
@@ -437,22 +603,28 @@ function openCustomerForm(customer = null) {
|
||||
document.getElementById('form-submit').textContent = c ? 'Päivitä' : 'Tallenna';
|
||||
document.getElementById('form-id').value = c ? c.id : '';
|
||||
document.getElementById('form-yritys').value = c ? c.yritys : '';
|
||||
document.getElementById('form-ytunnus').value = c ? c.ytunnus : '';
|
||||
document.getElementById('form-asennusosoite').value = c ? c.asennusosoite : '';
|
||||
document.getElementById('form-postinumero').value = c ? (c.postinumero || '') : '';
|
||||
document.getElementById('form-kaupunki').value = c ? (c.kaupunki || '') : '';
|
||||
document.getElementById('form-liittymanopeus').value = c ? c.liittymanopeus : '';
|
||||
document.getElementById('form-hinta').value = c ? c.hinta : '';
|
||||
document.getElementById('form-yhteyshenkilo').value = c ? c.yhteyshenkilö : '';
|
||||
document.getElementById('form-puhelin').value = c ? c.puhelin : '';
|
||||
document.getElementById('form-sahkoposti').value = c ? c.sahkoposti : '';
|
||||
document.getElementById('form-laskutusosoite').value = c ? c.laskutusosoite : '';
|
||||
document.getElementById('form-ytunnus').value = c ? (c.ytunnus || '') : '';
|
||||
document.getElementById('form-yhteyshenkilo').value = c ? (c.yhteyshenkilö || '') : '';
|
||||
document.getElementById('form-puhelin').value = c ? (c.puhelin || '') : '';
|
||||
document.getElementById('form-sahkoposti').value = c ? (c.sahkoposti || '') : '';
|
||||
document.getElementById('form-laskutusosoite').value = c ? (c.laskutusosoite || '') : '';
|
||||
document.getElementById('form-laskutuspostinumero').value = c ? (c.laskutuspostinumero || '') : '';
|
||||
document.getElementById('form-laskutuskaupunki').value = c ? (c.laskutuskaupunki || '') : '';
|
||||
document.getElementById('form-laskutussahkoposti').value = c ? c.laskutussahkoposti : '';
|
||||
document.getElementById('form-laskutussahkoposti').value = c ? (c.laskutussahkoposti || '') : '';
|
||||
document.getElementById('form-elaskuosoite').value = c ? (c.elaskuosoite || '') : '';
|
||||
document.getElementById('form-elaskuvalittaja').value = c ? (c.elaskuvalittaja || '') : '';
|
||||
document.getElementById('form-lisatiedot').value = c ? c.lisatiedot : '';
|
||||
document.getElementById('form-lisatiedot').value = c ? (c.lisatiedot || '') : '';
|
||||
|
||||
// Reset billing checkbox
|
||||
document.getElementById('form-billing-same').checked = false;
|
||||
document.getElementById('billing-fields').style.display = 'block';
|
||||
|
||||
// Liittymät
|
||||
const container = document.getElementById('liittymat-container');
|
||||
container.innerHTML = '';
|
||||
const liittymat = c ? (c.liittymat || []) : [{}];
|
||||
liittymat.forEach((l, i) => container.appendChild(createLiittymaRow(l, i)));
|
||||
|
||||
customerModal.style.display = 'flex';
|
||||
document.getElementById('form-yritys').focus();
|
||||
}
|
||||
@@ -471,14 +643,20 @@ async function deleteCustomer(id, name) {
|
||||
customerForm.addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
const id = document.getElementById('form-id').value;
|
||||
|
||||
// If "same as" checked, sync billing from first liittymä
|
||||
if (document.getElementById('form-billing-same').checked) {
|
||||
const firstRow = document.querySelector('.liittyma-row');
|
||||
if (firstRow) {
|
||||
document.getElementById('form-laskutusosoite').value = firstRow.querySelector('.l-asennusosoite').value;
|
||||
document.getElementById('form-laskutuspostinumero').value = firstRow.querySelector('.l-postinumero').value;
|
||||
document.getElementById('form-laskutuskaupunki').value = firstRow.querySelector('.l-kaupunki').value;
|
||||
}
|
||||
}
|
||||
|
||||
const data = {
|
||||
yritys: document.getElementById('form-yritys').value,
|
||||
ytunnus: document.getElementById('form-ytunnus').value,
|
||||
asennusosoite: document.getElementById('form-asennusosoite').value,
|
||||
postinumero: document.getElementById('form-postinumero').value,
|
||||
kaupunki: document.getElementById('form-kaupunki').value,
|
||||
liittymanopeus: document.getElementById('form-liittymanopeus').value,
|
||||
hinta: document.getElementById('form-hinta').value,
|
||||
yhteyshenkilö: document.getElementById('form-yhteyshenkilo').value,
|
||||
puhelin: document.getElementById('form-puhelin').value,
|
||||
sahkoposti: document.getElementById('form-sahkoposti').value,
|
||||
@@ -489,6 +667,7 @@ customerForm.addEventListener('submit', async (e) => {
|
||||
elaskuosoite: document.getElementById('form-elaskuosoite').value,
|
||||
elaskuvalittaja: document.getElementById('form-elaskuvalittaja').value,
|
||||
lisatiedot: document.getElementById('form-lisatiedot').value,
|
||||
liittymat: collectLiittymatFromForm(),
|
||||
};
|
||||
|
||||
if (id) {
|
||||
|
||||
Reference in New Issue
Block a user