Skip to content

feat: add Purchase Orders & Corporate Invoicing (Phase 20)#59

Merged
JacobCoffee merged 7 commits intomainfrom
phase-20/purchase-orders
Mar 19, 2026
Merged

feat: add Purchase Orders & Corporate Invoicing (Phase 20)#59
JacobCoffee merged 7 commits intomainfrom
phase-20/purchase-orders

Conversation

@JacobCoffee
Copy link
Copy Markdown
Owner

@JacobCoffee JacobCoffee commented Mar 19, 2026

Summary

  • Purchase Order models: PurchaseOrder, PurchaseOrderLineItem, PurchaseOrderPayment, PurchaseOrderCreditNote with full lifecycle (draft → sent → partially_paid → paid → overpaid → cancelled)
  • Service layer: Create PO, record payments, issue credit notes, cancel, status transitions with select_for_update() locking and cancelled-PO guards
  • Invoice PDF generation: reportlab-based PDF with conference letterhead, bill-to, line items table, financial summary, payment history (conference-prefixed filenames)
  • Stripe Invoicing integration: Create/send/sync Stripe Invoices from POs (card + ACH), webhook handler for invoice.paid events
  • QuickBooks Online integration: Create/send/sync QBO invoices via REST API (wire + ACH), OAuth token refresh, payment status sync
  • Manage dashboard UI: Full PO management — list with status filters, create form with dynamic line items, detail view with inline payment recording, credit notes, Send PO/Stripe/QBO actions
  • Financial dashboard: PO metrics section with status breakdown, revenue/collected/outstanding stats, payments by method, recent POs table
  • Django Admin: PurchaseOrderAdmin with readonly payment/credit inlines to prevent bypassing service layer
  • Seed data: 8 demo POs covering all lifecycle states (draft, sent, partially paid, fully paid, overpaid, cancelled, credit noted, multi-payment)
  • Tests: 43 tests covering models, services, lifecycle guards, admin, send flow, credit-only settlement

Test plan

  • Run make dev to seed database, visit /manage/<slug>/purchase-orders/ to test CRUD
  • Create a PO, add line items, verify totals compute correctly
  • Record partial and full payments, verify status transitions
  • Issue credit notes, verify balance adjustments
  • Download Invoice PDF, confirm conference name in filename
  • Test "Mark as Sent" button on draft POs
  • Check /manage/<slug>/financial/ for PO metrics section
  • Verify Django admin at /admin/ shows POs with readonly payment/credit inlines
  • make ci passes (only pre-existing flaky badge test fails)

🤖 Generated with Claude Code

JacobCoffee and others added 2 commits March 19, 2026 13:05
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings March 19, 2026 18:14
@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@JacobCoffee JacobCoffee review requested due to automatic review settings March 19, 2026 18:15
JacobCoffee and others added 3 commits March 19, 2026 13:22
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…-only settlement

- Add select_for_update() locking in record_payment/issue_credit_note
- Reject mutations on cancelled POs (ValueError)
- Fix credit-only PO settlement (was stuck in draft/sent)
- Add send_purchase_order() service + Send PO view/URL/button
- Make admin payment/credit inlines read-only
- Add 5 new tests for lifecycle guards and send flow

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…PO metrics

- Invoice PDF generation via reportlab with conference-prefixed filenames
- Stripe Invoicing integration (create/send/sync invoices via Stripe API)
- QuickBooks Online integration via httpx (create/send/sync invoices)
- PO metrics on financial dashboard (status breakdown, revenue, balance, payments by method)
- Conference model: QBO OAuth fields (realm_id, tokens, client credentials)
- PurchaseOrder model: stripe_invoice_id/url and qbo_invoice_id/url fields

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings March 19, 2026 18:45
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a corporate/offline purchase order system to the registration domain, with organizer-facing management UI plus optional integrations for Stripe Invoicing and QuickBooks Online.

Changes:

  • Introduces Purchase Order data model + service layer for lifecycle, payments, credits, and PDF invoice generation.
  • Adds organizer dashboard views/URLs/templates and admin registration for managing purchase orders (plus financial dashboard rollups).
  • Adds new external integration modules for Stripe Invoice creation/sync and QBO invoice creation/sync (and adds httpx dependency + QBO credential fields on Conference).

Reviewed changes

Copilot reviewed 25 out of 26 changed files in this pull request and generated 15 comments.

Show a summary per file
File Description
uv.lock Adds httpx to the locked dependency set.
pyproject.toml Adds httpx>=0.28.1 dependency.
tests/test_registration/test_purchase_orders.py Adds unit/integration tests for PO models + PO service layer.
src/django_program/registration/services/stripe_invoicing.py Implements Stripe Invoice creation + sync + webhook handler for POs.
src/django_program/registration/services/qbo_invoicing.py Implements QBO invoice creation + sync + webhook handler for POs (via httpx).
src/django_program/registration/services/purchase_orders.py Adds PO service layer: create, pay, credit, status updates, cancel, PDF generation.
src/django_program/registration/purchase_order.py Adds PO-related Django models (PO, line items, payments, credit notes).
src/django_program/registration/models.py Re-exports PO models via registration.models and __all__.
src/django_program/registration/migrations/0020_purchaseorder_purchaseordercreditnote_and_more.py Creates the new PO models.
src/django_program/registration/migrations/0021_add_qbo_invoice_fields.py Adds Stripe/QBO invoice ID+URL fields to PurchaseOrder.
src/django_program/registration/admin.py Registers PurchaseOrder admin with inlines for line items/payments/credits.
src/django_program/manage/views_purchase_orders.py Adds organizer dashboard views for listing/creating/managing POs + invoice actions.
src/django_program/manage/urls_purchase_orders.py Adds manage URL routing for PO dashboard endpoints.
src/django_program/manage/urls.py Mounts purchase order URL routes under the manage area.
src/django_program/manage/views_financial.py Adds purchase order rollups to financial dashboard context.
src/django_program/manage/templates/django_program/manage/purchase_order_list.html Adds PO list page template (filtering + table).
src/django_program/manage/templates/django_program/manage/purchase_order_form.html Adds PO creation form template + JS for line items.
src/django_program/manage/templates/django_program/manage/purchase_order_detail.html Adds PO detail page template (line items, payments, credits, actions).
src/django_program/manage/templates/django_program/manage/financial_dashboard.html Displays new PO rollups/sections on financial dashboard.
src/django_program/manage/templates/django_program/manage/base.html Adds collapsible sidebar UI behavior + Purchase Orders nav link.
src/django_program/manage/views.py Extends conference edit to include KPI targets form handling.
src/django_program/manage/forms.py Adds KPITargetsForm.
src/django_program/manage/templates/django_program/manage/conference_edit.html Adds KPI Targets section to conference edit page.
src/django_program/conference/models.py Adds QBO credential/token fields to Conference.
src/django_program/conference/migrations/0010_add_qbo_fields.py Migrates the new QBO fields onto Conference.
examples/seed.py Seeds KPI targets + purchase orders into example dataset.
Comments suppressed due to low confidence (1)

src/django_program/manage/views.py:873

  • ConferenceEditView.post() routes successful submissions through _forms_valid(), which calls super().form_valid(form) and bypasses this class’s form_valid() override (so the success flash message won’t be set). Consider calling self.form_valid(form) (or moving the messaging logic into _forms_valid) so behavior stays consistent.
    def _forms_valid(self, form: ConferenceForm, kpi_form: KPITargetsForm) -> HttpResponse:
        """Save both the conference and KPI targets forms."""
        response = super().form_valid(form)
        kpi = kpi_form.save(commit=False)
        kpi.conference = self.conference
        kpi.save()
        return response

    def form_valid(self, form: ConferenceForm) -> HttpResponse:
        """Save the form and add a success message.

        Args:
            form: The validated conference form.

        Returns:
            A redirect response to the success URL.
        """
        messages.success(self.request, "Conference updated successfully.")
        return super().form_valid(form)

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/django_program/manage/views.py
Comment thread src/django_program/registration/services/qbo_invoicing.py
Comment thread src/django_program/registration/services/stripe_invoicing.py
Comment thread src/django_program/registration/services/qbo_invoicing.py
Comment thread src/django_program/manage/views_financial.py Outdated
Comment thread src/django_program/manage/views_purchase_orders.py
Comment thread src/django_program/registration/services/stripe_invoicing.py Outdated
Comment thread src/django_program/manage/views_purchase_orders.py
Comment thread src/django_program/manage/templates/django_program/manage/base.html Outdated
- Validate amount > 0 and method in record_payment/issue_credit_note
- Use conference currency_symbol in invoice PDF instead of hardcoded $
- Wrap handle_invoice_paid_webhook in transaction.atomic()
- Cap QBO webhook sync to 50 POs to prevent timeouts
- Fix PO balance_outstanding to subtract credits and exclude cancelled POs
- Add today to detail view context for payment date default
- Fix double-minus display bug in list template for negative balances
- Annotate total_paid/total_credited in list view and admin to avoid N+1
- PurchaseOrder properties use annotations when available
- Add sidebar accessibility (role=button, tabindex, aria-expanded, keydown)
- Add 81 new tests: view tests, Stripe invoicing tests, QBO invoicing tests

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@JacobCoffee JacobCoffee merged commit fe996d1 into main Mar 19, 2026
11 of 13 checks passed
@JacobCoffee JacobCoffee deleted the phase-20/purchase-orders branch March 19, 2026 19:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants