Add ERPNext push logic for invoices and payments (Phase F2)

- ErpnextSink implements InvoiceSink. Pushing an issued invoice ensures the Customer and Address (read-back of the returned name, local PIN/state/GSTIN checks), creates the Sales Invoice, optionally submits it in a second call, and attaches the archived PDF as a private file once.
- Idempotent: a mirrored-name 409 is compared by grand total (same total records as synced, different total is a conflict and never overwrites); series mode looks up an existing document by its remarks before re-posting; an identical re-push sends nothing.
- Create and submit are separate calls so a draft survives a failed submit. A failed push is recorded in erpnext_sync with a readable message. Bulk push never aborts on one failing row.
- Payment push requires a submitted parent, maps TDS to Payment Entry deductions and is idempotent through the stored entry name.
- Migration M11 rebuilds erpnext_sync to allow a conflict status and adds attachment_sha256.
- Commands for single and bulk push, payment push, sync status and the open-in-ERPNext URL, with typed wrappers. No UI yet.

Tested against a mock server only. The Payment Entry deductions fields, the series-mode submit route and the payment amount semantics need a live ERPNext check.
This commit is contained in:
2026-10-04 20:33:45 +05:30
parent 86b4d436fa
commit d3b5fbb1b8
12 changed files with 2356 additions and 18 deletions
+2 -1
View File
@@ -1,7 +1,7 @@
//! ERPNext / Frappe integration. All HTTP runs in Rust (see `client`); `mapping` is pure and
//! snapshot-tested; `config` owns the stored settings and the API secret.
// Parts of this module are consumed by the push commands in the next step.
// Parts of this module are only used by the later UI step.
#![allow(dead_code)]
pub mod client;
@@ -9,6 +9,7 @@ pub mod config;
pub mod discovery;
pub mod errors;
pub mod mapping;
pub mod push;
#[cfg(test)]
pub(crate) mod testutil;