Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Statamic Invoices Laravel Package

goldnead/statamic-invoices

A Statamic addon for creating and managing invoices in your control panel. Define invoice data and line items, render branded templates, and keep billing documents alongside your content. A lightweight option for Laravel and Statamic sites needing simple invoicing without an external billing platfor...

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Package role and release scope. v2.8.0 is a brand-context correctness release. It fixes a genuine defect: on multi-brand hosts, console and scheduled runs read the package's bundled defaults instead of the settings saved on the settings page, and invoices:pending wrote invoices with an empty seller rather than the brand's. For a single-brand install the release content is effectively a bug fix. For a multi-brand host it changes three things at once — how many times a command runs, where its configuration comes from, and what files it produces. The version bump to a minor is defensible for single-brand and misleading for multi-brand; the changelog should carry an explicit upgrade section, and multi-brand operators should plan it as a behavior-change release rather than a patch.

Architecture fit.

  • Brand resolution at the command boundary is the right shape: iterate brands, scope queries to that brand's rows, hydrate that brand's saved settings. It composes cleanly with the v2.7.0 delivery state machine — retries now follow per-brand policy, which is architecturally correct but moves retry policy out of deploy-time env config and into database state.
  • --brand=<handle or id> is the necessary escape hatch: it enables targeted reruns and lets operators shard schedules per brand across nodes.
  • Deliberately keeping invoices:held and invoices:brand-check cross-brand is a good decision — it preserves the global support queue that the v2.7.0 runbook depends on.
  • Carrying brand context into queued jobs (invoices:export pdf --queue) closes a real correctness hole; previously the worker would render with default-brand settings.
  • The v2.7.0 caveat is unchanged and still the most important one: send() on a failed row bypasses the atomic claim, so duplicate sends remain possible regardless of this release.

Integration feasibility. Low code effort, materially higher verification effort than v2.7.0. No migration is described in the release notes — confirm. Budget roughly 0.5 dev-day for single-brand and 1.5–2 dev-days for multi-brand, dominated by inventorying export consumers, auditing historical invoices:pending records, and per-brand smoke tests. Multi-brand hosts with automated ingestion of export files have a coordination dependency outside engineering that can dominate the calendar.

Technical risk.

Risk Severity Notes
CLI/scheduled runs now read saved settings, not env/config High (multi-brand) The v2.7.0 kill switch (INVOICES_RETRY_SCHEDULE=false) may no longer disable retries. Retry schedule, attempt count, tax and text behavior can now change via the UI with no deploy and no code review. Verify precedence before relying on config for rollback.
Export artifact change High where ingestion is automated One file per brand; belege.csv becomes belege_marke-2.csv; CSV to stdout requires --brand. A consumer that globs the old filename will silently ingest one brand or none.
Historical invoices written with empty seller Medium-High Invoices created by invoices:pending on multi-brand hosts before 2.8.0 carry the package's empty seller. This is a document-correctness issue, not just cosmetic; audit and backfill.
Per-brand retry policy drift Medium Retry timing is now per-brand data. Staging no longer proves production, and a settings edit is a live behavior change with no deployment record.
Coarse exit codes / silent partial failure Medium A failing brand does not stop the loop; the exit code is only the first failure. Cron alerting on exit code alone cannot identify which brand failed.
Queued job payload change Medium Jobs queued before deploy lack brand context and will run with default-brand settings. Drain queues before rollout.
Command runtime and overlap Medium Per-brand loops multiply runtime by brand count; long invoices:export runs can collide with withoutOverlapping where they previously fit comfortably.
brand_id 0 / orphaned brands Low-Medium release maps brand_id 0 to the default brand; export reports unknown brands and exits 1. Confirm pending and retry behavior for these rows — writing under the default brand could mis-attribute documents.
--brand accepts handle or id Low Typo and ambiguity risk; confirm invalid values fail fast with non-zero exit rather than no-op (a silent no-op in cron is a missed run).
Semver communication Low-Medium For multi-brand, this is an output-format and behavior break. Request a "Behavior changes" heading and a documented filename scheme.
v2.7.0 risks carried forward — Scheduler absent, migration skipped, duplicate send(), invoices:release guard, backoff timing vs SLA, no jitter. All unchanged.

Key questions for the vendor.

  1. What is the precedence between saved settings (DB), published config, and env vars for console and scheduled runs? Can env still force-disable retry, and is INVOICES_RETRY_SCHEDULE=false still authoritative?
  2. Does 2.8.0 ship any migration or settings-schema change? If upgrading from 2.6.x directly, must the 2.7.0 invoice_deliveries migration run first?
  3. What happens on an unknown or malformed --brand value — fail fast with non-zero exit, or no-op? A no-op in a cron entry is a silent outage.
  4. Can the multi-brand loop emit machine-readable per-brand results (--json, per-brand exit summary), and do log lines include brand handle/id so alerting can group by brand without parsing prose?
  5. How are brand handles slugged into export filenames, is the suffix template configurable and stable, and what does --brand plus --output produce exactly (suffix or exact path)?
  6. For invoices:pending on multi-brand hosts before 2.8.0: what was actually written for seller/brand, and is there a supported audit or backfill command?
  7. What do pending, retry, and release do with brand_id 0 and orphaned brand references — consistent with export's report-and-exit-1?
  8. Is the brand added to queued job payloads in a backward-compatible field, and what is the recommended drain order for jobs already in flight?
  9. Does the per-brand loop re-bootstrap the application or brand context per iteration (memory, DB connections, mail transport reuse)? Any timeout or batching on hosts with many brands?
  10. Can invoices:retry --brand=X safely run for different brands in parallel on multiple nodes with onOneServer?
  11. Does invoices:retry re-read the brand's retry settings on every run, and does raising max_attempts mid-flight reopen rows already marked failed_final?
  12. invoices:release now uses the invoice's brand, so subject and filename can differ from what the same invoice produced earlier. Confirm this is strictly the saved template with no cached rendering, and that brand_id 0 → default is intentional for document identity.

Integration Approach

Stack fit. Assumptions are unchanged from v2.7.0: database-backed deliveries, configured mail transport, cron-driven schedule:run, no queue worker required. The new variable is brand dimensionality. Multi-brand hosts must choose an operating model: one scheduler entry per host looping all brands, or one entry per brand using --brand. The first is simpler, the second gives per-brand isolation and bounded runtimes. Single-brand installs genuinely need nothing beyond a normal patch cycle.

Migration path.

  1. Brand data audit first, before any code change: sample invoices created by invoices:pending on multi-brand hosts, inspect seller and brand_id, and decide whether remediation happens before or after rollout.
  2. Inventory every downstream consumer of invoices:export: globs, SFTP drops, BI imports, filenames expected, and anything reading CSV from stdout (now requires --brand). This is the critical path if automated ingestion exists.
  3. Pin 2.8.0 on staging; confirm no migration ships; run each affected command with and without --brand; capture exit codes, filenames, and log formatting.
  4. Review each brand's saved delivery, tax, and text settings — that is now the runtime source for CLI behavior. Establish who may edit them and freeze edits during the rollout window.
  5. Drain or pause queued export jobs before deploy so no in-flight job runs brand-less under the new code.
  6. Re-run the v2.7.0 forced-failure retry test against a non-default brand. This validates the 2.8.0 fix and the retry path in a single exercise.
  7. Deploy low-activity, then observe one full scheduled cycle of each changed command, plus one full retry backoff window (~30–40 minutes) before declaring done.

Compatibility and rollback. Code rollback is structurally safe — there is no schema change to unwind — and it restores package-config sourcing and the single combined export file. Two caveats. First, artifacts already produced under the new scheme persist (per-brand files, corrected sellers), so consumers must tolerate a mixed window. Second, if retry behavior is now settings-driven, rollback restores env behavior; the kill switch must be re-tested after rollback, not assumed. Given the settings/source ambiguity, roll forward is usually the better path; decide the rollback trigger before deploy rather than after.

Sequencing recommendation. Keep 2.8.0 in its own release train, separate from v2.7.0's scheduler-enablement gate. Suggested order: (a) 2.8.0 with retries still disabled — verify brand correctness of pending, export, and release; (b) re-verify the retry kill switch and then enable retries per brand; (c) app-caller audit for stray send() calls. Bundling 2.8.0 with retry enablement is the specific mistake to avoid: during an incident you would not be able to tell whether brand settings or the claim logic caused it.

Operational Impact

Maintenance. Two new surfaces need owners. First, the settings page becomes production configuration: changes there alter scheduled behavior immediately, so it needs change control, a review step, and freeze windows around releases. Second, export filename stability becomes a cross-team contract with finance or BI, not an internal detail. Also confirm that archived or empty brands are skipped cleanly so the per-brand loop does not generate spurious runs and alerts. Status enumerations (failed_final from v2.7.0) now need brand attribution in dashboards and filters.

Support. Runbook changes before go-live:

  • Cross-brand triage stays intact. invoices:held remains global, which is correct. Confirm the output shows brand so support can route the ticket without a second query.
  • "Customer says the invoice never arrived." Check held, then read the invoice's brand_id before releasing. invoices:release now sends under the invoice's own brand, so subject line and attachment name may differ from the previous send — that is expected, not a regression.
  • "The export failed." Exit code 1 means at least one brand failed, not which. Support needs either the brand in the log line or the routine of re-running --brand=<suspected> to isolate.
  • "I changed a setting to fix one invoice." Settings are now global-per-brand scheduled behavior. Support must not adjust delivery settings as a per-invoice remedy.

Alerting should combine: the v2.7.0 error-level line for failed_final, a per-brand count of failed_final rows older than one hour, and per-brand export freshness (expected filename exists and is recent). The freshness check is what catches the "second brand's file never appeared" case that exit codes and file-presence-any checks both miss.

Scaling.

  • Command cost is now multiplicative in brand count. At small brand counts it is negligible; at dozens of brands with heavy PDF exports, prefer sharded --brand schedules across nodes with onOneServer so a single run does not monopolize a scheduler tick.
  • Retry volume is per brand. Per-brand schedules can naturally stagger load if configured differently, but left at shared defaults they concentrate exactly as before.
  • The missing jitter from v2.7.0 remains the main burst risk during a provider-wide outage: every brand's failed invoices retry on the same boundary.
  • The retry command now depends on database-backed settings in addition to invoice rows. That is an extra dependency on a healthy DB and on sane setting values; a malformed setting is a fleet-wide retry failure, not a single-row one.

Failure modes.

Failure Symptom Detection Recovery
Settings edited in UI break CLI behavior Retries stop, accelerate, or render wrong templates with no deploy Diff saved settings against expected; per-brand failed_final count Revert setting; add change control and a settings snapshot
Env kill switch no longer effective Disabling retry via env has no effect Test the disable path each release; watch retry attempts after "disabling" Use the actual supported switch; get vendor confirmation
Export filename change breaks ingestion Accounting or BI import stops, or only one brand is ingested Per-brand file freshness alert Update consumer to per-brand globs, or run with --brand to fixed names
One brand fails inside the loop Some brands succeed, one silently does not; exit 1 only Per-brand artifact freshness; brand-tagged logs Rerun --brand=<failed>
In-flight queued export lacks brand Documents render with default-brand settings Spot-check PDFs post-deploy Drain queue pre-deploy; re-queue affected jobs
Historical invoices with empty seller Blank or wrong seller on issued documents Data audit on pre-2.8.0 rows Backfill and reissue where legally required
Cron absent or misconfigured (carried from 2.7.0) failed rows accumulate, never advance Row-age alert on failed Fix scheduler; invoices:retry is idempotent
App calls send() on a failed row (carried) Duplicate mail Customer complaint Caller audit; enforce a single send path
Release guard on young sending rows (carried) Operator blocked, support ticket Ticket pattern Wait out the window or lower delivery.release_sending_after_minutes with the duplicate-send tradeoff understood
Migration skipped (carried) Failed sends stuck in sending sending rows older than 10 minutes Run migration; invoices:release to unstick

Ramp-up. Single-brand: unchanged from the prior assessment — about half a day of dev including the forced-failure test, 2–3 hours of ops, a 30-minute support briefing, roughly one week to confident operation. Multi-brand: add 1–2 dev-days for the brand audit, export-consumer inventory, and per-brand smoke tests, plus calendar time for the finance/BI filename coordination and for standing up settings change control. Realistic elapsed time is two to three weeks, with the export-consumer update as the critical path whenever automated ingestion exists. The retry path remains easy to deploy and easy to leave unverified; combined with settings-driven behavior, an unverified multi-brand rollout is worse than doing nothing, because it will look correct in one brand and be quietly broken in another.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast