Fix/footer UI (#296)
* feat: enhance journal entry handling with follow-up entries and related RPC * fix: improve validation for journal entry lines to ensure proper submission criteria * feat: add commit_method and rubric_version columns to journal_entries for enhanced tracking * fix: ensure conditional addition of commit_method and rubric_version columns in journal_entries * Update supabase/migrations/20260421120000_journal_entries_with_related_rpc.sql Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --------- Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
This commit is contained in:
co-authored by
greptile-apps[bot]
parent
dd920355b3
commit
64cd6a0989
@@ -221,9 +221,29 @@ export default function JournalEntryForm({
|
||||
setLines(updated)
|
||||
}
|
||||
|
||||
const totalDebit = lines.reduce((sum, l) => sum + (parseFloat(l.debit_amount) || 0), 0)
|
||||
const totalCredit = lines.reduce((sum, l) => sum + (parseFloat(l.credit_amount) || 0), 0)
|
||||
const isBalanced = Math.round((totalDebit - totalCredit) * 100) === 0 && totalDebit > 0
|
||||
// Only lines with both an account and a non-zero amount end up in the submit
|
||||
// payload (see the filter in handleConfirm). Compute totals and balance from
|
||||
// those same lines so the enable-gate matches what the API will actually see.
|
||||
const submittableLines = lines.filter((l) => {
|
||||
const d = parseFloat(l.debit_amount) || 0
|
||||
const c = parseFloat(l.credit_amount) || 0
|
||||
return !!l.account_number && (d > 0 || c > 0)
|
||||
})
|
||||
const incompleteLineCount = lines.filter((l) => {
|
||||
const d = parseFloat(l.debit_amount) || 0
|
||||
const c = parseFloat(l.credit_amount) || 0
|
||||
const hasAmount = d > 0 || c > 0
|
||||
const hasAccount = !!l.account_number
|
||||
// Row counts as incomplete if exactly one of (account, amount) is present.
|
||||
return hasAccount !== hasAmount
|
||||
}).length
|
||||
const totalDebit = submittableLines.reduce((sum, l) => sum + (parseFloat(l.debit_amount) || 0), 0)
|
||||
const totalCredit = submittableLines.reduce((sum, l) => sum + (parseFloat(l.credit_amount) || 0), 0)
|
||||
const isBalanced =
|
||||
Math.round((totalDebit - totalCredit) * 100) === 0
|
||||
&& totalDebit > 0
|
||||
&& submittableLines.length >= 2
|
||||
&& incompleteLineCount === 0
|
||||
|
||||
const rate = parseFloat(exchangeRate) || 0
|
||||
// If user has manually entered a foreign amount, use that; otherwise derive from SEK total
|
||||
@@ -734,12 +754,18 @@ export default function JournalEntryForm({
|
||||
{!canWrite && <Lock className="mr-2 h-4 w-4" />}
|
||||
Granska & skapa
|
||||
</Button>
|
||||
{(!description || !selectedPeriod || isUploading || periodMismatch) && (
|
||||
{(!description || !selectedPeriod || isUploading || periodMismatch || incompleteLineCount > 0 || (!isBalanced && submittableLines.length < 2)) && (
|
||||
<div className="text-xs text-muted-foreground space-y-0.5 text-right">
|
||||
{!description && <p>Ange en beskrivning</p>}
|
||||
{!selectedPeriod && <p>Välj en räkenskapsperiod</p>}
|
||||
{periodMismatch === 'no_period' && <p>Skapa ett räkenskapsår som matchar datumet</p>}
|
||||
{isUploading && <p>Vänta tills filerna laddats upp</p>}
|
||||
{incompleteLineCount > 0 && (
|
||||
<p>Alla rader med belopp måste ha ett konto (och tvärtom)</p>
|
||||
)}
|
||||
{submittableLines.length < 2 && incompleteLineCount === 0 && (
|
||||
<p>Minst två rader med konto och belopp krävs</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -309,6 +309,15 @@ export default function JournalEntryList({ periodId }: Props) {
|
||||
<span className="text-sm text-muted-foreground w-24">
|
||||
{entry.entry_date}
|
||||
</span>
|
||||
{entry.out_of_period && (
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="text-xs font-normal shrink-0"
|
||||
title="Bokförd i ett senare räkenskapsår, men avser det valda året (t.ex. betalning av en faktura utställd i det valda året)."
|
||||
>
|
||||
Efterföljande
|
||||
</Badge>
|
||||
)}
|
||||
{(entry.status === 'reversed' || entry.source_type === 'storno' || entry.source_type === 'correction') && (
|
||||
<JournalEntryStatusBadge entry={entry} showStatus={entry.status === 'reversed'} />
|
||||
)}
|
||||
@@ -344,6 +353,15 @@ export default function JournalEntryList({ periodId }: Props) {
|
||||
<span className="text-sm text-muted-foreground">
|
||||
{entry.entry_date}
|
||||
</span>
|
||||
{entry.out_of_period && (
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="text-xs font-normal shrink-0"
|
||||
title="Bokförd i ett senare räkenskapsår, men avser det valda året."
|
||||
>
|
||||
Efterföljande
|
||||
</Badge>
|
||||
)}
|
||||
<span className="ml-auto flex items-center gap-1">
|
||||
{attachmentCounts[entry.id] ? (
|
||||
<span className="flex items-center gap-0.5 text-muted-foreground" title={`${attachmentCounts[entry.id]} underlag`}>
|
||||
|
||||
Reference in New Issue
Block a user