Skip to content

Commit 3bd5d6f

Browse files
jbagsikcursoragent
andcommitted
Deduplicate validation persistence in ValidateArtifactJob
Extract ArtifactValidationPersister for shared KOSIT record+attach logic; veraPDF verdicts persist via supplemental closures so the shared seam does not type-hint optional validator packages. Closes mooxphp/e-billing#7. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent e8d993d commit 3bd5d6f

4 files changed

Lines changed: 85 additions & 20 deletions

File tree

packages/e-billing/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
### Changed
1313

14+
- Deduplicated KOSIT/veraPDF validation persistence in `ValidateArtifactJob` via `ArtifactValidationPersister`; supplemental verdicts (veraPDF) stay as closures so the shared seam does not type-hint optional validator packages. No behaviour change.
1415
- Replaced `GenerateXmlJob` / `ValidateXmlJob` / `MergeZugferdPdfJob` with `GenerateArtifactJob``ValidateArtifactJob` (generate-first pipeline).
1516
- `gateway_status` enum is now format-agnostic: `generating`, `generation_failed`, `validating`, `validated`, `validation_failed`, `validator_error`, `ignored_foreign`.
1617
- Hybrid ZUGFeRD artifacts are built before validation; deliverable PDFs are unencrypted; `artifact_content_hash` is populated on validation pass.

packages/e-billing/src/Jobs/ValidateArtifactJob.php

Lines changed: 42 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
use Moox\EBilling\Formats\FormatRegistry;
2323
use Moox\EBilling\Models\EbillingDocument;
2424
use Moox\EBilling\Services\InboxMessagePipelineFinalizer;
25+
use Moox\EBilling\Support\ArtifactValidationPersister;
2526
use Moox\EBilling\Support\EBillingArtifactNaming;
2627
use Moox\Jobs\Traits\JobProgress;
2728
use Moox\KositValidator\Actions\RecordKositValidation;
@@ -61,6 +62,7 @@ public function handle(
6162
RecordKositValidation $recordKositValidation,
6263
VeraPdfService $veraPdf,
6364
RecordVeraPdfValidation $recordVeraPdfValidation,
65+
ArtifactValidationPersister $validationPersister,
6466
InboxMessagePipelineFinalizer $pipelineFinalizer,
6567
): void {
6668
$this->setProgress(0);
@@ -190,6 +192,11 @@ public function handle(
190192
($veraPdfResult !== null && ! $veraPdfPassed) ? $veraPdfResult->errors() : [],
191193
));
192194

195+
$supplementalPersisters = $this->supplementalValidationPersisters(
196+
$veraPdfResult,
197+
$recordVeraPdfValidation,
198+
);
199+
193200
if ($passed) {
194201
$deliverablePath = $document?->deliverableStoragePath($definition->artifactKind);
195202
if ($deliverablePath === null || $deliverablePath === '') {
@@ -204,24 +211,22 @@ public function handle(
204211
$hash = hash('sha256', $artifactContent);
205212

206213
DB::transaction(function () use (
214+
$validationPersister,
207215
$recordKositValidation,
208-
$recordVeraPdfValidation,
209216
$kositResult,
210-
$veraPdfResult,
217+
$supplementalPersisters,
211218
$document,
212219
$attachment,
213220
$hash,
214221
): void {
215-
$kositValidation = $recordKositValidation($kositResult);
222+
$validationPersister->persist(
223+
$document,
224+
$kositResult,
225+
$recordKositValidation,
226+
$supplementalPersisters,
227+
);
216228

217229
if ($document !== null) {
218-
$document->kositValidations()->attach($kositValidation->id);
219-
220-
if ($veraPdfResult instanceof VeraPdfResult) {
221-
$veraPdfValidation = $recordVeraPdfValidation($veraPdfResult);
222-
$document->veraPdfValidations()->attach($veraPdfValidation->id);
223-
}
224-
225230
$document->artifact_content_hash = $hash;
226231
$document->gateway_status = EBillingAttachmentProcessingStatus::Validated;
227232
$document->processed_at = now();
@@ -236,24 +241,22 @@ public function handle(
236241
$failureMessage = $errorStrings !== [] ? implode('; ', $errorStrings) : 'Artifact validation failed';
237242

238243
DB::transaction(function () use (
244+
$validationPersister,
239245
$recordKositValidation,
240-
$recordVeraPdfValidation,
241246
$kositResult,
242-
$veraPdfResult,
247+
$supplementalPersisters,
243248
$attachment,
244249
$document,
245250
$failureMessage,
246251
): void {
247-
$kositValidation = $recordKositValidation($kositResult);
252+
$validationPersister->persist(
253+
$document,
254+
$kositResult,
255+
$recordKositValidation,
256+
$supplementalPersisters,
257+
);
248258

249259
if ($document !== null) {
250-
$document->kositValidations()->attach($kositValidation->id);
251-
252-
if ($veraPdfResult instanceof VeraPdfResult) {
253-
$veraPdfValidation = $recordVeraPdfValidation($veraPdfResult);
254-
$document->veraPdfValidations()->attach($veraPdfValidation->id);
255-
}
256-
257260
$document->gateway_status = EBillingAttachmentProcessingStatus::ValidationFailed;
258261
$document->save();
259262
}
@@ -276,6 +279,25 @@ public function handle(
276279
$this->setProgress(100);
277280
}
278281

282+
/**
283+
* @return list<\Closure(EbillingDocument): void>
284+
*/
285+
private function supplementalValidationPersisters(
286+
mixed $veraPdfResult,
287+
RecordVeraPdfValidation $recordVeraPdfValidation,
288+
): array {
289+
if (! $veraPdfResult instanceof VeraPdfResult) {
290+
return [];
291+
}
292+
293+
return [
294+
static function (EbillingDocument $document) use ($veraPdfResult, $recordVeraPdfValidation): void {
295+
$veraPdfValidation = $recordVeraPdfValidation($veraPdfResult);
296+
$document->veraPdfValidations()->attach($veraPdfValidation->id);
297+
},
298+
];
299+
}
300+
279301
public function failed(?Throwable $exception = null): void
280302
{
281303
$attachment = InboxAttachment::query()->find($this->inboxAttachmentId);
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Moox\EBilling\Support;
6+
7+
use Closure;
8+
use Moox\EBilling\Models\EbillingDocument;
9+
use Moox\KositValidator\Actions\RecordKositValidation;
10+
use Moox\KositValidator\DTOs\KositResult;
11+
12+
final class ArtifactValidationPersister
13+
{
14+
/**
15+
* Persist KOSIT verdict and attach optional supplemental verdicts (e.g. veraPDF via closure).
16+
*
17+
* Supplemental persisters stay outside this class so e-billing does not type-hint optional
18+
* validator packages in the shared persistence seam.
19+
*
20+
* @param list<Closure(EbillingDocument): void> $supplementalPersisters
21+
*/
22+
public function persist(
23+
?EbillingDocument $document,
24+
KositResult $kositResult,
25+
RecordKositValidation $recordKositValidation,
26+
array $supplementalPersisters = [],
27+
): void {
28+
$kositValidation = $recordKositValidation($kositResult);
29+
30+
if ($document === null) {
31+
return;
32+
}
33+
34+
$document->kositValidations()->attach($kositValidation->id);
35+
36+
foreach ($supplementalPersisters as $persistSupplemental) {
37+
$persistSupplemental($document);
38+
}
39+
}
40+
}

packages/e-billing/tests/Feature/Jobs/GenerateThenValidateArtifactJobTest.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Moox\EBilling\Formats\FormatRegistry;
1515
use Moox\EBilling\Jobs\ValidateArtifactJob;
1616
use Moox\EBilling\Services\InboxMessagePipelineFinalizer;
17+
use Moox\EBilling\Support\ArtifactValidationPersister;
1718
use Moox\EBilling\Tests\Support\InvoiceFixtures;
1819
use Moox\EBilling\Tests\Support\PipelineFixtures;
1920
use Moox\EBilling\Tests\TestCase;
@@ -113,6 +114,7 @@ function runValidateArtifactJob(int $attachmentId): void
113114
app(RecordKositValidation::class),
114115
app(VeraPdfService::class),
115116
app(RecordVeraPdfValidation::class),
117+
app(ArtifactValidationPersister::class),
116118
app(InboxMessagePipelineFinalizer::class),
117119
);
118120
}

0 commit comments

Comments
 (0)