Skip to content

feat: TurboQuote list sorting and filters, quote summary, open quotes and custom line item images - #136

Open
amitsharma-turbodocx wants to merge 3 commits into
mainfrom
feature/turboquote-revamp-api
Open

amitsharma-turbodocx wants to merge 3 commits into
mainfrom
feature/turboquote-revamp-api

Conversation

@amitsharma-turbodocx

@amitsharma-turbodocx amitsharma-turbodocx commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Closes #133

TurboQuote catches up with the API: list sorting, more list filters, the quote summary / creators / next-number reads, open quotes, product quote images and images on custom line items. Every new parameter is optional, so existing calls send exactly what they sent before.

What's new (all six SDKs)

  • Sorting on every list: sortBy + sortDirection (asc | desc).
    • Quotes: updatedOn · createdOn · total · validUntil · quoteNumber
    • Products: name · listPrice · createdOn
    • Price books: name · discountPercent · overrideCount · validFrom · validTo · createdOn
    • Bundles: name · itemCount · totalListPrice · totalFinalPrice · createdOn
    • Companies: name · contactCount · lastActivityDate
    • Contacts (both lists): name · createdOn
    • Types: name · usageCount
  • Quote list filters: companyIds, currencies (currency takes one or several), expired in statuses, createdBy, createdByApiKeys, validUntilWindow / validUntilFrom / validUntilTo, summaryCurrency.
    • The response gains statusCounts and summary.
    • Each quote in the list carries isExpired, isSignatureExpired and lineItemCount.
  • Other list filters: products take several billingFrequency / currency values, bundles several currency values, contacts several companyIds.
  • New methods: getQuoteSummary, listQuoteCreators, getNextQuoteNumber, getCompanyOpenQuotes, getContactOpenQuotes, getProductQuoteImages, and custom line item images (getQuoteLineItemImages, getLineItemImages, addLineItemImages, reorderLineItemImages, removeLineItemImage). The parity count goes from 68 to 79.
  • Request fields:
    • An optional name on duplicateQuote / duplicateProduct / duplicatePriceBook / duplicateBundle.
    • email on companies (create, update, bulk).
    • resetProductPricing on updatePriceBook.
  • Response fields: price book overrideCount; bundle itemCount and billingTotals; company email; company.contactCount on a company's contact list.
  • Docs:
    • The delete methods document the refused deletes: a sent or accepted quote, a company or contact with open quotes, a company's last contact, the default price book.
    • README sections in every language, the new openapi.yaml paths, the PHP CHANGELOG and the parity rule.

Per language

  • JS:
    • Union types for every sort column and for QuoteListStatusFilter / QuoteValidUntilWindow.
    • OneOrMany<T> for the multi-value filters.
    • HttpClient.put.
  • Python:
    • TypedDict fields.
    • name= keyword on the duplicates.
    • HttpClient.put.
  • Go:
    • New option fields.
    • BillingFrequencies / CurrencyCodes next to the existing single fields, sent through a query:"…" tag, so no existing field changes type.
    • Variadic DuplicateOptions, UpdateCompanyRequest.ClearEmail(), and Put / PostImagesMultipart on the HTTP client.
    • Constants: SortAsc / SortDesc, QuoteSortBy*, ValidUntil*, QuoteListStatusExpired.
  • PHP:
    • New constructor arguments go at the end, so positional calls still work.
    • string|array for the multi-value filters.
    • Typed QuoteSummary, QuoteStatusCounts, QuoteCreator, NextQuoteNumber, OpenQuotesResponse and LineItemImage.
    • HttpClient::put; FileTypeDetector now recognises PNG and JPEG.
  • Java:
    • New setters, and (id, name) overloads for the duplicates.
    • ListCompanyContactsOptions extends PaginationParams, so the existing listCompanyContacts(companyId, PaginationParams) signature stays.
    • Typed models, plus HttpClient.put and delete(path, Type).
  • Ruby:
    • name: keyword on the duplicates.
    • HttpClient#put; the image parts helper is shared with product images.

Tests (written first and run to fail, then the code was added; all suites in Docker for Go, PHP, Java, Ruby and Python):

SDK Result
JS 441 passed, tsc --noEmit clean
Python 456 passed
Go go test ./... ok, go vet clean
PHP 483 passed, phpstan level 8 clean, cs-fix clean
Java 398 passed
Ruby 430 examples, 0 failures

They cover:

  • query params, including repeated keys, for every new sort and filter;
  • each new method's path, method and unwrapping;
  • images-only multipart with the type detected from the bytes;
  • a named duplicate sends { name }, an unnamed one sends no body;
  • the explicit null company email on PATCH;
  • resetProductPricing;
  • the 409 CANNOT_DELETE_SENT_QUOTE reaching the conflict error class with its code.

Notes for release

  • Go: ListCompanyContacts now takes *ListCompanyContactsOptions instead of *PaginationParams (same Limit / Offset / Query fields plus the sort). Callers that pass nil are unaffected; callers that build &PaginationParams{…} change the type name.
  • All SDKs: the quote status type lists only the released statuses: draft, sent, accepted, declined, voided (JS/Python types, Go constants, PHP enum, Ruby constants). Code that referenced the removed value no longer compiles or type-checks.

CI fix: the Java WebhookSignatureVerifierTest.stringBodyOverloadUsesDefaultTolerance read the clock twice and failed when a second ticked over between signing and verifying; it now reads it once.

  • Publish order: publish after the matching API release. The API rejects unknown query params and body keys, so sending the new options to an older API is a 400. Callers that don't use them are unaffected.
  • Applies to every SDK version: deleting a sent quote now returns a 409 from the API. It's documented here so callers know to void instead.
  • Also API-side: a product now holds at most 3 images (was 5). Adding a 4th is a 400 (MAX_IMAGES_EXCEEDED). A product that already has more keeps them until an image is added, and a duplicate copies the first three. The SDKs never capped the count client-side, so no code changes; the docs say 3.
  • No version bump in this PR; the release bump is separate, as with earlier features.

🤖 Generated with Claude Code

… and custom line item images

All six SDKs (JS, Python, Go, PHP, Java, Ruby):
- sortBy / sortDirection on every TurboQuote list; quote list filters for several companies and
  currencies, the "expired" status, created by, created by API keys, Valid until window and dates,
  and the summary currency; several billing frequencies / currencies on products and bundles,
  several companies on contacts
- getQuoteSummary, listQuoteCreators, getNextQuoteNumber, getCompanyOpenQuotes,
  getContactOpenQuotes, getProductQuoteImages, and custom line item images
  (getQuoteLineItemImages, getLineItemImages, addLineItemImages, reorderLineItemImages,
  removeLineItemImage)
- an optional name on the four duplicate calls; company email; resetProductPricing on
  updatePriceBook; new response fields (statusCounts, summary, isExpired, lineItemCount,
  overrideCount, itemCount, billingTotals, company.contactCount)
- the refused deletes (sent / accepted quote, company or contact with open quotes, last contact,
  default price book) documented on the delete methods
- README sections, openapi.yaml paths, PHP CHANGELOG and the parity rule

Closes #133

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@amitsharma-turbodocx amitsharma-turbodocx self-assigned this Oct 20, 2026
private static Map<String, Object> map(Object... keyValues) {
Map<String, Object> m = new LinkedHashMap<>();
for (int i = 0; i < keyValues.length; i += 2) {
m.put((String) keyValues[i], keyValues[i + 1]);
Quote approvals are not released, so open quotes are described as draft or
sent, and the status counts no longer expose a pending-approval count.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@amitsharma-turbodocx
amitsharma-turbodocx marked this pull request as ready for review October 20, 2026 15:48
…Java webhook test

- QuoteStatus in JS, Python, Go, PHP and Ruby lists draft, sent, accepted,
  declined and voided only. Tests check the exact set.
- SendQuote's Go doc comment describes sending for signature.
- Parity rule: unreleased quote features are not wrapped, documented or typed.
- Java WebhookSignatureVerifierTest read the clock twice, so it failed when a
  second ticked over between signing and verifying. It reads it once now.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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.

feat(turboquote): list sorting and filters, quote summary, open quotes and custom line item images

1 participant