Andrew Wallo 10 mēnešus atpakaļ
vecāks
revīzija
a7cb06067a

+ 2
- 0
app/Enums/Accounting/BillStatus.php Parādīt failu

7
 
7
 
8
 enum BillStatus: string implements HasColor, HasLabel
8
 enum BillStatus: string implements HasColor, HasLabel
9
 {
9
 {
10
+    case Overdue = 'overdue';
10
     case Partial = 'partial';
11
     case Partial = 'partial';
11
     case Paid = 'paid';
12
     case Paid = 'paid';
12
     case Unpaid = 'unpaid';
13
     case Unpaid = 'unpaid';
20
     public function getColor(): string | array | null
21
     public function getColor(): string | array | null
21
     {
22
     {
22
         return match ($this) {
23
         return match ($this) {
24
+            self::Overdue => 'danger',
23
             self::Partial, self::Unpaid => 'warning',
25
             self::Partial, self::Unpaid => 'warning',
24
             self::Paid => 'success',
26
             self::Paid => 'success',
25
             self::Void => 'gray',
27
             self::Void => 'gray',

+ 13
- 3
app/Filament/Company/Pages/Accounting/Transactions.php Parādīt failu

12
 use App\Filament\Forms\Components\JournalEntryRepeater;
12
 use App\Filament\Forms\Components\JournalEntryRepeater;
13
 use App\Filament\Tables\Actions\ReplicateBulkAction;
13
 use App\Filament\Tables\Actions\ReplicateBulkAction;
14
 use App\Models\Accounting\Account;
14
 use App\Models\Accounting\Account;
15
+use App\Models\Accounting\Invoice;
15
 use App\Models\Accounting\JournalEntry;
16
 use App\Models\Accounting\JournalEntry;
16
 use App\Models\Accounting\Transaction;
17
 use App\Models\Accounting\Transaction;
17
 use App\Models\Banking\BankAccount;
18
 use App\Models\Banking\BankAccount;
262
                     'account',
263
                     'account',
263
                     'bankAccount.account',
264
                     'bankAccount.account',
264
                     'journalEntries.account',
265
                     'journalEntries.account',
265
-                ]);
266
+                ])
267
+                    ->where(function (Builder $query) {
268
+                        $query->whereDoesntHaveMorph(
269
+                            'transactionable',
270
+                            [Invoice::class],
271
+                            function (Builder $query) {
272
+                                $query->where('type', TransactionType::Journal);
273
+                            }
274
+                        );
275
+                    });
266
             })
276
             })
267
             ->columns([
277
             ->columns([
268
                 Tables\Columns\TextColumn::make('posted_at')
278
                 Tables\Columns\TextColumn::make('posted_at')
275
                     ->toggleable(isToggledHiddenByDefault: true),
285
                     ->toggleable(isToggledHiddenByDefault: true),
276
                 Tables\Columns\TextColumn::make('description')
286
                 Tables\Columns\TextColumn::make('description')
277
                     ->label('Description')
287
                     ->label('Description')
278
-                    ->limit(30)
288
+                    ->limit(50)
279
                     ->toggleable(),
289
                     ->toggleable(),
280
                 Tables\Columns\TextColumn::make('bankAccount.account.name')
290
                 Tables\Columns\TextColumn::make('bankAccount.account.name')
281
                     ->label('Account')
291
                     ->label('Account')
320
                     ->options(TransactionType::class),
330
                     ->options(TransactionType::class),
321
                 $this->buildDateRangeFilter('posted_at', 'Posted', true),
331
                 $this->buildDateRangeFilter('posted_at', 'Posted', true),
322
                 $this->buildDateRangeFilter('updated_at', 'Last Modified'),
332
                 $this->buildDateRangeFilter('updated_at', 'Last Modified'),
323
-            ], layout: Tables\Enums\FiltersLayout::Modal)
333
+            ])
324
             ->filtersFormSchema(fn (array $filters): array => [
334
             ->filtersFormSchema(fn (array $filters): array => [
325
                 Grid::make()
335
                 Grid::make()
326
                     ->schema([
336
                     ->schema([

+ 19
- 13
app/Filament/Company/Resources/Sales/InvoiceResource.php Parādīt failu

29
 use Filament\Support\Enums\MaxWidth;
29
 use Filament\Support\Enums\MaxWidth;
30
 use Filament\Tables;
30
 use Filament\Tables;
31
 use Filament\Tables\Table;
31
 use Filament\Tables\Table;
32
+use Illuminate\Database\Eloquent\Builder;
32
 use Illuminate\Database\Eloquent\Collection;
33
 use Illuminate\Database\Eloquent\Collection;
33
 use Illuminate\Database\Eloquent\Model;
34
 use Illuminate\Database\Eloquent\Model;
34
 use Illuminate\Support\Carbon;
35
 use Illuminate\Support\Carbon;
447
                     ->sortable(),
448
                     ->sortable(),
448
                 Tables\Columns\TextColumn::make('invoice_number')
449
                 Tables\Columns\TextColumn::make('invoice_number')
449
                     ->label('Number')
450
                     ->label('Number')
450
-                    ->searchable(),
451
+                    ->searchable()
452
+                    ->sortable(),
451
                 Tables\Columns\TextColumn::make('client.name')
453
                 Tables\Columns\TextColumn::make('client.name')
452
                     ->sortable(),
454
                     ->sortable(),
453
                 Tables\Columns\TextColumn::make('total')
455
                 Tables\Columns\TextColumn::make('total')
460
                     ->currency(),
462
                     ->currency(),
461
             ])
463
             ])
462
             ->filters([
464
             ->filters([
463
-                Tables\Filters\SelectFilter::make('status')
464
-                    ->options(InvoiceStatus::class)
465
-                    ->native(false),
466
                 Tables\Filters\SelectFilter::make('client')
465
                 Tables\Filters\SelectFilter::make('client')
467
                     ->relationship('client', 'name')
466
                     ->relationship('client', 'name')
468
                     ->searchable()
467
                     ->searchable()
469
                     ->preload(),
468
                     ->preload(),
469
+                Tables\Filters\SelectFilter::make('status')
470
+                    ->options(InvoiceStatus::class)
471
+                    ->native(false),
472
+                Tables\Filters\TernaryFilter::make('has_payments')
473
+                    ->label('Has Payments')
474
+                    ->queries(
475
+                        true: fn (Builder $query) => $query->whereHas('payments'),
476
+                        false: fn (Builder $query) => $query->whereDoesntHave('payments'),
477
+                    ),
470
                 DateRangeFilter::make('date')
478
                 DateRangeFilter::make('date')
471
                     ->fromLabel('From Date')
479
                     ->fromLabel('From Date')
472
-                    ->untilLabel('Until Date')
473
-                    ->indicatorLabel('Date Range'),
474
-            ], layout: Tables\Enums\FiltersLayout::Modal)
475
-            ->filtersFormWidth(MaxWidth::Small)
476
-            ->filtersTriggerAction(
477
-                fn (Tables\Actions\Action $action) => $action
478
-                    ->label('Filter')
479
-                    ->slideOver(),
480
-            )
480
+                    ->untilLabel('To Date')
481
+                    ->indicatorLabel('Date'),
482
+                DateRangeFilter::make('due_date')
483
+                    ->fromLabel('From Due Date')
484
+                    ->untilLabel('To Due Date')
485
+                    ->indicatorLabel('Due'),
486
+            ])
481
             ->actions([
487
             ->actions([
482
                 Tables\Actions\ActionGroup::make([
488
                 Tables\Actions\ActionGroup::make([
483
                     Tables\Actions\EditAction::make(),
489
                     Tables\Actions\EditAction::make(),

+ 41
- 0
app/Filament/Company/Resources/Sales/InvoiceResource/Pages/ListInvoices.php Parādīt failu

2
 
2
 
3
 namespace App\Filament\Company\Resources\Sales\InvoiceResource\Pages;
3
 namespace App\Filament\Company\Resources\Sales\InvoiceResource\Pages;
4
 
4
 
5
+use App\Enums\Accounting\InvoiceStatus;
5
 use App\Filament\Company\Resources\Sales\InvoiceResource;
6
 use App\Filament\Company\Resources\Sales\InvoiceResource;
7
+use App\Models\Accounting\Invoice;
6
 use Filament\Actions;
8
 use Filament\Actions;
9
+use Filament\Resources\Components\Tab;
7
 use Filament\Resources\Pages\ListRecords;
10
 use Filament\Resources\Pages\ListRecords;
11
+use Illuminate\Database\Eloquent\Builder;
8
 
12
 
9
 class ListInvoices extends ListRecords
13
 class ListInvoices extends ListRecords
10
 {
14
 {
16
             Actions\CreateAction::make(),
20
             Actions\CreateAction::make(),
17
         ];
21
         ];
18
     }
22
     }
23
+
24
+    public function getTabs(): array
25
+    {
26
+        return [
27
+            'all' => Tab::make()
28
+                ->label('All'),
29
+
30
+            'overdue' => Tab::make()
31
+                ->label('Overdue')
32
+                ->modifyQueryUsing(function (Builder $query) {
33
+                    $query->where('status', InvoiceStatus::Overdue);
34
+                })
35
+                ->badge(Invoice::where('status', InvoiceStatus::Overdue)->count()),
36
+
37
+            'unpaid' => Tab::make()
38
+                ->label('Unpaid')
39
+                ->modifyQueryUsing(function (Builder $query) {
40
+                    $query->whereIn('status', [
41
+                        InvoiceStatus::Unsent,
42
+                        InvoiceStatus::Sent,
43
+                        InvoiceStatus::Partial,
44
+                    ]);
45
+                })
46
+                ->badge(Invoice::whereIn('status', [
47
+                    InvoiceStatus::Unsent,
48
+                    InvoiceStatus::Sent,
49
+                    InvoiceStatus::Partial,
50
+                ])->count()),
51
+
52
+            'draft' => Tab::make()
53
+                ->label('Draft')
54
+                ->modifyQueryUsing(function (Builder $query) {
55
+                    $query->where('status', InvoiceStatus::Draft);
56
+                })
57
+                ->badge(Invoice::where('status', InvoiceStatus::Draft)->count()),
58
+        ];
59
+    }
19
 }
60
 }

+ 8
- 0
app/Models/Accounting/Invoice.php Parādīt failu

13
 use App\Observers\InvoiceObserver;
13
 use App\Observers\InvoiceObserver;
14
 use Illuminate\Database\Eloquent\Attributes\CollectedBy;
14
 use Illuminate\Database\Eloquent\Attributes\CollectedBy;
15
 use Illuminate\Database\Eloquent\Attributes\ObservedBy;
15
 use Illuminate\Database\Eloquent\Attributes\ObservedBy;
16
+use Illuminate\Database\Eloquent\Casts\Attribute;
16
 use Illuminate\Database\Eloquent\Factories\HasFactory;
17
 use Illuminate\Database\Eloquent\Factories\HasFactory;
17
 use Illuminate\Database\Eloquent\Model;
18
 use Illuminate\Database\Eloquent\Model;
18
 use Illuminate\Database\Eloquent\Relations\BelongsTo;
19
 use Illuminate\Database\Eloquent\Relations\BelongsTo;
100
             ->where('type', TransactionType::Journal);
101
             ->where('type', TransactionType::Journal);
101
     }
102
     }
102
 
103
 
104
+    protected function isCurrentlyOverdue(): Attribute
105
+    {
106
+        return Attribute::get(function () {
107
+            return $this->due_date->isBefore(today()) && $this->canBeOverdue();
108
+        });
109
+    }
110
+
103
     public function isDraft(): bool
111
     public function isDraft(): bool
104
     {
112
     {
105
         return $this->status === InvoiceStatus::Draft;
113
         return $this->status === InvoiceStatus::Draft;

+ 1
- 1
app/Observers/InvoiceObserver.php Parādīt failu

33
 
33
 
34
     public function saving(Invoice $invoice): void
34
     public function saving(Invoice $invoice): void
35
     {
35
     {
36
-        if ($invoice->due_date->isBefore(today()) && $invoice->canBeOverdue()) {
36
+        if ($invoice->is_currently_overdue) {
37
             $invoice->status = InvoiceStatus::Overdue;
37
             $invoice->status = InvoiceStatus::Overdue;
38
         }
38
         }
39
     }
39
     }

+ 5
- 1
app/Providers/FilamentCompaniesServiceProvider.php Parādīt failu

52
 use Filament\Panel;
52
 use Filament\Panel;
53
 use Filament\PanelProvider;
53
 use Filament\PanelProvider;
54
 use Filament\Support\Colors\Color;
54
 use Filament\Support\Colors\Color;
55
+use Filament\Support\Enums\MaxWidth;
55
 use Filament\Tables;
56
 use Filament\Tables;
56
 use Filament\Widgets;
57
 use Filament\Widgets;
57
 use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
58
 use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
266
         });
267
         });
267
 
268
 
268
         Tables\Table::configureUsing(static function (Tables\Table $table): void {
269
         Tables\Table::configureUsing(static function (Tables\Table $table): void {
269
-            $table->paginationPageOptions([5, 10, 25, 50, 100]);
270
+            $table
271
+                ->paginationPageOptions([5, 10, 25, 50, 100])
272
+                ->filtersFormWidth(MaxWidth::Small)
273
+                ->filtersTriggerAction(fn (Tables\Actions\Action $action) => $action->slideOver());
270
         }, isImportant: true);
274
         }, isImportant: true);
271
     }
275
     }
272
 
276
 

+ 3
- 1
database/factories/Accounting/DocumentLineItemFactory.php Parādīt failu

30
 
30
 
31
         return [
31
         return [
32
             'company_id' => 1,
32
             'company_id' => 1,
33
-            'offering_id' => 1,
33
+            'offering_id' => $offering->id,
34
             'description' => $this->faker->sentence,
34
             'description' => $this->faker->sentence,
35
             'quantity' => $quantity,
35
             'quantity' => $quantity,
36
             'unit_price' => $unitPrice,
36
             'unit_price' => $unitPrice,
49
                 $lineItem->salesDiscounts()->sync($offering->salesDiscounts->pluck('id')->toArray());
49
                 $lineItem->salesDiscounts()->sync($offering->salesDiscounts->pluck('id')->toArray());
50
             }
50
             }
51
 
51
 
52
+            $lineItem->refresh();
53
+
52
             $taxTotal = $lineItem->calculateTaxTotal()->getAmount();
54
             $taxTotal = $lineItem->calculateTaxTotal()->getAmount();
53
             $discountTotal = $lineItem->calculateDiscountTotal()->getAmount();
55
             $discountTotal = $lineItem->calculateDiscountTotal()->getAmount();
54
 
56
 

+ 7
- 3
database/factories/Accounting/InvoiceFactory.php Parādīt failu

58
                 return;
58
                 return;
59
             }
59
             }
60
 
60
 
61
+            $this->recalculateTotals($invoice);
62
+
61
             $invoice->approveDraft();
63
             $invoice->approveDraft();
62
         });
64
         });
63
     }
65
     }
66
     {
68
     {
67
         return $this->afterCreating(function (Invoice $invoice) use ($invoiceStatus, $max, $min) {
69
         return $this->afterCreating(function (Invoice $invoice) use ($invoiceStatus, $max, $min) {
68
             if ($invoice->isDraft()) {
70
             if ($invoice->isDraft()) {
71
+
72
+                $this->recalculateTotals($invoice);
73
+
69
                 $invoice->approveDraft();
74
                 $invoice->approveDraft();
70
             }
75
             }
71
 
76
 
72
-            $this->recalculateTotals($invoice);
73
-
74
             $invoice->refresh();
77
             $invoice->refresh();
75
 
78
 
76
             $totalAmountDue = $invoice->getRawOriginal('amount_due');
79
             $totalAmountDue = $invoice->getRawOriginal('amount_due');
125
 
128
 
126
             $this->recalculateTotals($invoice);
129
             $this->recalculateTotals($invoice);
127
 
130
 
128
-            if ($invoice->due_date->isBefore(today()) && $invoice->canBeOverdue()) {
131
+            if ($invoice->is_currently_overdue) {
129
                 $invoice->updateQuietly([
132
                 $invoice->updateQuietly([
130
                     'status' => InvoiceStatus::Overdue,
133
                     'status' => InvoiceStatus::Overdue,
131
                 ]);
134
                 ]);
136
     protected function recalculateTotals(Invoice $invoice): void
139
     protected function recalculateTotals(Invoice $invoice): void
137
     {
140
     {
138
         if ($invoice->lineItems()->exists()) {
141
         if ($invoice->lineItems()->exists()) {
142
+            $invoice->refresh();
139
             $subtotal = $invoice->lineItems()->sum('subtotal') / 100;
143
             $subtotal = $invoice->lineItems()->sum('subtotal') / 100;
140
             $taxTotal = $invoice->lineItems()->sum('tax_total') / 100;
144
             $taxTotal = $invoice->lineItems()->sum('tax_total') / 100;
141
             $discountTotal = $invoice->lineItems()->sum('discount_total') / 100;
145
             $discountTotal = $invoice->lineItems()->sum('discount_total') / 100;

Notiek ielāde…
Atcelt
Saglabāt