feat(profiler): søgbart opslagsværktøj over Nemhandel OIOUBL + Peppol BIS/PINT-profiler (/profiler) + ⌘K-entry
Build & Deploy / deploy (push) Successful in 1m45s

This commit is contained in:
Asger Geel Weirsøe
2026-06-15 16:28:38 +02:00
parent 72ac103d63
commit a4531482d1
3 changed files with 397 additions and 0 deletions
+241
View File
@@ -0,0 +1,241 @@
// E-dokumentprofiler (Nemhandel OIOUBL + Peppol BIS/PINT) til /profiler-opslaget.
// Data vedligeholdes her; siden bygger søge-/filter-UI'et af det.
export interface EProfile {
standard: string;
navn: string;
id: string;
customization: string;
dokumenter: string[];
anvendelse: string;
bemaerkninger: string;
}
export const profiles: EProfile[] = [
{
standard: 'Nemhandel OIOUBL (UBL 2.1)',
navn: 'Procurement-BilSim-1.0 (simpel fakturering)',
id: 'Procurement-BilSim-1.0',
customization: 'OIOUBL-2.x',
dokumenter: ['Invoice', 'CreditNote', 'Reminder'],
anvendelse: 'Ren fakturering: leverandoer sender faktura/kreditnota/rykker til kunde. Ingen ordreproces og intet kraevet svar.',
bemaerkninger: 'Mest udbredte rene faktura-pakke i Nemhandel. Hedder hos Sproom BilSimSupplier/BilSimCustomer.',
},
{
standard: 'Nemhandel OIOUBL (UBL 2.1)',
navn: 'Procurement-BilSimR-1.0 (simpel fakturering m. kraevet svar)',
id: 'Procurement-BilSimR-1.0',
customization: 'OIOUBL-2.x',
dokumenter: ['Invoice', 'CreditNote', 'Reminder', 'ApplicationResponse'],
anvendelse: 'Som BilSim, men kunden skal altid kvittere med et ApplicationResponse (accept/afvisning).',
bemaerkninger: "R = 'Response required'. Hos Sproom BilSimRCustomer.",
},
{
standard: 'Nemhandel OIOUBL (UBL 2.1)',
navn: 'urn:www.nesubl.eu:profiles:profile5:ver2.0 (NES5)',
id: 'urn:www.nesubl.eu:profiles:profile5:ver2.0',
customization: 'OIOUBL-2.x',
dokumenter: ['Invoice', 'CreditNote'],
anvendelse: 'Internationalt NES-fakturaprofil: kunde modtager faktura og kreditnota fra leverandoer.',
bemaerkninger: 'Aeldre/NES-baseret faktureringsprofil. Hos Sproom Nes5Customer.',
},
{
standard: 'Nemhandel OIOUBL (UBL 2.1)',
navn: 'Reference-Utility-1.0 (forsyningsspecifikation)',
id: 'Reference-Utility-1.0',
customization: 'OIOUBL-2.x',
dokumenter: ['UtilityStatement', 'ApplicationResponse'],
anvendelse: 'Forsyningsbranchen (el/vand/varme/gas): leverandoer sender detaljeret forbrugs-/forsyningsspecifikation der ledsager fakturaen; kunden kan svare med ApplicationResponse.',
bemaerkninger: 'Bruges som tillaeg til BilSim-fakturering. Hos Sproom UtsSupplier/UtsCustomer.',
},
{
standard: 'Nemhandel OIOUBL (UBL 2.1)',
navn: 'Procurement-OrdSim-1.0 (simpel ordre)',
id: 'Procurement-OrdSim-1.0',
customization: 'OIOUBL-2.x',
dokumenter: ['Order', 'OrderResponseSimple'],
anvendelse: 'Ren ordreproces: kunde sender ordre, leverandoer svarer med simpelt ordresvar. Ingen fakturering i profilen.',
bemaerkninger: 'Hos Sproom OrdSimCustomer/OrdSimSupplier.',
},
{
standard: 'Nemhandel OIOUBL (UBL 2.1)',
navn: 'Procurement-OrdSimR-1.0 (simpel ordre m. kraevet svar)',
id: 'Procurement-OrdSimR-1.0',
customization: 'OIOUBL-2.x',
dokumenter: ['Order', 'OrderResponseSimple'],
anvendelse: 'Som OrdSim, men leverandoer skal altid sende ordresvar.',
bemaerkninger: 'R = svar paakraevet. Hos Sproom OrdSimRSupplier.',
},
{
standard: 'Nemhandel OIOUBL (UBL 2.1)',
navn: 'Procurement-OrdSim-BilSim-1.0 (ordre + fakturering)',
id: 'Procurement-OrdSim-BilSim-1.0',
customization: 'OIOUBL-2.x',
dokumenter: ['Order', 'OrderResponseSimple', 'Invoice', 'CreditNote', 'Reminder'],
anvendelse: 'Komplet simpel handel: simpel ordre efterfulgt af simpel fakturering. Daekker bestilling til betaling.',
bemaerkninger: 'Samlepakke ordre+faktura. Hos Sproom OrdSimBilSimSupplier/Customer.',
},
{
standard: 'Nemhandel OIOUBL (UBL 2.1)',
navn: 'Procurement-OrdSimR-BilSim-1.0',
id: 'Procurement-OrdSimR-BilSim-1.0',
customization: 'OIOUBL-2.x',
dokumenter: ['Order', 'OrderResponseSimple', 'Invoice', 'CreditNote', 'Reminder'],
anvendelse: 'Som OrdSim-BilSim, men ordresvar er paakraevet.',
bemaerkninger: 'Hos Sproom OrdSimRBilSimSupplier.',
},
{
standard: 'Nemhandel OIOUBL (UBL 2.1)',
navn: 'Procurement-OrdSim-BilSimR-1.0',
id: 'Procurement-OrdSim-BilSimR-1.0',
customization: 'OIOUBL-2.x',
dokumenter: ['Order', 'OrderResponseSimple', 'Invoice', 'CreditNote', 'Reminder', 'ApplicationResponse'],
anvendelse: 'Simpel ordre + fakturering hvor faktura-svar (ApplicationResponse) er paakraevet.',
bemaerkninger: 'Kombination af krav paa faktura-svar.',
},
{
standard: 'Nemhandel OIOUBL (UBL 2.1)',
navn: 'Procurement-OrdSimR-BilSimR-1.0',
id: 'Procurement-OrdSimR-BilSimR-1.0',
customization: 'OIOUBL-2.x',
dokumenter: ['Order', 'OrderResponseSimple', 'Invoice', 'CreditNote', 'Reminder', 'ApplicationResponse'],
anvendelse: 'Simpel ordre og fakturering hvor baade ordresvar og faktura-svar er paakraevet.',
bemaerkninger: 'Hos Sproom OrdSimRBilSimRSupplier.',
},
{
standard: 'Nemhandel OIOUBL (UBL 2.1)',
navn: 'Procurement-OrdAdv-BilSim-1.0 (avanceret ordre + fakturering)',
id: 'Procurement-OrdAdv-BilSim-1.0',
customization: 'OIOUBL-2.x',
dokumenter: ['Order', 'OrderResponse (avanceret/fuld)', 'OrderChange', 'OrderCancellation', 'Invoice', 'CreditNote', 'Reminder', 'ApplicationResponse'],
anvendelse: 'Avanceret ordreproces (med ordreaendring/-annullering og fuldt ordresvar) efterfulgt af simpel fakturering.',
bemaerkninger: 'Avanceret variant. Hos Sproom OrdAdvBilSimSupplier/Customer.',
},
{
standard: 'Nemhandel OIOUBL (UBL 2.1)',
navn: 'urn:www.nesubl.eu:profiles:profile3:ver2.0 (NES3)',
id: 'urn:www.nesubl.eu:profiles:profile3:ver2.0',
customization: 'OIOUBL-2.x',
dokumenter: ['Order'],
anvendelse: 'Leverandoer modtager ordrer fra kunder (uden ordresvar/fakturering i profilen).',
bemaerkninger: 'Aeldre NES-ordreprofil. Hos Sproom Nes3Supplier (anbefaler i stedet OrdSim-varianter).',
},
{
standard: 'Peppol BIS 3.0',
navn: 'Peppol BIS Billing 3.0',
id: 'urn:fdc:peppol.eu:2017:poacc:billing:01:1.0',
customization: 'urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0',
dokumenter: ['Invoice', 'CreditNote'],
anvendelse: 'Den centrale e-faktura/kreditnota-pakke i Peppol. CIUS af EN 16931. Bruges til EU-graensehandel og DK/Peppol-fakturering.',
bemaerkninger: 'Langt den mest brugte BIS3-profil. Invoice Response er en separat profil.',
},
{
standard: 'Peppol BIS 3.0',
navn: 'Peppol BIS Self-Billing 3.0',
id: 'urn:fdc:peppol.eu:2017:poacc:selfbilling:01:1.0',
customization: 'urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0#conformant#urn:fdc:peppol.eu:2017:poacc:selfbilling:international:3.0',
dokumenter: ['Invoice (self-billed)', 'CreditNote'],
anvendelse: 'Selvfakturering: koeber udsteder fakturaen paa vegne af saelger.',
bemaerkninger: 'Genbruger Billing-syntaks med self-billing customization.',
},
{
standard: 'Peppol BIS 3.0',
navn: 'Peppol BIS Invoice Response 3.0',
id: 'urn:fdc:peppol.eu:poacc:bis:invoice_response:3',
customization: 'urn:fdc:peppol.eu:poacc:trns:invoice_response:3',
dokumenter: ['ApplicationResponse (Invoice Response)'],
anvendelse: 'Koeber sender struktureret status/svar paa en modtaget faktura (accepteret, afvist, under behandling, betalt).',
bemaerkninger: 'Bruges sammen med Billing til at lukke faktura-livscyklussen.',
},
{
standard: 'Peppol BIS 3.0',
navn: 'Peppol BIS Message Level Response 3.0 (MLR)',
id: 'urn:fdc:peppol.eu:poacc:bis:mlr:3',
customization: 'urn:fdc:peppol.eu:poacc:trns:mlr:3',
dokumenter: ['ApplicationResponse (Message Level Response)'],
anvendelse: 'Teknisk kvittering paa beskedniveau: bekraefter modtagelse/validering af et hvilket som helst dokument.',
bemaerkninger: 'Transport-/syntaks-niveau svar, ikke forretningsmaessigt indhold.',
},
{
standard: 'Peppol BIS 3.0',
navn: 'Peppol BIS Ordering 3.0',
id: 'urn:fdc:peppol.eu:poacc:bis:ordering:3',
customization: 'urn:fdc:peppol.eu:poacc:trns:order:3 / urn:fdc:peppol.eu:poacc:trns:order_response:3',
dokumenter: ['Order', 'Order Response'],
anvendelse: 'Fuld ordreproces: koeber sender ordre, saelger svarer med ordrebekraeftelse/-svar.',
bemaerkninger: 'Order = Trdm01, Order Response = Trdm76.',
},
{
standard: 'Peppol BIS 3.0',
navn: 'Peppol BIS Order Only 3.0',
id: 'urn:fdc:peppol.eu:poacc:bis:order_only:3',
customization: 'urn:fdc:peppol.eu:poacc:trns:order:3',
dokumenter: ['Order'],
anvendelse: 'Kun afsendelse af ordre uden forventet struktureret ordresvar.',
bemaerkninger: 'Enkel ordre-only proces.',
},
{
standard: 'Peppol BIS 3.0',
navn: 'Peppol BIS Order Agreement 3.0 (Advanced Ordering)',
id: 'urn:fdc:peppol.eu:poacc:bis:order_agreement:3',
customization: 'urn:fdc:peppol.eu:poacc:trns:order_agreement:3',
dokumenter: ['Order', 'Order Response (Advanced)'],
anvendelse: 'Avanceret ordre der etablerer en bindende ordreaftale mellem parterne.',
bemaerkninger: 'Bruges hvor ordresvar danner juridisk aftale.',
},
{
standard: 'Peppol BIS 3.0',
navn: 'Peppol BIS Catalogue 3.0',
id: 'urn:fdc:peppol.eu:poacc:bis:catalogue:3',
customization: 'urn:fdc:peppol.eu:poacc:trns:catalogue:3',
dokumenter: ['Catalogue', 'ApplicationResponse (Catalogue Response)'],
anvendelse: 'Saelger sender produktkatalog (priser/varer) til koeber, der kan svare med accept/afvisning.',
bemaerkninger: 'Grundlag for efterfoelgende ordrer.',
},
{
standard: 'Peppol BIS 3.0',
navn: 'Peppol BIS Catalogue without Response 3.0',
id: 'urn:fdc:peppol.eu:poacc:bis:catalogue_wo_response:3',
customization: 'urn:fdc:peppol.eu:poacc:trns:catalogue:3',
dokumenter: ['Catalogue'],
anvendelse: 'Katalogudsendelse uden forventet katalogsvar.',
bemaerkninger: 'Variant uden Catalogue Response.',
},
{
standard: 'Peppol BIS 3.0',
navn: 'Peppol BIS Punch Out 3.0',
id: 'urn:fdc:peppol.eu:poacc:bis:punch_out:3',
customization: 'urn:fdc:peppol.eu:poacc:trns:punch_out:3',
dokumenter: ['Catalogue (Punch Out)'],
anvendelse: "Punch-out katalog: koeber 'hopper ud' til saelgers webshop og henter en kurv tilbage.",
bemaerkninger: 'Bruges i e-handels-/webshop-integrationer.',
},
{
standard: 'Peppol BIS 3.0',
navn: 'Peppol BIS Despatch Advice 3.0',
id: 'urn:fdc:peppol.eu:poacc:bis:despatch_advice:3',
customization: 'urn:fdc:peppol.eu:poacc:trns:despatch_advice:3',
dokumenter: ['Despatch Advice (folgeseddel)'],
anvendelse: 'Saelger sender forsendelses-/leveringsmeddelelse (hvad er afsendt, hvornaar, hvordan) inden faktura.',
bemaerkninger: 'Logistik-dokument mellem ordre og faktura.',
},
{
standard: 'Peppol BIS 4.0 / PINT',
navn: 'EU PINT Billing v1.0.0 (forloeber for BIS Billing 4.0)',
id: 'urn:peppol:pint:billing-1@eu-1 (PINT EU Billing)',
customization: 'urn:peppol:pint:billing-1@eu-1',
dokumenter: ['Invoice', 'CreditNote'],
anvendelse: 'Peppols internationale fakturamodel (PINT) tilpasset EU. Implementerer BIS Billing 3.0 som PINT-kompatibel uden aendringer; baner vej for graenseoverskridende fakturering mod lande med egne PINT-specs (AU/JP/MY/NZ/SG m.fl.).',
bemaerkninger: 'Udgivet okt. 2025. NB: identifikator-format afviger fra BIS3 og kan aendre sig i v1.1.x - verificer mod docs.peppol.eu/poac/pint inden brug.',
},
{
standard: 'Peppol BIS 4.0 / PINT',
navn: 'Peppol BIS Billing 4.0 (kommende)',
id: '(endnu ikke endeligt fastlagt)',
customization: '(endnu ikke endeligt fastlagt)',
dokumenter: ['Invoice', 'CreditNote (forventet)'],
anvendelse: 'Naeste generation der lægger BIS og PINT sammen til ét globalt faktura-format. Forventes tidligt 2026.',
bemaerkninger: "STATUS: under udarbejdelse pr. juni 2026 - ID'er og indhold ikke endeligt publiceret. Brug BIS Billing 3.0 / EU PINT i mellemtiden.",
},
];
export const standarder = [...new Set(profiles.map((p) => p.standard))];
+155
View File
@@ -0,0 +1,155 @@
---
import Base from '../layouts/Base.astro';
import { profiles, standarder } from '../data/profiles';
const searchString = (p: (typeof profiles)[number]) =>
[p.standard, p.navn, p.id, p.customization, p.dokumenter.join(' '), p.anvendelse, p.bemaerkninger]
.join(' ')
.toLowerCase();
---
<Base
title="Profil-opslag (Nemhandel & Peppol)"
description="Søgbart opslag over Nemhandel OIOUBL- og Peppol BIS/PINT-profiler: profil-id'er, customization-id'er, indeholdte dokumenter og typisk anvendelse."
>
<h1 class="text-4xl font-bold tracking-tight">Profil-opslag</h1>
<p class="mt-4 max-w-2xl text-muted">
Browse og søg i e-dokumentprofilerne for <strong class="text-text">Nemhandel OIOUBL</strong> og
<strong class="text-text">Peppol BIS/PINT</strong> — profil-/proces-id, customization-id, hvilke
dokumenter profilen indeholder og typisk anvendelse. Klik på et id for at kopiere det.
</p>
<div class="mt-8 flex flex-col gap-4 sm:flex-row sm:items-center">
<div class="relative flex-1">
<span class="pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-muted">⌕</span>
<input
id="p-search"
type="search"
placeholder="Søg profil, id, dokument…"
autocomplete="off"
aria-label="Søg profiler"
class="w-full rounded-xl border border-border bg-surface/60 py-2.5 pl-9 pr-4 outline-none focus:border-accent"
/>
</div>
<span id="p-count" class="text-sm text-muted">{profiles.length} profiler</span>
</div>
<div class="mt-3 flex flex-wrap gap-2">
<button type="button" class="p-chip" data-std="" aria-pressed="true">Alle</button>
{standarder.map((s) => <button type="button" class="p-chip" data-std={s} aria-pressed="false">{s}</button>)}
</div>
<div class="mt-6 space-y-3">
{
profiles.map((p) => (
<article
data-profile
data-standard={p.standard}
data-search={searchString(p)}
class="rounded-2xl border border-border bg-surface/60 p-5 transition-colors hover:border-accent/50"
>
<div class="flex flex-wrap items-center gap-2">
<span class="rounded-full border border-accent/40 bg-accent/10 px-2.5 py-0.5 text-xs font-medium text-accent">
{p.standard}
</span>
</div>
<h2 class="mt-2 text-lg font-semibold tracking-tight">{p.navn}</h2>
<dl class="mt-3 grid gap-1 text-xs sm:grid-cols-[10rem_1fr]">
<dt class="text-muted">Profil/proces-id</dt>
<dd>
<button type="button" data-copy={p.id} title="Klik for at kopiere" class="copy break-all text-left font-mono">
{p.id}
</button>
</dd>
<dt class="text-muted">Customization-id</dt>
<dd>
<button type="button" data-copy={p.customization} title="Klik for at kopiere" class="copy break-all text-left font-mono">
{p.customization}
</button>
</dd>
</dl>
<div class="mt-3 flex flex-wrap gap-1.5">
{p.dokumenter.map((d) => (
<span class="rounded-full border border-border bg-surface-2 px-2 py-0.5 text-xs">{d}</span>
))}
</div>
<p class="mt-3 text-sm text-muted">{p.anvendelse}</p>
<p class="mt-2 text-xs text-muted/80">
<span class="font-semibold text-muted">Bemærk:</span> {p.bemaerkninger}
</p>
</article>
))
}
</div>
<p id="p-none" class="mt-6 text-sm text-muted" hidden>Ingen profiler matcher.</p>
<p class="mt-10 text-xs text-muted/80">
Kilder: Nemhandel/OIOUBL-profiler (jf. Sproom) og Peppol BIS/PINT (docs.peppol.eu). Øjebliksbillede —
verificér altid id'er mod den officielle dokumentation før brug.
</p>
<style is:inline>
.p-chip {
border: 1px solid var(--color-border);
color: var(--color-muted);
border-radius: 0.6rem;
padding: 0.35rem 0.8rem;
font-size: 0.85rem;
cursor: pointer;
background: transparent;
transition: color 0.15s, background-color 0.15s, border-color 0.15s;
}
.p-chip:hover { color: var(--color-text); }
.p-chip[aria-pressed="true"] {
background: var(--color-surface-2);
color: var(--color-text);
border-color: color-mix(in srgb, var(--color-accent) 40%, var(--color-border));
}
.copy { transition: color 0.15s; }
.copy:hover { color: var(--color-accent); text-decoration: underline; text-underline-offset: 3px; }
</style>
<script is:inline>
(function () {
var cards = [].slice.call(document.querySelectorAll('[data-profile]'));
var search = document.getElementById('p-search');
var chips = [].slice.call(document.querySelectorAll('.p-chip'));
var count = document.getElementById('p-count');
var none = document.getElementById('p-none');
var q = '', std = '';
function apply() {
var shown = 0;
cards.forEach(function (c) {
var okS = !std || c.getAttribute('data-standard') === std;
var okQ = !q || c.getAttribute('data-search').indexOf(q) > -1;
var hit = okS && okQ;
c.hidden = !hit;
if (hit) shown++;
});
count.textContent = shown + (shown === 1 ? ' profil' : ' profiler');
none.hidden = shown > 0;
}
search.addEventListener('input', function () { q = search.value.trim().toLowerCase(); apply(); });
chips.forEach(function (b) {
b.addEventListener('click', function () {
std = b.getAttribute('data-std') || '';
chips.forEach(function (x) { x.setAttribute('aria-pressed', x === b ? 'true' : 'false'); });
apply();
});
});
document.addEventListener('click', function (e) {
var btn = e.target.closest && e.target.closest('[data-copy]');
if (!btn || !navigator.clipboard) return;
navigator.clipboard.writeText(btn.getAttribute('data-copy')).then(function () {
var orig = btn.textContent;
btn.textContent = 'kopieret ✓';
setTimeout(function () { btn.textContent = orig; }, 1200);
}).catch(function () {});
});
apply();
})();
</script>
</Base>
+1
View File
@@ -10,6 +10,7 @@ export async function GET() {
{ type: 'page', title: 'Blog', url: '/blog' },
{ type: 'page', title: 'Tags', url: '/tags' },
{ type: 'page', title: 'NemHandel-opslag', url: '/nemhandel' },
{ type: 'page', title: 'Profil-opslag (Nemhandel & Peppol)', url: '/profiler' },
{ type: 'page', title: 'Spørg mit CV (AI-chat)', url: '/chat' },
{ type: 'page', title: 'Status (homelab)', url: '/status' },
{ type: 'page', title: 'Now', url: '/now' },