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).
Summary
Akaunting stores uploaded attachments (scanned/PDF/image copies of bills & invoices) permanently via
plank/laravel-mediableunderstorage/app/uploads, tracked in themedia/mediablestables. The REST API accepts these as a multipartattachmentfield on documents (invoice/bill, create & update) and transactions (payments, create & update).aktcurrently sendsapplication/jsononly, 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.pdfakt invoice create ... --attachment ./scan.pngakt payment create --bill 3 --attachment ./receipt.pdfakt bill update <id> --attachment ./new.pdfakt payment update <id> --attachment ./receipt.pdf# attach to an EXISTING transactionakt bill update <id> --remove-attachment# clears existing attachment(s)akt payment update <id> --remove-attachmentList / download:
akt bill attachments <id># list attached files (id, name, size)akt bill download-attachment <id> [--out <dir>]# download to diskinvoiceandpayment)Implementation notes (from Akaunting 3.1.x source)
attachment[](array). The client must switch from JSON to multipart when files are present (e.g.requestsfiles=) and not sendContent-Type: application/jsonfor those requests.attachment.*-> mimes (config('filesystems.mimes')) + max size.remove_attachment(seeapp/Jobs/Document/UpdateDocument.phpandapp/Jobs/Banking/UpdateTransaction.php).UpdateTransactionhandlesattachmentupload +remove_attachment.PUT /transactions/{id}.PUT /documents/{doc}/transactions/{id}route — the flatApi\Banking\Transactions::updaterejects any request containingdocument_id(transactions.messages.update_document_transaction_error). Reuse akt's existing payment delete-resolver pattern to choose the route.CreateDocument/UpdateDocument) and transactions (CreateTransaction/UpdateTransaction) acceptattachment.search=type:<invoice|bill>scope on documents.Acceptance criteria