fix(import): keep mapping confirmation visible (#1684)

* fix(import): keep mapping confirmation visible

Signed-off-by: Emil <emilmattsson14@gmail.com>

* fix(import): keep source names masked

---------

Signed-off-by: Emil <emilmattsson14@gmail.com>
This commit is contained in:
Mattsson
2026-08-18 23:20:23 +02:00
committed by GitHub
parent dfa7097f3a
commit b069d9a9fe
2 changed files with 68 additions and 20 deletions
+1
View File
@@ -1055,6 +1055,7 @@ One line per decision: `[YYYY-MM-DD] <decision>: <why>`. Appended by agents and
[2026-08-18] Shopify webshop_orders port: vat_breakdown is reconstructed from the ORDER-LEVEL taxLines (net = tax / rate, remainder as a 0%-bucket, refuse on missing rates or overshoot) instead of summing line items like the WooCommerce sync: Shopify's discountedTotalSet excludes cart-level discount allocations and lineItems is a paginated connection, so part-summing can silently produce a wrong per-rate net, while tax-per-rate and the charged total are authoritative order-level facts. Refund VAT is always prorated from the parent's mix (Shopify's Refund object exposes no per-rate tax without paging refundLineItems per refund). [2026-08-18] Shopify webshop_orders port: vat_breakdown is reconstructed from the ORDER-LEVEL taxLines (net = tax / rate, remainder as a 0%-bucket, refuse on missing rates or overshoot) instead of summing line items like the WooCommerce sync: Shopify's discountedTotalSet excludes cart-level discount allocations and lineItems is a paginated connection, so part-summing can silently produce a wrong per-rate net, while tax-per-rate and the charged total are authoritative order-level facts. Refund VAT is always prorated from the parent's mix (Shopify's Refund object exposes no per-rate tax without paging refundLineItems per refund).
[2026-08-18] Shopify order feed keeps its paid-only qualification (PAID/PARTIALLY_REFUNDED/REFUNDED) after the webshop_orders port, unlike WooCommerce which also imports unpaid orders for the invoice flow: widening qualification is a product decision, out of scope for the port; unpaid orders re-surface via updatedAt when payment captures. The line-item snapshot is stored only when the parts reconstruct the charged total to the ore (else [] and the invoice conversion falls back to one aggregate line), and the bookkeeping-lock row filter was dropped: an Orders-page row behind the lock is an overview row, not permanent inbox noise, and booking is still blocked by the lock triggers (parity with WooCommerce). [2026-08-18] Shopify order feed keeps its paid-only qualification (PAID/PARTIALLY_REFUNDED/REFUNDED) after the webshop_orders port, unlike WooCommerce which also imports unpaid orders for the invoice flow: widening qualification is a product decision, out of scope for the port; unpaid orders re-surface via updatedAt when payment captures. The line-item snapshot is stored only when the parts reconstruct the charged total to the ore (else [] and the invoice conversion falls back to one aggregate line), and the bookkeeping-lock row filter was dropped: an Orders-page row behind the lock is an overview row, not permanent inbox noise, and booking is still blocked by the lock triggers (parity with WooCommerce).
[2026-08-18] Skattekontoutdrag sum mismatch (opening + events != closing) demoted from a hard 400 to a preview confirm gate showing ingående/händelser/utgående/differens, mirroring the orgnr-mismatch gate: Sebastian's real export was refused on it (2026-08-18) with no way forward and no figures to diagnose; nothing is booked at import and dedup makes a later complete re-import safe, so refusing the file only blocked the rows that WERE readable. Parser also takes the earliest opening / latest closing across several marker pairs, reads a marker saldo from a trailing running-saldo column, and accepts U+2212 / plus-sign amounts; the route logs the figures (amounts and counts, never row text) so the next report is diagnosable from Vercel logs. Kept the hard reject only for zero readable rows. [2026-08-18] Skattekontoutdrag sum mismatch (opening + events != closing) demoted from a hard 400 to a preview confirm gate showing ingående/händelser/utgående/differens, mirroring the orgnr-mismatch gate: Sebastian's real export was refused on it (2026-08-18) with no way forward and no figures to diagnose; nothing is booked at import and dedup makes a later complete re-import safe, so refusing the file only blocked the rows that WERE readable. Parser also takes the earliest opening / latest closing across several marker pairs, reads a marker saldo from a trailing running-saldo column, and accepts U+2212 / plus-sign amounts; the route logs the figures (amounts and counts, never row text) so the next report is diagnosable from Vercel logs. Kept the hard reject only for zero readable rows.
[2026-08-18] Issue #1668 keeps VAT confirmation in a dedicated sticky end column and makes truncated source names reveal on hover, focus, and activation: column truncation alone would still strand the hard-blocking action at responsive widths, while activation gives touch users the same full-text affordance.
[2026-08-18] Issue #1659 exposes one canonical per-period VAT deadline resolver from deadline-config and makes both the MCP close check and VAT period default consume it: monthly, quarterly, annual, over-40M, and January/August rules must not drift across parallel formulas again; the MCP adapter alone applies the same banking-day adjustment as generated tax deadlines. [2026-08-18] Issue #1659 exposes one canonical per-period VAT deadline resolver from deadline-config and makes both the MCP close check and VAT period default consume it: monthly, quarterly, annual, over-40M, and January/August rules must not drift across parallel formulas again; the MCP adapter alone applies the same banking-day adjustment as generated tax deadlines.
[2026-08-18] PR #1679 reports deadline_unavailable instead of guessing when VAT settings are missing, and annual AB deadlines require the configured fiscal year to match the resolved report period: a missing or stale filing profile must not produce a plausible but legally wrong date. [2026-08-18] PR #1679 reports deadline_unavailable instead of guessing when VAT settings are missing, and annual AB deadlines require the configured fiscal year to match the resolved report period: a missing or stale filing profile must not produce a plausible but legally wrong date.
[2026-08-18] PR #1679 uses company_settings.entity_type as the sole annual VAT deadline source in MCP close checks: falling back to companies could mask missing or inconsistent filing settings, so annual deadlines now fail closed instead. [2026-08-18] PR #1679 uses company_settings.entity_type as the sole annual VAT deadline source in MCP close checks: falling back to companies could mask missing or inconsistent filing settings, so annual deadlines now fail closed instead.
+67 -20
View File
@@ -37,6 +37,12 @@ import {
vatTreatmentsForAccountClass, vatTreatmentsForAccountClass,
type AccountVatTreatment, type AccountVatTreatment,
} from '@/lib/vat/account-vat-treatment' } from '@/lib/vat/account-vat-treatment'
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from '@/components/ui/info-tooltip'
import { cn } from '@/lib/utils'
interface AccountMappingStepProps { interface AccountMappingStepProps {
mappings: AccountMapping[] mappings: AccountMapping[]
@@ -231,26 +237,31 @@ export default function AccountMappingStep({
</div> </div>
{/* Mapping table */} {/* Mapping table */}
<div className="border rounded-lg overflow-x-auto"> <div className="overflow-hidden rounded-lg border">
<Table> <Table className="table-fixed">
<TableHeader> <TableHeader>
<TableRow> <TableRow>
<TableHead className="w-36">Källkonto</TableHead> <TableHead className="w-36">Källkonto</TableHead>
<TableHead>Källnamn</TableHead> <TableHead className="w-64 max-w-64">Källnamn</TableHead>
<TableHead className="w-12"></TableHead> <TableHead className="w-12"></TableHead>
<TableHead className="w-64">Målkonto</TableHead> <TableHead className="w-64">Målkonto</TableHead>
<TableHead className="min-w-72">{t('vat_treatment_column')}</TableHead> <TableHead className="min-w-72">{t('vat_treatment_column')}</TableHead>
<TableHead className="w-24">Konfidens</TableHead> <TableHead className="w-24">Konfidens</TableHead>
<TableHead className="sticky right-0 z-20 w-32 min-w-32 border-l border-border bg-background text-right">
{t('vat_treatment_confirm')}
</TableHead>
</TableRow> </TableRow>
</TableHeader> </TableHeader>
<TableBody> <TableBody>
{paginatedMappings.map((mapping) => ( {paginatedMappings.map((mapping) => (
<TableRow <TableRow
key={mapping.sourceAccount} key={mapping.sourceAccount}
className={!mapping.targetAccount ? 'bg-destructive/5' : ''} className={cn('group', !mapping.targetAccount && 'bg-destructive/5')}
> >
<TableCell className="font-mono">{mapping.sourceAccount}</TableCell> <TableCell className="font-mono">{mapping.sourceAccount}</TableCell>
<TableCell className="text-muted-foreground">{mapping.sourceName}</TableCell> <TableCell className="max-w-64 text-muted-foreground">
<TruncatedSourceName sourceName={mapping.sourceName} />
</TableCell>
<TableCell> <TableCell>
<ArrowRight className="h-4 w-4 text-muted-foreground" /> <ArrowRight className="h-4 w-4 text-muted-foreground" />
</TableCell> </TableCell>
@@ -344,20 +355,6 @@ export default function AccountMappingStep({
<SelectItem value="0.06">6 %</SelectItem> <SelectItem value="0.06">6 %</SelectItem>
</SelectContent> </SelectContent>
</Select> </Select>
{mapping.requiresVatTreatmentReview && !mapping.vatTreatmentReviewed && (
<Button
type="button"
variant="outline"
size="sm"
onClick={() => onVatTreatmentChange(
mapping.sourceAccount,
mapping.defaultVatTreatment ?? null,
mapping.defaultVatRate ?? null,
)}
>
{t('vat_treatment_confirm')}
</Button>
)}
</div> </div>
) : ( ) : (
<span className="text-muted-foreground">-</span> <span className="text-muted-foreground">-</span>
@@ -372,11 +369,35 @@ export default function AccountMappingStep({
/> />
)} )}
</TableCell> </TableCell>
<TableCell
className={cn(
'sticky right-0 z-10 w-32 min-w-32 border-l border-border transition-colors',
mapping.targetAccount ? 'bg-background' : 'bg-destructive/5',
'group-hover:bg-muted/50 group-focus-within:bg-muted/50',
)}
>
{mapping.requiresVatTreatmentReview && !mapping.vatTreatmentReviewed && (
<Button
type="button"
variant="outline"
size="sm"
className="min-h-11 w-full sm:min-h-8"
aria-label={`${t('vat_treatment_confirm')}: ${mapping.sourceAccount}`}
onClick={() => onVatTreatmentChange(
mapping.sourceAccount,
mapping.defaultVatTreatment ?? null,
mapping.defaultVatRate ?? null,
)}
>
{t('vat_treatment_confirm')}
</Button>
)}
</TableCell>
</TableRow> </TableRow>
))} ))}
{paginatedMappings.length === 0 && ( {paginatedMappings.length === 0 && (
<TableRow> <TableRow>
<TableCell colSpan={6} className="text-center text-muted-foreground py-8"> <TableCell colSpan={7} className="text-center text-muted-foreground py-8">
Inga konton matchar filtret Inga konton matchar filtret
</TableCell> </TableCell>
</TableRow> </TableRow>
@@ -435,6 +456,32 @@ export default function AccountMappingStep({
) )
} }
function TruncatedSourceName({ sourceName }: { sourceName: string }) {
const [open, setOpen] = useState(false)
return (
<Tooltip open={open} onOpenChange={setOpen}>
<TooltipTrigger asChild>
<button
type="button"
aria-expanded={open}
className="flex min-h-10 w-full min-w-0 items-center rounded-sm text-left focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
onClick={() => setOpen(true)}
>
<span className="block min-w-0 truncate">{sourceName}</span>
</button>
</TooltipTrigger>
<TooltipContent
side="top"
className="max-w-xs break-words"
data-ph-mask=""
>
{sourceName}
</TooltipContent>
</Tooltip>
)
}
function ConfidenceBadge({ function ConfidenceBadge({
confidence, confidence,
isOverride, isOverride,