瀏覽代碼

Merge pull request #184 from andrewdwallo/feat/exports

Feat/exports
3.x
Andrew Wallo 3 月之前
父節點
當前提交
edccd0e911
沒有連結到貢獻者的電子郵件帳戶。

+ 5
- 0
app/Filament/Company/Resources/Accounting/TransactionResource.php 查看文件

@@ -4,6 +4,7 @@ namespace App\Filament\Company\Resources\Accounting;
4 4
 
5 5
 use App\Enums\Accounting\TransactionType;
6 6
 use App\Filament\Company\Resources\Accounting\TransactionResource\Pages;
7
+use App\Filament\Exports\Accounting\TransactionExporter;
7 8
 use App\Filament\Forms\Components\DateRangeSelect;
8 9
 use App\Filament\Tables\Actions\EditTransactionAction;
9 10
 use App\Filament\Tables\Actions\ReplicateBulkAction;
@@ -155,6 +156,10 @@ class TransactionResource extends Resource
155 156
                 $filters['updated_at'],
156 157
             ])
157 158
             ->filtersFormWidth(MaxWidth::ThreeExtraLarge)
159
+            ->headerActions([
160
+                Tables\Actions\ExportAction::make()
161
+                    ->exporter(TransactionExporter::class),
162
+            ])
158 163
             ->actions([
159 164
                 Tables\Actions\Action::make('markAsReviewed')
160 165
                     ->label('Mark as reviewed')

+ 5
- 0
app/Filament/Company/Resources/Purchases/BillResource.php 查看文件

@@ -12,6 +12,7 @@ use App\Enums\Accounting\PaymentMethod;
12 12
 use App\Enums\Setting\PaymentTerms;
13 13
 use App\Filament\Company\Resources\Purchases\BillResource\Pages;
14 14
 use App\Filament\Company\Resources\Purchases\VendorResource\RelationManagers\BillsRelationManager;
15
+use App\Filament\Exports\Accounting\BillExporter;
15 16
 use App\Filament\Forms\Components\CreateAdjustmentSelect;
16 17
 use App\Filament\Forms\Components\CreateCurrencySelect;
17 18
 use App\Filament\Forms\Components\CreateOfferingSelect;
@@ -427,6 +428,10 @@ class BillResource extends Resource
427 428
                     ->untilLabel('To due date')
428 429
                     ->indicatorLabel('Due'),
429 430
             ])
431
+            ->headerActions([
432
+                Tables\Actions\ExportAction::make()
433
+                    ->exporter(BillExporter::class),
434
+            ])
430 435
             ->actions([
431 436
                 Tables\Actions\ActionGroup::make([
432 437
                     Tables\Actions\ActionGroup::make([

+ 5
- 0
app/Filament/Company/Resources/Purchases/VendorResource.php 查看文件

@@ -6,6 +6,7 @@ use App\Enums\Accounting\BillStatus;
6 6
 use App\Enums\Common\ContractorType;
7 7
 use App\Enums\Common\VendorType;
8 8
 use App\Filament\Company\Resources\Purchases\VendorResource\Pages;
9
+use App\Filament\Exports\Common\VendorExporter;
9 10
 use App\Filament\Forms\Components\AddressFields;
10 11
 use App\Filament\Forms\Components\CreateCurrencySelect;
11 12
 use App\Filament\Forms\Components\CustomSection;
@@ -210,6 +211,10 @@ class VendorResource extends Resource
210 211
             ->filters([
211 212
                 //
212 213
             ])
214
+            ->headerActions([
215
+                Tables\Actions\ExportAction::make()
216
+                    ->exporter(VendorExporter::class),
217
+            ])
213 218
             ->actions([
214 219
                 Tables\Actions\ActionGroup::make([
215 220
                     Tables\Actions\ActionGroup::make([

+ 5
- 0
app/Filament/Company/Resources/Sales/ClientResource.php 查看文件

@@ -3,6 +3,7 @@
3 3
 namespace App\Filament\Company\Resources\Sales;
4 4
 
5 5
 use App\Filament\Company\Resources\Sales\ClientResource\Pages;
6
+use App\Filament\Exports\Common\ClientExporter;
6 7
 use App\Filament\Forms\Components\AddressFields;
7 8
 use App\Filament\Forms\Components\CreateCurrencySelect;
8 9
 use App\Filament\Forms\Components\CustomSection;
@@ -299,6 +300,10 @@ class ClientResource extends Resource
299 300
             ->filters([
300 301
                 //
301 302
             ])
303
+            ->headerActions([
304
+                Tables\Actions\ExportAction::make()
305
+                    ->exporter(ClientExporter::class),
306
+            ])
302 307
             ->actions([
303 308
                 Tables\Actions\ActionGroup::make([
304 309
                     Tables\Actions\ActionGroup::make([

+ 5
- 0
app/Filament/Company/Resources/Sales/EstimateResource.php 查看文件

@@ -12,6 +12,7 @@ use App\Enums\Setting\PaymentTerms;
12 12
 use App\Filament\Company\Resources\Sales\ClientResource\RelationManagers\EstimatesRelationManager;
13 13
 use App\Filament\Company\Resources\Sales\EstimateResource\Pages;
14 14
 use App\Filament\Company\Resources\Sales\EstimateResource\Widgets;
15
+use App\Filament\Exports\Accounting\EstimateExporter;
15 16
 use App\Filament\Forms\Components\CreateAdjustmentSelect;
16 17
 use App\Filament\Forms\Components\CreateClientSelect;
17 18
 use App\Filament\Forms\Components\CreateCurrencySelect;
@@ -418,6 +419,10 @@ class EstimateResource extends Resource
418 419
                     ->untilLabel('To expiration date')
419 420
                     ->indicatorLabel('Due'),
420 421
             ])
422
+            ->headerActions([
423
+                Tables\Actions\ExportAction::make()
424
+                    ->exporter(EstimateExporter::class),
425
+            ])
421 426
             ->actions([
422 427
                 Tables\Actions\ActionGroup::make([
423 428
                     Tables\Actions\ActionGroup::make([

+ 5
- 0
app/Filament/Company/Resources/Sales/InvoiceResource.php 查看文件

@@ -12,6 +12,7 @@ use App\Enums\Setting\PaymentTerms;
12 12
 use App\Filament\Company\Resources\Sales\ClientResource\RelationManagers\InvoicesRelationManager;
13 13
 use App\Filament\Company\Resources\Sales\InvoiceResource\Pages;
14 14
 use App\Filament\Company\Resources\Sales\InvoiceResource\Widgets;
15
+use App\Filament\Exports\Accounting\InvoiceExporter;
15 16
 use App\Filament\Forms\Components\CreateAdjustmentSelect;
16 17
 use App\Filament\Forms\Components\CreateClientSelect;
17 18
 use App\Filament\Forms\Components\CreateCurrencySelect;
@@ -476,6 +477,10 @@ class InvoiceResource extends Resource
476 477
                     ->untilLabel('To due date')
477 478
                     ->indicatorLabel('Due'),
478 479
             ])
480
+            ->headerActions([
481
+                Tables\Actions\ExportAction::make()
482
+                    ->exporter(InvoiceExporter::class),
483
+            ])
479 484
             ->actions([
480 485
                 Tables\Actions\ActionGroup::make([
481 486
                     Tables\Actions\ActionGroup::make([

+ 5
- 0
app/Filament/Company/Resources/Sales/RecurringInvoiceResource.php 查看文件

@@ -11,6 +11,7 @@ use App\Enums\Accounting\RecurringInvoiceStatus;
11 11
 use App\Enums\Setting\PaymentTerms;
12 12
 use App\Filament\Company\Resources\Sales\ClientResource\RelationManagers\RecurringInvoicesRelationManager;
13 13
 use App\Filament\Company\Resources\Sales\RecurringInvoiceResource\Pages;
14
+use App\Filament\Exports\Accounting\RecurringInvoiceExporter;
14 15
 use App\Filament\Forms\Components\CreateAdjustmentSelect;
15 16
 use App\Filament\Forms\Components\CreateClientSelect;
16 17
 use App\Filament\Forms\Components\CreateCurrencySelect;
@@ -354,6 +355,10 @@ class RecurringInvoiceResource extends Resource
354 355
                     ->options(RecurringInvoiceStatus::class)
355 356
                     ->native(false),
356 357
             ])
358
+            ->headerActions([
359
+                Tables\Actions\ExportAction::make()
360
+                    ->exporter(RecurringInvoiceExporter::class),
361
+            ])
357 362
             ->actions([
358 363
                 Tables\Actions\ActionGroup::make([
359 364
                     Tables\Actions\ActionGroup::make([

+ 63
- 0
app/Filament/Exports/Accounting/BillExporter.php 查看文件

@@ -0,0 +1,63 @@
1
+<?php
2
+
3
+namespace App\Filament\Exports\Accounting;
4
+
5
+use App\Models\Accounting\Bill;
6
+use Filament\Actions\Exports\ExportColumn;
7
+use Filament\Actions\Exports\Exporter;
8
+use Filament\Actions\Exports\Models\Export;
9
+
10
+class BillExporter extends Exporter
11
+{
12
+    protected static ?string $model = Bill::class;
13
+
14
+    public static function getColumns(): array
15
+    {
16
+        return [
17
+            ExportColumn::make('bill_number'),
18
+            ExportColumn::make('date')
19
+                ->date(),
20
+            ExportColumn::make('due_date')
21
+                ->date(),
22
+            ExportColumn::make('vendor.name'),
23
+            ExportColumn::make('status')
24
+                ->enum(),
25
+            ExportColumn::make('total')
26
+                ->money(),
27
+            ExportColumn::make('amount_paid')
28
+                ->money(),
29
+            ExportColumn::make('amount_due')
30
+                ->money(),
31
+            ExportColumn::make('subtotal')
32
+                ->money(),
33
+            ExportColumn::make('tax_total')
34
+                ->money(),
35
+            ExportColumn::make('discount_total')
36
+                ->money(),
37
+            ExportColumn::make('discount_rate'),
38
+            ExportColumn::make('currency_code'),
39
+            ExportColumn::make('order_number'),
40
+            ExportColumn::make('paid_at')
41
+                ->dateTime(),
42
+            ExportColumn::make('notes')
43
+                ->enabledByDefault(false),
44
+            ExportColumn::make('discount_method')
45
+                ->enabledByDefault(false)
46
+                ->enum(),
47
+            ExportColumn::make('discount_computation')
48
+                ->enabledByDefault(false)
49
+                ->enum(),
50
+        ];
51
+    }
52
+
53
+    public static function getCompletedNotificationBody(Export $export): string
54
+    {
55
+        $body = 'Your bill export has completed and ' . number_format($export->successful_rows) . ' ' . str('row')->plural($export->successful_rows) . ' exported.';
56
+
57
+        if ($failedRowsCount = $export->getFailedRowsCount()) {
58
+            $body .= ' ' . number_format($failedRowsCount) . ' ' . str('row')->plural($failedRowsCount) . ' failed to export.';
59
+        }
60
+
61
+        return $body;
62
+    }
63
+}

+ 65
- 0
app/Filament/Exports/Accounting/EstimateExporter.php 查看文件

@@ -0,0 +1,65 @@
1
+<?php
2
+
3
+namespace App\Filament\Exports\Accounting;
4
+
5
+use App\Models\Accounting\Estimate;
6
+use Filament\Actions\Exports\ExportColumn;
7
+use Filament\Actions\Exports\Exporter;
8
+use Filament\Actions\Exports\Models\Export;
9
+
10
+class EstimateExporter extends Exporter
11
+{
12
+    protected static ?string $model = Estimate::class;
13
+
14
+    public static function getColumns(): array
15
+    {
16
+        return [
17
+            ExportColumn::make('estimate_number'),
18
+            ExportColumn::make('date')
19
+                ->date(),
20
+            ExportColumn::make('expiration_date')
21
+                ->date(),
22
+            ExportColumn::make('client.name'),
23
+            ExportColumn::make('status')
24
+                ->enum(),
25
+            ExportColumn::make('total')
26
+                ->money(),
27
+            ExportColumn::make('subtotal')
28
+                ->money(),
29
+            ExportColumn::make('tax_total')
30
+                ->money(),
31
+            ExportColumn::make('discount_total')
32
+                ->money(),
33
+            ExportColumn::make('discount_rate'),
34
+            ExportColumn::make('currency_code'),
35
+            ExportColumn::make('reference_number'),
36
+            ExportColumn::make('approved_at')
37
+                ->dateTime(),
38
+            ExportColumn::make('accepted_at')
39
+                ->dateTime(),
40
+            ExportColumn::make('declined_at')
41
+                ->dateTime(),
42
+            ExportColumn::make('converted_at')
43
+                ->dateTime(),
44
+            ExportColumn::make('last_sent_at')
45
+                ->dateTime(),
46
+            ExportColumn::make('discount_method')
47
+                ->enabledByDefault(false)
48
+                ->enum(),
49
+            ExportColumn::make('discount_computation')
50
+                ->enabledByDefault(false)
51
+                ->enum(),
52
+        ];
53
+    }
54
+
55
+    public static function getCompletedNotificationBody(Export $export): string
56
+    {
57
+        $body = 'Your estimate export has completed and ' . number_format($export->successful_rows) . ' ' . str('row')->plural($export->successful_rows) . ' exported.';
58
+
59
+        if ($failedRowsCount = $export->getFailedRowsCount()) {
60
+            $body .= ' ' . number_format($failedRowsCount) . ' ' . str('row')->plural($failedRowsCount) . ' failed to export.';
61
+        }
62
+
63
+        return $body;
64
+    }
65
+}

+ 71
- 0
app/Filament/Exports/Accounting/InvoiceExporter.php 查看文件

@@ -0,0 +1,71 @@
1
+<?php
2
+
3
+namespace App\Filament\Exports\Accounting;
4
+
5
+use App\Models\Accounting\Invoice;
6
+use Filament\Actions\Exports\ExportColumn;
7
+use Filament\Actions\Exports\Exporter;
8
+use Filament\Actions\Exports\Models\Export;
9
+
10
+class InvoiceExporter extends Exporter
11
+{
12
+    protected static ?string $model = Invoice::class;
13
+
14
+    public static function getColumns(): array
15
+    {
16
+        return [
17
+            ExportColumn::make('invoice_number'),
18
+            ExportColumn::make('date')
19
+                ->date(),
20
+            ExportColumn::make('due_date')
21
+                ->date(),
22
+            ExportColumn::make('client.name'),
23
+            ExportColumn::make('status')
24
+                ->enum(),
25
+            ExportColumn::make('total')
26
+                ->money(),
27
+            ExportColumn::make('amount_paid')
28
+                ->money(),
29
+            ExportColumn::make('amount_due')
30
+                ->money(),
31
+            ExportColumn::make('subtotal')
32
+                ->money(),
33
+            ExportColumn::make('tax_total')
34
+                ->money(),
35
+            ExportColumn::make('discount_total')
36
+                ->money(),
37
+            ExportColumn::make('discount_rate'),
38
+            ExportColumn::make('currency_code'),
39
+            ExportColumn::make('order_number'),
40
+            ExportColumn::make('approved_at')
41
+                ->dateTime(),
42
+            ExportColumn::make('paid_at')
43
+                ->dateTime(),
44
+            ExportColumn::make('last_sent_at')
45
+                ->dateTime(),
46
+            ExportColumn::make('estimate.estimate_number')
47
+                ->label('Estimate number')
48
+                ->enabledByDefault(false),
49
+            ExportColumn::make('recurringInvoice.order_number')
50
+                ->label('Recurring invoice number')
51
+                ->enabledByDefault(false),
52
+            ExportColumn::make('discount_method')
53
+                ->enabledByDefault(false)
54
+                ->enum(),
55
+            ExportColumn::make('discount_computation')
56
+                ->enabledByDefault(false)
57
+                ->enum(),
58
+        ];
59
+    }
60
+
61
+    public static function getCompletedNotificationBody(Export $export): string
62
+    {
63
+        $body = 'Your invoice export has completed and ' . number_format($export->successful_rows) . ' ' . str('row')->plural($export->successful_rows) . ' exported.';
64
+
65
+        if ($failedRowsCount = $export->getFailedRowsCount()) {
66
+            $body .= ' ' . number_format($failedRowsCount) . ' ' . str('row')->plural($failedRowsCount) . ' failed to export.';
67
+        }
68
+
69
+        return $body;
70
+    }
71
+}

+ 99
- 0
app/Filament/Exports/Accounting/RecurringInvoiceExporter.php 查看文件

@@ -0,0 +1,99 @@
1
+<?php
2
+
3
+namespace App\Filament\Exports\Accounting;
4
+
5
+use App\Models\Accounting\RecurringInvoice;
6
+use Carbon\Carbon;
7
+use Filament\Actions\Exports\ExportColumn;
8
+use Filament\Actions\Exports\Exporter;
9
+use Filament\Actions\Exports\Models\Export;
10
+
11
+class RecurringInvoiceExporter extends Exporter
12
+{
13
+    protected static ?string $model = RecurringInvoice::class;
14
+
15
+    public static function getColumns(): array
16
+    {
17
+        return [
18
+            ExportColumn::make('order_number'),
19
+            ExportColumn::make('client.name'),
20
+            ExportColumn::make('status')
21
+                ->enum(),
22
+            ExportColumn::make('schedule')
23
+                ->formatStateUsing(function ($state, RecurringInvoice $record) {
24
+                    return $record->getScheduleDescription();
25
+                }),
26
+            ExportColumn::make('timeline')
27
+                ->formatStateUsing(function ($state, RecurringInvoice $record) {
28
+                    return $record->getTimelineDescription();
29
+                }),
30
+            ExportColumn::make('total')
31
+                ->money(),
32
+            ExportColumn::make('subtotal')
33
+                ->money(),
34
+            ExportColumn::make('tax_total')
35
+                ->money(),
36
+            ExportColumn::make('discount_total')
37
+                ->money(),
38
+            ExportColumn::make('discount_rate'),
39
+            ExportColumn::make('currency_code'),
40
+            ExportColumn::make('payment_terms')
41
+                ->enum(),
42
+            ExportColumn::make('start_date')
43
+                ->date(),
44
+            ExportColumn::make('end_date')
45
+                ->date(),
46
+            ExportColumn::make('next_date')
47
+                ->date(),
48
+            ExportColumn::make('last_date')
49
+                ->date(),
50
+            ExportColumn::make('approved_at')
51
+                ->dateTime(),
52
+            ExportColumn::make('ended_at')
53
+                ->dateTime(),
54
+            ExportColumn::make('occurrences_count'),
55
+            ExportColumn::make('max_occurrences'),
56
+            ExportColumn::make('send_time')
57
+                ->formatStateUsing(function (?Carbon $state) {
58
+                    return $state?->format('H:i');
59
+                }),
60
+            ExportColumn::make('frequency')
61
+                ->enabledByDefault(false)
62
+                ->enum(),
63
+            ExportColumn::make('interval_type')
64
+                ->enabledByDefault(false)
65
+                ->enum(),
66
+            ExportColumn::make('interval_value')
67
+                ->enabledByDefault(false),
68
+            ExportColumn::make('month')
69
+                ->enabledByDefault(false)
70
+                ->enum(),
71
+            ExportColumn::make('day_of_month')
72
+                ->enabledByDefault(false)
73
+                ->enum(),
74
+            ExportColumn::make('day_of_week')
75
+                ->enabledByDefault(false)
76
+                ->enum(),
77
+            ExportColumn::make('end_type')
78
+                ->enabledByDefault(false)
79
+                ->enum(),
80
+            ExportColumn::make('discount_method')
81
+                ->enabledByDefault(false)
82
+                ->enum(),
83
+            ExportColumn::make('discount_computation')
84
+                ->enabledByDefault(false)
85
+                ->enum(),
86
+        ];
87
+    }
88
+
89
+    public static function getCompletedNotificationBody(Export $export): string
90
+    {
91
+        $body = 'Your recurring invoice export has completed and ' . number_format($export->successful_rows) . ' ' . str('row')->plural($export->successful_rows) . ' exported.';
92
+
93
+        if ($failedRowsCount = $export->getFailedRowsCount()) {
94
+            $body .= ' ' . number_format($failedRowsCount) . ' ' . str('row')->plural($failedRowsCount) . ' failed to export.';
95
+        }
96
+
97
+        return $body;
98
+    }
99
+}

+ 63
- 0
app/Filament/Exports/Accounting/TransactionExporter.php 查看文件

@@ -0,0 +1,63 @@
1
+<?php
2
+
3
+namespace App\Filament\Exports\Accounting;
4
+
5
+use App\Models\Accounting\Transaction;
6
+use Filament\Actions\Exports\ExportColumn;
7
+use Filament\Actions\Exports\Exporter;
8
+use Filament\Actions\Exports\Models\Export;
9
+
10
+class TransactionExporter extends Exporter
11
+{
12
+    protected static ?string $model = Transaction::class;
13
+
14
+    public static function getColumns(): array
15
+    {
16
+        return [
17
+            ExportColumn::make('posted_at')
18
+                ->date(),
19
+            ExportColumn::make('description'),
20
+            ExportColumn::make('amount')
21
+                ->money(),
22
+            ExportColumn::make('account.name')
23
+                ->label('Category'),
24
+            ExportColumn::make('bankAccount.account.name')
25
+                ->label('Account'),
26
+            ExportColumn::make('type')
27
+                ->enum(),
28
+            ExportColumn::make('payeeable.name')
29
+                ->label('Payee'),
30
+            ExportColumn::make('payment_method')
31
+                ->enum(),
32
+            ExportColumn::make('notes')
33
+                ->enabledByDefault(false),
34
+            ExportColumn::make('transactionable_type')
35
+                ->label('Source type')
36
+                ->formatStateUsing(static function ($state) {
37
+                    return class_basename($state);
38
+                })
39
+                ->enabledByDefault(false),
40
+            ExportColumn::make('payeeable_type')
41
+                ->label('Payee type')
42
+                ->formatStateUsing(static function ($state) {
43
+                    return class_basename($state);
44
+                })
45
+                ->enabledByDefault(false),
46
+            ExportColumn::make('is_payment')
47
+                ->enabledByDefault(false),
48
+            ExportColumn::make('reviewed')
49
+                ->enabledByDefault(false),
50
+        ];
51
+    }
52
+
53
+    public static function getCompletedNotificationBody(Export $export): string
54
+    {
55
+        $body = 'Your transaction export has completed and ' . number_format($export->successful_rows) . ' ' . str('row')->plural($export->successful_rows) . ' exported.';
56
+
57
+        if ($failedRowsCount = $export->getFailedRowsCount()) {
58
+            $body .= ' ' . number_format($failedRowsCount) . ' ' . str('row')->plural($failedRowsCount) . ' failed to export.';
59
+        }
60
+
61
+        return $body;
62
+    }
63
+}

+ 104
- 0
app/Filament/Exports/Common/ClientExporter.php 查看文件

@@ -0,0 +1,104 @@
1
+<?php
2
+
3
+namespace App\Filament\Exports\Common;
4
+
5
+use App\Models\Common\Client;
6
+use Filament\Actions\Exports\ExportColumn;
7
+use Filament\Actions\Exports\Exporter;
8
+use Filament\Actions\Exports\Models\Export;
9
+
10
+class ClientExporter extends Exporter
11
+{
12
+    protected static ?string $model = Client::class;
13
+
14
+    public static function getColumns(): array
15
+    {
16
+        return [
17
+            ExportColumn::make('name'),
18
+            ExportColumn::make('account_number'),
19
+            ExportColumn::make('primaryContact.full_name')
20
+                ->label('Primary contact'),
21
+            ExportColumn::make('primaryContact.email')
22
+                ->label('Email'),
23
+            ExportColumn::make('primaryContact.first_available_phone')
24
+                ->label('Phone'),
25
+            ExportColumn::make('currency_code'),
26
+            ExportColumn::make('balance') // TODO: Potentially find an easier way to calculate this
27
+                ->state(function (Client $record) {
28
+                    return $record->invoices()
29
+                        ->unpaid()
30
+                        ->get()
31
+                        ->sumMoneyInDefaultCurrency('amount_due');
32
+                })
33
+                ->money(),
34
+            ExportColumn::make('overdue_amount')
35
+                ->state(function (Client $record) {
36
+                    return $record->invoices()
37
+                        ->overdue()
38
+                        ->get()
39
+                        ->sumMoneyInDefaultCurrency('amount_due');
40
+                })
41
+                ->money(),
42
+            ExportColumn::make('billingAddress.address_string')
43
+                ->label('Billing address')
44
+                ->enabledByDefault(false),
45
+            ExportColumn::make('billingAddress.address_line_1')
46
+                ->label('Billing address line 1'),
47
+            ExportColumn::make('billingAddress.address_line_2')
48
+                ->label('Billing address line 2'),
49
+            ExportColumn::make('billingAddress.city')
50
+                ->label('Billing city'),
51
+            ExportColumn::make('billingAddress.state.name')
52
+                ->label('Billing state'),
53
+            ExportColumn::make('billingAddress.postal_code')
54
+                ->label('Billing postal code'),
55
+            ExportColumn::make('billingAddress.country.name')
56
+                ->label('Billing country'),
57
+            ExportColumn::make('shippingAddress.recipient')
58
+                ->label('Shipping recipient')
59
+                ->enabledByDefault(false),
60
+            ExportColumn::make('shippingAddress.phone')
61
+                ->label('Shipping phone')
62
+                ->enabledByDefault(false),
63
+            ExportColumn::make('shippingAddress.address_string')
64
+                ->label('Shipping address')
65
+                ->enabledByDefault(false),
66
+            ExportColumn::make('shippingAddress.address_line_1')
67
+                ->label('Shipping address line 1')
68
+                ->enabledByDefault(false),
69
+            ExportColumn::make('shippingAddress.address_line_2')
70
+                ->label('Shipping address line 2')
71
+                ->enabledByDefault(false),
72
+            ExportColumn::make('shippingAddress.city')
73
+                ->label('Shipping city')
74
+                ->enabledByDefault(false),
75
+            ExportColumn::make('shippingAddress.state.name')
76
+                ->label('Shipping state')
77
+                ->enabledByDefault(false),
78
+            ExportColumn::make('shippingAddress.postal_code')
79
+                ->label('Shipping postal code')
80
+                ->enabledByDefault(false),
81
+            ExportColumn::make('shippingAddress.country.name')
82
+                ->label('Shipping country')
83
+                ->enabledByDefault(false),
84
+            ExportColumn::make('shippingAddress.notes')
85
+                ->label('Delivery instructions')
86
+                ->enabledByDefault(false),
87
+            ExportColumn::make('website')
88
+                ->enabledByDefault(false),
89
+            ExportColumn::make('notes')
90
+                ->enabledByDefault(false),
91
+        ];
92
+    }
93
+
94
+    public static function getCompletedNotificationBody(Export $export): string
95
+    {
96
+        $body = 'Your client export has completed and ' . number_format($export->successful_rows) . ' ' . str('row')->plural($export->successful_rows) . ' exported.';
97
+
98
+        if ($failedRowsCount = $export->getFailedRowsCount()) {
99
+            $body .= ' ' . number_format($failedRowsCount) . ' ' . str('row')->plural($failedRowsCount) . ' failed to export.';
100
+        }
101
+
102
+        return $body;
103
+    }
104
+}

+ 85
- 0
app/Filament/Exports/Common/VendorExporter.php 查看文件

@@ -0,0 +1,85 @@
1
+<?php
2
+
3
+namespace App\Filament\Exports\Common;
4
+
5
+use App\Enums\Accounting\BillStatus;
6
+use App\Models\Common\Vendor;
7
+use Filament\Actions\Exports\ExportColumn;
8
+use Filament\Actions\Exports\Exporter;
9
+use Filament\Actions\Exports\Models\Export;
10
+
11
+class VendorExporter extends Exporter
12
+{
13
+    protected static ?string $model = Vendor::class;
14
+
15
+    public static function getColumns(): array
16
+    {
17
+        return [
18
+            ExportColumn::make('name'),
19
+            ExportColumn::make('type')
20
+                ->enum(),
21
+            ExportColumn::make('contractor_type')
22
+                ->enum(),
23
+            ExportColumn::make('account_number'),
24
+            ExportColumn::make('contact.full_name')
25
+                ->label('Primary contact'),
26
+            ExportColumn::make('contact.email')
27
+                ->label('Email'),
28
+            ExportColumn::make('contact.first_available_phone')
29
+                ->label('Phone'),
30
+            ExportColumn::make('currency_code'),
31
+            ExportColumn::make('balance')
32
+                ->state(function (Vendor $record) {
33
+                    return $record->bills()
34
+                        ->unpaid()
35
+                        ->get()
36
+                        ->sumMoneyInDefaultCurrency('amount_due');
37
+                })
38
+                ->money(),
39
+            ExportColumn::make('overdue_amount')
40
+                ->state(function (Vendor $record) {
41
+                    return $record->bills()
42
+                        ->where('status', BillStatus::Overdue)
43
+                        ->get()
44
+                        ->sumMoneyInDefaultCurrency('amount_due');
45
+                })
46
+                ->money(),
47
+            ExportColumn::make('address.address_string')
48
+                ->label('Address')
49
+                ->enabledByDefault(false),
50
+            ExportColumn::make('address.address_line_1')
51
+                ->label('Address line 1'),
52
+            ExportColumn::make('address.address_line_2')
53
+                ->label('Address line 2'),
54
+            ExportColumn::make('address.city')
55
+                ->label('City'),
56
+            ExportColumn::make('address.state.name')
57
+                ->label('State'),
58
+            ExportColumn::make('address.postal_code')
59
+                ->label('Postal code'),
60
+            ExportColumn::make('address.country.name')
61
+                ->label('Country'),
62
+            ExportColumn::make('ssn')
63
+                ->label('SSN')
64
+                ->enabledByDefault(false),
65
+            ExportColumn::make('ein')
66
+                ->label('EIN')
67
+                ->enabledByDefault(false),
68
+            ExportColumn::make('website')
69
+                ->enabledByDefault(false),
70
+            ExportColumn::make('notes')
71
+                ->enabledByDefault(false),
72
+        ];
73
+    }
74
+
75
+    public static function getCompletedNotificationBody(Export $export): string
76
+    {
77
+        $body = 'Your vendor export has completed and ' . number_format($export->successful_rows) . ' ' . str('row')->plural($export->successful_rows) . ' exported.';
78
+
79
+        if ($failedRowsCount = $export->getFailedRowsCount()) {
80
+            $body .= ' ' . number_format($failedRowsCount) . ' ' . str('row')->plural($failedRowsCount) . ' failed to export.';
81
+        }
82
+
83
+        return $body;
84
+    }
85
+}

+ 7
- 0
app/Providers/Filament/CompanyPanelProvider.php 查看文件

@@ -174,6 +174,7 @@ class CompanyPanelProvider extends PanelProvider
174 174
             })
175 175
             ->globalSearch(false)
176 176
             ->sidebarCollapsibleOnDesktop()
177
+            ->databaseNotifications()
177 178
             ->viteTheme('resources/css/filament/company/theme.css')
178 179
             ->brandLogo(static fn () => view('components.icons.logo'))
179 180
             ->tenant(Company::class)
@@ -299,6 +300,12 @@ class CompanyPanelProvider extends PanelProvider
299 300
         TextEntry::configureUsing(function (TextEntry $component): void {
300 301
             $component->placeholder('–');
301 302
         });
303
+
304
+        Tables\Actions\ExportAction::configureUsing(function (Tables\Actions\ExportAction $action) {
305
+            $action
306
+                ->color('primary')
307
+                ->slideOver();
308
+        });
302 309
     }
303 310
 
304 311
     /**

+ 66
- 0
app/Providers/MacroServiceProvider.php 查看文件

@@ -15,10 +15,12 @@ use App\Utilities\Currency\CurrencyConverter;
15 15
 use BackedEnum;
16 16
 use Carbon\CarbonInterface;
17 17
 use Closure;
18
+use Filament\Actions\Exports\ExportColumn;
18 19
 use Filament\Forms\Components\DatePicker;
19 20
 use Filament\Forms\Components\Field;
20 21
 use Filament\Forms\Components\TextInput;
21 22
 use Filament\Infolists\Components\TextEntry;
23
+use Filament\Support\Contracts\HasLabel;
22 24
 use Filament\Support\Enums\IconPosition;
23 25
 use Filament\Support\RawJs;
24 26
 use Filament\Tables\Columns\TextColumn;
@@ -475,5 +477,69 @@ class MacroServiceProvider extends ServiceProvider
475 477
 
476 478
             return $this->setTimezone($timezone)->format($dateFormat);
477 479
         });
480
+
481
+        ExportColumn::macro('money', function () {
482
+            $this->formatStateUsing(static function ($state) {
483
+                if (blank($state) || ! is_int($state)) {
484
+                    return 0.00;
485
+                }
486
+
487
+                return CurrencyConverter::convertCentsToFloat($state);
488
+            });
489
+
490
+            return $this;
491
+        });
492
+
493
+        ExportColumn::macro('date', function () {
494
+            $this->formatStateUsing(static function ($state) {
495
+                if (blank($state)) {
496
+                    return null;
497
+                }
498
+
499
+                try {
500
+                    return Carbon::parse($state)->toDateString();
501
+                } catch (\Exception) {
502
+                    return null;
503
+                }
504
+            });
505
+
506
+            return $this;
507
+        });
508
+
509
+        ExportColumn::macro('dateTime', function () {
510
+            $this->formatStateUsing(static function ($state) {
511
+                if (blank($state)) {
512
+                    return null;
513
+                }
514
+
515
+                try {
516
+                    return Carbon::parse($state)->toDateTimeString();
517
+                } catch (\Exception) {
518
+                    return null;
519
+                }
520
+            });
521
+
522
+            return $this;
523
+        });
524
+
525
+        ExportColumn::macro('enum', function () {
526
+            $this->formatStateUsing(static function ($state) {
527
+                if (blank($state)) {
528
+                    return null;
529
+                }
530
+
531
+                if (! ($state instanceof BackedEnum)) {
532
+                    return $state;
533
+                }
534
+
535
+                if ($state instanceof HasLabel) {
536
+                    return $state->getLabel();
537
+                }
538
+
539
+                return $state->value;
540
+            });
541
+
542
+            return $this;
543
+        });
478 544
     }
479 545
 }

+ 1
- 1
app/Utilities/Currency/CurrencyConverter.php 查看文件

@@ -83,7 +83,7 @@ class CurrencyConverter
83 83
 
84 84
     public static function convertCentsToFloat(int $amount, ?string $currency = null): float
85 85
     {
86
-        $currency ??= CurrencyAccessor::getDefaultCurrency();
86
+        $currency ??= 'USD';
87 87
 
88 88
         return money($amount, $currency)->getValue();
89 89
     }

+ 90
- 91
composer.lock 查看文件

@@ -497,16 +497,16 @@
497 497
         },
498 498
         {
499 499
             "name": "aws/aws-sdk-php",
500
-            "version": "3.344.0",
500
+            "version": "3.344.6",
501 501
             "source": {
502 502
                 "type": "git",
503 503
                 "url": "https://github.com/aws/aws-sdk-php.git",
504
-                "reference": "787a8ec6301657d9cbdb389db4fa92243c68666a"
504
+                "reference": "eb0bc621472592545539329499961a15a3f9f6dc"
505 505
             },
506 506
             "dist": {
507 507
                 "type": "zip",
508
-                "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/787a8ec6301657d9cbdb389db4fa92243c68666a",
509
-                "reference": "787a8ec6301657d9cbdb389db4fa92243c68666a",
508
+                "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/eb0bc621472592545539329499961a15a3f9f6dc",
509
+                "reference": "eb0bc621472592545539329499961a15a3f9f6dc",
510 510
                 "shasum": ""
511 511
             },
512 512
             "require": {
@@ -588,9 +588,9 @@
588 588
             "support": {
589 589
                 "forum": "https://github.com/aws/aws-sdk-php/discussions",
590 590
                 "issues": "https://github.com/aws/aws-sdk-php/issues",
591
-                "source": "https://github.com/aws/aws-sdk-php/tree/3.344.0"
591
+                "source": "https://github.com/aws/aws-sdk-php/tree/3.344.6"
592 592
             },
593
-            "time": "2025-06-04T18:36:41+00:00"
593
+            "time": "2025-06-12T18:03:59+00:00"
594 594
         },
595 595
         {
596 596
             "name": "aws/aws-sdk-php-laravel",
@@ -1029,16 +1029,16 @@
1029 1029
         },
1030 1030
         {
1031 1031
             "name": "codewithdennis/filament-simple-alert",
1032
-            "version": "v3.0.18",
1032
+            "version": "v3.0.19",
1033 1033
             "source": {
1034 1034
                 "type": "git",
1035 1035
                 "url": "https://github.com/CodeWithDennis/filament-simple-alert.git",
1036
-                "reference": "50bb18626b8b6c3b9780a778e9781f726574e1e7"
1036
+                "reference": "88214c9ce5f0a7855c016c7a6bd57acb5fb78a46"
1037 1037
             },
1038 1038
             "dist": {
1039 1039
                 "type": "zip",
1040
-                "url": "https://api.github.com/repos/CodeWithDennis/filament-simple-alert/zipball/50bb18626b8b6c3b9780a778e9781f726574e1e7",
1041
-                "reference": "50bb18626b8b6c3b9780a778e9781f726574e1e7",
1040
+                "url": "https://api.github.com/repos/CodeWithDennis/filament-simple-alert/zipball/88214c9ce5f0a7855c016c7a6bd57acb5fb78a46",
1041
+                "reference": "88214c9ce5f0a7855c016c7a6bd57acb5fb78a46",
1042 1042
                 "shasum": ""
1043 1043
             },
1044 1044
             "require": {
@@ -1098,7 +1098,7 @@
1098 1098
                     "type": "github"
1099 1099
                 }
1100 1100
             ],
1101
-            "time": "2025-02-10T09:29:26+00:00"
1101
+            "time": "2025-06-09T11:29:49+00:00"
1102 1102
         },
1103 1103
         {
1104 1104
             "name": "danharrin/date-format-converter",
@@ -1799,16 +1799,16 @@
1799 1799
         },
1800 1800
         {
1801 1801
             "name": "filament/actions",
1802
-            "version": "v3.3.20",
1802
+            "version": "v3.3.26",
1803 1803
             "source": {
1804 1804
                 "type": "git",
1805 1805
                 "url": "https://github.com/filamentphp/actions.git",
1806
-                "reference": "151f776552ee10d70591c2649708bc4b0a7cba91"
1806
+                "reference": "67dd0da772f19e2d74e60eb53f99330faf183892"
1807 1807
             },
1808 1808
             "dist": {
1809 1809
                 "type": "zip",
1810
-                "url": "https://api.github.com/repos/filamentphp/actions/zipball/151f776552ee10d70591c2649708bc4b0a7cba91",
1811
-                "reference": "151f776552ee10d70591c2649708bc4b0a7cba91",
1810
+                "url": "https://api.github.com/repos/filamentphp/actions/zipball/67dd0da772f19e2d74e60eb53f99330faf183892",
1811
+                "reference": "67dd0da772f19e2d74e60eb53f99330faf183892",
1812 1812
                 "shasum": ""
1813 1813
             },
1814 1814
             "require": {
@@ -1848,20 +1848,20 @@
1848 1848
                 "issues": "https://github.com/filamentphp/filament/issues",
1849 1849
                 "source": "https://github.com/filamentphp/filament"
1850 1850
             },
1851
-            "time": "2025-06-03T06:15:27+00:00"
1851
+            "time": "2025-06-13T14:47:50+00:00"
1852 1852
         },
1853 1853
         {
1854 1854
             "name": "filament/filament",
1855
-            "version": "v3.3.20",
1855
+            "version": "v3.3.26",
1856 1856
             "source": {
1857 1857
                 "type": "git",
1858 1858
                 "url": "https://github.com/filamentphp/panels.git",
1859
-                "reference": "94ee92244d2a64666fb8c1ea50cb7315ebceb63b"
1859
+                "reference": "b060d2d01a969e3b6541ab4f1e24c745352f51c1"
1860 1860
             },
1861 1861
             "dist": {
1862 1862
                 "type": "zip",
1863
-                "url": "https://api.github.com/repos/filamentphp/panels/zipball/94ee92244d2a64666fb8c1ea50cb7315ebceb63b",
1864
-                "reference": "94ee92244d2a64666fb8c1ea50cb7315ebceb63b",
1863
+                "url": "https://api.github.com/repos/filamentphp/panels/zipball/b060d2d01a969e3b6541ab4f1e24c745352f51c1",
1864
+                "reference": "b060d2d01a969e3b6541ab4f1e24c745352f51c1",
1865 1865
                 "shasum": ""
1866 1866
             },
1867 1867
             "require": {
@@ -1913,20 +1913,20 @@
1913 1913
                 "issues": "https://github.com/filamentphp/filament/issues",
1914 1914
                 "source": "https://github.com/filamentphp/filament"
1915 1915
             },
1916
-            "time": "2025-05-27T18:46:23+00:00"
1916
+            "time": "2025-06-12T15:10:00+00:00"
1917 1917
         },
1918 1918
         {
1919 1919
             "name": "filament/forms",
1920
-            "version": "v3.3.20",
1920
+            "version": "v3.3.26",
1921 1921
             "source": {
1922 1922
                 "type": "git",
1923 1923
                 "url": "https://github.com/filamentphp/forms.git",
1924
-                "reference": "d73cdda057a4f5bd409eab9573101e73edb404cc"
1924
+                "reference": "586a13f9d2a6f395ffdc8557730c85a5858b4c4f"
1925 1925
             },
1926 1926
             "dist": {
1927 1927
                 "type": "zip",
1928
-                "url": "https://api.github.com/repos/filamentphp/forms/zipball/d73cdda057a4f5bd409eab9573101e73edb404cc",
1929
-                "reference": "d73cdda057a4f5bd409eab9573101e73edb404cc",
1928
+                "url": "https://api.github.com/repos/filamentphp/forms/zipball/586a13f9d2a6f395ffdc8557730c85a5858b4c4f",
1929
+                "reference": "586a13f9d2a6f395ffdc8557730c85a5858b4c4f",
1930 1930
                 "shasum": ""
1931 1931
             },
1932 1932
             "require": {
@@ -1969,11 +1969,11 @@
1969 1969
                 "issues": "https://github.com/filamentphp/filament/issues",
1970 1970
                 "source": "https://github.com/filamentphp/filament"
1971 1971
             },
1972
-            "time": "2025-06-03T13:40:37+00:00"
1972
+            "time": "2025-06-12T15:10:19+00:00"
1973 1973
         },
1974 1974
         {
1975 1975
             "name": "filament/infolists",
1976
-            "version": "v3.3.20",
1976
+            "version": "v3.3.26",
1977 1977
             "source": {
1978 1978
                 "type": "git",
1979 1979
                 "url": "https://github.com/filamentphp/infolists.git",
@@ -2024,7 +2024,7 @@
2024 2024
         },
2025 2025
         {
2026 2026
             "name": "filament/notifications",
2027
-            "version": "v3.3.20",
2027
+            "version": "v3.3.26",
2028 2028
             "source": {
2029 2029
                 "type": "git",
2030 2030
                 "url": "https://github.com/filamentphp/notifications.git",
@@ -2076,16 +2076,16 @@
2076 2076
         },
2077 2077
         {
2078 2078
             "name": "filament/support",
2079
-            "version": "v3.3.20",
2079
+            "version": "v3.3.26",
2080 2080
             "source": {
2081 2081
                 "type": "git",
2082 2082
                 "url": "https://github.com/filamentphp/support.git",
2083
-                "reference": "4f9793ad3339301ca53ea6f2c984734f7ac38ce7"
2083
+                "reference": "7d850347ffbd8c84d84040ffb8c5ceb0f709a9fe"
2084 2084
             },
2085 2085
             "dist": {
2086 2086
                 "type": "zip",
2087
-                "url": "https://api.github.com/repos/filamentphp/support/zipball/4f9793ad3339301ca53ea6f2c984734f7ac38ce7",
2088
-                "reference": "4f9793ad3339301ca53ea6f2c984734f7ac38ce7",
2087
+                "url": "https://api.github.com/repos/filamentphp/support/zipball/7d850347ffbd8c84d84040ffb8c5ceb0f709a9fe",
2088
+                "reference": "7d850347ffbd8c84d84040ffb8c5ceb0f709a9fe",
2089 2089
                 "shasum": ""
2090 2090
             },
2091 2091
             "require": {
@@ -2131,20 +2131,20 @@
2131 2131
                 "issues": "https://github.com/filamentphp/filament/issues",
2132 2132
                 "source": "https://github.com/filamentphp/filament"
2133 2133
             },
2134
-            "time": "2025-06-03T06:16:13+00:00"
2134
+            "time": "2025-06-12T15:02:34+00:00"
2135 2135
         },
2136 2136
         {
2137 2137
             "name": "filament/tables",
2138
-            "version": "v3.3.20",
2138
+            "version": "v3.3.26",
2139 2139
             "source": {
2140 2140
                 "type": "git",
2141 2141
                 "url": "https://github.com/filamentphp/tables.git",
2142
-                "reference": "1a107a8411549297b97d1142b1f7a5fa7a65e32b"
2142
+                "reference": "920204cd5ec1550209cf398fea8dba3dece979de"
2143 2143
             },
2144 2144
             "dist": {
2145 2145
                 "type": "zip",
2146
-                "url": "https://api.github.com/repos/filamentphp/tables/zipball/1a107a8411549297b97d1142b1f7a5fa7a65e32b",
2147
-                "reference": "1a107a8411549297b97d1142b1f7a5fa7a65e32b",
2146
+                "url": "https://api.github.com/repos/filamentphp/tables/zipball/920204cd5ec1550209cf398fea8dba3dece979de",
2147
+                "reference": "920204cd5ec1550209cf398fea8dba3dece979de",
2148 2148
                 "shasum": ""
2149 2149
             },
2150 2150
             "require": {
@@ -2183,20 +2183,20 @@
2183 2183
                 "issues": "https://github.com/filamentphp/filament/issues",
2184 2184
                 "source": "https://github.com/filamentphp/filament"
2185 2185
             },
2186
-            "time": "2025-06-02T09:43:47+00:00"
2186
+            "time": "2025-06-12T15:01:25+00:00"
2187 2187
         },
2188 2188
         {
2189 2189
             "name": "filament/widgets",
2190
-            "version": "v3.3.20",
2190
+            "version": "v3.3.26",
2191 2191
             "source": {
2192 2192
                 "type": "git",
2193 2193
                 "url": "https://github.com/filamentphp/widgets.git",
2194
-                "reference": "048c5a4bf0477efbe2910c54a1aeb55c64cf1348"
2194
+                "reference": "5b956f884aaef479f6091463cb829e7c9f2afc2c"
2195 2195
             },
2196 2196
             "dist": {
2197 2197
                 "type": "zip",
2198
-                "url": "https://api.github.com/repos/filamentphp/widgets/zipball/048c5a4bf0477efbe2910c54a1aeb55c64cf1348",
2199
-                "reference": "048c5a4bf0477efbe2910c54a1aeb55c64cf1348",
2198
+                "url": "https://api.github.com/repos/filamentphp/widgets/zipball/5b956f884aaef479f6091463cb829e7c9f2afc2c",
2199
+                "reference": "5b956f884aaef479f6091463cb829e7c9f2afc2c",
2200 2200
                 "shasum": ""
2201 2201
             },
2202 2202
             "require": {
@@ -2227,7 +2227,7 @@
2227 2227
                 "issues": "https://github.com/filamentphp/filament/issues",
2228 2228
                 "source": "https://github.com/filamentphp/filament"
2229 2229
             },
2230
-            "time": "2025-04-23T06:39:59+00:00"
2230
+            "time": "2025-06-12T15:11:14+00:00"
2231 2231
         },
2232 2232
         {
2233 2233
             "name": "firebase/php-jwt",
@@ -4515,16 +4515,16 @@
4515 4515
         },
4516 4516
         {
4517 4517
             "name": "matomo/device-detector",
4518
-            "version": "6.4.5",
4518
+            "version": "6.4.6",
4519 4519
             "source": {
4520 4520
                 "type": "git",
4521 4521
                 "url": "https://github.com/matomo-org/device-detector.git",
4522
-                "reference": "270bbc41f80994e80805ac377b67324eba53c412"
4522
+                "reference": "6f07f615199851548db47a900815d2ea2cdcde08"
4523 4523
             },
4524 4524
             "dist": {
4525 4525
                 "type": "zip",
4526
-                "url": "https://api.github.com/repos/matomo-org/device-detector/zipball/270bbc41f80994e80805ac377b67324eba53c412",
4527
-                "reference": "270bbc41f80994e80805ac377b67324eba53c412",
4526
+                "url": "https://api.github.com/repos/matomo-org/device-detector/zipball/6f07f615199851548db47a900815d2ea2cdcde08",
4527
+                "reference": "6f07f615199851548db47a900815d2ea2cdcde08",
4528 4528
                 "shasum": ""
4529 4529
             },
4530 4530
             "require": {
@@ -4581,7 +4581,7 @@
4581 4581
                 "source": "https://github.com/matomo-org/matomo",
4582 4582
                 "wiki": "https://dev.matomo.org/"
4583 4583
             },
4584
-            "time": "2025-02-26T17:37:32+00:00"
4584
+            "time": "2025-06-10T10:00:59+00:00"
4585 4585
         },
4586 4586
         {
4587 4587
             "name": "monolog/monolog",
@@ -4808,16 +4808,16 @@
4808 4808
         },
4809 4809
         {
4810 4810
             "name": "nesbot/carbon",
4811
-            "version": "3.9.1",
4811
+            "version": "3.10.0",
4812 4812
             "source": {
4813 4813
                 "type": "git",
4814 4814
                 "url": "https://github.com/CarbonPHP/carbon.git",
4815
-                "reference": "ced71f79398ece168e24f7f7710462f462310d4d"
4815
+                "reference": "c1397390dd0a7e0f11660f0ae20f753d88c1f3d9"
4816 4816
             },
4817 4817
             "dist": {
4818 4818
                 "type": "zip",
4819
-                "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/ced71f79398ece168e24f7f7710462f462310d4d",
4820
-                "reference": "ced71f79398ece168e24f7f7710462f462310d4d",
4819
+                "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/c1397390dd0a7e0f11660f0ae20f753d88c1f3d9",
4820
+                "reference": "c1397390dd0a7e0f11660f0ae20f753d88c1f3d9",
4821 4821
                 "shasum": ""
4822 4822
             },
4823 4823
             "require": {
@@ -4825,9 +4825,9 @@
4825 4825
                 "ext-json": "*",
4826 4826
                 "php": "^8.1",
4827 4827
                 "psr/clock": "^1.0",
4828
-                "symfony/clock": "^6.3 || ^7.0",
4828
+                "symfony/clock": "^6.3.12 || ^7.0",
4829 4829
                 "symfony/polyfill-mbstring": "^1.0",
4830
-                "symfony/translation": "^4.4.18 || ^5.2.1|| ^6.0 || ^7.0"
4830
+                "symfony/translation": "^4.4.18 || ^5.2.1 || ^6.0 || ^7.0"
4831 4831
             },
4832 4832
             "provide": {
4833 4833
                 "psr/clock-implementation": "1.0"
@@ -4835,14 +4835,13 @@
4835 4835
             "require-dev": {
4836 4836
                 "doctrine/dbal": "^3.6.3 || ^4.0",
4837 4837
                 "doctrine/orm": "^2.15.2 || ^3.0",
4838
-                "friendsofphp/php-cs-fixer": "^3.57.2",
4838
+                "friendsofphp/php-cs-fixer": "^3.75.0",
4839 4839
                 "kylekatarnls/multi-tester": "^2.5.3",
4840
-                "ondrejmirtes/better-reflection": "^6.25.0.4",
4841 4840
                 "phpmd/phpmd": "^2.15.0",
4842
-                "phpstan/extension-installer": "^1.3.1",
4843
-                "phpstan/phpstan": "^1.11.2",
4844
-                "phpunit/phpunit": "^10.5.20",
4845
-                "squizlabs/php_codesniffer": "^3.9.0"
4841
+                "phpstan/extension-installer": "^1.4.3",
4842
+                "phpstan/phpstan": "^2.1.17",
4843
+                "phpunit/phpunit": "^10.5.46",
4844
+                "squizlabs/php_codesniffer": "^3.13.0"
4846 4845
             },
4847 4846
             "bin": [
4848 4847
                 "bin/carbon"
@@ -4910,7 +4909,7 @@
4910 4909
                     "type": "tidelift"
4911 4910
                 }
4912 4911
             ],
4913
-            "time": "2025-05-01T19:51:51+00:00"
4912
+            "time": "2025-06-12T10:24:28+00:00"
4914 4913
         },
4915 4914
         {
4916 4915
             "name": "nette/schema",
@@ -5492,16 +5491,16 @@
5492 5491
         },
5493 5492
         {
5494 5493
             "name": "phpseclib/phpseclib",
5495
-            "version": "3.0.43",
5494
+            "version": "3.0.44",
5496 5495
             "source": {
5497 5496
                 "type": "git",
5498 5497
                 "url": "https://github.com/phpseclib/phpseclib.git",
5499
-                "reference": "709ec107af3cb2f385b9617be72af8cf62441d02"
5498
+                "reference": "1d0b5e7e1434678411787c5a0535e68907cf82d9"
5500 5499
             },
5501 5500
             "dist": {
5502 5501
                 "type": "zip",
5503
-                "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/709ec107af3cb2f385b9617be72af8cf62441d02",
5504
-                "reference": "709ec107af3cb2f385b9617be72af8cf62441d02",
5502
+                "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/1d0b5e7e1434678411787c5a0535e68907cf82d9",
5503
+                "reference": "1d0b5e7e1434678411787c5a0535e68907cf82d9",
5505 5504
                 "shasum": ""
5506 5505
             },
5507 5506
             "require": {
@@ -5582,7 +5581,7 @@
5582 5581
             ],
5583 5582
             "support": {
5584 5583
                 "issues": "https://github.com/phpseclib/phpseclib/issues",
5585
-                "source": "https://github.com/phpseclib/phpseclib/tree/3.0.43"
5584
+                "source": "https://github.com/phpseclib/phpseclib/tree/3.0.44"
5586 5585
             },
5587 5586
             "funding": [
5588 5587
                 {
@@ -5598,7 +5597,7 @@
5598 5597
                     "type": "tidelift"
5599 5598
                 }
5600 5599
             ],
5601
-            "time": "2024-12-14T21:12:59+00:00"
5600
+            "time": "2025-06-15T09:59:26+00:00"
5602 5601
         },
5603 5602
         {
5604 5603
             "name": "psr/cache",
@@ -6598,7 +6597,7 @@
6598 6597
         },
6599 6598
         {
6600 6599
             "name": "squirephp/model",
6601
-            "version": "v3.9.0",
6600
+            "version": "v3.10.0",
6602 6601
             "source": {
6603 6602
                 "type": "git",
6604 6603
                 "url": "https://github.com/squirephp/model.git",
@@ -6652,7 +6651,7 @@
6652 6651
         },
6653 6652
         {
6654 6653
             "name": "squirephp/repository",
6655
-            "version": "v3.9.0",
6654
+            "version": "v3.10.0",
6656 6655
             "source": {
6657 6656
                 "type": "git",
6658 6657
                 "url": "https://github.com/squirephp/repository.git",
@@ -9606,16 +9605,16 @@
9606 9605
         },
9607 9606
         {
9608 9607
             "name": "filp/whoops",
9609
-            "version": "2.18.1",
9608
+            "version": "2.18.2",
9610 9609
             "source": {
9611 9610
                 "type": "git",
9612 9611
                 "url": "https://github.com/filp/whoops.git",
9613
-                "reference": "8fcc6a862f2e7b94eb4221fd0819ddba3d30ab26"
9612
+                "reference": "89dabca1490bc77dbcab41c2b20968c7e44bf7c3"
9614 9613
             },
9615 9614
             "dist": {
9616 9615
                 "type": "zip",
9617
-                "url": "https://api.github.com/repos/filp/whoops/zipball/8fcc6a862f2e7b94eb4221fd0819ddba3d30ab26",
9618
-                "reference": "8fcc6a862f2e7b94eb4221fd0819ddba3d30ab26",
9616
+                "url": "https://api.github.com/repos/filp/whoops/zipball/89dabca1490bc77dbcab41c2b20968c7e44bf7c3",
9617
+                "reference": "89dabca1490bc77dbcab41c2b20968c7e44bf7c3",
9619 9618
                 "shasum": ""
9620 9619
             },
9621 9620
             "require": {
@@ -9665,7 +9664,7 @@
9665 9664
             ],
9666 9665
             "support": {
9667 9666
                 "issues": "https://github.com/filp/whoops/issues",
9668
-                "source": "https://github.com/filp/whoops/tree/2.18.1"
9667
+                "source": "https://github.com/filp/whoops/tree/2.18.2"
9669 9668
             },
9670 9669
             "funding": [
9671 9670
                 {
@@ -9673,7 +9672,7 @@
9673 9672
                     "type": "github"
9674 9673
                 }
9675 9674
             ],
9676
-            "time": "2025-06-03T18:56:14+00:00"
9675
+            "time": "2025-06-11T20:42:19+00:00"
9677 9676
         },
9678 9677
         {
9679 9678
             "name": "hamcrest/hamcrest-php",
@@ -10060,23 +10059,23 @@
10060 10059
         },
10061 10060
         {
10062 10061
             "name": "nunomaduro/collision",
10063
-            "version": "v8.8.0",
10062
+            "version": "v8.8.1",
10064 10063
             "source": {
10065 10064
                 "type": "git",
10066 10065
                 "url": "https://github.com/nunomaduro/collision.git",
10067
-                "reference": "4cf9f3b47afff38b139fb79ce54fc71799022ce8"
10066
+                "reference": "44ccb82e3e21efb5446748d2a3c81a030ac22bd5"
10068 10067
             },
10069 10068
             "dist": {
10070 10069
                 "type": "zip",
10071
-                "url": "https://api.github.com/repos/nunomaduro/collision/zipball/4cf9f3b47afff38b139fb79ce54fc71799022ce8",
10072
-                "reference": "4cf9f3b47afff38b139fb79ce54fc71799022ce8",
10070
+                "url": "https://api.github.com/repos/nunomaduro/collision/zipball/44ccb82e3e21efb5446748d2a3c81a030ac22bd5",
10071
+                "reference": "44ccb82e3e21efb5446748d2a3c81a030ac22bd5",
10073 10072
                 "shasum": ""
10074 10073
             },
10075 10074
             "require": {
10076
-                "filp/whoops": "^2.18.0",
10077
-                "nunomaduro/termwind": "^2.3.0",
10075
+                "filp/whoops": "^2.18.1",
10076
+                "nunomaduro/termwind": "^2.3.1",
10078 10077
                 "php": "^8.2.0",
10079
-                "symfony/console": "^7.2.5"
10078
+                "symfony/console": "^7.3.0"
10080 10079
             },
10081 10080
             "conflict": {
10082 10081
                 "laravel/framework": "<11.44.2 || >=13.0.0",
@@ -10084,15 +10083,15 @@
10084 10083
             },
10085 10084
             "require-dev": {
10086 10085
                 "brianium/paratest": "^7.8.3",
10087
-                "larastan/larastan": "^3.2",
10088
-                "laravel/framework": "^11.44.2 || ^12.6",
10089
-                "laravel/pint": "^1.21.2",
10090
-                "laravel/sail": "^1.41.0",
10091
-                "laravel/sanctum": "^4.0.8",
10086
+                "larastan/larastan": "^3.4.2",
10087
+                "laravel/framework": "^11.44.2 || ^12.18",
10088
+                "laravel/pint": "^1.22.1",
10089
+                "laravel/sail": "^1.43.1",
10090
+                "laravel/sanctum": "^4.1.1",
10092 10091
                 "laravel/tinker": "^2.10.1",
10093
-                "orchestra/testbench-core": "^9.12.0 || ^10.1",
10094
-                "pestphp/pest": "^3.8.0",
10095
-                "sebastian/environment": "^7.2.0 || ^8.0"
10092
+                "orchestra/testbench-core": "^9.12.0 || ^10.4",
10093
+                "pestphp/pest": "^3.8.2",
10094
+                "sebastian/environment": "^7.2.1 || ^8.0"
10096 10095
             },
10097 10096
             "type": "library",
10098 10097
             "extra": {
@@ -10155,7 +10154,7 @@
10155 10154
                     "type": "patreon"
10156 10155
                 }
10157 10156
             ],
10158
-            "time": "2025-04-03T14:33:09+00:00"
10157
+            "time": "2025-06-11T01:04:21+00:00"
10159 10158
         },
10160 10159
         {
10161 10160
             "name": "pestphp/pest",

+ 31
- 0
database/migrations/2025_06_15_182955_create_notifications_table.php 查看文件

@@ -0,0 +1,31 @@
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
+    /**
10
+     * Run the migrations.
11
+     */
12
+    public function up(): void
13
+    {
14
+        Schema::create('notifications', function (Blueprint $table) {
15
+            $table->uuid('id')->primary();
16
+            $table->string('type');
17
+            $table->morphs('notifiable');
18
+            $table->text('data');
19
+            $table->timestamp('read_at')->nullable();
20
+            $table->timestamps();
21
+        });
22
+    }
23
+
24
+    /**
25
+     * Reverse the migrations.
26
+     */
27
+    public function down(): void
28
+    {
29
+        Schema::dropIfExists('notifications');
30
+    }
31
+};

+ 35
- 0
database/migrations/2025_06_15_183012_create_imports_table.php 查看文件

@@ -0,0 +1,35 @@
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
+    /**
10
+     * Run the migrations.
11
+     */
12
+    public function up(): void
13
+    {
14
+        Schema::create('imports', function (Blueprint $table) {
15
+            $table->id();
16
+            $table->timestamp('completed_at')->nullable();
17
+            $table->string('file_name');
18
+            $table->string('file_path');
19
+            $table->string('importer');
20
+            $table->unsignedInteger('processed_rows')->default(0);
21
+            $table->unsignedInteger('total_rows');
22
+            $table->unsignedInteger('successful_rows')->default(0);
23
+            $table->foreignId('user_id')->constrained()->cascadeOnDelete();
24
+            $table->timestamps();
25
+        });
26
+    }
27
+
28
+    /**
29
+     * Reverse the migrations.
30
+     */
31
+    public function down(): void
32
+    {
33
+        Schema::dropIfExists('imports');
34
+    }
35
+};

+ 35
- 0
database/migrations/2025_06_15_183013_create_exports_table.php 查看文件

@@ -0,0 +1,35 @@
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
+    /**
10
+     * Run the migrations.
11
+     */
12
+    public function up(): void
13
+    {
14
+        Schema::create('exports', function (Blueprint $table) {
15
+            $table->id();
16
+            $table->timestamp('completed_at')->nullable();
17
+            $table->string('file_disk');
18
+            $table->string('file_name')->nullable();
19
+            $table->string('exporter');
20
+            $table->unsignedInteger('processed_rows')->default(0);
21
+            $table->unsignedInteger('total_rows');
22
+            $table->unsignedInteger('successful_rows')->default(0);
23
+            $table->foreignId('user_id')->constrained()->cascadeOnDelete();
24
+            $table->timestamps();
25
+        });
26
+    }
27
+
28
+    /**
29
+     * Reverse the migrations.
30
+     */
31
+    public function down(): void
32
+    {
33
+        Schema::dropIfExists('exports');
34
+    }
35
+};

+ 30
- 0
database/migrations/2025_06_15_183014_create_failed_import_rows_table.php 查看文件

@@ -0,0 +1,30 @@
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
+    /**
10
+     * Run the migrations.
11
+     */
12
+    public function up(): void
13
+    {
14
+        Schema::create('failed_import_rows', function (Blueprint $table) {
15
+            $table->id();
16
+            $table->json('data');
17
+            $table->foreignId('import_id')->constrained()->cascadeOnDelete();
18
+            $table->text('validation_error')->nullable();
19
+            $table->timestamps();
20
+        });
21
+    }
22
+
23
+    /**
24
+     * Reverse the migrations.
25
+     */
26
+    public function down(): void
27
+    {
28
+        Schema::dropIfExists('failed_import_rows');
29
+    }
30
+};

Loading…
取消
儲存