feat(accounting): incoming-invoices inbox with camera capture + triage/re-bill
Adds the Accounting → Incoming invoices frontend on top of the existing /api/admin/expenses backend: - accounting.service.ts: typed client (inbound upload/list/get/update/ categorize, expense list, re-bill, supplier-payment, categories). - AccountingInboxPage: capture a supplier invoice via the device CAMERA (<input accept="image/*" capture="environment">) or a PDF/image upload; inbox list with status badges + parsed summary; a triage modal to confirm fields and pick a disposition (re-bill / pass-through / company expense / duplicate / declined). Re-bill uses the customer picker and mints an editable scheduled invoice (chains categorize -> rebill). - AccountingLayout: "Incoming invoices" sub-nav item + AccountingIndex that redirects /admin/accounting to the first enabled sub-feature. - App.tsx: /admin/accounting/inbox route (gated by incomingInvoices). - i18n: accounting.inbox/disposition/markup + subnav.incomingInvoices + common.saving (EN + DE, DE authored natively). Camera capture needs no native app — the mobile web input drives the device camera straight into the upload endpoint. OCR/QR auto-extraction is still a backend follow-up (extractionService is a no-op), so fields are confirmed manually in the triage modal for now. Verified: npm run build green; en/de JSON valid.
This commit is contained in:
@@ -64,7 +64,8 @@ import {
|
||||
import { CustomerAuthProvider } from './contexts/CustomerAuthContext';
|
||||
import { AdminLayout, AdminAuthWrapper } from './components/admin';
|
||||
import { ClientsLayout } from './components/admin/ClientsLayout';
|
||||
import { AccountingLayout } from './components/admin/AccountingLayout';
|
||||
import { AccountingLayout, AccountingIndex } from './components/admin/AccountingLayout';
|
||||
import { AccountingInboxPage } from './pages/admin/accounting/AccountingInboxPage';
|
||||
import { RequireFeature } from './components/admin/RequireFeature';
|
||||
import { PageErrorBoundary, OfflineIndicator, SkipLink, DynamicFavicon, RobotsMetaTags, CMSContentBlock, Loading } from './components/common';
|
||||
import { MaintenanceWrapper } from './components/MaintenanceWrapper';
|
||||
@@ -268,10 +269,13 @@ function App() {
|
||||
pages. Each sub-route is independently flagged. */}
|
||||
<Route element={<RequireFeature flag="accounting" />}>
|
||||
<Route path="accounting" element={<AccountingLayout />}>
|
||||
<Route element={<RequireFeature flag="incomingInvoices" />}>
|
||||
<Route path="inbox" element={<AccountingInboxPage />} />
|
||||
</Route>
|
||||
<Route element={<RequireFeature flag="taxReport" />}>
|
||||
<Route path="tax-report" element={<TaxReportPage />} />
|
||||
</Route>
|
||||
<Route index element={<Navigate to="/admin/accounting/tax-report" replace />} />
|
||||
<Route index element={<AccountingIndex />} />
|
||||
</Route>
|
||||
</Route>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user