@@ -741,3 +741,4 @@ One line per decision: `[YYYY-MM-DD] <decision>: <why>`. Appended by agents and
|
||||
[2026-08-01] Out-of-order SIE IB activity is bounded by the target fiscal-period end, not its start: this excludes later-first imports while preserving same-period continuation suppression; successor IB resync checks the current error state plus a real target-period entry because result.success is finalized later, keeping replacement on a new engine voucher plus storno without letting a no-op import succeed through resync alone.
|
||||
[2026-08-01] Successor SIE IB replacement uses a specialized engine RPC instead of loosening the owner-only generic relink RPC: non-viewer members and scoped service-role imports are supported, while one period-row lock and expected-pointer CAS make the replacement voucher, storno, reversal status, pointer swap, and voucher sequence increments commit or roll back together.
|
||||
[2026-08-02] Out-of-order SIE IB resync requires exact date adjacency: the nearest later fiscal period can sit beyond a missing middle year, and replacing its authoritative IB with a non-adjacent UB would make that later period temporarily wrong until the gap was imported.
|
||||
[2026-08-03] Issue #1267 localizes the latest-posted-voucher label in the web views while PDF and spreadsheet exports remain Swedish: translating one export label would create mixed-language files, so full export localization stays a separate surface-wide change.
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
import React, { useState, useEffect, useCallback } from 'react'
|
||||
import Link from 'next/link'
|
||||
import { useTranslations } from 'next-intl'
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Input } from '@/components/ui/input'
|
||||
@@ -19,7 +20,7 @@ import { ContextPicker } from '@/components/common/ContextPicker'
|
||||
import { cn, formatDate } from '@/lib/utils'
|
||||
import { getErrorMessage } from '@/lib/errors/get-error-message'
|
||||
import { roundOre } from '@/lib/money'
|
||||
import { formatLatestVouchers, LATEST_VOUCHERS_LABEL } from '@/lib/reports/latest-vouchers-format'
|
||||
import { formatLatestVouchers } from '@/lib/reports/latest-vouchers-format'
|
||||
import { formatVoucher } from '@/lib/bookkeeping/voucher-series-resolver'
|
||||
import { AccountNumber } from '@/components/ui/account-number'
|
||||
import { ReportExportMenu } from '@/components/reports/ReportExportMenu'
|
||||
@@ -715,10 +716,12 @@ export function BalanceSheetView({ periodId, dateRange, onNavigateToAccount }: {
|
||||
}
|
||||
|
||||
export function ResultatrapportView({ periodId, dateRange, dimensionFilter = null, onNavigateToAccount }: { periodId: string; dateRange: DateRangeValue; dimensionFilter?: DimensionFilterValue | null; onNavigateToAccount: (account: string) => void }) {
|
||||
const t = useTranslations('reports')
|
||||
const [data, setData] = useState<ResultatrapportReport | null>(null)
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const reportQs = reportQuery(periodId, dateRange, dimensionFilter)
|
||||
const latestVouchers = formatLatestVouchers(data?.latest_vouchers)
|
||||
|
||||
useEffect(() => {
|
||||
setLoading(true)
|
||||
@@ -786,9 +789,9 @@ export function ResultatrapportView({ periodId, dateRange, dimensionFilter = nul
|
||||
{ format: 'xlsx', href: `/api/reports/resultatrapport/xlsx?${reportQs}` },
|
||||
]}
|
||||
/>
|
||||
{formatLatestVouchers(data.latest_vouchers) && (
|
||||
{latestVouchers && (
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{LATEST_VOUCHERS_LABEL}: {formatLatestVouchers(data.latest_vouchers)}
|
||||
{t('latest_posted_vouchers')}: {latestVouchers}
|
||||
</p>
|
||||
)}
|
||||
|
||||
@@ -873,10 +876,12 @@ export function ResultatrapportView({ periodId, dateRange, dimensionFilter = nul
|
||||
}
|
||||
|
||||
export function BalansrapportView({ periodId, dateRange, onNavigateToAccount }: { periodId: string; dateRange: DateRangeValue; onNavigateToAccount: (account: string) => void }) {
|
||||
const t = useTranslations('reports')
|
||||
const [data, setData] = useState<BalansrapportReport | null>(null)
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const reportQs = reportQuery(periodId, dateRange)
|
||||
const latestVouchers = formatLatestVouchers(data?.latest_vouchers)
|
||||
|
||||
useEffect(() => {
|
||||
setLoading(true)
|
||||
@@ -939,9 +944,9 @@ export function BalansrapportView({ periodId, dateRange, onNavigateToAccount }:
|
||||
{ format: 'xlsx', href: `/api/reports/balansrapport/xlsx?${reportQs}` },
|
||||
]}
|
||||
/>
|
||||
{formatLatestVouchers(data.latest_vouchers) && (
|
||||
{latestVouchers && (
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{LATEST_VOUCHERS_LABEL}: {formatLatestVouchers(data.latest_vouchers)}
|
||||
{t('latest_posted_vouchers')}: {latestVouchers}
|
||||
</p>
|
||||
)}
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@ import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||
import { getLatestPostedVouchers } from '../latest-vouchers'
|
||||
import { formatLatestVouchers, LATEST_VOUCHERS_LABEL } from '../latest-vouchers-format'
|
||||
import { createQueuedMockSupabase } from '@/tests/helpers'
|
||||
import enMessages from '../../../messages/en.json'
|
||||
import svMessages from '../../../messages/sv.json'
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
@@ -184,4 +186,9 @@ describe('formatLatestVouchers', () => {
|
||||
// the books, and a reconciler told the wrong one chases a phantom gap.
|
||||
expect(LATEST_VOUCHERS_LABEL).toBe('Senaste bokförda verifikat')
|
||||
})
|
||||
|
||||
it('keeps the web label explicit in both supported locales', () => {
|
||||
expect(svMessages.reports.latest_posted_vouchers).toBe(LATEST_VOUCHERS_LABEL)
|
||||
expect(enMessages.reports.latest_posted_vouchers).toBe('Latest posted vouchers')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -9,7 +9,9 @@ import type { LatestVoucherPerSeries } from '@/types'
|
||||
*/
|
||||
|
||||
/**
|
||||
* Swedish-only report surface (see .claude/rules/i18n.md).
|
||||
* Swedish label for PDF and spreadsheet exports, whose complete document
|
||||
* chrome currently renders in Swedish. Client views use the localized
|
||||
* `reports.latest_posted_vouchers` message instead.
|
||||
*
|
||||
* The wording is load-bearing: this is the last POSTED number, not the last
|
||||
* number the sequence counter handed out. A reconciler who assumes the other
|
||||
|
||||
@@ -5381,6 +5381,7 @@
|
||||
"download_excel": "Download Excel",
|
||||
"download_csv": "Download CSV",
|
||||
"download_xml": "Download XML file",
|
||||
"latest_posted_vouchers": "Latest posted vouchers",
|
||||
"pdf_download_failed_title": "The PDF could not be generated",
|
||||
"pdf_download_timeout": "The server did not respond in time. No PDF was saved. Try again.",
|
||||
"pdf_download_network": "No connection to the server. No PDF was saved. Check your internet connection and try again.",
|
||||
|
||||
@@ -5381,6 +5381,7 @@
|
||||
"download_excel": "Ladda ner Excel",
|
||||
"download_csv": "Ladda ner CSV",
|
||||
"download_xml": "Ladda ner XML-fil",
|
||||
"latest_posted_vouchers": "Senaste bokförda verifikat",
|
||||
"pdf_download_failed_title": "PDF:en kunde inte skapas",
|
||||
"pdf_download_timeout": "Servern svarade inte i tid. Ingen PDF sparades. Försök igen.",
|
||||
"pdf_download_network": "Ingen kontakt med servern. Ingen PDF sparades. Kontrollera din internetanslutning och försök igen.",
|
||||
|
||||
Reference in New Issue
Block a user