Repository navigation
feat(api)!: pdf()/pdfContent(), typed sub-resource filters, parent id as create() argument - #71
Merged
Merged
Conversation
…parent id as create() argument - pdf() always returns the *Pdf model, pdfContent() the raw bytes; the bool $rawPdf flag is gone. - Sub-resource lists take SubResourceFilter (sorting, pagination) or the generated *PaymentFilter/*PropertyValueFilter, and all have listPage()/iterateAll(). - Payments, comments, tags, property values and recurring e-mail receivers take the parent id as the first argument of create(), like items; the parent id field is removed from their create options. - Rector set: PdfContentRector and ParentIdToCreateArgumentRector. - Docs: delete() works on documents in any status (checked in the sandbox).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes the API rough edges found while rewriting the examples against 4.x.
1.
pdf()/pdfContent()instead ofpdf(..., rawPdf: true)All seven document APIs (invoices, offers, confirmations, credit notes, delivery notes, letters, reminders):
pdf(int $id, ?PdfType $type = null): XPdf: JSON, metadata plus base64pdfContent(int $id, ?PdfType $type = null): string: raw bytes (format=pdf, checked viagetPdf())Both carry
#[NoDiscard]. Why two methods instead of a conditional return type: the flag picks a different wire format and a different return type. A boolean flag argument is a smell, and a PHPStan conditional type only helps callers that run PHPStan; plain PHP and IDEs would still seeXPdf|string. Shared query building is inAbstractApi::pdfQuery().2. Typed sorting and pagination for sub-resources
The spec documents filter fields only for payments (
from,to,type,user_id) and property values ({parent}_property_id,value). So:filter, parent fieldomitted because it's an argument):InvoicePaymentFilter,CreditNotePaymentFilter,IncomingPaymentFilter(typeaslist<PaymentType>), andClient/Article/Supplier/IncomingPropertyValueFilter.PaymentsApi/PropertyValuesApiget aTFiltertemplate and reject a filter of another resource at runtime (InvalidArgumentException), like they already do for options.SubResourceFilter(orderBy,page,perPage,extra) for items, comments, tags, reminder items, recurring e-mail receivers and property definitions. I didn't generate ~35 near-identical per-resource classes that would only hold sort and paging fields.list(),listPage()anditerateAll(). In the sandbox,/invoice-itemsand/invoice-commentsreturn@page/@per_page/@totaland honourper_page/order_by.list(int $parentId, ?array $actionKeys = null, SubResourceFilter|array $filters = []).actionKeysstays second so existing positional calls don't silently land in$filters.3. Parent ID as the first argument of
create()Convention: the parent ID is always an argument, never an options field.
create(int $parentId, $options)now applies to payments, comments, tags, property values and recurring e-mail receivers, likeItemsApialready did.Why this one and not "always in the options":
list($parentId),listPage($parentId)anditerateAll($parentId). You address the parent the same way for reads and writes.$billomat->invoices->payments->create($invoiceId, …)), and the options only describe the sub-resource itself, so one options object can be reused for several parents.The parent fields are
readOnlyin the definitions (still in the read models) andAbstractSubResourceApi::createFor()adds them to the payload.ItemsApichanges are limited tolist/listPage/iterateAllandcreate(now viacreateFor());update()and*ItemUpdateOptionsfrom #70 are untouched.4.
delete()on documentsThe docs claimed
delete()only works in DRAFT. Sandbox check (9 requests, throwaway client + invoice, cleaned up): I created a draft invoice with two items and calledcomplete(), which set the status to OVERDUE.delete()returned 200, andget()afterwards returnednull. Together with the earlier runs (completed, cancelled, won and lost documents of every type), the doc pages now saydelete()works in any status. Wherecancel()exists (invoices, offers, confirmations, credit notes, reminders), they recommend it for completed documents (bookkeeping/GoBD, gaps in the number sequence). Letters and delivery notes have nocancel()since #66, so their pages don't suggest one.5. Rector set
Two new rules in
rector/upgrade-4.0.php, with fixture tests and the end-to-end fixture extended:PdfContentRector:pdf($id, $type, rawPdf: true)/pdf($id, $type, true)→pdfContent($id, $type).rawPdf: falseis dropped, and a runtime flag is left alone.ParentIdToCreateArgumentRector:create(new InvoicePaymentCreateOptions(invoiceId: $id, …))(named or first positional) →create($id, new InvoicePaymentCreateOptions(…))for all payment/comment/tag/property-value/e-mail-receiver options. Options built in a variable stay manual and are listed in the UPGRADE "What stays manual" section.Also updated
Unit tests (new sub-resource list/page/iterate, filter mismatch and create payload tests,
pdf()JSON andpdfContent()type tests), examples (04-payments.phpusesInvoicePaymentFilter+OrderBy), the docs on all affected resource pages plus pagination/filtering/http-layer/error-handling, README quickstart, AGENTS.md, CHANGELOG[Unreleased]and UPGRADE-4.0.md (before/after sections for all three BC breaks, plus the Rector table).Checks
composer lint,analyse,refactor:dry,generate:checkandtest(3596 tests) pass.composer mutate -- --git-diff-lines --git-diff-base=origin/4.x …gives a covered MSI of 98 %.