Completes Phase 2: Adds reconciliation features
Implements balance assertions, reconciliation API endpoints, a reconciliation UI dashboard, and automated daily balance checks. This provides comprehensive reconciliation tools to ensure accounting accuracy and catch discrepancies early. Updates roadmap to mark Phase 2 as complete.
This commit is contained in:
parent
c0277dfc98
commit
6d84479f7d
7 changed files with 963 additions and 6 deletions
|
|
@ -271,7 +271,7 @@
|
|||
<div v-if="failedAssertions.length > 0" class="q-mb-md">
|
||||
<q-banner class="bg-negative text-white" rounded>
|
||||
<template v-slot:avatar>
|
||||
<q-icon name="error" color="white" />
|
||||
<q-icon name="error" color="white"></q-icon>
|
||||
</template>
|
||||
<div class="text-weight-bold">{% raw %}{{ failedAssertions.length }}{% endraw %} Failed Assertion{% raw %}{{ failedAssertions.length > 1 ? 's' : '' }}{% endraw %}</div>
|
||||
</q-banner>
|
||||
|
|
@ -377,6 +377,127 @@
|
|||
</q-card-section>
|
||||
</q-card>
|
||||
|
||||
<!-- Reconciliation Dashboard (Super User Only) -->
|
||||
<q-card v-if="isSuperUser">
|
||||
<q-card-section>
|
||||
<div class="row items-center justify-between q-mb-md">
|
||||
<h6 class="q-my-none">Reconciliation Dashboard</h6>
|
||||
<q-btn
|
||||
size="sm"
|
||||
color="primary"
|
||||
@click="runFullReconciliation"
|
||||
:loading="reconciliation.checking"
|
||||
icon="sync"
|
||||
label="Check All"
|
||||
>
|
||||
<q-tooltip>Re-check all balance assertions</q-tooltip>
|
||||
</q-btn>
|
||||
</div>
|
||||
|
||||
<!-- Summary Stats -->
|
||||
<div v-if="reconciliation.summary" class="row q-gutter-md q-mb-md">
|
||||
<!-- Assertions Stats -->
|
||||
<q-card flat bordered class="col">
|
||||
<q-card-section>
|
||||
<div class="text-caption text-grey">Balance Assertions</div>
|
||||
<div class="text-h6">{% raw %}{{ reconciliation.summary.assertions.total }}{% endraw %}</div>
|
||||
<div class="text-caption">
|
||||
<span class="text-positive">{% raw %}{{ reconciliation.summary.assertions.passed }}{% endraw %} passed</span> |
|
||||
<span class="text-negative">{% raw %}{{ reconciliation.summary.assertions.failed }}{% endraw %} failed</span> |
|
||||
<span class="text-grey">{% raw %}{{ reconciliation.summary.assertions.pending }}{% endraw %} pending</span>
|
||||
</div>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
|
||||
<!-- Journal Entries Stats -->
|
||||
<q-card flat bordered class="col">
|
||||
<q-card-section>
|
||||
<div class="text-caption text-grey">Journal Entries</div>
|
||||
<div class="text-h6">{% raw %}{{ reconciliation.summary.entries.total }}{% endraw %}</div>
|
||||
<div class="text-caption">
|
||||
<span class="text-positive">{% raw %}{{ reconciliation.summary.entries.cleared }}{% endraw %} cleared</span> |
|
||||
<span class="text-orange">{% raw %}{{ reconciliation.summary.entries.pending }}{% endraw %} pending</span> |
|
||||
<span class="text-warning">{% raw %}{{ reconciliation.summary.entries.flagged }}{% endraw %} flagged</span>
|
||||
</div>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
|
||||
<!-- Accounts Stats -->
|
||||
<q-card flat bordered class="col">
|
||||
<q-card-section>
|
||||
<div class="text-caption text-grey">Total Accounts</div>
|
||||
<div class="text-h6">{% raw %}{{ reconciliation.summary.accounts.total }}{% endraw %}</div>
|
||||
<div class="text-caption text-grey">
|
||||
Last checked: {% raw %}{{ reconciliation.summary.last_checked ? formatDate(reconciliation.summary.last_checked) : 'Never' }}{% endraw %}
|
||||
</div>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</div>
|
||||
|
||||
<!-- Discrepancies Alert -->
|
||||
<q-banner v-if="reconciliation.discrepancies && reconciliation.discrepancies.total_discrepancies > 0" class="bg-warning text-dark q-mb-md" rounded>
|
||||
<template v-slot:avatar>
|
||||
<q-icon name="warning" color="orange"></q-icon>
|
||||
</template>
|
||||
<div class="text-weight-bold">
|
||||
{% raw %}{{ reconciliation.discrepancies.total_discrepancies }}{% endraw %} Discrepancy(ies) Found
|
||||
</div>
|
||||
<div class="text-caption">
|
||||
{% raw %}{{ reconciliation.discrepancies.failed_assertions.length }}{% endraw %} failed assertions,
|
||||
{% raw %}{{ reconciliation.discrepancies.flagged_entries.length }}{% endraw %} flagged entries
|
||||
</div>
|
||||
<template v-slot:action>
|
||||
<q-btn flat label="View Details" @click="reconciliation.showDiscrepancies = !reconciliation.showDiscrepancies"></q-btn>
|
||||
</template>
|
||||
</q-banner>
|
||||
|
||||
<!-- Discrepancies Details -->
|
||||
<div v-if="reconciliation.showDiscrepancies && reconciliation.discrepancies">
|
||||
<!-- Failed Assertions -->
|
||||
<div v-if="reconciliation.discrepancies.failed_assertions.length > 0" class="q-mb-md">
|
||||
<div class="text-subtitle2 q-mb-sm">Failed Assertions</div>
|
||||
<q-list bordered separator dense>
|
||||
<q-item v-for="assertion in reconciliation.discrepancies.failed_assertions" :key="assertion.id">
|
||||
<q-item-section avatar>
|
||||
<q-icon name="error" color="negative" size="sm"></q-icon>
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
<q-item-label>{% raw %}{{ getAccountName(assertion.account_id) }}{% endraw %}</q-item-label>
|
||||
<q-item-label caption>
|
||||
Expected: {% raw %}{{ formatSats(assertion.expected_balance_sats) }}{% endraw %} |
|
||||
Actual: {% raw %}{{ formatSats(assertion.checked_balance_sats) }}{% endraw %} |
|
||||
Diff: {% raw %}{{ formatSats(assertion.difference_sats) }}{% endraw %}
|
||||
</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</div>
|
||||
|
||||
<!-- Flagged Entries -->
|
||||
<div v-if="reconciliation.discrepancies.flagged_entries.length > 0">
|
||||
<div class="text-subtitle2 q-mb-sm">Flagged Entries</div>
|
||||
<q-list bordered separator dense>
|
||||
<q-item v-for="entry in reconciliation.discrepancies.flagged_entries" :key="entry.id">
|
||||
<q-item-section avatar>
|
||||
<q-icon name="flag" color="warning" size="sm"></q-icon>
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
<q-item-label>{% raw %}{{ entry.description }}{% endraw %}</q-item-label>
|
||||
<q-item-label caption>{% raw %}{{ formatDate(entry.entry_date) }}{% endraw %}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- No issues message -->
|
||||
<div v-if="reconciliation.summary && (!reconciliation.discrepancies || reconciliation.discrepancies.total_discrepancies === 0)" class="text-center text-positive q-pa-md">
|
||||
<q-icon name="check_circle" size="lg" color="positive"></q-icon>
|
||||
<div class="q-mt-sm">All accounts reconciled successfully!</div>
|
||||
</div>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
|
||||
<!-- Quick Actions -->
|
||||
<q-card>
|
||||
<q-card-section>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue