- 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.
35 lines
3.4 KiB
JavaScript
35 lines
3.4 KiB
JavaScript
window.TimeBillingReview = (() => {
|
||
const labels={draft:'Kladde',operations:'Drift',development:'Udvikling'};
|
||
const needsReview=entry=>Object.hasOwn(labels,entry.billing_method || entry._billing_method || '');
|
||
const badge=entry=>needsReview(entry)?`<span class="badge text-bg-warning">${labels[entry.billing_method || entry._billing_method]} · Chefkontrol</span>`:'';
|
||
async function approve(entry,hours) {
|
||
const response=await fetch(`/api/v1/timetracking/time/${Number(entry.id)}/settlement-options`,{credentials:'include'});
|
||
if(!response.ok)throw new Error('Afregningsmuligheder kunne ikke hentes');
|
||
const data=await response.json();
|
||
const host=document.createElement('div');host.className='modal fade';host.tabIndex=-1;
|
||
host.innerHTML='<div class="modal-dialog"><form class="modal-content"><div class="modal-header"><h5 class="modal-title">Chefkontrol – vælg afregning</h5><button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Luk"></button></div><div class="modal-body"><p data-label></p><label class="form-label">Endelig afregning</label><select class="form-select" required><option value="">Vælg afregning…</option></select><div class="small mt-3" role="status"></div></div><div class="modal-footer"><button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Annuller</button><button type="submit" class="btn btn-success">Godkend</button></div></form></div>';
|
||
host.querySelector('[data-label]').textContent=labels[entry.billing_method || entry._billing_method]+' · '+(entry.description || entry.beskrivelse || 'Tidsregistrering');
|
||
const select=host.querySelector('select');select.add(new Option('Faktura','invoice'));
|
||
(data.prepaid_cards||[]).forEach(c=>select.add(new Option(`Klippekort #${c.card_number || c.id}`,`card_${c.id}`)));
|
||
(data.agreements||[]).forEach(a=>select.add(new Option(`Fastpris #${a.agreement_number || a.id}`,`fpa_${a.id}`)));
|
||
document.body.append(host);const modal=new bootstrap.Modal(host);
|
||
return new Promise(resolve=>{
|
||
let approved=false;
|
||
host.addEventListener('hidden.bs.modal',()=>{modal.dispose();host.remove();resolve(approved);},{once:true});
|
||
host.querySelector('form').onsubmit=async event=>{
|
||
event.preventDefault();const button=host.querySelector('[type=submit]');button.disabled=true;
|
||
const value=select.value,payload={billing_method:value};
|
||
if(value.startsWith('card_')){payload.billing_method='prepaid';payload.prepaid_card_id=Number(value.slice(5));}
|
||
if(value.startsWith('fpa_')){payload.billing_method='subscription';payload.fixed_price_agreement_id=Number(value.slice(4));}
|
||
if(Number.isFinite(Number(hours))&&Number(hours)>0)payload.fakturerbar_tid_min=Math.round(Number(hours)*60);
|
||
try {
|
||
const result=await fetch(`/api/v1/timetracking/time/${Number(entry.id)}/approve`,{method:'POST',credentials:'include',headers:{'Content-Type':'application/json'},body:JSON.stringify(payload)});
|
||
const body=await result.json().catch(()=>({}));if(!result.ok)throw new Error(body.detail || 'Kunne ikke godkende');
|
||
approved=true;modal.hide();
|
||
}catch(error){host.querySelector('[role=status]').textContent=error.message;button.disabled=false;}
|
||
};modal.show();
|
||
});
|
||
}
|
||
return {needsReview,badge,approve};
|
||
})();
|