- Introduced tests for mail templates including ranking, rendering, and attachment handling. - Added tests for time billing selection and approval processes, ensuring proper validation and state management. - Implemented tests for case types in JavaScript, focusing on loading and editing functionality. - Created tests for contact mail AI integration, ensuring proper parsing and error handling. - Developed tests for dark theme CSS contrast and loading order. - Added tests for invoice fee service, covering various scenarios including exemptions and invalid inputs. - Implemented vendor resource tests, ensuring proper file handling and query validation.
52 lines
3.3 KiB
JavaScript
52 lines
3.3 KiB
JavaScript
(() => {
|
|
'use strict';
|
|
const get = id => document.getElementById(id);
|
|
const describe = product => `${product.sku_internal || product.economic_product_number || product.id} · ${product.name} · ${Number(product.sales_price).toLocaleString('da-DK', {minimumFractionDigits:2})} ${product.sales_currency || 'DKK'}`;
|
|
window.loadInvoiceFeeProduct = async () => {
|
|
const current = get('invoiceFeeProductCurrent');
|
|
try {
|
|
const response = await fetch('/api/v1/settings/invoice_fee_product_id');
|
|
if (!response.ok) throw new Error('Gebyrindstillingen mangler. Kør migrationen først.');
|
|
const setting = await response.json();
|
|
if (!setting.value) { current.textContent = 'Ingen standardvare valgt. Tidligere gebyrregler gælder.'; return; }
|
|
const productResponse = await fetch(`/api/v1/products/${encodeURIComponent(setting.value)}`);
|
|
if (!productResponse.ok) throw new Error('Den valgte gebyrvare findes ikke længere. Vælg en ny vare.');
|
|
current.textContent = describe(await productResponse.json());
|
|
} catch (error) { current.textContent = error.message; }
|
|
};
|
|
async function save(productId) {
|
|
const status = get('invoiceFeeProductStatus');
|
|
status.textContent = 'Gemmer…';
|
|
try {
|
|
const response = await fetch('/api/v1/settings/invoice_fee_product_id', {method:'PUT',headers:{'Content-Type':'application/json'},body:JSON.stringify({value:String(productId)})});
|
|
if (!response.ok) throw new Error((await response.json()).detail || 'Kunne ikke gemme');
|
|
get('invoiceFeeProductResults').replaceChildren();
|
|
await window.loadInvoiceFeeProduct(); status.textContent = 'Gemt. Gælder kun nye ordrer.';
|
|
} catch (error) { status.textContent = error.message; }
|
|
}
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
let timer, sequence = 0;
|
|
get('invoiceFeeProductDisable').addEventListener('click', () => save(''));
|
|
get('invoiceFeeProductSearch').addEventListener('input', () => {
|
|
clearTimeout(timer);
|
|
const seq = ++sequence, q = get('invoiceFeeProductSearch').value.trim();
|
|
const results = get('invoiceFeeProductResults'); results.replaceChildren();
|
|
if (q.length < 2) return;
|
|
timer = setTimeout(async () => {
|
|
try {
|
|
const response = await fetch(`/api/v1/products?q=${encodeURIComponent(q)}&status=active`);
|
|
if (!response.ok) throw new Error('Varesøgning fejlede');
|
|
const products = await response.json(); if (seq !== sequence) return;
|
|
results.replaceChildren();
|
|
products.slice(0,20).forEach(product => {
|
|
const button = document.createElement('button'); button.type = 'button';
|
|
button.className = 'list-group-item list-group-item-action'; button.textContent = describe(product);
|
|
button.addEventListener('click', () => save(product.id)); results.append(button);
|
|
});
|
|
if (!products.length) results.textContent = 'Ingen varer fundet.';
|
|
} catch (error) { if (seq === sequence) results.textContent = error.message; }
|
|
}, 250);
|
|
});
|
|
});
|
|
})();
|