Files
orderPRO/.paul/phases/136-fakturownia-invoice-idempotency/136-01-SUMMARY.md
Jacek Pyziak bdf415501f feat(136): fakturownia invoice idempotency
Phase 136 complete:

- add local pending/failed external invoice attempt state

- reconcile delegated invoices by stable Fakturownia oid before duplicate POST

- document INVOICE-IDEMP-115 resolution and verification gaps
2026-05-17 17:40:12 +02:00

9.8 KiB

phase, plan, subsystem, tags, requires, provides, affects, tech-stack, key-files, key-decisions, patterns-established, duration, started, completed
phase plan subsystem tags requires provides affects tech-stack key-files key-decisions patterns-established duration started completed
136-fakturownia-invoice-idempotency 01 accounting
fakturownia
invoices
idempotency
oid
retry
phase provides
115 Delegated Fakturownia invoice issuance flow and local invoice snapshots
phase provides
134 Confirmed INVOICE-IDEMP-115 backlog item
Retry-safe delegated Fakturownia invoice issuance using stable oid
Local pending/failed external invoice attempt state
Lookup-first reconciliation before duplicate external POST
invoices
fakturownia
accounting
retry
added patterns
external oid reconciliation
pending external attempt state
created modified
database/migrations/20260517_000118_add_invoice_external_idempotency_state.sql
tests/Unit/FakturowniaInvoiceIdempotencyTest.php
src/Modules/Accounting/InvoiceRepository.php
src/Modules/Accounting/InvoiceService.php
src/Modules/Settings/FakturowniaApiClient.php
DOCS/ARCHITECTURE.md
DOCS/DB_SCHEMA.md
DOCS/TECH_CHANGELOG.md
.paul/codebase/architecture.md
.paul/codebase/db_schema.md
.paul/codebase/tech_changelog.md
.paul/codebase/todo.md
Use orders.internal_order_number as Fakturownia oid; fallback to orderpro-{order_id} only if missing.
Official GET /invoices.json?oid=... is the reconciliation mechanism because invoice create has no documented Idempotency-Key support.
POST failures are retried safely by remote lookup and local failed_retryable state, not by blind duplicate POST.
Delegated external creates should persist pending local state before the remote POST.
Uncertain remote failures should perform lookup-after-failure before surfacing retry instructions.
~2h 2026-05-17T15:36:00Z 2026-05-17T15:36:00Z

Phase 136 Plan 01: Fakturownia Invoice Idempotency Summary

Delegated Fakturownia invoices now use stable oid reconciliation and local pending state so retries do not blindly create duplicate external invoices after a timeout.

Performance

Metric Value
Duration ~2h
Started 2026-05-17 17:36 Europe/Warsaw
Completed 2026-05-17 17:36 Europe/Warsaw
Tasks 3 completed
Files modified 17, including PAUL transition files

Acceptance Criteria Results

Criterion Status Notes
AC-1: Delegated Invoice Uses Stable OID Pass InvoiceService resolves oid from orders.internal_order_number, falls back to orderpro-{order_id}, sends it in the Fakturownia payload and stores it locally.
AC-2: Retry Does Not Double POST When External Invoice Already Exists Pass Runtime checks local (config_id, external_oid) and GET /invoices.json?oid=... before POST /invoices.json; unit test covers no second create call.
AC-3: Successful POST Finalizes Pending Row Pass InvoiceRepository creates pending_external rows and finalizes them with external id, number, PDF URL and issued status.
AC-4: Uncertain Failure Remains Recoverable Pass On POST exception, the service looks up by oid; if found, it auto-attaches, otherwise marks failed_retryable and throws retry-safe guidance.
AC-5: Docs And Tests Describe The Contract Pass with env gap Unit test file and DOCS/PAUL notes were added; PHPUnit execution was blocked because vendor/bin/phpunit is missing.

Accomplishments

  • Added invoice idempotency state columns and unique (config_id, external_oid) protection for delegated Fakturownia invoices.
  • Refactored delegated invoice issuance to lookup local and remote invoices before create, persist pending attempts, finalize success, and auto-attach remote invoices found after uncertain failures.
  • Added focused unit coverage for lookup-first retry, pending finalize, auto-attach after connection failure, and retryable failure marking.
  • Documented INVOICE-IDEMP-115 as resolved in DOCS and PAUL codebase notes.

Task Commits

Phase commit: HEAD feat(136): fakturownia invoice idempotency

Task Commit Type Description
Task 1: Add local delegated invoice attempt state HEAD feat Migration and repository support for pending/finalize/failed external invoice attempts.
Task 2: Implement Fakturownia lookup-first delegated flow HEAD feat Stable oid, lookup-first API flow, post-failure reconciliation and auto-attach.
Task 3: Add regression tests and documentation HEAD test/docs Unit tests plus DOCS/PAUL schema, architecture and changelog updates.

Files Created/Modified

File Change Purpose
database/migrations/20260517_000118_add_invoice_external_idempotency_state.sql Created Adds delegated external invoice status, oid, attempt timestamp, error message and unique config/oid index.
src/Modules/Accounting/InvoiceRepository.php Modified Adds lookup, pending insert, finalize and failed retryable helpers.
src/Modules/Accounting/InvoiceService.php Modified Implements stable oid, lookup-first create, auto-attach and retry-safe failure handling.
src/Modules/Settings/FakturowniaApiClient.php Modified Adds findInvoiceByOid() and shared response normalization.
tests/Unit/FakturowniaInvoiceIdempotencyTest.php Created Covers retry/lookup/finalize/failed idempotency behavior.
DOCS/ARCHITECTURE.md Modified Documents delegated Fakturownia retry algorithm.
DOCS/DB_SCHEMA.md Modified Documents new invoice idempotency columns and unique key.
DOCS/TECH_CHANGELOG.md Modified Records Phase 136 technical change and verification gaps.
.paul/codebase/architecture.md Modified Mirrors architecture contract for PAUL context.
.paul/codebase/db_schema.md Modified Mirrors schema contract for PAUL context.
.paul/codebase/tech_changelog.md Modified Mirrors technical changelog.
.paul/codebase/todo.md Modified Marks INVOICE-IDEMP-115 as resolved.
.paul/PROJECT.md Modified Marks Phase 136 as shipped and Phase 137 ready.
.paul/ROADMAP.md Modified Closes Phase 136 and advances milestone progress.
.paul/STATE.md Modified Moves loop to complete and routes next action to Phase 137.
.paul/changelog/2026-05-17.md Created Human-readable PAUL daily changelog.
.paul/phases/136-fakturownia-invoice-idempotency/136-01-SUMMARY.md Created This completion summary.

Decisions Made

Decision Rationale Impact
Stable oid is orders.internal_order_number. Operator selected it and it is already the internal order reference used across the app. Retries can reconcile the same external invoice deterministically.
Use Fakturownia GET /invoices.json?oid=... instead of Idempotency-Key. Official docs expose lookup by oid; no documented invoice create idempotency header was found. Idempotency is application-managed and compatible with current API.
Keep failed uncertain attempts as failed_retryable. A failure may still have created the remote invoice, so the next attempt must re-check by oid. Operators get a safe retry path without manual duplicate risk.

Deviations from Plan

Summary

Type Count Impact
Execution mode 1 Plan allowed auto delegation, but implementation was done inline because the active developer instruction requires explicit user authorization before spawning agents. No functional impact.
Verification gap 3 Migration, PHPUnit and Sonar could not run in this environment; gaps are documented below and in STATE.
Scope addition 1 Added an ad-hoc SQLite repository smoke as a verification fallback while PHPUnit/MySQL were unavailable.

Deferred Items

  • Run php bin/migrate.php when local XAMPP MySQL is online.
  • Run vendor/bin/phpunit tests/Unit/FakturowniaInvoiceIdempotencyTest.php after project dependencies provide PHPUnit.
  • Run sonar-scanner when the CLI is available in PATH.

Verification Results

Command Result
C:\xampp\php\php.exe -l src\Modules\Accounting\InvoiceRepository.php Pass
C:\xampp\php\php.exe -l src\Modules\Accounting\InvoiceService.php Pass
C:\xampp\php\php.exe -l src\Modules\Settings\FakturowniaApiClient.php Pass
C:\xampp\php\php.exe -l tests\Unit\FakturowniaInvoiceIdempotencyTest.php Pass
`rg -n "INVOICE-IDEMP-115 external_oid
Ad-hoc SQLite repository smoke Pass: pending/finalize/failed helpers exercised
git diff --check Pass, CRLF warnings only
php bin/migrate.php Blocked: local MySQL refused connection
vendor/bin/phpunit tests/Unit/FakturowniaInvoiceIdempotencyTest.php Blocked: vendor/bin/phpunit missing
sonar-scanner --version Blocked: command not found

Issues Encountered

Issue Resolution
Local MySQL/XAMPP refused connection during migration run. Migration left pending and documented as follow-up.
vendor/bin/phpunit is not present in checkout. Added tests and verified syntax; PHPUnit execution documented as environment gap.
sonar-scanner is not available in PATH. Skill/tooling gap documented in STATE and SUMMARY.

Next Phase Readiness

Ready:

  • INVOICE-IDEMP-115 is implemented and documented.
  • Phase 137 can start from a cleaner accounting/Fakturownia backlog state.

Concerns:

  • Migration must still be applied on a live database before the new runtime path can persist external attempt state.
  • Targeted PHPUnit should be run once project dependencies are available.

Blockers:

  • None for planning Phase 137.

Phase: 136-fakturownia-invoice-idempotency, Plan: 01 Completed: 2026-05-17