Feat/voucher docs (#664)

* feat: implement inbox document picker and linking functionality

* feat: implement self-billing invoice functionality

- Added support for registering self-billed invoices received from customers.
- Updated the invoice schema to include fields for self-billing metadata such as `is_self_billed`, `external_invoice_number`, `self_billing_agreement_ref`, and `received_date`.
- Created API route for handling self-billed invoice submissions, including validation and error handling.
- Implemented database migrations to add necessary columns and constraints for self-billing invoices.
- Developed tests to ensure correct behavior of self-billing invoice creation and validation rules.
- Updated Swedish localization files to include new terms related to self-billing.

* feat: enforce SIE import requirement for non-Fortnox providers in migration process

* feat: streamline invoice processing and enhance error logging across APIs
This commit is contained in:
Mattsson
2026-06-04 13:14:57 +02:00
committed by GitHub
parent c1be9f15dd
commit 3e42fc6f32
45 changed files with 2675 additions and 529 deletions
@@ -42,7 +42,7 @@ type ArcimProvider = 'fortnox' | 'visma' | 'briox' | 'bokio' | 'bjornlunden'
const ARCIM_PROVIDERS: { id: ArcimProvider; name: string; authType: 'oauth' | 'token' }[] = [
{ id: 'fortnox', name: 'Fortnox', authType: 'oauth' },
{ id: 'visma', name: 'Visma eEkonomi', authType: 'oauth' },
{ id: 'visma', name: 'Visma', authType: 'oauth' },
{ id: 'bokio', name: 'Bokio', authType: 'token' },
{ id: 'bjornlunden', name: 'Björn Lundén', authType: 'token' },
{ id: 'briox', name: 'Briox', authType: 'token' },
@@ -349,16 +349,24 @@ function ProviderStep({
{ARCIM_PROVIDERS.map((provider) => {
const comingSoon = COMING_SOON_PROVIDERS.has(provider.id)
const alreadyConnected = activeConsents.some(c => c.provider === provider.id)
// Non-Fortnox providers only expose entity data (customers,
// suppliers, invoices) via API — the ledger must arrive via SIE
// first. Gate the connection entry until a completed SIE import
// exists so users don't authenticate into a flow that can't
// import anything yet. The /migrate route enforces this
// server-side regardless; this is just the matching UX.
const needsSieFirst = !hasSieImport && provider.id !== 'fortnox'
const isDisabled = comingSoon || alreadyConnected || needsSieFirst
return (
<button
key={provider.id}
disabled={comingSoon || alreadyConnected}
disabled={isDisabled}
className={`relative flex items-center gap-4 rounded-lg border p-4 text-left transition-all ${
comingSoon || alreadyConnected
isDisabled
? 'cursor-not-allowed border-border/50 opacity-60'
: 'border-border hover:border-primary/50 hover:bg-accent/50 active:scale-[0.98]'
}`}
onClick={() => !comingSoon && !alreadyConnected && onSelect(provider.id)}
onClick={() => !isDisabled && onSelect(provider.id)}
>
<img
src={PROVIDER_LOGOS[provider.id]}
@@ -378,9 +386,20 @@ function ProviderStep({
Ansluten
</span>
)}
{needsSieFirst && !comingSoon && !alreadyConnected && (
<span className="inline-flex items-center rounded-full bg-amber-500/10 px-2 py-0.5 text-[10px] font-medium text-amber-600 dark:text-amber-500">
SIE krävs först
</span>
)}
</div>
<p className="text-xs text-muted-foreground">
{alreadyConnected ? 'Använd "Synka igen" ovan' : provider.authType === 'oauth' ? 'Anslut via inloggning' : 'Anslut med API-nyckel'}
{alreadyConnected
? 'Använd "Synka igen" ovan'
: needsSieFirst
? 'Importera SIE-fil först'
: provider.authType === 'oauth'
? 'Anslut via inloggning'
: 'Anslut med API-nyckel'}
</p>
</div>
</button>