https://invoicecraftly.com/_assets/js/features/localization/norway-editor-foundation.a5ea0b1e812d.js
1import { getMarketInvoiceNumberingPolicy } from '../invoice-numbering/market-numbering-policy.f4f4cec91dfe.js'; 2import { makulerNorwegianInvoice } from '../invoice-numbering/regulated-document-void-service.1c519d02736b.js'; 3import { getStorageAdapter } from '../invoice-storage/storage-adapter.b2ee2abd8251.js'; 4import { getEditorInvoice, onEditorRendered } from '../../app/main.599ec0e5abb9.js'; 5import { appendPolicyNote, applyReadonlyNumberField } from '../editor-controls/editor-field-primitives.87e6260d0551.js'; 6 7const MARKET_ID = 'norway'; 8const NUMBER_NOTE_ID = 'norway-numbering-policy-note'; 9const PREFIX_NOTE_ID = 'norway-number-prefix-policy-note'; 10// Slice 6 of the editor-kernel post-#126 hardening skill: `storage` is kept only for 11// makulerNorwegianInvoice() below, a domain write triggered by an explicit user action (voiding an 12// invoice) â a legitimate direct storage use, not the private read-hydration this slice removes. 13const storage = getStorageAdapter(); 14 15function protectSystemAssignedNumbering(root = document) { 16 const policy = getMarketInvoiceNumberingPolicy(MARKET_ID); 17 if (policy.mode !== 'system-controlled') return; 18 19 const numberControls = []; 20 if (root.nodeType === Node.ELEMENT_NODE && root.matches?.('[data-path="invoiceNumber"]')) numberControls.push(root); 21 numberControls.push(...(root.querySelectorAll?.('[data-path="invoiceNumber"]') || [])); 22 numberControls.forEach((control) => applyReadonlyNumberField(control, { 23 noteId: NUMBER_NOTE_ID, 24 noteText: 'Fakturanummeret tildeles maskinelt fra virksomhetens lokale nummerserie og kan ikke overstyres i editoren.' 25 })); 26 27 const prefixControls = []; 28 if (root.nodeType === Node.ELEMENT_NODE && root.matches?.('[data-path="invoiceNumberPrefix"]')) prefixControls.push(root); 29 prefixControls.push(...(root.querySelectorAll?.('[data-path="invoiceNumberPrefix"]') || [])); 30 prefixControls.forEach((control) => applyReadonlyNumberField(control, { 31 noteId: PREFIX_NOTE_ID, 32 noteText: 'Nummerformatet er lÃ¥st for den norske nummerserien.' 33 })); 34} 35 36// Slice 5 of the editor-kernel post-#126 hardening skill: the kernel now always renders 37// seller.taxId as the always-required, correctly labeled/placeholder'd/formatted organization-number 38// field directly (country-profiles.js's editorFields.requiredSellerTaxId, consumed by main.js's 39// optionalTaxIdHTML() override) â this function's former field creation, relabeling, button hiding, 40// and repositioning are all gone. All that remains is attaching the policy note the kernel doesn't 41// know how to phrase. 42function ensureSellerOrganizationNumberNote() { 43 const control = document.querySelector('[data-validation-section="seller"] [data-path="seller.taxId"]'); 44 if (control) { 45 appendPolicyNote( 46 control, 47 'norway-organization-number-note', 48 'InvoiceCraftly kontrollerer formatet, men verifiserer ikke registreringen mot Brønnøysundregistrene.' 49 ); 50 } 51} 52 53// Slice 5 of the editor-kernel post-#126 hardening skill (company-identity step): this used to build/ 54// insert the legalForm/businessRegisterStatus/liquidationStatus selects, toggle the latter two's 55// visibility, and resolve the shared note's text â all three are now kernel-rendered directly from 56// country-profiles.js's editorFields.seller (visibleWhen/notes). The former updateSellerAddressLabel() 57// (address-label step) â relabeling the kernel's own seller address field based on legalForm â is now 58// the same profile's editorFields.sellerAddressLabel, resolved by main.js's 59// resolveSellerAddressLabelKey() at render time and kept live by applyConditionalFieldVisibility(). 60// Nothing legalForm-dependent remains in this file. 61 62// Tax-reporting step (editor-kernel post-#126 hardening skill): this used to build/insert the 63// exchange-rate/rate-date/rate-source group, compute its own foreignCurrencyVatSelected() visibility 64// condition, and rewrite its dynamic label by hand â all now kernel-rendered directly from 65// country-profiles.js's editorFields.taxReporting (visibleWhen's AND-composed conditions plus 66// labelParams). Nothing currency/vatStatus-dependent remains in this file. 67 68function updateMakulerActions(invoice = getEditorInvoice()) { 69 const currentAction = document.getElementById('more-remove-local-copy'); 70 const status = invoice?.metadata?.regulatedNumbering?.status || null; 71 if (currentAction) { 72 const label = currentAction.querySelector('span');
73 currentAction.dataset.norwayMakulerAction = 'true'; 74 if (!invoice || invoice.localization?.marketId !== MARKET_ID) { 75 currentAction.disabled = true; 76 currentAction.title = 'Norsk dokumentstatus lastes.'; 77 if (label) label.textContent = 'Makuler faktura'; 78 } else if (status === 'issued' || invoice.metadata?.lastExportedAt) { 79 currentAction.disabled = true; 80 currentAction.title = 'En utsendt faktura skal korrigeres med kreditnota, ikke makuleres.'; 81 if (label) label.textContent = 'Bruk kreditnota'; 82 } else if (status === 'void' || invoice.lifecycle?.document === 'voided') { 83 currentAction.disabled = true; 84 currentAction.title = 'Denne fakturaen er allerede makulert og nummeret beholdes i nummerserien.'; 85 if (label) label.textContent = 'Makulert'; 86 } else { 87 currentAction.disabled = false; 88 currentAction.removeAttribute('aria-disabled'); 89 currentAction.title = 'Makuler en ikke-utsendt faktura og behold nummeret i nummerserien'; 90 if (label) label.textContent = 'Makuler faktura'; 91 } 92 } 93 94 const explanation = 'à pne fakturaen først for Ã¥ bruke Makuler. Nummeret og historikken skal beholdes.'; 95 document.querySelectorAll('[data-recent-action="delete"]').forEach((button) => { 96 button.disabled = true; 97 button.setAttribute('aria-disabled', 'true'); 98 button.title = explanation; 99 button.dataset.norwayNumberingProtected = 'true'; 100 const label = button.querySelector('span'); 101 if (label && label.textContent !== 'à pne for Ã¥ makulere') label.textContent = 'à pne for Ã¥ makulere'; 102 }); 103} 104 105function localizeDeliveryPreview() { 106 document.querySelectorAll('.pv-delivery strong').forEach((label) => { 107 if (/^Delivery:/i.test(label.textContent || '')) label.textContent = 'Levering:'; 108 if (/^Place:/i.test(label.textContent || '')) label.textContent = 'Leveringssted:'; 109 }); 110} 111 112function applyNorwayFoundation(root = document) { 113 protectSystemAssignedNumbering(root); 114 ensureSellerOrganizationNumberNote(); 115 updateMakulerActions(); 116 localizeDeliveryPreview(); 117} 118 119function closeFoundationModal(returnTarget) { 120 const modalRoot = document.getElementById('modal-root'); 121 if (modalRoot) modalRoot.innerHTML = ''; 122 returnTarget?.focus?.({ preventScroll: true }); 123} 124 125function openMakulerDialog(returnTarget) { 126 const modalRoot = document.getElementById('modal-root'); 127 const invoice = getEditorInvoice(); 128 const documentId = invoice?.id; 129 if (!modalRoot || !invoice || invoice.localization?.marketId !== MARKET_ID) return; 130 131 if (invoice.metadata?.lastExportedAt || invoice.metadata?.regulatedNumbering?.status === 'issued') { 132 modalRoot.innerHTML = ` 133 <div class="modal-backdrop" data-no-makuler-backdrop> 134 <div class="modal" role="dialog" aria-modal="true" aria-labelledby="no-makuler-title"> 135 <h3 id="no-makuler-title">Bruk kreditnota</h3> 136 <p>Denne fakturaen er allerede registrert som utsendt. En utsendt faktura skal ikke makuleres i InvoiceCraftly.</p> 137 <button class="tb-btn primary" type="button" data-no-makuler-close>Lukk</button> 138 </div> 139 </div>`; 140 modalRoot.querySelector('[data-no-makuler-close]')?.addEventListener('click', () => closeFoundationModal(returnTarget)); 141 return; 142 } 143 144 modalRoot.innerHTML = ` 145 <div class="modal-backdrop" data-no-makuler-backdrop> 146 <div class="modal" role="dialog" aria-modal="true" aria-labelledby="no-makuler-title" aria-describedby="no-makuler-description"> 147 <button type="button" class="modal-close" data-no-makuler-close aria-label="Lukk">Ã</button> 148 <span class="modal-kicker">Norsk nummerserie</span> 149 <h3 id="no-makuler-title">Makuler ${invoice.invoiceNumber || 'fakturaen'}?</h3> 150 <p id="no-makuler-description">Nummeret beholdes permanent i nummerserien og dokumentet markeres som makulert. Bruk dette bare for en faktura som ikke er sendt ut.</p> 151 <div class="field"> 152 <label for="no-makuler-reason">à rsak</label> 153 <textarea id="no-makuler-reason" rows="3" placeholder="For eksempel feil kunde eller opprettet ved en feil"></textarea> 154 </div> 155 <p class="market-policy-note" data-no-makuler-error role="alert"></p> 156 <div class="business-defaults-actions"> 157 <button type="button" class="tb-btn secondary-action" data-no-makuler-close>Avbryt</button> 158 <button type="button" class="tb-btn primary" data-no-makuler-confirm>Makuler og behold nummeret</button> 159 </div> 160 </div> 161 </div>`; 162 163 modalRoot.querySelectorAll('[data-no-makuler-close]').forEach((button) => button.addEventListener('click', () => closeFoundationModal(returnTarget))); 164 modalRoot.querySelector('[data-no-makuler-backdrop]')?.addEventListener('click', (event) => { 165 if (event.target === event.currentTarget) closeFoundationModal(returnTarget); 166 }); 167 const reason = modalRoot.querySelector('#no-makuler-reason'); 168 const confirm = modalRoot.querySelector('[data-no-makuler-confirm]'); 169 const error = modalRoot.querySelector('[data-no-makuler-error]'); 170 confirm?.addEventListener('click', async () => { 171 const value = reason?.value?.trim() || ''; 172 if (!value) { 173 if (error) error.textContent = 'Skriv en kort Ã¥rsak før fakturaen makuleres.'; 174 reason?.focus(); 175 return; 176 } 177 confirm.disabled = true; 178 try {
179 await makulerNorwegianInvoice(storage, { documentId, reason: value }); 180 window.location.assign('/no/editor/?type=invoice'); 181 } catch (voidError) { 182 confirm.disabled = false; 183 if (error) error.textContent = voidError?.message || 'Kunne ikke makulere fakturaen. Prøv igjen.'; 184 } 185 }); 186} 187 188// Slice L: dropped this function's own lang/title/meta-description/canonical/dataset.marketId 189// assignments â app.js always calls installNorwayEditorFoundation() before 190// installNorwayEditorPresentation() in the same synchronous install() closure, so presentation's 191// own copies of these same five assignments were already overwriting these ones immediately, 192// every time, with no code running in between to observe the intermediate values. Confirmed dead, 193// not just redundant; removing them changes no behavior. dataset.feature84dNumbering has no 194// equivalent in presentation.js and stays. 195export function installNorwayEditorFoundation() { 196 document.body.dataset.feature84dNumbering = 'foundation'; 197 198 applyNorwayFoundation(document); 199 200 document.addEventListener('beforeinput', (event) => { 201 if (event.target?.matches?.('[data-path="invoiceNumber"], [data-path="invoiceNumberPrefix"]')) { 202 event.preventDefault(); 203 } 204 }, true); 205 206 document.addEventListener('click', (event) => { 207 const action = event.target.closest?.('#more-remove-local-copy[data-norway-makuler-action="true"]'); 208 if (!action || action.disabled) return; 209 event.preventDefault(); 210 event.stopImmediatePropagation(); 211 openMakulerDialog(action); 212 }, true); 213 214 // Tax-reporting step: dropped the input/change listeners that used to re-run applyNorwayFoundation() 215 // on seller.registration.vatStatus/currency changes â nothing left in applyNorwayFoundation() depends 216 // on either path any more (that was solely ensureTaxReportingFields(), now fully declarative). The 217 // kernel's own applyConditionalFieldVisibility() (main.js) keeps the tax-reporting group's visibility 218 // and dynamic label in sync directly. 219 220 // Slice 6 of the editor-kernel post-#126 hardening skill: replaces the body-wide MutationObserver 221 // that used to re-run applyNorwayFoundation() on every element addition anywhere in <body> (a Rule 4 222 // violation) â main.js's onEditorRendered() fires this exact callback once, synchronously, right 223 // after every renderEditor() pass, the one event that actually matters here. 224 onEditorRendered(() => applyNorwayFoundation(document)); 225}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.