* feat(transactions): select and bulk-ignore any unbooked skattekonto row (#2127) Skattekonto rows in the inbox only got a checkbox when they carried a deterministic booking suggestion, because the only bulk action was Bokför valda. A migration backlog on the skattekonto (rows from before the first fiscal year, history already booked via SIE) therefore had to be ignored one row at a time, while bank rows next to them could be bulk-ignored. - isSkvSelectable: every unbooked, non-ignored skattekonto row is selectable (checkbox, shift-range, Markera alla). - Bulk Bokför keeps its eligibility rule: button count, confirmation summary and submit all read one skvBookableSelectedRows list, so a mixed selection books only the deterministic subset. - Bulk Ignorera now spans bank + skattekonto selections: one confirmation (body names where each kind is restored), one progress counter, one toast, bank rows via POST /transactions/:id/ignore and skattekonto rows via the per-row PATCH .../ignore, 5-wide. - The bank-only confirm/toast strings move from hardcoded Swedish to transactions.batch_ignore_* keys (sv + en). Bullet 2 of the issue (unbooked skattekonto rows "not in att göra after migration") is scoped out; see DECISIONS.md. Closes #2127 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WFhSQWzu5SyXB6kG5ActZc * feat(transactions): warn when bulk Ignorera covers rows that look like affärshändelser Compliance review on #2140: any unbooked skattekonto row can now be bulk-ignored, including rows with a deterministic booking suggestion (interest, charges) that BFL 5 kap. says should be booked. The ignore stays allowed, a migrated backlog is exactly such rows already present in the imported books, but the confirmation now says how many of the selected rows carry a suggestion and no duplicate hint, and what Ignorera is for. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WFhSQWzu5SyXB6kG5ActZc --------- Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5.1
Jakob Wennberg
parent
ea45e9dc2f
commit
823a0f73d8
@@ -1457,6 +1457,8 @@ One line per decision: `[YYYY-MM-DD] <decision>: <why>`. Appended by agents and
|
||||
[2026-09-01] F2 bank-data staleness: ship freshness reads only (last_synced_at/consent_expires/error_message on gnubok_connect_bank + new GET /api/v1/.../bank-connections, scope companies:read mirroring the MCP mapping): the daily cron already syncs server-side, so visibility is what the incident lacked; an agent-triggerable sync is a product bet (EB call cost, runaway agents) and was deferred by Emil.
|
||||
[2026-09-01] Verifikationsserie in the Ny verifikation modal is a closed dropdown instead of a one-letter free-text field: a typo there silently opens a brand-new series with its own number sequence, and the letters only mean anything if everyone uses the same ones. The letters are NOT prescribed by law (BFL 5 kap. 7 § requires only unbroken systematic numbering within each series), and the incumbents disagree: Björn Lundén uses A Huvudserie, F Kundfakturor, I Inbetalningar, L Leverantörsfakturor, N Löner, U Utbetalningar, J Bokslut. We ship FORTNOX's table verbatim (A Redovisning, B Kundfakturor, C Inbetalningar från kunder, D Leverantörsfakturor, E Utbetalningar till leverantörer, F Kassa, G Avskrivning, H Periodisering, I Bokslut, J Revisor, K Lön, L Kontantfaktura, M Momsrapport), from their own Systemdokumentation, because Fortnox is the system most companies migrate here from and an imported ledger should keep its meaning. REJECTED an earlier draft that labelled A as Kundfakturor: A is the general series manual entries land in (the one point Fortnox and BL agree on, and Fortnox allows manuell kontering ONLY in A), and migration 20260526120700 ships every source_type defaulting to 'A', so every existing company's A series already holds everything. Calling it Kundfakturor would mislabel their entire history and the modal's own default. The list is closed but any letter the company already configured, or that a draft was saved with, is appended so no existing value can fall out of the picker. Also: tabbing or clicking into an untouched amount field now proposes the outstanding difference (pre-selected, so typing replaces it) when the row already has an account and the difference belongs on that side. This deliberately reverses part of the note in updateLine that said a balancing amount must never auto-fill: that note was about filling on ACCOUNT selection, which stole the amount before the user had a chance to split it. Filling on focus keeps the split case intact because the proposal is selected text, and it fixes the common moms case where the last line is just the remainder.
|
||||
[2026-09-01] Settings PUT cross-field VAT validations scoped to touched field groups (vat-completeness, 40m-monthly, periodisk sammanstallning), not fixed at onboarding: partial saves from surfaces without VAT fields (invoice bank-details dialog) were hard-blocked by pre-existing vat_registered-without-number state (Marketio Lab case). The invariant still holds on every save that touches its group; explicit null now counts as a clear instead of falling back to the stored value during validation. Onboarding-side VAT number collection left as follow-up.
|
||||
[2026-09-01] #2127 skattekonto bulk: every unbooked, non-ignored skattekonto row in the inbox is selectable (isSkvSelectable); bulk Bokför keeps re-filtering through isSkvBulkEligible (button count, summary and submit read one skvBookableSelectedRows list) and bulk Ignorera spans bank + skattekonto selections in one confirmation, calling the per-row PATCH .../ignore 5-wide since no batch endpoint exists. Bullet 2 of the issue (unbooked skattekonto rows "not in att göra after migration") is scoped out: neither Hem's Att göra (lib/worklist book_transaction) nor the nav badge counts skattekonto_transactions, by construction of the canonical predicate, while the inbox itself lists them regardless of date. Whether skattekonto rows join the Att bokföra count is a founder call; reply asks the reporter where they looked.
|
||||
[2026-09-01] #2128 (row checkbox always visible) left open, no PR: #2093 (merged the same morning, after the Discord report) already made CHECKBOX_REVEAL_CLASS rest at opacity-50 with border-foreground and go solid on hover/focus/checked/coarse pointer, on all 8 list surfaces. The literal ask (fully solid at rest) is a one-token change to that constant but a design change across every list page, so it stays with the founder rather than being bumped in a bug-fix batch.
|
||||
[2026-09-01] #2126 payment-account-missing wording: made the message currency-aware on the client (getErrorMessage branch on details.currency via describeMissingInvoicePaymentAccount) plus a currency-neutral rewrite of the registry entry, instead of threading messageSv/messageEn through the nine server emit sites: every dashboard caller already sends details.currency, API/MCP consumers get the clarified registry text (names bankgiro/plusgiro/Swish for SEK, IBAN otherwise), and the staged-operation commit path (bare string) calls the helper directly. The reporter saw the pre-#2088 text; #2088 added the Inställningar -> Fakturering pointer the same evening, this removes the "valutakonto" reading.
|
||||
[2026-08-24] Declined to enable Peppol access for Low-Stack Technologies (enskild firma, personnummer-based org nr): both send validation and receive registration deliberately refuse personnummer identifiers (GDPR; DIGG recommends 0088 GLN) and no GLN support exists, so enabling would only surface errors; request row left as 'requested' pending founder call.
|
||||
[2026-08-26] invoice@arcim.io email unblock (Jakob's own test account, EXECUTED on prod, never re-run): the in-app delete had already anonymized + banned tombstone 7086c4d1 at 06:48 UTC, but the tombstone keeps auth.users.email by design (2026-07-24), which blocks re-signup. Replicated the 2026-07-30 Orback pattern: email scrambled to anonymized+<uuid>@tombstone.invalid, the stale auth.sessions row deleted, AND the Google auth.identities row (sub 103263015636623485631) deleted rather than scrubbed: an OAuth identity matches on (provider, provider_id), not email, so a scrubbed email alone would have re-bound the next Google sign-in to the banned tombstone. Hard delete of auth.users was never an option: companies.created_by cascades and the 4 154 posted CashLeads Media AB entries are BFL 7 kap. 2 § retained (delete_user_account was dropped in 20260706100000 for exactly this). The 3 archived companies stay as an inert tenant with 0 members.
|
||||
|
||||
@@ -159,6 +159,18 @@ function isSkvBulkEligible(row: SkattekontoTransactionWithSuggestion): boolean {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Every unbooked skattekonto row in the inbox can be selected: bulk Ignorera
|
||||
* applies to all of them, and bulk Bokför re-filters the selection through
|
||||
* isSkvBulkEligible at both the button and submit time. Before #2127 only
|
||||
* rows with a deterministic suggestion got a checkbox, so a migration
|
||||
* backlog on the skattekonto (pre-first-fiscal-year rows, already-booked
|
||||
* history) could only be ignored one row at a time.
|
||||
*/
|
||||
function isSkvSelectable(row: SkattekontoTransactionWithSuggestion): boolean {
|
||||
return !row.journal_entry_id && !row.is_ignored
|
||||
}
|
||||
|
||||
function buildInvoiceMap(rows: InvoiceWithCustomer[] | null): Record<string, InvoiceWithCustomer> {
|
||||
if (!rows) return {}
|
||||
return rows.reduce<Record<string, InvoiceWithCustomer>>((acc, inv) => {
|
||||
@@ -830,16 +842,17 @@ export default function TransactionsPage() {
|
||||
[exitingIds, inboxItems],
|
||||
)
|
||||
|
||||
// ... plus the visible skattekonto rows whose booking is deterministic
|
||||
// (rule matched, no duplicate hint, unbooked, genomförd). These go through
|
||||
// the skatteverket extension's bokfor-batch endpoint instead.
|
||||
// ... plus the visible unbooked skattekonto rows. Ignorera applies to all
|
||||
// of them; Bokför only to the deterministic subset (rule matched, no
|
||||
// duplicate hint, genomförd), which goes through the skatteverket
|
||||
// extension's bokfor-batch endpoint instead.
|
||||
const selectableSkvIds = useMemo(
|
||||
() =>
|
||||
inboxItems
|
||||
.filter(
|
||||
(item) =>
|
||||
item.source === 'skatteverket' &&
|
||||
isSkvBulkEligible(item.data) &&
|
||||
isSkvSelectable(item.data) &&
|
||||
!exitingIds.has(item.data.id),
|
||||
)
|
||||
.map((item) => item.data.id),
|
||||
@@ -865,6 +878,13 @@ export default function TransactionsPage() {
|
||||
[skvRows, skvSelectedIds],
|
||||
)
|
||||
|
||||
// The subset of the selection that bulk Bokför can take: the button, the
|
||||
// confirmation summary and the submit all read this one list.
|
||||
const skvBookableSelectedRows = useMemo(
|
||||
() => skvSelectedRows.filter(isSkvBulkEligible),
|
||||
[skvSelectedRows],
|
||||
)
|
||||
|
||||
// Bulk confirmation summary: selected rows grouped by their deterministic
|
||||
// suggestion ("3 × Intäktsränta skattekonto → 8314") with per-group sums.
|
||||
// Count-based on purpose: voucher numbers are assigned atomically at
|
||||
@@ -874,7 +894,7 @@ export default function TransactionsPage() {
|
||||
string,
|
||||
{ label: string; account: string; count: number; sum: number }
|
||||
>()
|
||||
for (const row of skvSelectedRows) {
|
||||
for (const row of skvBookableSelectedRows) {
|
||||
const suggestion = row.booking_suggestion
|
||||
if (!suggestion) continue
|
||||
const label = suggestion.label ?? suggestion.account_name ?? row.transaktionstext
|
||||
@@ -890,7 +910,7 @@ export default function TransactionsPage() {
|
||||
groups.set(key, group)
|
||||
}
|
||||
return Array.from(groups.values())
|
||||
}, [skvSelectedRows])
|
||||
}, [skvBookableSelectedRows])
|
||||
|
||||
const skvBulkTotal = useMemo(
|
||||
() => roundOre(skvBulkGroups.reduce((sum, g) => sum + g.sum, 0)),
|
||||
@@ -2854,7 +2874,7 @@ export default function TransactionsPage() {
|
||||
async function handleSkvBulkConfirm() {
|
||||
// Re-check eligibility at submit time: a refetch may have attached a
|
||||
// duplicate hint or booked a row while the selection sat idle.
|
||||
const ids = skvSelectedRows.filter(isSkvBulkEligible).map((r) => r.id)
|
||||
const ids = skvBookableSelectedRows.map((r) => r.id)
|
||||
if (ids.length === 0) {
|
||||
setSkvBulkConfirmOpen(false)
|
||||
setSkvSelectedIds(new Set())
|
||||
@@ -3142,20 +3162,46 @@ export default function TransactionsPage() {
|
||||
}
|
||||
|
||||
async function handleBatchIgnore() {
|
||||
const ids = Array.from(selectedIds)
|
||||
const bankIds = Array.from(selectedIds)
|
||||
// The selection can outlive a refetch: only rows that are still
|
||||
// ignorable go to the server (a booked skattekonto row would 409).
|
||||
const skvIgnorable = skvSelectedRows.filter(isSkvSelectable)
|
||||
const skvIds = skvIgnorable.map((r) => r.id)
|
||||
const total = bankIds.length + skvIds.length
|
||||
if (total === 0) return
|
||||
// Rows with a booking suggestion and no duplicate hint look like
|
||||
// affärshändelser that should be booked (BFL 5 kap.), not ignored. The
|
||||
// ignore stays allowed (a migrated backlog is exactly such rows, already
|
||||
// in the imported books), but the dialog says how many of them there
|
||||
// are so the choice is informed, not accidental.
|
||||
const skvLikelyBusiness = skvIgnorable.filter(
|
||||
(r) => r.booking_suggestion != null && r.match_suggestion == null,
|
||||
).length
|
||||
const body =
|
||||
bankIds.length > 0 && skvIds.length > 0
|
||||
? t('batch_ignore_confirm_body_mixed')
|
||||
: skvIds.length > 0
|
||||
? t('batch_ignore_confirm_body_skv')
|
||||
: t('batch_ignore_confirm_body_bank')
|
||||
const ok = await confirm({
|
||||
title: `Ignorera ${ids.length} transaktioner?`,
|
||||
description: 'Transaktionerna försvinner från listan utan att bokföras. Du kan återställa dem under Bankavstämning.',
|
||||
confirmLabel: 'Ignorera',
|
||||
cancelLabel: 'Avbryt',
|
||||
title: t('batch_ignore_confirm_title', { count: total }),
|
||||
description:
|
||||
skvLikelyBusiness > 0
|
||||
? `${body} ${t('batch_ignore_confirm_skv_suggested', { count: skvLikelyBusiness })}`
|
||||
: body,
|
||||
confirmLabel: t('batch_ignore_confirm_cta'),
|
||||
cancelLabel: t('batch_ignore_confirm_cancel'),
|
||||
variant: 'warning',
|
||||
})
|
||||
if (!ok) return
|
||||
|
||||
const ignoredIds = new Set<string>()
|
||||
setBatchProgress({ done: 0, total: ids.length })
|
||||
setBatchProgress({ done: 0, total })
|
||||
let completed = 0
|
||||
const results = await mapWithConcurrency(ids, BATCH_CONCURRENCY, async (id) => {
|
||||
const tick = () => {
|
||||
completed++
|
||||
setBatchProgress({ done: completed, total })
|
||||
}
|
||||
const bankResults = await mapWithConcurrency(bankIds, BATCH_CONCURRENCY, async (id) => {
|
||||
let ignored = false
|
||||
try {
|
||||
const res = await fetch(`/api/transactions/${id}/ignore`, { method: 'POST' })
|
||||
@@ -3163,54 +3209,86 @@ export default function TransactionsPage() {
|
||||
} catch {
|
||||
ignored = false
|
||||
}
|
||||
completed++
|
||||
setBatchProgress({ done: completed, total: ids.length })
|
||||
tick()
|
||||
return ignored
|
||||
})
|
||||
let successes = 0
|
||||
const failures: string[] = []
|
||||
ids.forEach((id, i) => {
|
||||
if (results[i]) {
|
||||
successes++
|
||||
ignoredIds.add(id)
|
||||
} else {
|
||||
const tx = transactions.find((t) => t.id === id)
|
||||
failures.push(tx?.description || id)
|
||||
// Same endpoint as the per-row Ignorera on SkattekontoInboxCard; there is
|
||||
// no batch variant, and 5-wide concurrency keeps this snappy for the
|
||||
// migration-sized backlogs it exists for.
|
||||
const skvResults = await mapWithConcurrency(skvIds, BATCH_CONCURRENCY, async (id) => {
|
||||
let ignored = false
|
||||
try {
|
||||
const res = await fetch(
|
||||
`/api/extensions/ext/skatteverket/skattekonto/transaktioner/${id}/ignore`,
|
||||
{
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ is_ignored: true }),
|
||||
},
|
||||
)
|
||||
ignored = res.ok
|
||||
} catch {
|
||||
ignored = false
|
||||
}
|
||||
tick()
|
||||
return ignored
|
||||
})
|
||||
if (ignoredIds.size > 0) {
|
||||
const ignoredBank = new Set(bankIds.filter((_, i) => bankResults[i]))
|
||||
const ignoredSkv = new Set(skvIds.filter((_, i) => skvResults[i]))
|
||||
const successes = ignoredBank.size + ignoredSkv.size
|
||||
const failed = total - successes
|
||||
|
||||
if (successes > 0) {
|
||||
setExitingIds((prev) => {
|
||||
const next = new Set(prev)
|
||||
for (const id of ignoredIds) next.add(id)
|
||||
for (const id of ignoredBank) next.add(id)
|
||||
for (const id of ignoredSkv) next.add(id)
|
||||
return next
|
||||
})
|
||||
setTotalUncategorizedCount((prev) => Math.max(0, (prev ?? ignoredIds.size) - ignoredIds.size))
|
||||
if (ignoredBank.size > 0) {
|
||||
setTotalUncategorizedCount((prev) => Math.max(0, (prev ?? ignoredBank.size) - ignoredBank.size))
|
||||
}
|
||||
setTimeout(() => {
|
||||
setTransactions((prev) =>
|
||||
prev.map((t) => (ignoredIds.has(t.id) ? { ...t, is_ignored: true } : t))
|
||||
)
|
||||
if (ignoredBank.size > 0) {
|
||||
setTransactions((prev) =>
|
||||
prev.map((t) => (ignoredBank.has(t.id) ? { ...t, is_ignored: true } : t))
|
||||
)
|
||||
}
|
||||
if (ignoredSkv.size > 0) {
|
||||
setSkvRows((prev) =>
|
||||
prev.map((r) => (ignoredSkv.has(r.id) ? { ...r, is_ignored: true } : r))
|
||||
)
|
||||
}
|
||||
setExitingIds((prev) => {
|
||||
const next = new Set(prev)
|
||||
for (const id of ignoredIds) next.delete(id)
|
||||
for (const id of ignoredBank) next.delete(id)
|
||||
for (const id of ignoredSkv) next.delete(id)
|
||||
return next
|
||||
})
|
||||
}, 350)
|
||||
}
|
||||
setBatchProgress(null)
|
||||
if (failures.length === 0) {
|
||||
if (failed === 0) {
|
||||
// Restore lives where the rows now live: Bankavstämning for bank rows,
|
||||
// the Skattekonto page for skattekonto rows.
|
||||
toast({
|
||||
title: 'Klart',
|
||||
description: `${successes} transaktioner ignorerade`,
|
||||
action: (
|
||||
<ToastAction altText="Öppna Bankavstämning" asChild>
|
||||
<Link href="/reconciliation">Avstämning</Link>
|
||||
</ToastAction>
|
||||
),
|
||||
title: t('batch_done_title'),
|
||||
description: t('batch_ignore_done_description', { count: successes }),
|
||||
action:
|
||||
ignoredBank.size > 0 ? (
|
||||
<ToastAction altText={t('batch_ignore_open_reconciliation_alt')} asChild>
|
||||
<Link href="/reconciliation">{t('batch_ignore_open_reconciliation')}</Link>
|
||||
</ToastAction>
|
||||
) : (
|
||||
<ToastAction altText={t('batch_ignore_open_skattekonto_alt')} asChild>
|
||||
<Link href="/skattekonto">{t('batch_ignore_open_skattekonto')}</Link>
|
||||
</ToastAction>
|
||||
),
|
||||
})
|
||||
} else {
|
||||
toast({
|
||||
title: 'Delvis klart',
|
||||
description: `${successes} ignorerade, ${failures.length} misslyckades`,
|
||||
title: t('batch_partial_title'),
|
||||
description: t('batch_ignore_partial_description', { success: successes, failed }),
|
||||
variant: 'destructive',
|
||||
})
|
||||
}
|
||||
@@ -3790,9 +3868,6 @@ export default function TransactionsPage() {
|
||||
>
|
||||
{t('batch_bulk_book')}
|
||||
</Button>
|
||||
<button type="button" className={QUIET_LINK_CLASS} onClick={handleBatchIgnore}>
|
||||
{t('batch_ignore')}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={cn(QUIET_LINK_CLASS, 'hover:text-destructive')}
|
||||
@@ -3803,12 +3878,19 @@ export default function TransactionsPage() {
|
||||
</>
|
||||
)}
|
||||
{/* SKV rows book through the skatteverket extension: one
|
||||
summary confirmation, then draft+commit per row. */}
|
||||
{skvSelectedIds.size > 0 && (
|
||||
summary confirmation, then draft+commit per row. Only
|
||||
the deterministic subset of the selection is bookable;
|
||||
the rest is still ignorable below. */}
|
||||
{skvBookableSelectedRows.length > 0 && (
|
||||
<Button size="sm" onClick={() => setSkvBulkConfirmOpen(true)}>
|
||||
{t('batch_skv_book', { count: skvSelectedIds.size })}
|
||||
{t('batch_skv_book', { count: skvBookableSelectedRows.length })}
|
||||
</Button>
|
||||
)}
|
||||
{/* Ignorera spans both selections (bank + skattekonto):
|
||||
one confirmation, one progress counter, one toast. */}
|
||||
<button type="button" className={QUIET_LINK_CLASS} onClick={handleBatchIgnore}>
|
||||
{t('batch_ignore')}
|
||||
</button>
|
||||
{(selectedIds.size < selectableInboxIds.length ||
|
||||
skvSelectedIds.size < selectableSkvIds.length) && (
|
||||
<button
|
||||
@@ -3887,7 +3969,7 @@ export default function TransactionsPage() {
|
||||
bookingSuggestion={item.data.booking_suggestion}
|
||||
isExiting={exitingIds.has(item.data.id)}
|
||||
processing={skvBulkSubmitting}
|
||||
selectable={isSkvBulkEligible(item.data)}
|
||||
selectable={isSkvSelectable(item.data)}
|
||||
isSelected={skvSelectedIds.has(item.data.id)}
|
||||
onToggleSelect={toggleSkvSelect}
|
||||
onBokfor={handleSkvBokfor}
|
||||
@@ -4267,10 +4349,10 @@ export default function TransactionsPage() {
|
||||
<ConfirmationDialog
|
||||
open
|
||||
onOpenChange={setSkvBulkConfirmOpen}
|
||||
title={t('skv_bulk_title', { count: skvSelectedIds.size })}
|
||||
title={t('skv_bulk_title', { count: skvBookableSelectedRows.length })}
|
||||
isSubmitting={skvBulkSubmitting}
|
||||
confirmLabel={t('skv_bulk_confirm')}
|
||||
warningText={t('skv_bulk_warning', { count: skvSelectedIds.size })}
|
||||
warningText={t('skv_bulk_warning', { count: skvBookableSelectedRows.length })}
|
||||
onConfirm={handleSkvBulkConfirm}
|
||||
>
|
||||
<div className="space-y-2 py-2 text-sm">
|
||||
@@ -4293,7 +4375,7 @@ export default function TransactionsPage() {
|
||||
</div>
|
||||
))}
|
||||
<div className="flex items-baseline justify-between gap-4 border-t border-border pt-2 font-medium">
|
||||
<span>{t('skv_bulk_total', { count: skvSelectedIds.size })}</span>
|
||||
<span>{t('skv_bulk_total', { count: skvBookableSelectedRows.length })}</span>
|
||||
<span className="tabular-nums">
|
||||
{skvBulkTotal > 0 ? '+' : ''}
|
||||
{formatCurrency(skvBulkTotal)}
|
||||
|
||||
@@ -6240,6 +6240,19 @@
|
||||
"batch_clear": "Clear selection",
|
||||
"batch_progress": "Booking {done} of {total}…",
|
||||
"batch_skv_book": "Post selected ({count})",
|
||||
"batch_ignore_confirm_title": "Ignore {count, plural, one {1 row} other {# rows}}?",
|
||||
"batch_ignore_confirm_body_bank": "The transactions disappear from the list without being posted. You can restore them under Bank reconciliation.",
|
||||
"batch_ignore_confirm_body_skv": "The tax account events are hidden from the list without being posted. Use this for events that are not business events of the company, for example rows from before the first fiscal year. You can restore them under Tax account.",
|
||||
"batch_ignore_confirm_body_mixed": "The rows disappear from the list without being posted. Restore bank transactions under Bank reconciliation and tax account events under Tax account.",
|
||||
"batch_ignore_confirm_skv_suggested": "{count, plural, one {1 of the tax account rows has a booking suggestion and looks like a business event that should be posted.} other {# of the tax account rows have booking suggestions and look like business events that should be posted.}} Only ignore rows that are already in the books or predate the first fiscal year.",
|
||||
"batch_ignore_confirm_cta": "Ignore",
|
||||
"batch_ignore_confirm_cancel": "Cancel",
|
||||
"batch_ignore_done_description": "{count, plural, one {1 row ignored} other {# rows ignored}}",
|
||||
"batch_ignore_partial_description": "{success} ignored, {failed} failed",
|
||||
"batch_ignore_open_reconciliation": "Reconciliation",
|
||||
"batch_ignore_open_reconciliation_alt": "Open Bank reconciliation",
|
||||
"batch_ignore_open_skattekonto": "Tax account",
|
||||
"batch_ignore_open_skattekonto_alt": "Open Tax account",
|
||||
"batch_done_title": "Done",
|
||||
"batch_partial_title": "Partially done",
|
||||
"batch_categorize_done_description": "{count, plural, one {1 transaction posted} other {# transactions posted}}",
|
||||
|
||||
@@ -6240,6 +6240,19 @@
|
||||
"batch_clear": "Avmarkera",
|
||||
"batch_progress": "Bokför {done} av {total}…",
|
||||
"batch_skv_book": "Bokför valda ({count})",
|
||||
"batch_ignore_confirm_title": "Ignorera {count, plural, one {1 rad} other {# rader}}?",
|
||||
"batch_ignore_confirm_body_bank": "Transaktionerna försvinner från listan utan att bokföras. Du kan återställa dem under Bankavstämning.",
|
||||
"batch_ignore_confirm_body_skv": "Skattekontohändelserna döljs från listan utan att bokföras. Använd för händelser som inte är firmans affärshändelser, till exempel rader från tiden före första räkenskapsåret. Du kan återställa dem under Skattekonto.",
|
||||
"batch_ignore_confirm_body_mixed": "Raderna försvinner från listan utan att bokföras. Banktransaktioner återställer du under Bankavstämning, skattekontohändelser under Skattekonto.",
|
||||
"batch_ignore_confirm_skv_suggested": "{count, plural, one {1 av skattekontoraderna har ett bokföringsförslag och ser ut som en affärshändelse som ska bokföras.} other {# av skattekontoraderna har bokföringsförslag och ser ut som affärshändelser som ska bokföras.}} Ignorera bara rader som redan finns i bokföringen eller ligger före första räkenskapsåret.",
|
||||
"batch_ignore_confirm_cta": "Ignorera",
|
||||
"batch_ignore_confirm_cancel": "Avbryt",
|
||||
"batch_ignore_done_description": "{count, plural, one {1 rad ignorerad} other {# rader ignorerade}}",
|
||||
"batch_ignore_partial_description": "{success} ignorerade, {failed} misslyckades",
|
||||
"batch_ignore_open_reconciliation": "Avstämning",
|
||||
"batch_ignore_open_reconciliation_alt": "Öppna Bankavstämning",
|
||||
"batch_ignore_open_skattekonto": "Skattekonto",
|
||||
"batch_ignore_open_skattekonto_alt": "Öppna Skattekonto",
|
||||
"batch_done_title": "Klart",
|
||||
"batch_partial_title": "Delvis klart",
|
||||
"batch_categorize_done_description": "{count, plural, one {1 transaktion bokförd} other {# transaktioner bokförda}}",
|
||||
|
||||
Reference in New Issue
Block a user