Skip to content

Commit fe03ca5

Browse files
nielsdrost7CopilotCopilotcoderabbitai[bot]wysiwyg
authored
[IP-126]: Feature: Implement peppol (#113)
* Making export possible refs #85 * Some more Export tests refs #85 * Making export possible refs #85 * Adds the maatwebsite/excel package and prepares all exports, export services and actions that call the export services refs #85 * Intermediary commits for the exports refs #85 * Intermediary commits for the exports refs #85 * Intermediary commits for the exports refs #85 * Exports for Projects, Tasks, Products, Payments, Invoices, Quotes, Expenses, Relations, Contacts refs #85 * Exports for Projects, Tasks, Products, Payments, Invoices, Quotes, Expenses, Relations, Contacts refs #85 * just a new phpstan baseline (8 more errors) * Initial plan * Add comprehensive Peppol integration with HTTP clients, services, actions, and tests Co-authored-by: nielsdrost7 <[email protected]> * Add comprehensive Peppol documentation and example configuration Co-authored-by: nielsdrost7 <[email protected]> * Add decorator tests and complete documentation with files summary Co-authored-by: nielsdrost7 <[email protected]> * Add Peppol fields to relations table and create supporting enums and traits Co-authored-by: nielsdrost7 <[email protected]> * Refactor HTTP client architecture: simplify to single request() method, remove convenience methods Co-authored-by: nielsdrost7 <[email protected]> * Update Modules/Invoices/Providers/InvoicesServiceProvider.php Co-authored-by: Copilot <[email protected]> * Update Modules/Invoices/Tests/Unit/Peppol/Services/PeppolServiceTest.php Co-authored-by: Copilot <[email protected]> * Update Modules/Invoices/Tests/Unit/Actions/SendInvoiceToPeppolActionTest.php Co-authored-by: Copilot <[email protected]> * Expand Peppol infrastructure: comprehensive format enum, endpoint schemes, Strategy Pattern handlers, and configurable settings Co-authored-by: nielsdrost7 <[email protected]> * Implement Strategy Pattern for format handlers with PEPPOL BIS and UBL handlers, update PeppolService with LogsApiRequests trait Co-authored-by: nielsdrost7 <[email protected]> * Implement complete e-invoice.be API: Participants, Tracking, Webhooks, and Health clients with comprehensive documentation Co-authored-by: nielsdrost7 <[email protected]> * Add comprehensive implementation summary documenting all Peppol features and architecture Co-authored-by: nielsdrost7 <[email protected]> * Implement CII format handler with comprehensive UN/CEFACT standard compliance Co-authored-by: nielsdrost7 <[email protected]> * Fix ApiClient references across test suite and register CII handler Co-authored-by: nielsdrost7 <[email protected]> * Add comprehensive tests for PeppolDocumentFormat enum with 15 test cases Co-authored-by: nielsdrost7 <[email protected]> * Update Modules/Clients/Database/Migrations/2025_10_01_002042_add_peppol_fields_to_relations_table.php Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Update Modules/Invoices/Peppol/Clients/EInvoiceBe/EInvoiceBeClient.php Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Implement missing Peppol format handlers and comprehensive test suite (#108) * Initial plan * Add missing Peppol format handlers and PeppolEndpointScheme tests Co-authored-by: nielsdrost7 <[email protected]> * Add comprehensive tests for format handlers and update README with format documentation Co-authored-by: nielsdrost7 <[email protected]> * 📝 Add docstrings to `copilot/implement-format-handlers` (#109) Docstrings generation was requested by @nielsdrost7. * #108 (comment) The following files were modified: * `Modules/Invoices/Peppol/FormatHandlers/EhfHandler.php` * `Modules/Invoices/Peppol/FormatHandlers/FacturXHandler.php` * `Modules/Invoices/Peppol/FormatHandlers/FacturaeHandler.php` * `Modules/Invoices/Peppol/FormatHandlers/FatturaPaHandler.php` * `Modules/Invoices/Peppol/FormatHandlers/OioublHandler.php` * `Modules/Invoices/Peppol/FormatHandlers/ZugferdHandler.php` Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: nielsdrost7 <[email protected]> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * feat: Implement PEPPOL e-invoicing with dynamic provider architecture (#104) * Initial plan * Add core PEPPOL database migrations, models, providers, and events Co-authored-by: nielsdrost7 <[email protected]> * Add Jobs, Services, and enhanced configuration for PEPPOL lifecycle management Co-authored-by: nielsdrost7 <[email protected]> * Add audit listener, console commands, and comprehensive architecture documentation Co-authored-by: nielsdrost7 <[email protected]> * Refactor PEPPOL to use enums, remove JSON columns, use key-value config, and logging trait Co-authored-by: nielsdrost7 <[email protected]> * Implement dynamic provider discovery by scanning Providers directory Co-authored-by: nielsdrost7 <[email protected]> * Update Modules/Invoices/Models/CustomerPeppolValidationHistory.php Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * 📝 Add docstrings to `copilot/add-peppol-architecture-components` (#105) Docstrings generation was requested by @nielsdrost7. * #104 (comment) The following files were modified: * `Modules/Clients/Database/Migrations/2025_10_02_000007_add_peppol_validation_fields_to_relations_table.php` * `Modules/Clients/Models/Relation.php` * `Modules/Invoices/Console/Commands/PollPeppolStatusCommand.php` * `Modules/Invoices/Console/Commands/RetryFailedPeppolTransmissionsCommand.php` * `Modules/Invoices/Console/Commands/TestPeppolIntegrationCommand.php` * `Modules/Invoices/Database/Migrations/2025_10_02_000001_create_peppol_integrations_table.php` * `Modules/Invoices/Database/Migrations/2025_10_02_000002_create_peppol_integration_config_table.php` * `Modules/Invoices/Database/Migrations/2025_10_02_000003_create_peppol_transmissions_table.php` * `Modules/Invoices/Database/Migrations/2025_10_02_000004_create_peppol_transmission_responses_table.php` * `Modules/Invoices/Database/Migrations/2025_10_02_000005_create_customer_peppol_validation_history_table.php` * `Modules/Invoices/Database/Migrations/2025_10_02_000006_create_customer_peppol_validation_responses_table.php` * `Modules/Invoices/Enums/PeppolConnectionStatus.php` * `Modules/Invoices/Enums/PeppolErrorType.php` * `Modules/Invoices/Enums/PeppolTransmissionStatus.php` * `Modules/Invoices/Enums/PeppolValidationStatus.php` * `Modules/Invoices/Events/Peppol/PeppolAcknowledgementReceived.php` * `Modules/Invoices/Events/Peppol/PeppolEvent.php` * `Modules/Invoices/Events/Peppol/PeppolIdValidationCompleted.php` * `Modules/Invoices/Events/Peppol/PeppolIntegrationCreated.php` * `Modules/Invoices/Events/Peppol/PeppolIntegrationTested.php` * `Modules/Invoices/Events/Peppol/PeppolTransmissionCreated.php` * `Modules/Invoices/Events/Peppol/PeppolTransmissionDead.php` * `Modules/Invoices/Events/Peppol/PeppolTransmissionFailed.php` * `Modules/Invoices/Events/Peppol/PeppolTransmissionPrepared.php` * `Modules/Invoices/Events/Peppol/PeppolTransmissionSent.php` * `Modules/Invoices/Jobs/Peppol/PeppolStatusPoller.php` * `Modules/Invoices/Jobs/Peppol/RetryFailedTransmissions.php` * `Modules/Invoices/Jobs/Peppol/SendInvoiceToPeppolJob.php` * `Modules/Invoices/Listeners/Peppol/LogPeppolEventToAudit.php` * `Modules/Invoices/Models/CustomerPeppolValidationHistory.php` * `Modules/Invoices/Models/CustomerPeppolValidationResponse.php` * `Modules/Invoices/Models/PeppolIntegration.php` * `Modules/Invoices/Models/PeppolIntegrationConfig.php` * `Modules/Invoices/Models/PeppolTransmission.php` * `Modules/Invoices/Models/PeppolTransmissionResponse.php` * `Modules/Invoices/Peppol/Contracts/ProviderInterface.php` * `Modules/Invoices/Peppol/FormatHandlers/FormatHandlerFactory.php` * `Modules/Invoices/Peppol/Providers/BaseProvider.php` * `Modules/Invoices/Peppol/Providers/EInvoiceBe/EInvoiceBeProvider.php` * `Modules/Invoices/Peppol/Providers/ProviderFactory.php` * `Modules/Invoices/Peppol/Providers/Storecove/StorecoveProvider.php` * `Modules/Invoices/Peppol/Services/PeppolManagementService.php` * `Modules/Invoices/Peppol/Services/PeppolTransformerService.php` * `Modules/Invoices/Traits/LogsPeppolActivity.php` Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * CodeRabbit Generated Unit Tests: Add PEPPOL PHPUnit test suite and testing documentation (#107) Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: nielsdrost7 <[email protected]> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Revert "feat: Implement PEPPOL e-invoicing with dynamic provider architecture…" (#112) This reverts commit 3462b27. * Copilot/add peppol architecture components (#114) * Initial plan * Add core PEPPOL database migrations, models, providers, and events Co-authored-by: nielsdrost7 <[email protected]> * Add Jobs, Services, and enhanced configuration for PEPPOL lifecycle management Co-authored-by: nielsdrost7 <[email protected]> * Add audit listener, console commands, and comprehensive architecture documentation Co-authored-by: nielsdrost7 <[email protected]> * Refactor PEPPOL to use enums, remove JSON columns, use key-value config, and logging trait Co-authored-by: nielsdrost7 <[email protected]> * Implement dynamic provider discovery by scanning Providers directory Co-authored-by: nielsdrost7 <[email protected]> * Update Modules/Invoices/Models/CustomerPeppolValidationHistory.php Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * 📝 Add docstrings to `copilot/add-peppol-architecture-components` (#105) Docstrings generation was requested by @nielsdrost7. * #104 (comment) The following files were modified: * `Modules/Clients/Database/Migrations/2025_10_02_000007_add_peppol_validation_fields_to_relations_table.php` * `Modules/Clients/Models/Relation.php` * `Modules/Invoices/Console/Commands/PollPeppolStatusCommand.php` * `Modules/Invoices/Console/Commands/RetryFailedPeppolTransmissionsCommand.php` * `Modules/Invoices/Console/Commands/TestPeppolIntegrationCommand.php` * `Modules/Invoices/Database/Migrations/2025_10_02_000001_create_peppol_integrations_table.php` * `Modules/Invoices/Database/Migrations/2025_10_02_000002_create_peppol_integration_config_table.php` * `Modules/Invoices/Database/Migrations/2025_10_02_000003_create_peppol_transmissions_table.php` * `Modules/Invoices/Database/Migrations/2025_10_02_000004_create_peppol_transmission_responses_table.php` * `Modules/Invoices/Database/Migrations/2025_10_02_000005_create_customer_peppol_validation_history_table.php` * `Modules/Invoices/Database/Migrations/2025_10_02_000006_create_customer_peppol_validation_responses_table.php` * `Modules/Invoices/Enums/PeppolConnectionStatus.php` * `Modules/Invoices/Enums/PeppolErrorType.php` * `Modules/Invoices/Enums/PeppolTransmissionStatus.php` * `Modules/Invoices/Enums/PeppolValidationStatus.php` * `Modules/Invoices/Events/Peppol/PeppolAcknowledgementReceived.php` * `Modules/Invoices/Events/Peppol/PeppolEvent.php` * `Modules/Invoices/Events/Peppol/PeppolIdValidationCompleted.php` * `Modules/Invoices/Events/Peppol/PeppolIntegrationCreated.php` * `Modules/Invoices/Events/Peppol/PeppolIntegrationTested.php` * `Modules/Invoices/Events/Peppol/PeppolTransmissionCreated.php` * `Modules/Invoices/Events/Peppol/PeppolTransmissionDead.php` * `Modules/Invoices/Events/Peppol/PeppolTransmissionFailed.php` * `Modules/Invoices/Events/Peppol/PeppolTransmissionPrepared.php` * `Modules/Invoices/Events/Peppol/PeppolTransmissionSent.php` * `Modules/Invoices/Jobs/Peppol/PeppolStatusPoller.php` * `Modules/Invoices/Jobs/Peppol/RetryFailedTransmissions.php` * `Modules/Invoices/Jobs/Peppol/SendInvoiceToPeppolJob.php` * `Modules/Invoices/Listeners/Peppol/LogPeppolEventToAudit.php` * `Modules/Invoices/Models/CustomerPeppolValidationHistory.php` * `Modules/Invoices/Models/CustomerPeppolValidationResponse.php` * `Modules/Invoices/Models/PeppolIntegration.php` * `Modules/Invoices/Models/PeppolIntegrationConfig.php` * `Modules/Invoices/Models/PeppolTransmission.php` * `Modules/Invoices/Models/PeppolTransmissionResponse.php` * `Modules/Invoices/Peppol/Contracts/ProviderInterface.php` * `Modules/Invoices/Peppol/FormatHandlers/FormatHandlerFactory.php` * `Modules/Invoices/Peppol/Providers/BaseProvider.php` * `Modules/Invoices/Peppol/Providers/EInvoiceBe/EInvoiceBeProvider.php` * `Modules/Invoices/Peppol/Providers/ProviderFactory.php` * `Modules/Invoices/Peppol/Providers/Storecove/StorecoveProvider.php` * `Modules/Invoices/Peppol/Services/PeppolManagementService.php` * `Modules/Invoices/Peppol/Services/PeppolTransformerService.php` * `Modules/Invoices/Traits/LogsPeppolActivity.php` Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * CodeRabbit Generated Unit Tests: Add PEPPOL PHPUnit test suite and testing documentation (#107) Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * ran pint * ran pint * ran phpstan, pint * pint, tmp --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: Copilot <[email protected]> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: wysiwyg <[email protected]>
1 parent 99eb214 commit fe03ca5

File tree

105 files changed

+16278
-46
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

105 files changed

+16278
-46
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\Schema;
6+
7+
return new class () extends Migration {
8+
/**
9+
* Run the migrations.
10+
*/
11+
public function up(): void
12+
{
13+
Schema::table('relations', function (Blueprint $table) {
14+
$table->string('peppol_id', 100)->nullable()->after('vat_number')
15+
->comment('Peppol participant identifier (e.g., BE:0123456789)');
16+
17+
$table->string('peppol_format', 50)->nullable()->after('peppol_id')
18+
->comment('Preferred Peppol document format (matches PeppolDocumentFormat values)');
19+
20+
$table->boolean('enable_e_invoicing')->default(false)->after('peppol_format')
21+
->comment('Whether e-invoicing via Peppol is enabled for this customer');
22+
});
23+
}
24+
25+
/**
26+
* Reverse the migrations.
27+
*/
28+
public function down(): void
29+
{
30+
Schema::table('relations', function (Blueprint $table) {
31+
$table->dropColumn(['peppol_id', 'peppol_format', 'enable_e_invoicing']);
32+
});
33+
}
34+
};
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\Schema;
6+
7+
return new class () extends Migration {
8+
/**
9+
* Add Peppol validation columns to the relations table.
10+
*
11+
* Adds nullable columns: `peppol_scheme` (string(50)) for Peppol endpoint scheme,
12+
* `peppol_validation_status` (string(20)) for quick lookup of validation state,
13+
* `peppol_validation_message` (text) for the last validation message, and
14+
* `peppol_validated_at` (timestamp) for when the Peppol ID was last validated.
15+
*/
16+
public function up(): void
17+
{
18+
Schema::table('relations', function (Blueprint $table): void {
19+
$table->string('peppol_scheme', 50)->nullable()->after('peppol_id')
20+
->comment('Peppol endpoint scheme (e.g., BE:CBE, DE:VAT)');
21+
22+
$table->string('peppol_validation_status', 20)->nullable()->after('enable_e_invoicing')
23+
->comment('Quick lookup: valid, invalid, not_found, error, null');
24+
25+
$table->text('peppol_validation_message')->nullable()->after('peppol_validation_status')
26+
->comment('Last validation result message');
27+
28+
$table->timestamp('peppol_validated_at')->nullable()->after('peppol_validation_message')
29+
->comment('When was the Peppol ID last validated');
30+
});
31+
}
32+
33+
/**
34+
* Removes Peppol-related columns from the `relations` table.
35+
*
36+
* Drops the columns: `peppol_scheme`, `peppol_validation_status`, `peppol_validation_message`, and `peppol_validated_at`.
37+
*/
38+
public function down(): void
39+
{
40+
Schema::table('relations', function (Blueprint $table): void {
41+
$table->dropColumn(['peppol_scheme', 'peppol_validation_status', 'peppol_validation_message', 'peppol_validated_at']);
42+
});
43+
}
44+
};

Modules/Clients/Models/Relation.php

Lines changed: 56 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -17,40 +17,49 @@
1717
use Modules\Core\Models\User;
1818
use Modules\Core\Traits\BelongsToCompany;
1919
use Modules\Expenses\Models\Expense;
20+
use Modules\Invoices\Enums\PeppolValidationStatus;
21+
use Modules\Invoices\Models\CustomerPeppolValidationHistory;
2022
use Modules\Invoices\Models\Invoice;
2123
use Modules\Payments\Models\Payment;
2224
use Modules\Projects\Models\Project;
2325
use Modules\Projects\Models\Task;
2426
use Modules\Quotes\Models\Quote;
2527

2628
/**
27-
* @property int $id
28-
* @property int $company_id
29-
* @property int|null $primary_contact_id
30-
* @property string $relation_type
31-
* @property string $relation_status
32-
* @property string $relation_number
33-
* @property string $company_name
34-
* @property string|null $trading_name
35-
* @property string|null $unique_name
36-
* @property string|null $id_number
37-
* @property string|null $coc_number
38-
* @property string|null $vat_number
39-
* @property Carbon $registered_at
40-
* @property mixed $created_at
41-
* @property mixed $updated_at
42-
* @property Invoice[] $invoices
43-
* @property Quote[] $quotes
44-
* @property Project[] $projects
45-
* @property Contact $contact
46-
* @property string|null $currency_code
47-
* @property string|null $language
48-
* @property Company $company
49-
* @property Collection|Contact[] $contacts
50-
* @property Collection|Expense[] $expenses
51-
* @property Collection|Payment[] $payments
52-
* @property Collection|User[] $users
53-
* @property Task[] $tasks
29+
* @property int $id
30+
* @property int $company_id
31+
* @property int|null $primary_contact_id
32+
* @property string $relation_type
33+
* @property string $relation_status
34+
* @property string $relation_number
35+
* @property string $company_name
36+
* @property string|null $trading_name
37+
* @property string|null $unique_name
38+
* @property string|null $id_number
39+
* @property string|null $coc_number
40+
* @property string|null $vat_number
41+
* @property string|null $peppol_id
42+
* @property string|null $peppol_scheme
43+
* @property string|null $peppol_format
44+
* @property bool $enable_e_invoicing
45+
* @property PeppolValidationStatus|null $peppol_validation_status
46+
* @property string|null $peppol_validation_message
47+
* @property Carbon|null $peppol_validated_at
48+
* @property Carbon $registered_at
49+
* @property mixed $created_at
50+
* @property mixed $updated_at
51+
* @property Invoice[] $invoices
52+
* @property Quote[] $quotes
53+
* @property Project[] $projects
54+
* @property Contact $contact
55+
* @property string|null $currency_code
56+
* @property string|null $language
57+
* @property Company $company
58+
* @property Collection|Contact[] $contacts
59+
* @property Collection|Expense[] $expenses
60+
* @property Collection|Payment[] $payments
61+
* @property Collection|User[] $users
62+
* @property Task[] $tasks
5463
*/
5564
class Relation extends Model
5665
{
@@ -62,8 +71,11 @@ class Relation extends Model
6271
protected $table = 'relations';
6372

6473
protected $casts = [
65-
'relation_type' => RelationType::class,
66-
'relation_status' => RelationStatus::class,
74+
'relation_type' => RelationType::class,
75+
'relation_status' => RelationStatus::class,
76+
'enable_e_invoicing' => 'boolean',
77+
'peppol_validation_status' => PeppolValidationStatus::class,
78+
'peppol_validated_at' => 'datetime',
6779
];
6880

6981
protected $guarded = [];
@@ -157,11 +169,26 @@ public function tasks(): HasMany
157169
return $this->hasMany(Task::class, 'customer_id');
158170
}
159171

172+
/**
173+
* Define a one-to-many relationship to User models.
174+
*
175+
* @return HasMany the has-many relationship for User models
176+
*/
160177
public function users(): HasMany
161178
{
162179
return $this->hasMany(User::class);
163180
}
164181

182+
/**
183+
* Get the Peppol validation history records for this relation.
184+
*
185+
* @return \Illuminate\Database\Eloquent\Relations\HasMany collection of CustomerPeppolValidationHistory models related by `customer_id`
186+
*/
187+
public function peppolValidationHistory(): HasMany
188+
{
189+
return $this->hasMany(CustomerPeppolValidationHistory::class, 'customer_id');
190+
}
191+
165192
/*
166193
|--------------------------------------------------------------------------
167194
| Accessors

Modules/Core/Models/Company.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,6 @@ class Company extends Model implements HasName, HasCurrentTenantLabel
8989
| Relationships
9090
|--------------------------------------------------------------------------
9191
*/
92-
/** @phpstan-ignore-next-line */
9392
public function customerAdmins(): BelongsToMany
9493
{
9594
return $this->users()

Modules/Core/Tests/Unit/DateFieldAutoPopulationTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ protected function setUp(): void
3333
{
3434
parent::setUp();
3535

36-
$this->user = User::factory()->create();
37-
$this->company = Company::factory()->create();
36+
$this->user = User::factory()->create(); /** @var User $user */
37+
$this->company = Company::factory()->create(); /** @var Company $company */
3838
$this->user->companies()->attach($this->company);
3939
}
4040

Modules/Expenses/Filament/Company/Widgets/RecentExpensesWidget.php

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
use Illuminate\Database\Eloquent\Relations\Relation;
99
use Modules\Core\Helpers\EnumHelper;
1010
use Modules\Expenses\Enums\ExpenseStatus;
11-
use Modules\Expenses\Models\Expense;
1211

1312
class RecentExpensesWidget extends TableWidget
1413
{
@@ -18,7 +17,7 @@ class RecentExpensesWidget extends TableWidget
1817

1918
protected function getTableQuery(): Builder|Relation|null
2019
{
21-
return Expense::query()->latest()->limit(10);
20+
return \Modules\Expenses\Models\Expense::query()->latest()->limit(10); /** @var Builder $query */
2221
}
2322

2423
protected function getTableColumns(): array
@@ -27,8 +26,8 @@ protected function getTableColumns(): array
2726
TextColumn::make('expense_status')
2827
->label(trans('ip.expense_status'))
2928
->badge()
30-
->formatStateUsing(fn ($state) => EnumHelper::safeEnum(ExpenseStatus::class, $state)?->label() ?? '-')
31-
->color(fn ($state) => EnumHelper::safeEnum(ExpenseStatus::class, $state)?->color() ?? 'secondary'),
29+
->formatStateUsing(fn ($state) => (EnumHelper::safeEnum(ExpenseStatus::class, $state) && method_exists(EnumHelper::safeEnum(ExpenseStatus::class, $state), 'label')) ? EnumHelper::safeEnum(ExpenseStatus::class, $state)->label() : '-')
30+
->color(fn ($state) => (EnumHelper::safeEnum(ExpenseStatus::class, $state) && method_exists(EnumHelper::safeEnum(ExpenseStatus::class, $state), 'color')) ? EnumHelper::safeEnum(ExpenseStatus::class, $state)->color() : 'secondary'),
3231
TextColumn::make('expenseCategory.category_name')->label(trans('ip.expense_category')),
3332
TextColumn::make('amount')->label(trans('ip.amount')),
3433
];
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
<?php
2+
3+
namespace Modules\Invoices\Actions;
4+
5+
use Illuminate\Http\Client\RequestException;
6+
use InvalidArgumentException;
7+
use Modules\Invoices\Models\Invoice;
8+
use Modules\Invoices\Peppol\Services\PeppolService;
9+
10+
/**
11+
* SendInvoiceToPeppolAction - Action for sending invoices to Peppol network.
12+
*
13+
* This action handles the process of gathering invoice information and
14+
* sending it to the Peppol network through the PeppolService. It provides
15+
* a clean interface for both the EditInvoice page and the ListInvoices table.
16+
*/
17+
class SendInvoiceToPeppolAction
18+
{
19+
/**
20+
* The Peppol service instance.
21+
*
22+
* @var PeppolService
23+
*/
24+
protected PeppolService $peppolService;
25+
26+
/**
27+
* Constructor.
28+
*
29+
* @param PeppolService $peppolService The Peppol service
30+
*/
31+
public function __construct(PeppolService $peppolService)
32+
{
33+
$this->peppolService = $peppolService;
34+
}
35+
36+
/**
37+
* Execute the action to send an invoice to Peppol.
38+
*
39+
* This method gathers all necessary information from the invoice and
40+
* submits it to the Peppol network. It returns the result of the operation.
41+
*
42+
* @param Invoice $invoice The invoice to send
43+
* @param array<string, mixed> $additionalData Optional additional data (e.g., Peppol ID)
44+
*
45+
* @return array<string, mixed> The result of the operation
46+
*
47+
* @throws RequestException If the Peppol API request fails
48+
* @throws InvalidArgumentException If the invoice data is invalid
49+
*/
50+
public function execute(Invoice $invoice, array $additionalData = []): array
51+
{
52+
// Load necessary relationships
53+
$invoice->load(['customer', 'invoiceItems']);
54+
55+
// Validate that invoice is in a state that can be sent
56+
$this->validateInvoiceState($invoice);
57+
58+
// Send to Peppol
59+
$result = $this->peppolService->sendInvoiceToPeppol($invoice, $additionalData);
60+
61+
// Optionally, you could update the invoice record here
62+
// to track that it was sent to Peppol (e.g., add a peppol_document_id field)
63+
// $invoice->update(['peppol_document_id' => $result['document_id']]);
64+
65+
return $result;
66+
}
67+
68+
/**
69+
* Get the status of a previously sent invoice from Peppol.
70+
*
71+
* @param string $documentId The Peppol document ID
72+
*
73+
* @return array<string, mixed> Status information
74+
*
75+
* @throws RequestException If the API request fails
76+
*/
77+
public function getStatus(string $documentId): array
78+
{
79+
return $this->peppolService->getDocumentStatus($documentId);
80+
}
81+
82+
/**
83+
* Cancel a Peppol document transmission.
84+
*
85+
* @param string $documentId The Peppol document ID
86+
*
87+
* @return bool True if cancellation was successful
88+
*
89+
* @throws RequestException If the API request fails
90+
*/
91+
public function cancel(string $documentId): bool
92+
{
93+
return $this->peppolService->cancelDocument($documentId);
94+
}
95+
96+
/**
97+
* Validate that the invoice is in a valid state for Peppol transmission.
98+
*
99+
* @param Invoice $invoice The invoice to validate
100+
*
101+
* @return void
102+
*
103+
* @throws InvalidArgumentException If validation fails
104+
*/
105+
protected function validateInvoiceState(Invoice $invoice): void
106+
{
107+
// Check if invoice is in draft status - drafts should not be sent
108+
if ($invoice->invoice_status === 'draft') {
109+
throw new InvalidArgumentException('Cannot send draft invoices to Peppol');
110+
}
111+
112+
// Additional business logic validation can be added here
113+
}
114+
}

0 commit comments

Comments
 (0)