Add the ERPNext settings UI, History and detail controls, and voiced.invoice.v1 export (Phase F3)
- Settings gets an Integrations tab: connection fields with a write-only API secret, a CA PEM option, Test connection with versions and warnings, live mapping lists loaded after a test, naming mode, toggles and its own Save. - Pushing for a GST-registered vendor is refused until a connection test result is stored. The UI shows the reason and disables Send. - History shows an ERPNext status column from one bulk call, a row Send and Open in ERPNext, and a bulk Send selected with a draft-or-submit confirm step and per-row results that never abort the batch. The detail view gets an ERPNext panel and per-payment send. All of it is hidden or disabled when the integration is not configured. - Optional auto-push after issue runs in the background and never blocks or fails the issue. - Generic export: a deterministic voiced.invoice.v1 JSON (and CSV line items) for selected invoices, documented in docs/voiced-invoice-v1.md with a golden test. Money is integer paise plus a decimal string. The new screens have not been run in a webview yet. The ERPNext Payment Entry deduction fields still need a live check.
This commit is contained in:
@@ -10,6 +10,7 @@ import InvoiceDetail from "./views/InvoiceDetail";
|
||||
import Clients from "./views/Clients";
|
||||
import { ToastProvider, useToast } from "./components/ToastProvider";
|
||||
import AppSettings from "./views/AppSettings";
|
||||
import { requestSettingsTab } from "./lib/settingsTab";
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
@@ -89,6 +90,11 @@ function AppInner() {
|
||||
);
|
||||
}
|
||||
|
||||
const openIntegrations = () => {
|
||||
requestSettingsTab("integrations");
|
||||
setView("settings");
|
||||
};
|
||||
|
||||
const toggleTheme = async () => {
|
||||
const next: Settings = { ...settings, theme: settings.theme === "g100" ? "g10" : "g100" };
|
||||
setSettings(next);
|
||||
@@ -115,6 +121,7 @@ function AppInner() {
|
||||
<InvoiceHistory
|
||||
settings={settings}
|
||||
active={view === "invoices"}
|
||||
onOpenSettings={openIntegrations}
|
||||
onOpen={(id) => {
|
||||
setDetailId(id);
|
||||
setView("invoice");
|
||||
@@ -127,6 +134,7 @@ function AppInner() {
|
||||
invoiceId={detailId}
|
||||
settings={settings}
|
||||
onBack={() => setView("invoices")}
|
||||
onOpenSettings={openIntegrations}
|
||||
onDuplicated={(draftId) => {
|
||||
setOpenDraftId(draftId);
|
||||
setView("new");
|
||||
|
||||
Reference in New Issue
Block a user