Skip to content

Add attachment support (upload & download) for bills, invoices, and payments #3

Description

@AsyncAlchemist

Summary

Akaunting stores uploaded attachments (scanned/PDF/image copies of bills & invoices) permanently via plank/laravel-mediable under storage/app/uploads, tracked in the media / mediables tables. The REST API accepts these as a multipart attachment field on documents (invoice/bill, create & update) and transactions (payments, create & update). akt currently sends application/json only, so it can neither upload nor download attachments.

Motivation

Operators want to attach the source document (supplier bill PDF, receipt photo) to a bill/invoice/payment — including payments/transactions that already exist — and retrieve it later, entirely from the CLI.

Proposed CLI

Upload (repeatable for multiple files):

  • akt bill create --contact 11 --item '...' --attachment ./supplier-bill.pdf
  • akt invoice create ... --attachment ./scan.png
  • akt payment create --bill 3 --attachment ./receipt.pdf
  • akt bill update <id> --attachment ./new.pdf
  • akt payment update <id> --attachment ./receipt.pdf # attach to an EXISTING transaction
  • akt bill update <id> --remove-attachment # clears existing attachment(s)
  • akt payment update <id> --remove-attachment

List / download:

  • akt bill attachments <id> # list attached files (id, name, size)
  • akt bill download-attachment <id> [--out <dir>] # download to disk
  • (same for invoice and payment)

Implementation notes (from Akaunting 3.1.x source)

  • Upload is multipart/form-data; the field is attachment[] (array). The client must switch from JSON to multipart when files are present (e.g. requests files=) and not send Content-Type: application/json for those requests.
  • Validation: attachment.* -> mimes (config('filesystems.mimes')) + max size.
  • Removal on update uses the API param remove_attachment (see app/Jobs/Document/UpdateDocument.php and app/Jobs/Banking/UpdateTransaction.php).
  • Existing transactions are supported: UpdateTransaction handles attachment upload + remove_attachment.
    • Standalone income/expense payments update via PUT /transactions/{id}.
    • Document-linked payments (invoice/bill payments) must use the nested PUT /documents/{doc}/transactions/{id} route — the flat Api\Banking\Transactions::update rejects any request containing document_id (transactions.messages.update_document_transaction_error). Reuse akt's existing payment delete-resolver pattern to choose the route.
  • Both documents (CreateDocument/UpdateDocument) and transactions (CreateTransaction/UpdateTransaction) accept attachment.
  • Confirm how the document/transaction JSON exposes attachment media (URL/id) for the download path; a signed/media endpoint may be required since uploads are stored private.
  • Keep existing quirks in mind: company_id query param on every call, and search=type:<invoice|bill> scope on documents.

Acceptance criteria

  • Attach one or more files on create/update for bills, invoices, and payments — including attaching to a pre-existing transaction (standalone and document-linked).
  • List and download attachments for a given record.
  • Remove attachments on update.
  • Unit test for multipart body assembly + route selection; integration test that uploads a small temp file to an existing transaction, verifies it is attached, downloads it back, and cleans up (no leftover records/files).

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions