chore: repo-wide bloat sweep, remove dead code and fold duplicate helpers (#2150)
* chore: repo-wide bloat sweep, remove dead code and fold duplicate helpers Remove 33 dead files, ~270 unreferenced exports/types, 13 dead i18n namespaces and 4 unused dependencies; fold byte-identical helper copies into one canonical home each (lib/utils chunk/sleep/utcDateStamp, lib/dates/iso, lib/invariants/uuid, lib/xml/escape, lib/reports/sru/format, lib/pdf/number-text, lib/browser/panel-request, lib/api/v1/body + v1ValidationError rolled out to ~55 v1 routes, booking-template schemas). No behaviour change: v1 bodies and status codes, MCP tool schemas, DB writes and money math are untouched. Naive ore rounding was deliberately not swapped for roundOre; see DECISIONS.md 2026-09-02 for the full list of things left alone on purpose. tsc, lint, 19588 unit tests and check:guards green; antipattern baseline ratcheted (naive-ore-round 622 -> 620, hand-rolled-invariant 115 -> 113). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * test(transactions): import RawTransaction from @/types after the ingest re-export removal CI's type ratchet (check:types, full tsconfig) caught the one test file that still imported the type through lib/transactions/ingest. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> --------- 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
18cbc4c30a
commit
f266c386f3
@@ -69,83 +69,3 @@ export async function getAuditLog(
|
||||
count: includeCount ? count ?? 0 : 0,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get full history of a single record (all mutations)
|
||||
*/
|
||||
export async function getEntityHistory(
|
||||
supabase: SupabaseClient,
|
||||
companyId: string,
|
||||
tableName: string,
|
||||
recordId: string
|
||||
): Promise<AuditLogEntry[]> {
|
||||
|
||||
const { data, error } = await supabase
|
||||
.from('audit_log')
|
||||
.select('*')
|
||||
.eq('company_id', companyId)
|
||||
.eq('table_name', tableName)
|
||||
.eq('record_id', recordId)
|
||||
.order('created_at', { ascending: true })
|
||||
|
||||
if (error) {
|
||||
throw new Error(`Failed to fetch entity history: ${error.message}`)
|
||||
}
|
||||
|
||||
return (data as AuditLogEntry[]) || []
|
||||
}
|
||||
|
||||
/**
|
||||
* Trace the correction chain for a journal entry:
|
||||
* original → storno (reversal) → corrected entry
|
||||
*/
|
||||
export async function getCorrectionChain(
|
||||
supabase: SupabaseClient,
|
||||
companyId: string,
|
||||
journalEntryId: string
|
||||
): Promise<AuditLogEntry[]> {
|
||||
|
||||
// First, find the entry and its linked entries
|
||||
const { data: entry, error: entryError } = await supabase
|
||||
.from('journal_entries')
|
||||
.select('id, reverses_id, reversed_by_id, correction_of_id')
|
||||
.eq('id', journalEntryId)
|
||||
.eq('company_id', companyId)
|
||||
.single()
|
||||
|
||||
if (entryError || !entry) {
|
||||
throw new Error('Journal entry not found')
|
||||
}
|
||||
|
||||
// Collect all related entry IDs
|
||||
const relatedIds = new Set<string>([entry.id])
|
||||
if (entry.reverses_id) relatedIds.add(entry.reverses_id)
|
||||
if (entry.reversed_by_id) relatedIds.add(entry.reversed_by_id)
|
||||
if (entry.correction_of_id) relatedIds.add(entry.correction_of_id)
|
||||
|
||||
// Also look for entries that reference this one
|
||||
const { data: referencing } = await supabase
|
||||
.from('journal_entries')
|
||||
.select('id')
|
||||
.eq('company_id', companyId)
|
||||
.or(`reverses_id.eq.${journalEntryId},reversed_by_id.eq.${journalEntryId},correction_of_id.eq.${journalEntryId}`)
|
||||
|
||||
for (const ref of referencing || []) {
|
||||
relatedIds.add(ref.id)
|
||||
}
|
||||
|
||||
// Fetch audit log entries for all related IDs
|
||||
const { data, error } = await supabase
|
||||
.from('audit_log')
|
||||
.select('*')
|
||||
.eq('company_id', companyId)
|
||||
.eq('table_name', 'journal_entries')
|
||||
.in('record_id', Array.from(relatedIds))
|
||||
.order('created_at', { ascending: true })
|
||||
|
||||
if (error) {
|
||||
throw new Error(`Failed to fetch correction chain: ${error.message}`)
|
||||
}
|
||||
|
||||
return (data as AuditLogEntry[]) || []
|
||||
}
|
||||
|
||||
@@ -3,7 +3,8 @@ import { eventBus } from '@/lib/events'
|
||||
import { createLogger } from '@/lib/logger'
|
||||
import { fetchAllRows } from '@/lib/supabase/fetch-all'
|
||||
import { validatePeriodDuration } from '@/lib/bookkeeping/validate-period-duration'
|
||||
import type { FiscalPeriod, PeriodStatus } from '@/types'
|
||||
import { addDaysIso } from '@/lib/dates/iso'
|
||||
import type { FiscalPeriod } from '@/types'
|
||||
|
||||
const log = createLogger('period-service')
|
||||
|
||||
@@ -760,7 +761,7 @@ export async function createNextPeriod(
|
||||
.select('id')
|
||||
.eq('company_id', companyId)
|
||||
.eq('previous_period_id', currentPeriodId)
|
||||
.eq('period_start', addDaysUTC(nextEndStr, 1))
|
||||
.eq('period_start', addDaysIso(nextEndStr, 1))
|
||||
if (mischained && mischained.length > 0) {
|
||||
const successorIds = mischained.map((row: { id: string }) => row.id)
|
||||
const { error: relinkError } = await supabase
|
||||
@@ -819,7 +820,7 @@ export async function findNextPeriod(
|
||||
// off-by-one on servers in TZ+ when the day after period_end crosses a
|
||||
// DST spring-forward, because setDate(local) writes local-time fields
|
||||
// and toISOString() converts back through the shifted offset.
|
||||
const expectedStartStr = addDaysUTC(current.period_end, 1)
|
||||
const expectedStartStr = addDaysIso(current.period_end, 1)
|
||||
|
||||
// The chain is only trusted when it is date-adjacent. SIE import used to
|
||||
// point previous_period_id at the NEAREST later period regardless of the
|
||||
@@ -852,102 +853,6 @@ export async function findNextPeriod(
|
||||
return (byDate as FiscalPeriod | null) ?? null
|
||||
}
|
||||
|
||||
/** Add `days` to a YYYY-MM-DD string in pure UTC and return YYYY-MM-DD. */
|
||||
function addDaysUTC(isoDate: string, days: number): string {
|
||||
const d = new Date(isoDate + 'T00:00:00Z')
|
||||
d.setUTCDate(d.getUTCDate() + days)
|
||||
return d.toISOString().slice(0, 10)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a previous fiscal period before the given one.
|
||||
* Computes a 12-month period ending the day before the given period starts.
|
||||
* Updates previous_period_id chain so the given period points to the new one.
|
||||
*/
|
||||
export async function createPreviousPeriod(
|
||||
supabase: SupabaseClient,
|
||||
companyId: string,
|
||||
userId: string,
|
||||
currentPeriodId: string
|
||||
): Promise<FiscalPeriod> {
|
||||
|
||||
const { data: current, error: fetchError } = await supabase
|
||||
.from('fiscal_periods')
|
||||
.select('*')
|
||||
.eq('id', currentPeriodId)
|
||||
.eq('company_id', companyId)
|
||||
.single()
|
||||
|
||||
if (fetchError || !current) {
|
||||
throw new Error('Current fiscal period not found')
|
||||
}
|
||||
|
||||
// Compute previous period end (day before current start)
|
||||
const prevEnd = new Date(current.period_start + 'T12:00:00Z')
|
||||
prevEnd.setUTCDate(prevEnd.getUTCDate() - 1)
|
||||
|
||||
// Compute previous period start (1st of month, 12 months before prevEnd)
|
||||
const prevStart = new Date(prevEnd)
|
||||
prevStart.setUTCMonth(prevStart.getUTCMonth() - 11)
|
||||
prevStart.setUTCDate(1)
|
||||
|
||||
const prevStartStr = prevStart.toISOString().split('T')[0]
|
||||
const prevEndStr = prevEnd.toISOString().split('T')[0]
|
||||
|
||||
// Validate period duration
|
||||
const durationError = validatePeriodDuration(prevStartStr, prevEndStr, { isFirstPeriod: false })
|
||||
if (durationError) {
|
||||
throw new Error(durationError)
|
||||
}
|
||||
|
||||
// Check for overlapping periods
|
||||
const { data: overlapping } = await supabase
|
||||
.from('fiscal_periods')
|
||||
.select('id')
|
||||
.eq('company_id', companyId)
|
||||
.lte('period_start', prevEndStr)
|
||||
.gte('period_end', prevStartStr)
|
||||
.limit(1)
|
||||
|
||||
if (overlapping && overlapping.length > 0) {
|
||||
throw new Error('Previous fiscal period already exists or overlaps with an existing period')
|
||||
}
|
||||
|
||||
// Generate name
|
||||
const startYear = prevStart.getFullYear()
|
||||
const endYear = prevEnd.getFullYear()
|
||||
const name = startYear === endYear ? `FY ${startYear}` : `FY ${startYear}/${endYear}`
|
||||
|
||||
const { data: newPeriod, error: insertError } = await supabase
|
||||
.from('fiscal_periods')
|
||||
.insert({
|
||||
company_id: companyId,
|
||||
user_id: userId,
|
||||
name,
|
||||
period_start: prevStartStr,
|
||||
period_end: prevEndStr,
|
||||
})
|
||||
.select()
|
||||
.single()
|
||||
|
||||
if (insertError || !newPeriod) {
|
||||
throw new Error(`Failed to create previous period: ${insertError?.message}`)
|
||||
}
|
||||
|
||||
// Update the current period to point to the new one
|
||||
const { error: updateError } = await supabase
|
||||
.from('fiscal_periods')
|
||||
.update({ previous_period_id: newPeriod.id })
|
||||
.eq('id', currentPeriodId)
|
||||
.eq('company_id', companyId)
|
||||
|
||||
if (updateError) {
|
||||
throw new Error(`Failed to update period chain: ${updateError.message}`)
|
||||
}
|
||||
|
||||
return newPeriod as FiscalPeriod
|
||||
}
|
||||
|
||||
export type PeriodStatusValue = 'open' | 'locked' | 'closed'
|
||||
|
||||
export interface PeriodStatusForDate {
|
||||
@@ -1084,50 +989,3 @@ export async function resolvePeriodStatusForDate(
|
||||
}
|
||||
return { period_id: period.id, status: 'open', lock_date: null }
|
||||
}
|
||||
|
||||
/**
|
||||
* Get status summary for a fiscal period.
|
||||
*/
|
||||
export async function getPeriodStatus(
|
||||
supabase: SupabaseClient,
|
||||
companyId: string,
|
||||
userId: string,
|
||||
fiscalPeriodId: string
|
||||
): Promise<PeriodStatus> {
|
||||
|
||||
const { data: period, error: fetchError } = await supabase
|
||||
.from('fiscal_periods')
|
||||
.select('*')
|
||||
.eq('id', fiscalPeriodId)
|
||||
.eq('company_id', companyId)
|
||||
.single()
|
||||
|
||||
if (fetchError || !period) {
|
||||
throw new Error('Fiscal period not found')
|
||||
}
|
||||
|
||||
// Count draft entries in this period
|
||||
const { count: draftCount } = await supabase
|
||||
.from('journal_entries')
|
||||
.select('id', { count: 'exact', head: true })
|
||||
.eq('company_id', companyId)
|
||||
.eq('fiscal_period_id', fiscalPeriodId)
|
||||
.eq('status', 'draft')
|
||||
|
||||
// Check if next period exists via the chain pointer
|
||||
const { data: nextPeriod } = await supabase
|
||||
.from('fiscal_periods')
|
||||
.select('id')
|
||||
.eq('company_id', companyId)
|
||||
.eq('previous_period_id', fiscalPeriodId)
|
||||
.maybeSingle()
|
||||
|
||||
return {
|
||||
is_locked: !!period.locked_at,
|
||||
is_closed: period.is_closed,
|
||||
has_closing_entry: !!period.closing_entry_id,
|
||||
has_opening_balances: period.opening_balances_set,
|
||||
draft_count: draftCount ?? 0,
|
||||
next_period_exists: !!nextPeriod,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user