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:
@@ -59,6 +59,7 @@ import { SHORTCUT_LIST, useShortcuts } from "../hooks/useShortcuts";
|
||||
import PageSetupControls from "../components/PageSetupControls";
|
||||
import { useInvoicePdf } from "../hooks/useInvoicePdf";
|
||||
import { useInvoiceExport } from "../hooks/useInvoiceExport";
|
||||
import { autoPushIssued } from "../lib/erpnextUi";
|
||||
import ExportButton, { ExportModePicker } from "../components/ExportButton";
|
||||
import ExportFeedback from "../components/ExportFeedback";
|
||||
import { createExportDeps } from "../lib/exportDeps";
|
||||
@@ -649,6 +650,16 @@ export default function NewInvoice({ settings, onSettingsChange, active, onActiv
|
||||
detachDraft();
|
||||
api.listClients().then(setClients).catch(() => {});
|
||||
void refreshDrafts();
|
||||
// "Send on issue": in the background, so it never delays or fails the issue; the result arrives as a toast.
|
||||
void autoPushIssued(
|
||||
{ getConfig: api.erpnextGetConfig, push: api.erpnextPushInvoice },
|
||||
outcome.invoice,
|
||||
settings.gstRegistration !== "unregistered",
|
||||
)
|
||||
.then((note) => {
|
||||
if (note) toast[note.kind](note.title, note.subtitle);
|
||||
})
|
||||
.catch(() => {});
|
||||
// Unarchived invoices export by rendering and archiving first (see exportInvoice), so this still works.
|
||||
await exp.start(outcome.invoice, mode, "original");
|
||||
setSaving(false);
|
||||
|
||||
Reference in New Issue
Block a user