Commit Graph

63 Commits

Author SHA1 Message Date
48eb1b86a9 chore: add .gitignore for Python, Frappe, and editor artifacts
Keeps __pycache__/ and ns_app.egg-info/ out of the working tree status,
plus the usual bench, node, OS, and local-secret noise.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 05:13:02 -04:00
a08a02ad4b feat(statements): top up open late-fee invoices instead of stacking new ones
Statement generation raised a fresh LPF invoice every month, so a customer
who never paid accumulated a pile of small invoices, each carrying the flat
dunning_fee again. Interest was also recomputed from each invoice's due date
every run, re-billing periods already charged for.

Now a customer gets one fee invoice per collections episode:

- While an earlier fee invoice still carries a balance, the next run amends
  it and appends the new period's interest as a further line item, rather
  than creating a second invoice.
- Payment Entries allocated to a partly paid fee invoice are unlinked by the
  cancellation and re-applied to the amended invoice via
  reconcile_against_document (the primitive Payment Reconciliation uses), so
  the outstanding amount and Payment Ledger stay correct.
- Original posting and due dates are carried over. Re-dating to today would
  reset the invoice to Current in the statement's aging buckets and hide how
  long the balance has been owed.
- Interest accrues from the last run, tracked by a new
  custom_late_fee_billed_upto field on Sales Invoice, so no period is billed
  twice. Fee invoices predating the field fall back to their posting date,
  which is when they were billed, so no migration patch is needed.
- The flat dunning_fee is charged once, when a fee invoice is first raised,
  not again on every top-up.
- Unpaid fee invoices are in the interest base on the same terms as any
  other overdue receivable, so interest compounds onto the fee balance.

Amending means cancelling, which is only reversible for links we can
restore. If the open fee invoice has a Journal Entry or credit note applied,
a negative payment allocation, or a posting date in a frozen period, it is
left alone, the charge goes on a new invoice, and the reason is recorded on
the customer's timeline.

Verified against nsi.local with two rolled-back integration probes covering
the amend + re-link path (including two consecutive amendments) and the
blocked-amend fallback.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 06:15:55 -04:00
192af42614 fix(app): add modules.txt/patches.txt so bench keeps ns_app in apps.txt
ns_app was hand-created without the standard scaffolding files, so it was
missing modules.txt and patches.txt. bench's is_frappe_app() check requires
all of hooks.py, modules.txt, and patches.txt to exist; BenchApps.sync()
rebuilds sites/apps.txt from the folders that pass that check. sync() runs on
bench get-app, install-app (of any app), update, etc., so every such command
silently dropped ns_app from apps.txt.

Once ns_app was missing from apps.txt, `bench build --app ns_app` crashed
(esbuild get_public_path returned undefined), the sites/assets/ns_app symlink
was never created, and /assets/ns_app/js/customer_statements.js 404'd. That
left ns_statements undefined, so the Customer "Generate Statements" button's
handler did nothing and the statement screen never opened on production.

Add modules.txt ("NS App", matching app_title), an empty patches.txt, and the
"NS App" module package folder so bench recognizes ns_app as a Frappe app and
stops removing it. Run `bench --site <site> migrate` to register the module.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 10:02:53 -04:00
41d3fec08c fix(statements): raise customer address window 0.5in
Move the customer address window up to 2.5in per printed proof, and
pull the body padding-top back up to match.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 08:20:44 -04:00
ccc3fac69d fix(statements): nudge envelope windows to printed-proof positions
Company (return) address down 0.25in (to 0.8in) and customer address
down 1.5in (to 3.0in) to line up with the #9 double-window envelope,
per a printed proof. Push the body padding-top to clear the lower
customer window.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 08:06:37 -04:00
0aefdeb5ff fix(statements): set envelope geometry for #9 double-window
Reset the statement window positions to the app's proven #9 (9x4)
double-window geometry, mirroring sales_invoice_ns.html — recipient
window at top:1.5in/left:1.125in. It previously copied the dunning
format's 1.9in, which sits too low for a #9. Tighten the body
padding-top to keep clearance below the higher window, and correct the
stale #10 references in the template comment and docs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 07:52:07 -04:00
326adf865a fix(statements): restore Customer list button (merge listview_settings)
The list button was registered by reassigning
frappe.listview_settings['Customer'] in a globally-loaded script, but
ERPNext's own Customer list_js (loaded when the list opens) overwrote it,
so the button never appeared. Register it via doctype_list_js instead —
which Frappe appends after the doctype's own list_js — and merge into the
existing settings (wrapping onload, preserving ERPNext's add_fields)
rather than reassigning. The form button and shared ns_statements helpers
stay in customer_statements.js.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 20:36:21 -04:00
5181f4a177 docs: describe the customer statements feature
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 19:58:09 -04:00
46967208e9 feat(statements): Customer-form button, consolidated UI, disable-fee toggle
Replace customer_list.js with customer_statements.js (loaded globally),
which adds the statement UI to both entry points:
- Customer list: 'Generate Statements' multi-select action.
- Customer form: 'Generate Statement' button for a single customer.

Both open a popup with a 'Generate late payment fee' checkbox (default on)
that maps to generate_statements(skip_late_fee), so fee billing can be
turned off per run. Shared generate/print helpers live on ns_statements.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 19:52:15 -04:00
c20dd18287 feat(statements): untaxed late-fee series, skip-fee option, audit trail
- Bill late-fee invoices under a dedicated naming series (LPF-.YYYY.-),
  registered on Sales Invoice via after_migrate, so they are easy to spot
  and filter.
- Late fees are never taxed: a zero 'Actual' tax line keeps the taxes
  table non-empty so ERPNext skips auto-applying company/item tax
  templates (posts nothing to the ledger). Fee total == computed fee.
- generate_statements(skip_late_fee=...) generates a statement without
  billing a fee.
- Record every generation on the customer's timeline (add_comment) as an
  audit trail, noting Total Due and the fee invoice raised / skipped.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 19:52:03 -04:00
acd7df1129 feat(statements): bill late fee as a collectible Sales Invoice
Charge a late-payment fee when statements are generated so the customer's
receivable reflects it (the gap ERPNext Dunning leaves — it never
increases AR). The fee is billed as a submitted Sales Invoice (item ->
Dunning Type income account, rate = computed fee) rather than a Journal
Entry, so the app's existing payment flow (Run Payment / AutoPay /
multi-invoice) charges and settles it automatically via its Sales Invoice
references — a bare JE would sit uncollected.

Fee schedule/amounts come from the existing Dunning Type settings (yearly
rate_of_interest + flat dunning_fee), interest computed with ERPNext's own
Dunning formula. The fee Item is configured via a new Late Fee Item custom
field on Dunning Type (created in an after_migrate hook; ns_app/setup.py).
Nothing is auto-seeded: generation stops with a clear error if no Dunning
Type is configured or its income account / fee item is unset.

Billing is idempotent per customer/company/month, and prior fee invoices
are excluded from the interest base (no fee-on-fee). The fee invoice shows
on the statement flagged 'late fee', folded into Total Due (which equals
the customer's balance and is fully collectible).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 19:35:04 -04:00
9e2e86cead feat(statements): add Customer list "Generate Statements" button
Register doctype_list_js for Customer and add customer_list.js, which
lists customers with overdue invoices in a selection dialog (checkbox
table + select-all), then calls generate_statements and opens the
printable, one-page-per-customer document in a new window.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 19:08:14 -04:00
75a9c9d154 feat(statements): statement builder + printable envelope template
Add get_statement_data (open invoices, aging buckets, totals, formatted
customer + company addresses) and generate_statements, which renders one
page per customer via a Jinja template and returns a printable HTML
document. Recipient window geometry (top:1.9in/left:1.125in) mirrors the
existing double-window print formats for #10 envelope compatibility; each
page uses page-break-after:always. Late fee is a zero placeholder here.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 19:04:47 -04:00
63bf0b68f5 feat(statements): add overdue-customers query API
Add ns_app/api/statements.py with get_customers_with_overdue_invoices
(one aggregated row per customer with overdue Sales Invoices) plus the
_get_outstanding_invoices / _aging_bucket helpers used to build the
per-customer statement.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 19:01:03 -04:00
790d4d0e9d Merge pull request 'main' (#17) from main into production
Reviewed-on: #17
2026-06-15 14:14:35 +00:00
Ty Reynolds
4e0acde164 Added multiple invoice payment feature. Fixed autopay to run using security key. 2026-06-11 09:36:01 -04:00
Ty Reynolds
4ed575c18f New double window print formats. 2026-06-10 08:20:17 -04:00
Ty Reynolds
5c5ff483a4 auto fill customer after customer creation for every doctype. 2026-06-04 10:01:33 -04:00
Ty Reynolds
7789d13584 Added some fail safes for inconsequential errors. 2026-05-27 13:19:12 -04:00
87f40e6b83 Merge pull request 'add autopay and fix customer entry form.' (#13) from main into production
Reviewed-on: #13
2026-05-18 15:27:57 +00:00
Ty Reynolds
8c4f1f753e Finished sign up for auto pay feature from pay invoice form. Also, added a doc file for the payment flow. 2026-05-15 13:47:35 -04:00
Ty Reynolds
d45e7cbbe1 New invoice print format for new envelopes. 2026-05-11 15:40:04 -04:00
Ty Reynolds
f70c9a9321 Fixed integration error from update. 2026-05-11 08:28:01 -04:00
61ac7f563c Merge pull request 'update customer quick entry form and field descriptions' (#12) from main into production
Reviewed-on: #12
2026-05-07 23:47:50 +00:00
Ty Reynolds
8a4a4b7542 Added field descriptions for acessability mode on customer quick entry form. 2026-05-07 13:04:53 -04:00
Ty Reynolds
f03fa451d0 Fixed customer quick entry rollback error. 2026-05-07 11:46:08 -04:00
f89a672334 Merge pull request 'Added route options to monitor and auto populate fields based off where the quick entry form was spawned from.' (#9) from main into production
Reviewed-on: #9
2026-05-06 09:29:44 +00:00
0aef470e00 Merge branch 'production' into main 2026-05-06 09:28:52 +00:00
Ty Reynolds
b11c75c336 Added route options to monitor and auto populate fields based off where the quick entry form was spawned from. 2026-05-05 08:11:16 -04:00
47f795229a Merge pull request 'Removed duplicate name check.' (#7) from main into production
Reviewed-on: #7
2026-05-04 18:08:36 +00:00
Ty Reynolds
74d3d7f5c1 Removed duplicate name check. 2026-05-04 13:59:05 -04:00
81fa907f86 Merge pull request 'add additional commits' (#6) from main into production
Reviewed-on: #6
2026-04-28 13:37:17 +00:00
Ty Reynolds
4cc7a2aa28 Read me. 2026-04-27 10:22:58 -04:00
Ty Reynolds
e0912a4c60 added missed import. 2026-04-24 11:45:00 -04:00
Ty Reynolds
e7e9fc96c2 Added feature toggle for the check box on custom form. Turned off at the moment so we can push to prod even though this feature is complete. 2026-04-14 09:40:36 -04:00
Ty Reynolds
cc44683972 Changed the opt in for auto pay to be a check box on thecustom form. Improves work flow and is more professional. Added this as checkbox under the zip code entry field. Did no testing on the logic yet. 2026-04-08 08:02:42 -04:00
Ty Reynolds
d082c59bc4 Fixed the Collect JS loading after first instance issue. 2026-03-24 08:38:32 -04:00
Ty Reynolds
ac51c2ce4e Added card holder name and zip to manual payment form. Known bug for loading Collect.Js after 1st instance post cache clear. Working on a fix. 2026-03-23 13:24:59 -04:00
Ty Reynolds
bddbb35541 Sign up customer for autopay? feature when manual payment is ran. Version 1 2026-03-19 08:24:28 -04:00
Ty Reynolds
4e5b4e0a41 Version 1 of Collect.JS no autopay, invoice payment feature. 2026-03-18 15:59:03 -04:00
Ty Reynolds
1ef7e6968c Updated logic for collect checkout to use Security Key. 2026-03-13 09:02:25 -04:00
Ty Reynolds
cd57206be1 added to the collect checkout logic. 2026-03-12 14:24:34 -04:00
Ty Reynolds
961504dd83 payment api updated and running, invoice print format updated. 2026-03-12 10:35:25 -04:00
Ty Reynolds
aefb2d4cc6 Finished Quick Entry 2026-02-13 10:59:38 -05:00
Ty Reynolds
4357ce7fcc Updated invoice and autopay logic. 2026-02-10 08:42:57 -05:00
Ty Reynolds
75600fdbeb Custom Quick entry version 1 done. Zip API done and Customer backend added. 2026-02-09 15:03:17 -05:00
Ty Reynolds
a636cdd9fa Upgraded the autopay functions and finished up the iframe. 2026-02-02 08:08:00 -05:00
Ty Reynolds
22d514aa7b Made date bold like the other item headers. 2026-01-29 10:06:35 -05:00
Ty Reynolds
143c9b98e6 updated conditional balance due color in invoice print format 2026-01-27 11:29:14 -05:00
Ty Reynolds
372167f6fd updated quote print format 2026-01-22 09:41:05 -05:00