django_program.registration.services.purchase_orders

Service layer for purchase order lifecycle management.

Handles PO creation, payment recording, credit note issuance, status transitions, cancellation, and invoice PDF generation. All state-mutating functions use atomic transactions to maintain consistency.

Functions

cancel_purchase_order(purchase_order)

Cancel a purchase order.

create_purchase_order(*, conference, ...[, ...])

Create a purchase order with line items and computed totals.

generate_invoice_pdf(purchase_order)

Generate a professional invoice PDF for a purchase order.

generate_po_reference()

Generate a unique PO reference like PO-A1B2C3.

issue_credit_note(purchase_order, *, amount, ...)

Issue a credit note against a purchase order and update its status.

record_payment(purchase_order, *, amount, method)

Record a payment against a purchase order and update its status.

send_purchase_order(purchase_order)

Transition a draft purchase order to sent status.

update_po_status(purchase_order)

Recompute and save the PO status based on payments and credits.

django_program.registration.services.purchase_orders.generate_po_reference()[source]

Generate a unique PO reference like PO-A1B2C3.

Uses cryptographically random alphanumeric characters. Retries on collision up to a fixed limit.

Return type:

str

Returns:

A unique reference string.

Raises:

RuntimeError – If a unique reference cannot be generated after multiple attempts.

django_program.registration.services.purchase_orders.create_purchase_order(*, conference, organization_name, contact_email, contact_name, billing_address='', line_items, notes='', created_by=None)[source]

Create a purchase order with line items and computed totals.

Each entry in line_items should be a dict with keys: description, quantity, unit_price, and optionally ticket_type and addon (model instances or None).

Parameters:
  • conference (Conference) – The conference this PO belongs to.

  • organization_name (str) – Name of the purchasing organization.

  • contact_email (str) – Primary contact email address.

  • contact_name (str) – Primary contact person name.

  • billing_address (str) – Optional billing address text.

  • line_items (list[dict[str, object]]) – List of line item dicts to create.

  • notes (str) – Optional internal notes.

  • created_by (AbstractBaseUser | None) – The staff user creating the PO.

Return type:

PurchaseOrder

Returns:

The newly created PurchaseOrder with line items.

Raises:
  • RuntimeError – If a unique reference cannot be generated.

  • IntegrityError – On reference collision (extremely unlikely).

django_program.registration.services.purchase_orders.record_payment(purchase_order, *, amount, method, reference='', payment_date, entered_by=None, note='')[source]

Record a payment against a purchase order and update its status.

Parameters:
  • purchase_order (PurchaseOrder) – The PO to record payment against.

  • amount (Decimal) – Payment amount (must be positive).

  • method (str) – Payment method (one of PurchaseOrderPayment.Method values).

  • reference (str) – Optional external reference (e.g. wire transfer ID).

  • payment_date (date) – Date the payment was received.

  • entered_by (AbstractBaseUser | None) – Staff user recording the payment.

  • note (str) – Optional note about the payment.

Return type:

PurchaseOrderPayment

Returns:

The newly created PurchaseOrderPayment.

Raises:

ValueError – If the amount is not positive, the method is invalid, or the PO is cancelled.

django_program.registration.services.purchase_orders.issue_credit_note(purchase_order, *, amount, reason, issued_by=None)[source]

Issue a credit note against a purchase order and update its status.

Parameters:
  • purchase_order (PurchaseOrder) – The PO to issue a credit note against.

  • amount (Decimal) – Credit note amount (must be positive).

  • reason (str) – Explanation for the credit.

  • issued_by (AbstractBaseUser | None) – Staff user issuing the credit note.

Return type:

PurchaseOrderCreditNote

Returns:

The newly created PurchaseOrderCreditNote.

Raises:

ValueError – If the amount is not positive or the PO is cancelled.

django_program.registration.services.purchase_orders.update_po_status(purchase_order)[source]

Recompute and save the PO status based on payments and credits.

Status transitions: - draft remains if no payments/credits and status is draft - paid when balance_due is exactly zero and financial activity exists - overpaid when balance_due is negative - partially_paid when some payments exist but balance remains - sent when no payments exist and status is not draft

This function expects the caller to hold a row-level lock on the PO (via select_for_update) when called inside a transaction.

Parameters:

purchase_order (PurchaseOrder) – The PO whose status should be recomputed.

Return type:

None

django_program.registration.services.purchase_orders.send_purchase_order(purchase_order)[source]

Transition a draft purchase order to sent status.

Parameters:

purchase_order (PurchaseOrder) – The PO to mark as sent.

Raises:

ValueError – If the PO is not in draft status.

Return type:

None

django_program.registration.services.purchase_orders.cancel_purchase_order(purchase_order)[source]

Cancel a purchase order.

Sets the status to CANCELLED. Does not delete any associated payment or credit note records for audit purposes.

Parameters:

purchase_order (PurchaseOrder) – The PO to cancel.

Return type:

None

django_program.registration.services.purchase_orders.generate_invoice_pdf(purchase_order)[source]

Generate a professional invoice PDF for a purchase order.

Produces a PDF with conference letterhead, billing details, line items, financial summary, and payment history. Uses the same reportlab pattern as the invitation letter generator.

Parameters:

purchase_order (PurchaseOrder) – The purchase order to generate an invoice for.

Return type:

bytes

Returns:

The raw PDF bytes.