Share Products: - "Share Reference (Cash Vault or Suspense)" removed from the accounting step (create, edit, preview and the read-only view tab) -- every register now books a share transaction against the account the money actually reached, so no product needs a placeholder asset mapping any more. The field is now optional server-side, not required. Cashier: - Removed the Share Collection screen and its three service methods. It read m_share_account.cashier_collected, a column that has never existed in this schema, and no other screen ever called it -- it could not have produced a working entry. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
385 lines
16 KiB
TypeScript
385 lines
16 KiB
TypeScript
/**
|
|
* 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/.
|
|
*/
|
|
|
|
import { NgModule } from '@angular/core';
|
|
import { Routes, RouterModule } from '@angular/router';
|
|
|
|
import { Route } from '../core/route/route.service';
|
|
import { CashierOnlyGuard } from '../core/authentication/cashier-only.guard';
|
|
import { CashExchangeEntryComponent } from './cash-exchange-entry/cash-exchange-entry.component';
|
|
import { CurrencyExchangeDetailComponent } from './currency-exchange-detail/currency-exchange-detail.component';
|
|
import { CashTransferComponent } from './cash-transfer/cash-transfer.component';
|
|
import { CashSettlementComponent } from './cash-settlement/cash-settlement.component';
|
|
import { InterCashierTransferComponent } from './inter-cashier-transfer/inter-cashier-transfer.component';
|
|
import { CashierPositionComponent } from './cashier-position/cashier-position.component';
|
|
import { ReconciliationComponent } from './reconciliation/reconciliation.component';
|
|
import { CashHistoryComponent } from './cash-history/cash-history.component';
|
|
import { DayBookComponent } from './day-book/day-book.component';
|
|
import { VoucherReportComponent } from './voucher-report/voucher-report.component';
|
|
import { BranchDayComponent } from './branch-day/branch-day.component';
|
|
import { VaultPositionComponent } from './vault-position/vault-position.component';
|
|
import { AccountRegisterComponent } from './account-register/account-register.component';
|
|
import { ClosingCashReportComponent } from './closing-cash-report/closing-cash-report.component';
|
|
import { ExcessTransactionReportComponent } from './excess-transaction-report/excess-transaction-report.component';
|
|
|
|
const routes: Routes = [
|
|
Route.withShell([
|
|
{
|
|
path: 'clerk',
|
|
data: { title: 'Clerk', breadcrumb: 'Clerk' },
|
|
children: [
|
|
{
|
|
path: 'dashboard',
|
|
loadComponent: () =>
|
|
import('../clerk/clerk-dashboard/clerk-dashboard.component').then((m) => m.ClerkDashboardComponent),
|
|
data: { title: 'Clerk Dashboard', breadcrumb: 'Clerk Dashboard' }
|
|
},
|
|
{
|
|
path: 'withdrawal-request',
|
|
loadComponent: () =>
|
|
import('../clerk/withdrawal-request/withdrawal-request.component').then(
|
|
(m) => m.WithdrawalRequestComponent
|
|
),
|
|
data: { title: 'New Withdrawal Request', breadcrumb: 'New Withdrawal Request', mode: 'create' }
|
|
},
|
|
{
|
|
path: 'withdrawal-request/:id',
|
|
loadComponent: () =>
|
|
import('../clerk/withdrawal-edit/withdrawal-edit.component').then((m) => m.WithdrawalEditComponent),
|
|
data: { title: 'Edit Withdrawal Request', breadcrumb: 'Edit Withdrawal Request' }
|
|
},
|
|
{
|
|
path: 'withdrawal-requests',
|
|
loadComponent: () => import('../clerk/my-requests/my-requests.component').then((m) => m.MyRequestsComponent),
|
|
data: { title: 'My Withdrawal Requests', breadcrumb: 'My Withdrawal Requests' }
|
|
},
|
|
{
|
|
path: 'my-disbursement-requests',
|
|
loadComponent: () =>
|
|
import('../clerk/my-disbursement-requests/my-disbursement-requests.component').then(
|
|
(m) => m.MyDisbursementRequestsComponent
|
|
),
|
|
data: { title: 'Disbursements & Transfer', breadcrumb: 'Disbursements & Transfer' }
|
|
},
|
|
{
|
|
path: 'share-withdrawal-request',
|
|
loadComponent: () =>
|
|
import('../clerk/share-withdrawal-request/share-withdrawal-request.component').then(
|
|
(m) => m.ShareWithdrawalRequestComponent
|
|
),
|
|
data: { title: 'New Share Withdrawal Request', breadcrumb: 'New Share Withdrawal Request', mode: 'create' }
|
|
},
|
|
{
|
|
path: 'share-withdrawal-requests',
|
|
loadComponent: () =>
|
|
import('../clerk/my-share-withdrawal-requests/my-share-withdrawal-requests.component').then(
|
|
(m) => m.MyShareWithdrawalRequestsComponent
|
|
),
|
|
data: { title: 'My Share Requests', breadcrumb: 'My Share Requests' }
|
|
},
|
|
{
|
|
path: 'closure-requests',
|
|
loadComponent: () =>
|
|
import('../clerk/my-closure-requests/my-closure-requests.component').then(
|
|
(m) => m.MyClosureRequestsComponent
|
|
),
|
|
data: { title: 'My Closure Requests', breadcrumb: 'My Closure Requests' }
|
|
},
|
|
{
|
|
path: 'account-facilities',
|
|
loadComponent: () =>
|
|
import('../clerk/account-facilities/account-facilities.component').then(
|
|
(m) => m.AccountFacilitiesComponent
|
|
),
|
|
data: { title: 'Account Facilities', breadcrumb: 'Account Facilities' }
|
|
},
|
|
{
|
|
path: 'account-facilities/cheque-book',
|
|
loadComponent: () =>
|
|
import('../clerk/cheque-book/cheque-book.component').then((m) => m.ClerkChequeBookComponent),
|
|
data: { title: 'Cheque Book', breadcrumb: 'Cheque Book' }
|
|
},
|
|
{
|
|
path: 'account-facilities/cheque-book/:id',
|
|
loadComponent: () =>
|
|
import('../clerk/cheque-book/cheque-book-detail.component').then((m) => m.ClerkChequeBookDetailComponent),
|
|
data: { title: 'Cheque Book Detail', breadcrumb: 'Cheque Book Detail' }
|
|
},
|
|
{
|
|
path: 'photo-signature',
|
|
loadComponent: () =>
|
|
import('../clerk/photo-signature/photo-signature.component').then((m) => m.PhotoSignatureComponent),
|
|
data: { title: 'Photo & Signature', breadcrumb: 'Photo & Signature' }
|
|
},
|
|
// The same register the cashier screens use, without their cash-only filter: a clerk raises
|
|
// book entries — a closure settled to savings, a share redemption, an interest posting the
|
|
// system made on their approval — and cash-only would hide exactly those.
|
|
{
|
|
path: 'received-report',
|
|
component: VoucherReportComponent,
|
|
data: {
|
|
title: 'Received Vouchers',
|
|
breadcrumb: 'Received Vouchers',
|
|
side: 'CR',
|
|
cashOnly: false,
|
|
cashiersOnly: true
|
|
}
|
|
},
|
|
{
|
|
path: 'payment-report',
|
|
component: VoucherReportComponent,
|
|
data: {
|
|
title: 'Payment Vouchers',
|
|
breadcrumb: 'Payment Vouchers',
|
|
side: 'DR',
|
|
cashOnly: false,
|
|
cashiersOnly: true
|
|
}
|
|
},
|
|
// Both sides in one list, scoped to whoever is signed in — the "what did I do today"
|
|
// question the two registers above cannot answer, because an approval raises no voucher.
|
|
{
|
|
path: 'my-work',
|
|
loadComponent: () => import('../cashier/my-work/my-work.component').then((m) => m.MyWorkComponent),
|
|
data: { title: 'My Vouchers', breadcrumb: 'My Vouchers' }
|
|
},
|
|
{
|
|
path: 'end-of-day',
|
|
loadComponent: () =>
|
|
import('../cashier/end-of-day-report/end-of-day-report.component').then((m) => m.EndOfDayReportComponent),
|
|
data: { title: 'End of the Day Report', breadcrumb: 'End of the Day Report' }
|
|
},
|
|
// The system's own postings — interest, charges, escheats, journals. Both sides in one
|
|
// list: a night's run credits and debits, and splitting it across two screens would hide
|
|
// half of it from whichever one you opened.
|
|
{
|
|
path: 'system-vouchers',
|
|
component: VoucherReportComponent,
|
|
data: {
|
|
title: 'System Vouchers',
|
|
breadcrumb: 'System Vouchers',
|
|
side: 'ALL',
|
|
cashOnly: false,
|
|
cashiersOnly: false,
|
|
systemOnly: true
|
|
}
|
|
},
|
|
{
|
|
path: 'standing-instructions',
|
|
loadComponent: () =>
|
|
import('../clerk/standing-instructions/standing-instructions.component').then(
|
|
(m) => m.StandingInstructionsComponent
|
|
),
|
|
data: { title: 'Standing Instructions', breadcrumb: 'Standing Instructions' }
|
|
},
|
|
{
|
|
path: 'today-activity',
|
|
loadComponent: () =>
|
|
import('../clerk/today-activity/today-activity.component').then((m) => m.TodayActivityComponent),
|
|
data: { title: "Today's Activity", breadcrumb: "Today's Activity" }
|
|
},
|
|
{
|
|
path: 'dividend-dashboard',
|
|
loadComponent: () =>
|
|
import('../clerk/dividend-dashboard/dividend-dashboard.component').then(
|
|
(m) => m.DividendDashboardComponent
|
|
),
|
|
data: { title: 'Dividend Dashboard', breadcrumb: 'Dividend Dashboard' }
|
|
}
|
|
]
|
|
},
|
|
{
|
|
// Branch-level operations, kept out of the cashier tree on purpose: opening and closing the
|
|
// day governs the whole branch, and is not something a cashier does.
|
|
path: 'branch-ops',
|
|
data: { title: 'Branch Operations', breadcrumb: 'Branch Operations' },
|
|
children: [
|
|
{
|
|
path: 'day-register',
|
|
component: BranchDayComponent,
|
|
data: { title: 'Day Open / Day Close', breadcrumb: 'Day Register' }
|
|
},
|
|
{
|
|
// Same screen, narrowed to the days that were never closed. A separate route rather than
|
|
// a second link to the register, so choosing it visibly does something.
|
|
path: 'missed-close',
|
|
component: BranchDayComponent,
|
|
data: { title: 'Missed Close Register', breadcrumb: 'Missed Close', missedOnly: true }
|
|
},
|
|
{
|
|
// Lives here alongside the day register because both answer "what did this branch look
|
|
// like on that date"; it is linked from the Reports menu, where people go to find it.
|
|
path: 'account-register',
|
|
component: AccountRegisterComponent,
|
|
data: { title: 'Account Register', breadcrumb: 'Account Register' }
|
|
},
|
|
{
|
|
// Same branch-level scope as the day register: this is the vault's own stock, not any
|
|
// one cashier's float, so it sits with the other branch-wide screens.
|
|
path: 'vault-position',
|
|
component: VaultPositionComponent,
|
|
data: { title: 'Vault Position', breadcrumb: 'Vault Position' }
|
|
},
|
|
{
|
|
// The same day book the clerk and cashier read, reached from where a principal officer
|
|
// signing the day off actually looks. It carries the Officer Verification Sheet — each
|
|
// person's own credit, debit and net — which is the branch-level reading of the day, not
|
|
// a counter one.
|
|
path: 'day-book',
|
|
component: DayBookComponent,
|
|
data: { title: 'Day Book & Verification', breadcrumb: 'Day Book' }
|
|
},
|
|
// The one sheet the day is signed off on: the transaction summary, the drawer counted
|
|
// against the books, and the notes that say why it cannot be closed yet.
|
|
{
|
|
path: 'end-of-day',
|
|
loadComponent: () =>
|
|
import('./end-of-day-report/end-of-day-report.component').then((m) => m.EndOfDayReportComponent),
|
|
data: { title: 'End of the Day Report', breadcrumb: 'End of the Day Report' }
|
|
}
|
|
]
|
|
},
|
|
{
|
|
path: 'cashier',
|
|
data: { title: 'Cashier', breadcrumb: 'Cashier' },
|
|
children: [
|
|
{
|
|
path: 'cash-exchange',
|
|
component: CashExchangeEntryComponent,
|
|
data: { title: 'Cash Exchange Entry', breadcrumb: 'Cash Exchange Entry' }
|
|
},
|
|
{
|
|
path: 'exchange-detail',
|
|
component: CurrencyExchangeDetailComponent,
|
|
data: { title: 'Currency Exchange Detail', breadcrumb: 'Currency Exchange Detail' }
|
|
},
|
|
{
|
|
path: 'cash-transfer',
|
|
component: CashTransferComponent,
|
|
data: { title: 'Cash Transfer', breadcrumb: 'Cash Transfer' }
|
|
},
|
|
{
|
|
path: 'cash-settlement',
|
|
component: CashSettlementComponent,
|
|
data: { title: 'Cash Settlement', breadcrumb: 'Cash Settlement' }
|
|
},
|
|
{
|
|
path: 'inter-cashier',
|
|
component: InterCashierTransferComponent,
|
|
data: { title: 'Inter-Cashier Transfer', breadcrumb: 'Inter-Cashier Transfer' }
|
|
},
|
|
{
|
|
path: 'cashier-position',
|
|
component: CashierPositionComponent,
|
|
data: { title: 'Cashier Position', breadcrumb: 'Cashier Position' }
|
|
},
|
|
{
|
|
path: 'reconciliation',
|
|
component: ReconciliationComponent,
|
|
data: { title: 'Reconciliation', breadcrumb: 'Reconciliation' }
|
|
},
|
|
{
|
|
path: 'cash-history',
|
|
component: CashHistoryComponent,
|
|
data: { title: 'Cash History', breadcrumb: 'Cash History' }
|
|
},
|
|
{
|
|
path: 'day-book',
|
|
component: DayBookComponent,
|
|
data: { title: 'Day Book', breadcrumb: 'Day Book' }
|
|
},
|
|
// The register is read one side at a time: Received is the CR side, Payment the DR side.
|
|
// Same component both times — the side it shows comes from `data.side`.
|
|
//
|
|
// `cashOnly` is what makes these the cashier's own registers rather than the whole voucher
|
|
// book: a cashier deals in notes and coins, so only vouchers that actually moved cash
|
|
// belong here, and the two reports then reconcile against the drawer and the closing
|
|
// report. A back-office screen wanting every voucher points at the same component without
|
|
// the flag.
|
|
{
|
|
path: 'received-report',
|
|
component: VoucherReportComponent,
|
|
data: {
|
|
title: 'Received Report',
|
|
breadcrumb: 'Received Report',
|
|
side: 'CR',
|
|
cashOnly: true,
|
|
cashiersOnly: true
|
|
}
|
|
},
|
|
{
|
|
path: 'payment-report',
|
|
component: VoucherReportComponent,
|
|
data: {
|
|
title: 'Payment Report',
|
|
breadcrumb: 'Payment Report',
|
|
side: 'DR',
|
|
cashOnly: true,
|
|
cashiersOnly: true
|
|
}
|
|
},
|
|
{
|
|
path: 'my-work',
|
|
loadComponent: () => import('./my-work/my-work.component').then((m) => m.MyWorkComponent),
|
|
data: { title: 'My Vouchers', breadcrumb: 'My Vouchers' }
|
|
},
|
|
{
|
|
path: 'closing-report',
|
|
component: ClosingCashReportComponent,
|
|
data: { title: 'Closing Cash Report', breadcrumb: 'Closing Cash Report' }
|
|
},
|
|
{
|
|
path: 'end-of-day',
|
|
loadComponent: () =>
|
|
import('./end-of-day-report/end-of-day-report.component').then((m) => m.EndOfDayReportComponent),
|
|
data: { title: 'End of the Day Report', breadcrumb: 'End of the Day Report' }
|
|
},
|
|
{
|
|
path: 'excess-report',
|
|
component: ExcessTransactionReportComponent,
|
|
data: { title: 'Excess Transaction Report', breadcrumb: 'Excess Transaction Report' }
|
|
},
|
|
{
|
|
path: 'transaction-entry',
|
|
canActivate: [CashierOnlyGuard],
|
|
loadComponent: () =>
|
|
import('./transaction-entry/transaction-entry.component').then((m) => m.TransactionEntryComponent),
|
|
data: {
|
|
title: 'Cashier Transaction Entry',
|
|
breadcrumb: 'Transaction Entry',
|
|
hideShellLayout: true
|
|
}
|
|
},
|
|
{
|
|
path: 'my-deposit-requests',
|
|
loadComponent: () =>
|
|
import('./my-deposit-requests/my-deposit-requests.component').then((m) => m.MyDepositRequestsComponent),
|
|
data: { title: 'My Deposit Requests', breadcrumb: 'My Deposit Requests' }
|
|
},
|
|
{
|
|
path: 'cash-clearing-transfer-summary',
|
|
loadComponent: () =>
|
|
import('./cash-clearing-transfer-summary/cash-clearing-transfer-summary.component').then(
|
|
(m) => m.CashClearingTransferSummaryComponent
|
|
),
|
|
data: {
|
|
title: 'Cash / Clearing / Transfer - Summary Report',
|
|
breadcrumb: 'Cash / Clearing / Transfer - Summary Report'
|
|
}
|
|
}
|
|
]
|
|
}
|
|
])
|
|
];
|
|
|
|
@NgModule({
|
|
imports: [RouterModule.forChild(routes)],
|
|
exports: [RouterModule]
|
|
})
|
|
export class CashierRoutingModule {}
|