From 4ab21c3625f3525a86d9c7e844ccacc0ca068eae Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 11 Jan 2026 12:29:38 +0000 Subject: [PATCH 1/7] Initial plan From f2542202d39fe5fbfa2d4f7c670ae3630227720e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 11 Jan 2026 12:40:03 +0000 Subject: [PATCH 2/7] Fix logging, enum injection, and enum logic issues Co-authored-by: nielsdrost7 <47660417+nielsdrost7@users.noreply.github.com> --- Modules/Invoices/Http/Traits/LogsApiRequests.php | 6 +++--- Modules/Invoices/Peppol/Clients/BasePeppolClient.php | 8 +++++++- Modules/Invoices/Peppol/Enums/PeppolDocumentFormat.php | 7 ++++--- .../Invoices/Peppol/FormatHandlers/BaseFormatHandler.php | 8 ++++++-- .../Peppol/FormatHandlers/FormatHandlerFactory.php | 9 ++++++++- 5 files changed, 28 insertions(+), 10 deletions(-) diff --git a/Modules/Invoices/Http/Traits/LogsApiRequests.php b/Modules/Invoices/Http/Traits/LogsApiRequests.php index 49f9aebd7..5893e1459 100644 --- a/Modules/Invoices/Http/Traits/LogsApiRequests.php +++ b/Modules/Invoices/Http/Traits/LogsApiRequests.php @@ -58,7 +58,7 @@ protected function logRequest(string $method, string $uri, array $options): void return; } - Log::info('API Request', [ + Log::info('HTTP Request', [ 'method' => $method, 'uri' => $uri, 'options' => $this->sanitizeForLogging($options), @@ -81,7 +81,7 @@ protected function logResponse(string $method, string $uri, int $status, mixed $ return; } - Log::info('API Response', [ + Log::info('HTTP Response', [ 'method' => $method, 'uri' => $uri, 'status' => $status, @@ -102,7 +102,7 @@ protected function logResponse(string $method, string $uri, int $status, mixed $ */ protected function logError(string $type, string $method, string $uri, string $message, array $context = []): void { - Log::error("API {$type} Error", array_merge([ + Log::error("HTTP {$type} Error", array_merge([ 'method' => $method, 'uri' => $uri, 'message' => $message, diff --git a/Modules/Invoices/Peppol/Clients/BasePeppolClient.php b/Modules/Invoices/Peppol/Clients/BasePeppolClient.php index 4af305bcd..3f5226b69 100644 --- a/Modules/Invoices/Peppol/Clients/BasePeppolClient.php +++ b/Modules/Invoices/Peppol/Clients/BasePeppolClient.php @@ -85,7 +85,13 @@ public function getClient(): HttpClientExceptionHandler */ public function getRequestOptions(array $options = []): array { - // Implement logic or return options as needed + // Merge authentication headers with any existing headers + $authHeaders = $this->getAuthenticationHeaders(); + $existingHeaders = $options['headers'] ?? []; + + $options['headers'] = array_merge($authHeaders, $existingHeaders); + $options['timeout'] = $options['timeout'] ?? $this->getTimeout(); + return $options; } diff --git a/Modules/Invoices/Peppol/Enums/PeppolDocumentFormat.php b/Modules/Invoices/Peppol/Enums/PeppolDocumentFormat.php index 33ccdca01..ed32a08cb 100644 --- a/Modules/Invoices/Peppol/Enums/PeppolDocumentFormat.php +++ b/Modules/Invoices/Peppol/Enums/PeppolDocumentFormat.php @@ -159,8 +159,8 @@ public function description(): string self::ZUGFERD_10 => 'German standard combining PDF with embedded XML invoice data.', self::ZUGFERD_20 => 'Updated ZUGFeRD compatible with Factur-X. Uses CII format.', self::OIOUBL => 'Danish UBL-based format with national extensions.', - self::EHF_30 => 'Norwegian EHF 3.0 format for Peppol network.', - self::PEPPOL_BIS_30 => 'Default Peppol format for most countries. Based on UBL.', + self::EHF_30 => 'Norwegian EHF 3.0 format for PEPPOL network.', + self::PEPPOL_BIS_30 => 'Default PEPPOL format for most countries. Based on UBL.', }; } @@ -220,7 +220,8 @@ public function isMandatoryFor(?string $countryCode): bool return match ($this) { self::FATTURAPA_12 => $country === 'IT', - self::FACTURAE_32 => $country === 'ES', // For public administration + // Note: FACTURAE_32 is only mandatory for Spanish public administration + // Not for all invoices in Spain, so we return false default => false, }; } diff --git a/Modules/Invoices/Peppol/FormatHandlers/BaseFormatHandler.php b/Modules/Invoices/Peppol/FormatHandlers/BaseFormatHandler.php index 586d11750..924153dc9 100644 --- a/Modules/Invoices/Peppol/FormatHandlers/BaseFormatHandler.php +++ b/Modules/Invoices/Peppol/FormatHandlers/BaseFormatHandler.php @@ -22,11 +22,15 @@ abstract class BaseFormatHandler implements InvoiceFormatHandlerInterface protected PeppolDocumentFormat $format; /** - * Constructor. + * Set the format for this handler. + * + * This method is called by the factory after instantiation. * * @param PeppolDocumentFormat $format The format this handler supports + * + * @return void */ - public function __construct(PeppolDocumentFormat $format) + public function setFormat(PeppolDocumentFormat $format): void { $this->format = $format; } diff --git a/Modules/Invoices/Peppol/FormatHandlers/FormatHandlerFactory.php b/Modules/Invoices/Peppol/FormatHandlers/FormatHandlerFactory.php index 6e45c044e..7a4a6846e 100644 --- a/Modules/Invoices/Peppol/FormatHandlers/FormatHandlerFactory.php +++ b/Modules/Invoices/Peppol/FormatHandlers/FormatHandlerFactory.php @@ -53,7 +53,14 @@ public static function create(PeppolDocumentFormat $format): InvoiceFormatHandle throw new RuntimeException("No handler available for format: {$format->value}"); } - return app($handlerClass); + $handler = app($handlerClass); + + // Set the format on the handler if it extends BaseFormatHandler + if ($handler instanceof BaseFormatHandler) { + $handler->setFormat($format); + } + + return $handler; } /** From b1016bf001d90a41d30fa3ba6413ffb6eb34d9e9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 11 Jan 2026 12:42:08 +0000 Subject: [PATCH 3/7] Update BaseFormatHandler to support both constructor and setFormat patterns Co-authored-by: nielsdrost7 <47660417+nielsdrost7@users.noreply.github.com> --- .../Peppol/FormatHandlers/BaseFormatHandler.php | 12 ++++++++++++ .../Peppol/FormatHandlers/FormatHandlerFactory.php | 3 ++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/Modules/Invoices/Peppol/FormatHandlers/BaseFormatHandler.php b/Modules/Invoices/Peppol/FormatHandlers/BaseFormatHandler.php index 924153dc9..0347442b8 100644 --- a/Modules/Invoices/Peppol/FormatHandlers/BaseFormatHandler.php +++ b/Modules/Invoices/Peppol/FormatHandlers/BaseFormatHandler.php @@ -21,6 +21,18 @@ abstract class BaseFormatHandler implements InvoiceFormatHandlerInterface */ protected PeppolDocumentFormat $format; + /** + * Constructor. + * + * @param PeppolDocumentFormat|null $format The format this handler supports (optional) + */ + public function __construct(?PeppolDocumentFormat $format = null) + { + if ($format !== null) { + $this->format = $format; + } + } + /** * Set the format for this handler. * diff --git a/Modules/Invoices/Peppol/FormatHandlers/FormatHandlerFactory.php b/Modules/Invoices/Peppol/FormatHandlers/FormatHandlerFactory.php index 7a4a6846e..5941abbee 100644 --- a/Modules/Invoices/Peppol/FormatHandlers/FormatHandlerFactory.php +++ b/Modules/Invoices/Peppol/FormatHandlers/FormatHandlerFactory.php @@ -55,7 +55,8 @@ public static function create(PeppolDocumentFormat $format): InvoiceFormatHandle $handler = app($handlerClass); - // Set the format on the handler if it extends BaseFormatHandler + // Set the format on the handler to ensure it matches what was requested + // This is especially important for handlers that can handle multiple formats (UBL, ZUGFeRD) if ($handler instanceof BaseFormatHandler) { $handler->setFormat($format); } From cf1ba0ec60536a711b85271bf765da3382066b67 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 11 Jan 2026 12:44:48 +0000 Subject: [PATCH 4/7] Fix test base classes to use AbstractTestCase Co-authored-by: nielsdrost7 <47660417+nielsdrost7@users.noreply.github.com> --- .../Unit/Http/Decorators/HttpClientExceptionHandlerTest.php | 4 ++-- .../Tests/Unit/Peppol/Clients/DocumentsClientTest.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Modules/Invoices/Tests/Unit/Http/Decorators/HttpClientExceptionHandlerTest.php b/Modules/Invoices/Tests/Unit/Http/Decorators/HttpClientExceptionHandlerTest.php index df956139a..fa2eaf598 100644 --- a/Modules/Invoices/Tests/Unit/Http/Decorators/HttpClientExceptionHandlerTest.php +++ b/Modules/Invoices/Tests/Unit/Http/Decorators/HttpClientExceptionHandlerTest.php @@ -7,7 +7,7 @@ use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Log; use Mockery; -use Modules\Core\Tests\TestCase; +use Modules\Core\Tests\AbstractTestCase; use Modules\Invoices\Http\Clients\ApiClient; use Modules\Invoices\Http\Decorators\HttpClientExceptionHandler; use Modules\Invoices\Http\RequestMethod; @@ -22,7 +22,7 @@ * Uses HTTP fakes to simulate various scenarios. */ #[Group('peppol')] -class HttpClientExceptionHandlerTest extends TestCase +class HttpClientExceptionHandlerTest extends AbstractTestCase { protected HttpClientExceptionHandler $handler; diff --git a/Modules/Invoices/Tests/Unit/Peppol/Clients/DocumentsClientTest.php b/Modules/Invoices/Tests/Unit/Peppol/Clients/DocumentsClientTest.php index 2291db7f7..9c7c76f80 100644 --- a/Modules/Invoices/Tests/Unit/Peppol/Clients/DocumentsClientTest.php +++ b/Modules/Invoices/Tests/Unit/Peppol/Clients/DocumentsClientTest.php @@ -4,7 +4,7 @@ use Illuminate\Http\Client\RequestException; use Illuminate\Support\Facades\Http; -use Modules\Core\Tests\TestCase; +use Modules\Core\Tests\AbstractTestCase; use Modules\Invoices\Http\Clients\ApiClient; use Modules\Invoices\Http\Decorators\HttpClientExceptionHandler; use Modules\Invoices\Peppol\Clients\EInvoiceBe\DocumentsClient; @@ -18,7 +18,7 @@ * Verifies proper API integration and error handling. */ #[Group('peppol')] -class DocumentsClientTest extends TestCase +class DocumentsClientTest extends AbstractTestCase { protected DocumentsClient $client; From e86992384f388cefe5b49f79c640f5bdb195760f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 11 Jan 2026 12:47:01 +0000 Subject: [PATCH 5/7] Address code review feedback - improve format property handling Co-authored-by: nielsdrost7 <47660417+nielsdrost7@users.noreply.github.com> --- .../FormatHandlers/BaseFormatHandler.php | 20 +++++++++++++------ .../FormatHandlers/FormatHandlerFactory.php | 5 ++--- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/Modules/Invoices/Peppol/FormatHandlers/BaseFormatHandler.php b/Modules/Invoices/Peppol/FormatHandlers/BaseFormatHandler.php index 0347442b8..2de630d0f 100644 --- a/Modules/Invoices/Peppol/FormatHandlers/BaseFormatHandler.php +++ b/Modules/Invoices/Peppol/FormatHandlers/BaseFormatHandler.php @@ -17,9 +17,9 @@ abstract class BaseFormatHandler implements InvoiceFormatHandlerInterface /** * The format this handler supports. * - * @var PeppolDocumentFormat + * @var PeppolDocumentFormat|null */ - protected PeppolDocumentFormat $format; + protected ?PeppolDocumentFormat $format = null; /** * Constructor. @@ -61,6 +61,10 @@ abstract protected function validateFormatSpecific(Invoice $invoice): array; */ public function getFormat(): PeppolDocumentFormat { + if ($this->format === null) { + throw new \RuntimeException('Format has not been set on this handler. Call setFormat() first.'); + } + return $this->format; } @@ -69,18 +73,20 @@ public function getFormat(): PeppolDocumentFormat */ public function supports(Invoice $invoice): bool { + $format = $this->getFormat(); + // Check if customer's country matches format requirements $customerCountry = $invoice->customer?->country_code ?? null; // Mandatory formats must be used for their countries - if ($this->format->isMandatoryFor($customerCountry)) { + if ($format->isMandatoryFor($customerCountry)) { return true; } // Check if format is suitable for customer's country $suitableFormats = PeppolDocumentFormat::formatsForCountry($customerCountry); - return in_array($this->format, $suitableFormats, true); + return in_array($format, $suitableFormats, true); } /** @@ -122,7 +128,9 @@ public function validate(Invoice $invoice): array */ public function getMimeType(): string { - return $this->format->requiresPdfEmbedding() + $format = $this->getFormat(); + + return $format->requiresPdfEmbedding() ? 'application/pdf' : 'application/xml'; } @@ -132,7 +140,7 @@ public function getMimeType(): string */ public function getFileExtension(): string { - return $this->format->extension(); + return $this->getFormat()->extension(); } /** diff --git a/Modules/Invoices/Peppol/FormatHandlers/FormatHandlerFactory.php b/Modules/Invoices/Peppol/FormatHandlers/FormatHandlerFactory.php index 5941abbee..8b8f6c169 100644 --- a/Modules/Invoices/Peppol/FormatHandlers/FormatHandlerFactory.php +++ b/Modules/Invoices/Peppol/FormatHandlers/FormatHandlerFactory.php @@ -53,13 +53,12 @@ public static function create(PeppolDocumentFormat $format): InvoiceFormatHandle throw new RuntimeException("No handler available for format: {$format->value}"); } + /** @var BaseFormatHandler $handler */ $handler = app($handlerClass); // Set the format on the handler to ensure it matches what was requested // This is especially important for handlers that can handle multiple formats (UBL, ZUGFeRD) - if ($handler instanceof BaseFormatHandler) { - $handler->setFormat($format); - } + $handler->setFormat($format); return $handler; } From f5f59d9042c887a6d099621ee5bed20a9b12c6ae Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 11 Jan 2026 12:57:20 +0000 Subject: [PATCH 6/7] Address PR review feedback: import RuntimeException, fix test comments, remove empty test, clarify header merge order Co-authored-by: nielsdrost7 <47660417+nielsdrost7@users.noreply.github.com> --- Modules/Invoices/Peppol/Clients/BasePeppolClient.php | 4 +++- .../Peppol/FormatHandlers/BaseFormatHandler.php | 3 ++- .../Http/Decorators/HttpClientExceptionHandlerTest.php | 10 +--------- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/Modules/Invoices/Peppol/Clients/BasePeppolClient.php b/Modules/Invoices/Peppol/Clients/BasePeppolClient.php index 3f5226b69..65f0e0157 100644 --- a/Modules/Invoices/Peppol/Clients/BasePeppolClient.php +++ b/Modules/Invoices/Peppol/Clients/BasePeppolClient.php @@ -86,10 +86,12 @@ public function getClient(): HttpClientExceptionHandler public function getRequestOptions(array $options = []): array { // Merge authentication headers with any existing headers + // Auth headers are merged AFTER existing headers to ensure they take precedence + // and cannot be overridden by caller-provided headers for security $authHeaders = $this->getAuthenticationHeaders(); $existingHeaders = $options['headers'] ?? []; - $options['headers'] = array_merge($authHeaders, $existingHeaders); + $options['headers'] = array_merge($existingHeaders, $authHeaders); $options['timeout'] = $options['timeout'] ?? $this->getTimeout(); return $options; diff --git a/Modules/Invoices/Peppol/FormatHandlers/BaseFormatHandler.php b/Modules/Invoices/Peppol/FormatHandlers/BaseFormatHandler.php index 2de630d0f..4f6644915 100644 --- a/Modules/Invoices/Peppol/FormatHandlers/BaseFormatHandler.php +++ b/Modules/Invoices/Peppol/FormatHandlers/BaseFormatHandler.php @@ -5,6 +5,7 @@ use Modules\Invoices\Models\Invoice; use Modules\Invoices\Peppol\Enums\PeppolDocumentFormat; use Modules\Invoices\Peppol\Enums\PeppolEndpointScheme; +use RuntimeException; /** * BaseFormatHandler - Abstract base class for invoice format handlers. @@ -62,7 +63,7 @@ abstract protected function validateFormatSpecific(Invoice $invoice): array; public function getFormat(): PeppolDocumentFormat { if ($this->format === null) { - throw new \RuntimeException('Format has not been set on this handler. Call setFormat() first.'); + throw new RuntimeException('Format has not been set on this handler. Call setFormat() first.'); } return $this->format; diff --git a/Modules/Invoices/Tests/Unit/Http/Decorators/HttpClientExceptionHandlerTest.php b/Modules/Invoices/Tests/Unit/Http/Decorators/HttpClientExceptionHandlerTest.php index fa2eaf598..3619adbab 100644 --- a/Modules/Invoices/Tests/Unit/Http/Decorators/HttpClientExceptionHandlerTest.php +++ b/Modules/Invoices/Tests/Unit/Http/Decorators/HttpClientExceptionHandlerTest.php @@ -203,14 +203,6 @@ public function it_sanitizes_auth_credentials_in_logs(): void })); } - #[Test] - #[Group('http_client_failing')] - public function it_forwards_method_calls_to_wrapped_client(): void - { - // This test is not applicable since ApiClient only exposes request(). - $this->assertTrue(true); - } - #[Test] #[Group('http_client_failing')] public function it_makes_post_request_with_exception_handling(): void @@ -352,7 +344,7 @@ public function it_handles_http_exceptions(): void 'https://api.example.com/*' => Http::response(['error' => 'Not Found'], 404), ]); - /* act & assert */ + /* Act & Assert */ $this->expectException(RequestException::class); $this->handler->request(RequestMethod::GET, 'test'); } From 6c2e6ad7761220265f47634102dbd4a030b5a842 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 11 Jan 2026 13:04:02 +0000 Subject: [PATCH 7/7] style: apply Laravel Pint fixes --- pint_output.log | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 pint_output.log diff --git a/pint_output.log b/pint_output.log new file mode 100644 index 000000000..79f26c47c --- /dev/null +++ b/pint_output.log @@ -0,0 +1,5 @@ + + + No dirty files found. + +