310 lines
13 KiB
HTML
310 lines
13 KiB
HTML
<!--
|
|
Copyright since 2025 Mifos Initiative
|
|
|
|
This Source Code Form is subject to the terms of the Mozilla Public
|
|
License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
-->
|
|
|
|
@if (isJournalEntryLoaded) {
|
|
<div class="txn-detail-page">
|
|
<!-- Header Bar -->
|
|
<div class="txn-header-bar">
|
|
<div class="txn-header-bar__left">
|
|
<button mat-icon-button (click)="goBack()" class="txn-back-btn" matTooltip="Go Back">
|
|
<fa-icon icon="arrow-left"></fa-icon>
|
|
</button>
|
|
<div class="txn-header-bar__title-group">
|
|
<h3 class="txn-header-bar__title">
|
|
<fa-icon icon="receipt" class="m-r-8"></fa-icon>
|
|
Transaction Detail
|
|
</h3>
|
|
@if (transactionId) {
|
|
<span class="txn-header-bar__txn-id">ID: {{ transactionId }}</span>
|
|
}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- One branch's part of a transfer between branches: say so, name both branches, link the other part -->
|
|
@if (interBranch) {
|
|
<div class="txn-interbranch">
|
|
<div class="txn-interbranch__icon"><fa-icon icon="link"></fa-icon></div>
|
|
<div class="txn-interbranch__body">
|
|
<div class="txn-interbranch__title">
|
|
Inter-branch transfer
|
|
<span class="txn-interbranch__voucher">{{ interBranch.voucherNo }}</span>
|
|
</div>
|
|
<div class="txn-interbranch__route">
|
|
<strong>{{ branchesOf(interBranch.from) }}</strong>
|
|
<span class="txn-interbranch__arrow">→</span>
|
|
<strong>{{ branchesOf(interBranch.to) }}</strong>
|
|
<span class="txn-interbranch__amount">{{ interBranch.amount | formatNumber }}</span>
|
|
</div>
|
|
<div class="txn-interbranch__text">
|
|
This is the <strong>{{ thisBranchPart?.branchName }}</strong> branch's part of the transfer. Each branch
|
|
posts its own part, and the two are joined through Liability Transfer Suspense (200901), so they cancel out
|
|
across the bank.
|
|
</div>
|
|
<div class="txn-interbranch__legs">
|
|
@for (leg of interBranch.from; track leg.accountNo) {
|
|
<span class="txn-interbranch__leg"
|
|
><b>From</b> {{ leg.accountNo }} · {{ leg.accountName }} ({{ leg.branchName }})</span
|
|
>
|
|
}
|
|
@for (leg of interBranch.to; track leg.accountNo) {
|
|
<span class="txn-interbranch__leg"
|
|
><b>To</b> {{ leg.accountNo }} · {{ leg.accountName }} ({{ leg.branchName }})</span
|
|
>
|
|
}
|
|
</div>
|
|
@if (interBranch.raisedBy) {
|
|
<div class="txn-interbranch__text">
|
|
Raised by <strong>{{ interBranch.raisedBy }}</strong> on {{ interBranch.raisedAt | datetimeFormat }},
|
|
approved by <strong>{{ interBranch.approvedBy }}</strong> on
|
|
{{ interBranch.approvedAt | datetimeFormat }}.
|
|
</div>
|
|
}
|
|
<div class="txn-interbranch__text">
|
|
@for (part of otherBranchParts; track part.transactionId) {
|
|
The <strong>{{ part.branchName }}</strong> part:
|
|
<button type="button" class="txn-interbranch__link" (click)="openTransaction(part.transactionId)">
|
|
{{ part.transactionId }}
|
|
</button>
|
|
}
|
|
</div>
|
|
<div class="txn-interbranch__warn">
|
|
<fa-icon icon="exclamation-triangle" class="m-r-6"></fa-icon>
|
|
Do not revert one branch's part on its own: the other branch's part would stay and the books would not
|
|
balance.
|
|
</div>
|
|
</div>
|
|
</div>
|
|
}
|
|
|
|
<!-- Transaction Info Cards -->
|
|
@if (isViewTransaction()) {
|
|
@if (dataSource) {
|
|
<div class="txn-info-grid">
|
|
<div
|
|
class="txn-info-card"
|
|
[ngClass]="dataSource.data[0].manualEntry ? 'txn-info-card--manual' : 'txn-info-card--system'"
|
|
>
|
|
<div class="txn-info-card__icon">
|
|
<fa-icon [icon]="dataSource.data[0].manualEntry ? 'pencil-alt' : 'cog'"></fa-icon>
|
|
</div>
|
|
<div class="txn-info-card__body">
|
|
<div class="txn-info-card__label">Entry Type</div>
|
|
<div class="txn-info-card__value">
|
|
{{ dataSource.data[0].manualEntry ? 'Manual Entry' : 'System Entry' }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="txn-info-card txn-info-card--blue">
|
|
<div class="txn-info-card__icon"><fa-icon icon="building"></fa-icon></div>
|
|
<div class="txn-info-card__body">
|
|
<div class="txn-info-card__label">Office</div>
|
|
<div class="txn-info-card__value">{{ dataSource.data[0].officeName }}</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="txn-info-card txn-info-card--teal">
|
|
<div class="txn-info-card__icon"><fa-icon icon="calendar-alt"></fa-icon></div>
|
|
<div class="txn-info-card__body">
|
|
<div class="txn-info-card__label">Transaction Date</div>
|
|
<div class="txn-info-card__value">{{ dataSource.data[0].transactionDate | dateFormat }}</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="txn-info-card txn-info-card--purple">
|
|
<div class="txn-info-card__icon"><fa-icon icon="clock"></fa-icon></div>
|
|
<div class="txn-info-card__body">
|
|
<div class="txn-info-card__label">Submitted On</div>
|
|
<div class="txn-info-card__value txn-info-card__value--small">
|
|
{{ dataSource.data[0].submittedOnDate | datetimeFormat }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="txn-info-card txn-info-card--grey">
|
|
<div class="txn-info-card__icon"><fa-icon icon="user"></fa-icon></div>
|
|
<div class="txn-info-card__body">
|
|
<div class="txn-info-card__label">Created By</div>
|
|
<div class="txn-info-card__value">{{ dataSource.data[0].createdByUserName }}</div>
|
|
</div>
|
|
</div>
|
|
|
|
@if (dataSource.data[0].externalAssetOwner) {
|
|
<div class="txn-info-card txn-info-card--orange">
|
|
<div class="txn-info-card__icon"><fa-icon icon="link"></fa-icon></div>
|
|
<div class="txn-info-card__body">
|
|
<div class="txn-info-card__label">External Asset Owner</div>
|
|
<div class="txn-info-card__value">{{ dataSource.data[0].externalAssetOwner }}</div>
|
|
</div>
|
|
</div>
|
|
}
|
|
</div>
|
|
|
|
<!-- DR/CR Summary Bar -->
|
|
<div class="txn-summary-bar">
|
|
<div class="txn-summary-bar__item txn-summary-bar__item--dr">
|
|
<fa-icon icon="arrow-up" class="m-r-6"></fa-icon>
|
|
Total Debit: <strong>{{ getTotalDebit() | formatNumber }}</strong>
|
|
</div>
|
|
<div class="txn-summary-bar__divider"></div>
|
|
<div class="txn-summary-bar__item txn-summary-bar__item--cr">
|
|
<fa-icon icon="arrow-down" class="m-r-6"></fa-icon>
|
|
Total Credit: <strong>{{ getTotalCredit() | formatNumber }}</strong>
|
|
</div>
|
|
<div class="txn-summary-bar__divider"></div>
|
|
<div
|
|
class="txn-summary-bar__item"
|
|
[ngClass]="isBalanced() ? 'txn-summary-bar__item--balanced' : 'txn-summary-bar__item--unbalanced'"
|
|
>
|
|
<fa-icon [icon]="isBalanced() ? 'check-circle' : 'exclamation-triangle'" class="m-r-6"></fa-icon>
|
|
{{ isBalanced() ? 'Balanced' : 'Unbalanced' }}
|
|
</div>
|
|
</div>
|
|
}
|
|
}
|
|
|
|
<!-- Journal Entries Table -->
|
|
<mat-card class="txn-entries-card mat-elevation-z2">
|
|
<mat-card-header class="txn-entries-card__header">
|
|
<mat-card-title>
|
|
<fa-icon icon="table" class="m-r-6"></fa-icon>
|
|
Journal Entries
|
|
<span class="txn-entries-count">{{ dataSource?.data?.length || 0 }} entries</span>
|
|
</mat-card-title>
|
|
</mat-card-header>
|
|
<mat-card-content class="txn-entries-card__content">
|
|
<div class="txn-table-wrapper">
|
|
<table mat-table [dataSource]="dataSource" matSort class="txn-table">
|
|
<!-- Entry ID -->
|
|
<ng-container matColumnDef="id">
|
|
<th mat-header-cell *matHeaderCellDef mat-sort-header class="txn-th"># ID</th>
|
|
<td mat-cell *matCellDef="let entry" class="txn-td">
|
|
<span class="txn-entry-id">{{ entry.id }}</span>
|
|
</td>
|
|
</ng-container>
|
|
|
|
<!-- Account Type -->
|
|
<ng-container matColumnDef="glAccountType">
|
|
<th mat-header-cell *matHeaderCellDef mat-sort-header class="txn-th">Account Type</th>
|
|
<td mat-cell *matCellDef="let entry" class="txn-td">
|
|
<span class="txn-acct-type-badge" [ngClass]="getAccountTypeBadgeClass(entry.glAccountType?.value)">
|
|
{{ entry.glAccountType?.value }}
|
|
</span>
|
|
</td>
|
|
</ng-container>
|
|
|
|
<!-- Account Code -->
|
|
<ng-container matColumnDef="glAccountCode">
|
|
<th mat-header-cell *matHeaderCellDef mat-sort-header class="txn-th">Code</th>
|
|
<td mat-cell *matCellDef="let entry" class="txn-td">
|
|
<span class="txn-acct-code">{{ entry.glAccountCode }}</span>
|
|
</td>
|
|
</ng-container>
|
|
|
|
<!-- Account Name -->
|
|
<ng-container matColumnDef="glAccountName">
|
|
<th mat-header-cell *matHeaderCellDef mat-sort-header class="txn-th">Account Name</th>
|
|
<td mat-cell *matCellDef="let entry" class="txn-td">
|
|
<span class="txn-acct-name">{{ entry.glAccountName }}</span>
|
|
</td>
|
|
</ng-container>
|
|
|
|
<!-- Savings / CA Account No -->
|
|
<ng-container matColumnDef="savingsAccountNo">
|
|
<th mat-header-cell *matHeaderCellDef class="txn-th">Account No</th>
|
|
<td mat-cell *matCellDef="let entry" class="txn-td">
|
|
<span *ngIf="entry.savingsAccountNo" class="txn-acct-no-chip" [matTooltip]="entry.savingsAccountNo">
|
|
{{ entry.savingsAccountNo }}
|
|
</span>
|
|
<span *ngIf="!entry.savingsAccountNo" class="txn-amount--empty">—</span>
|
|
</td>
|
|
</ng-container>
|
|
|
|
<!-- DR/CR Badge -->
|
|
<ng-container matColumnDef="entryType">
|
|
<th mat-header-cell *matHeaderCellDef class="txn-th txn-th--center">DR / CR</th>
|
|
<td mat-cell *matCellDef="let entry" class="txn-td txn-td--center">
|
|
<span
|
|
class="txn-drcr-badge"
|
|
[ngClass]="entry.entryType?.value === 'DEBIT' ? 'txn-drcr-badge--dr' : 'txn-drcr-badge--cr'"
|
|
>
|
|
{{ entry.entryType?.value === 'DEBIT' ? 'DR' : 'CR' }}
|
|
</span>
|
|
</td>
|
|
</ng-container>
|
|
|
|
<!-- Debit -->
|
|
<ng-container matColumnDef="debit">
|
|
<th mat-header-cell *matHeaderCellDef mat-sort-header class="txn-th txn-th--amount txn-th--dr">
|
|
Debit (Dr)
|
|
</th>
|
|
<td mat-cell *matCellDef="let entry" class="txn-td txn-td--amount">
|
|
@if (entry.entryType?.value === 'DEBIT') {
|
|
<span class="txn-amount txn-amount--dr">
|
|
{{ entry.currency?.displaySymbol || entry.currency?.code }}
|
|
{{ entry.amount | formatNumber }}
|
|
</span>
|
|
} @else {
|
|
<span class="txn-amount--empty">—</span>
|
|
}
|
|
</td>
|
|
</ng-container>
|
|
|
|
<!-- Credit -->
|
|
<ng-container matColumnDef="credit">
|
|
<th mat-header-cell *matHeaderCellDef mat-sort-header class="txn-th txn-th--amount txn-th--cr">
|
|
Credit (Cr)
|
|
</th>
|
|
<td mat-cell *matCellDef="let entry" class="txn-td txn-td--amount">
|
|
@if (entry.entryType?.value === 'CREDIT') {
|
|
<span class="txn-amount txn-amount--cr">
|
|
{{ entry.currency?.displaySymbol || entry.currency?.code }}
|
|
{{ entry.amount | formatNumber }}
|
|
</span>
|
|
} @else {
|
|
<span class="txn-amount--empty">—</span>
|
|
}
|
|
</td>
|
|
</ng-container>
|
|
|
|
<tr mat-header-row *matHeaderRowDef="displayedColumns" class="txn-header-row"></tr>
|
|
<tr
|
|
mat-row
|
|
*matRowDef="let row; columns: displayedColumns"
|
|
(click)="viewJournalEntry(row)"
|
|
class="txn-data-row"
|
|
[ngClass]="row.entryType?.value === 'DEBIT' ? 'txn-row--dr' : 'txn-row--cr'"
|
|
></tr>
|
|
</table>
|
|
</div>
|
|
</mat-card-content>
|
|
</mat-card>
|
|
|
|
<!-- Back Button -->
|
|
<div class="txn-footer-actions">
|
|
<button mat-stroked-button color="primary" (click)="goBack()">
|
|
<fa-icon icon="arrow-left" class="m-r-6"></fa-icon> Back
|
|
</button>
|
|
</div>
|
|
</div>
|
|
}
|
|
|
|
@if (!isJournalEntryLoaded) {
|
|
<div class="txn-no-data">
|
|
<div class="txn-no-data__box">
|
|
<fa-icon icon="search" size="2x" class="txn-no-data__icon"></fa-icon>
|
|
<h4>No Journal Entry Found</h4>
|
|
<p>The transaction you are looking for does not exist or has been removed.</p>
|
|
<button mat-raised-button color="primary" (click)="goBack()">
|
|
<fa-icon icon="arrow-left" class="m-r-6"></fa-icon> Go Back
|
|
</button>
|
|
</div>
|
|
</div>
|
|
}
|