瀏覽代碼

export pdf summary table and refactor

3.x
Andrew Wallo 11 月之前
父節點
當前提交
4b7b8dfd27
共有 24 個文件被更改,包括 865 次插入743 次删除
  1. 2
    0
      app/Contracts/HasSummaryReport.php
  2. 24
    0
      app/Filament/Company/Pages/Concerns/HasReportTabs.php
  3. 8
    8
      app/Filament/Company/Pages/Reports.php
  4. 0
    4
      app/Filament/Company/Pages/Reports/AccountBalances.php
  5. 0
    4
      app/Filament/Company/Pages/Reports/AccountTransactions.php
  6. 5
    8
      app/Filament/Company/Pages/Reports/BalanceSheet.php
  7. 26
    0
      app/Filament/Company/Pages/Reports/BaseReportPage.php
  8. 5
    10
      app/Filament/Company/Pages/Reports/CashFlowStatement.php
  9. 5
    10
      app/Filament/Company/Pages/Reports/IncomeStatement.php
  10. 0
    4
      app/Filament/Company/Pages/Reports/TrialBalance.php
  11. 141
    46
      app/Services/ExportService.php
  12. 6
    8
      app/Transformers/IncomeStatementReportTransformer.php
  13. 5
    0
      app/Transformers/SummaryReportTransformer.php
  14. 59
    149
      resources/views/components/company/reports/account-transactions-report-pdf.blade.php
  15. 168
    284
      resources/views/components/company/reports/cash-flow-statement-pdf.blade.php
  16. 61
    0
      resources/views/components/company/reports/income-statement-summary-pdf.blade.php
  17. 114
    0
      resources/views/components/company/reports/layout.blade.php
  18. 94
    202
      resources/views/components/company/reports/report-pdf.blade.php
  19. 136
    0
      resources/views/components/company/reports/summary-report-pdf.blade.php
  20. 1
    1
      resources/views/components/company/tables/reports/income-statement-summary.blade.php
  21. 2
    2
      resources/views/components/report-tabs.blade.php
  22. 1
    1
      resources/views/filament/company/pages/reports/balance-sheet.blade.php
  23. 1
    1
      resources/views/filament/company/pages/reports/cash-flow-statement.blade.php
  24. 1
    1
      resources/views/filament/company/pages/reports/income-statement.blade.php

+ 2
- 0
app/Contracts/HasSummaryReport.php 查看文件

20
     public function getSummaryCategories(): array;
20
     public function getSummaryCategories(): array;
21
 
21
 
22
     public function getSummaryOverallTotals(): array;
22
     public function getSummaryOverallTotals(): array;
23
+
24
+    public function getSummaryPdfView(): string;
23
 }
25
 }

+ 24
- 0
app/Filament/Company/Pages/Concerns/HasReportTabs.php 查看文件

1
+<?php
2
+
3
+namespace App\Filament\Company\Pages\Concerns;
4
+
5
+use Livewire\Attributes\Url;
6
+
7
+trait HasReportTabs
8
+{
9
+    #[Url]
10
+    public ?string $activeTab = 'summary';
11
+
12
+    public function getTabs(): array
13
+    {
14
+        return [
15
+            'summary' => 'Summary',
16
+            'details' => 'Details',
17
+        ];
18
+    }
19
+
20
+    public function getActiveTab(): string
21
+    {
22
+        return $this->activeTab;
23
+    }
24
+}

+ 8
- 8
app/Filament/Company/Pages/Reports.php 查看文件

27
             ->schema([
27
             ->schema([
28
                 Section::make('Financial Statements')
28
                 Section::make('Financial Statements')
29
                     ->aside()
29
                     ->aside()
30
-                    ->description('Key financial statements that provide a snapshot of your company’s financial health.')
30
+                    ->description('Key financial statements that provide an overview of your company’s financial health and performance.')
31
                     ->extraAttributes(['class' => 'es-report-card'])
31
                     ->extraAttributes(['class' => 'es-report-card'])
32
                     ->schema([
32
                     ->schema([
33
                         ReportEntry::make('income_statement')
33
                         ReportEntry::make('income_statement')
34
                             ->hiddenLabel()
34
                             ->hiddenLabel()
35
                             ->heading('Income Statement')
35
                             ->heading('Income Statement')
36
-                            ->description('Tracks revenue and expenses to show profit or loss over a specific period of time.')
36
+                            ->description('Shows revenue, expenses, and net earnings over a period, indicating overall financial performance.')
37
                             ->icon('heroicon-o-chart-bar')
37
                             ->icon('heroicon-o-chart-bar')
38
                             ->iconColor(Color::Indigo)
38
                             ->iconColor(Color::Indigo)
39
                             ->url(IncomeStatement::getUrl()),
39
                             ->url(IncomeStatement::getUrl()),
40
                         ReportEntry::make('balance_sheet')
40
                         ReportEntry::make('balance_sheet')
41
                             ->hiddenLabel()
41
                             ->hiddenLabel()
42
                             ->heading('Balance Sheet')
42
                             ->heading('Balance Sheet')
43
-                            ->description('Snapshot of assets, liabilities, and equity at a specific point in time.')
43
+                            ->description('Displays your company’s assets, liabilities, and equity at a single point in time, showing overall financial health and stability.')
44
                             ->icon('heroicon-o-clipboard-document-list')
44
                             ->icon('heroicon-o-clipboard-document-list')
45
                             ->iconColor(Color::Emerald)
45
                             ->iconColor(Color::Emerald)
46
                             ->url(BalanceSheet::getUrl()),
46
                             ->url(BalanceSheet::getUrl()),
47
                         ReportEntry::make('cash_flow_statement')
47
                         ReportEntry::make('cash_flow_statement')
48
                             ->hiddenLabel()
48
                             ->hiddenLabel()
49
                             ->heading('Cash Flow Statement')
49
                             ->heading('Cash Flow Statement')
50
-                            ->description('Shows cash inflows and outflows over a specific period of time.')
50
+                            ->description('Tracks cash inflows and outflows, giving insight into liquidity and cash management over a period.')
51
                             ->icon('heroicon-o-document-currency-dollar')
51
                             ->icon('heroicon-o-document-currency-dollar')
52
                             ->iconColor(Color::Cyan)
52
                             ->iconColor(Color::Cyan)
53
                             ->url(CashFlowStatement::getUrl()),
53
                             ->url(CashFlowStatement::getUrl()),
54
                     ]),
54
                     ]),
55
                 Section::make('Detailed Reports')
55
                 Section::make('Detailed Reports')
56
                     ->aside()
56
                     ->aside()
57
-                    ->description('Dig into the details of your company’s transactions, balances, and accounts.')
57
+                    ->description('Detailed reports that provide a comprehensive view of your company’s financial transactions and account balances.')
58
                     ->extraAttributes(['class' => 'es-report-card'])
58
                     ->extraAttributes(['class' => 'es-report-card'])
59
                     ->schema([
59
                     ->schema([
60
                         ReportEntry::make('account_balances')
60
                         ReportEntry::make('account_balances')
61
                             ->hiddenLabel()
61
                             ->hiddenLabel()
62
                             ->heading('Account Balances')
62
                             ->heading('Account Balances')
63
-                            ->description('Summary view of balances and activity for all accounts.')
63
+                            ->description('Lists all accounts and their balances, including starting, debit, credit, net movement, and ending balances.')
64
                             ->icon('heroicon-o-currency-dollar')
64
                             ->icon('heroicon-o-currency-dollar')
65
                             ->iconColor(Color::Teal)
65
                             ->iconColor(Color::Teal)
66
                             ->url(AccountBalances::getUrl()),
66
                             ->url(AccountBalances::getUrl()),
67
                         ReportEntry::make('trial_balance')
67
                         ReportEntry::make('trial_balance')
68
                             ->hiddenLabel()
68
                             ->hiddenLabel()
69
                             ->heading('Trial Balance')
69
                             ->heading('Trial Balance')
70
-                            ->description('The sum of all debit and credit balances for all accounts on a single day. This helps to ensure that the books are in balance.')
70
+                            ->description('Summarizes all account debits and credits on a specific date to verify the ledger is balanced.')
71
                             ->icon('heroicon-o-scale')
71
                             ->icon('heroicon-o-scale')
72
                             ->iconColor(Color::Sky)
72
                             ->iconColor(Color::Sky)
73
                             ->url(TrialBalance::getUrl()),
73
                             ->url(TrialBalance::getUrl()),
74
                         ReportEntry::make('account_transactions')
74
                         ReportEntry::make('account_transactions')
75
                             ->hiddenLabel()
75
                             ->hiddenLabel()
76
                             ->heading('Account Transactions')
76
                             ->heading('Account Transactions')
77
-                            ->description('A record of all transactions for a company. The general ledger is the core of a company\'s financial records.')
77
+                            ->description('A record of all transactions, essential for monitoring and reconciling financial activity in the ledger.')
78
                             ->icon('heroicon-o-adjustments-horizontal')
78
                             ->icon('heroicon-o-adjustments-horizontal')
79
                             ->iconColor(Color::Amber)
79
                             ->iconColor(Color::Amber)
80
                             ->url(AccountTransactions::getUrl()),
80
                             ->url(AccountTransactions::getUrl()),

+ 0
- 4
app/Filament/Company/Pages/Reports/AccountBalances.php 查看文件

17
 {
17
 {
18
     protected static string $view = 'filament.company.pages.reports.detailed-report';
18
     protected static string $view = 'filament.company.pages.reports.detailed-report';
19
 
19
 
20
-    protected static ?string $slug = 'reports/account-balances';
21
-
22
-    protected static bool $shouldRegisterNavigation = false;
23
-
24
     protected ReportService $reportService;
20
     protected ReportService $reportService;
25
 
21
 
26
     protected ExportService $exportService;
22
     protected ExportService $exportService;

+ 0
- 4
app/Filament/Company/Pages/Reports/AccountTransactions.php 查看文件

27
 {
27
 {
28
     protected static string $view = 'filament.company.pages.reports.account-transactions';
28
     protected static string $view = 'filament.company.pages.reports.account-transactions';
29
 
29
 
30
-    protected static ?string $slug = 'reports/account-transactions';
31
-
32
-    protected static bool $shouldRegisterNavigation = false;
33
-
34
     protected ReportService $reportService;
30
     protected ReportService $reportService;
35
 
31
 
36
     protected ExportService $exportService;
32
     protected ExportService $exportService;

+ 5
- 8
app/Filament/Company/Pages/Reports/BalanceSheet.php 查看文件

4
 
4
 
5
 use App\Contracts\ExportableReport;
5
 use App\Contracts\ExportableReport;
6
 use App\DTO\ReportDTO;
6
 use App\DTO\ReportDTO;
7
+use App\Filament\Company\Pages\Concerns\HasReportTabs;
7
 use App\Filament\Forms\Components\DateRangeSelect;
8
 use App\Filament\Forms\Components\DateRangeSelect;
8
 use App\Services\ExportService;
9
 use App\Services\ExportService;
9
 use App\Services\ReportService;
10
 use App\Services\ReportService;
11
 use App\Transformers\BalanceSheetReportTransformer;
12
 use App\Transformers\BalanceSheetReportTransformer;
12
 use Filament\Forms\Form;
13
 use Filament\Forms\Form;
13
 use Filament\Support\Enums\Alignment;
14
 use Filament\Support\Enums\Alignment;
14
-use Livewire\Attributes\Url;
15
 use Symfony\Component\HttpFoundation\StreamedResponse;
15
 use Symfony\Component\HttpFoundation\StreamedResponse;
16
 
16
 
17
 class BalanceSheet extends BaseReportPage
17
 class BalanceSheet extends BaseReportPage
18
 {
18
 {
19
-    protected static string $view = 'filament.company.pages.reports.balance-sheet';
19
+    use HasReportTabs;
20
 
20
 
21
-    protected static bool $shouldRegisterNavigation = false;
21
+    protected static string $view = 'filament.company.pages.reports.balance-sheet';
22
 
22
 
23
     protected ReportService $reportService;
23
     protected ReportService $reportService;
24
 
24
 
25
     protected ExportService $exportService;
25
     protected ExportService $exportService;
26
 
26
 
27
-    #[Url]
28
-    public ?string $activeTab = 'summary';
29
-
30
     public function boot(ReportService $reportService, ExportService $exportService): void
27
     public function boot(ReportService $reportService, ExportService $exportService): void
31
     {
28
     {
32
         $this->reportService = $reportService;
29
         $this->reportService = $reportService;
77
 
74
 
78
     public function exportCSV(): StreamedResponse
75
     public function exportCSV(): StreamedResponse
79
     {
76
     {
80
-        return $this->exportService->exportToCsv($this->company, $this->report, endDate: $this->getFilterState('asOfDate'));
77
+        return $this->exportService->exportToCsv($this->company, $this->report, endDate: $this->getFilterState('asOfDate'), activeTab: $this->getActiveTab());
81
     }
78
     }
82
 
79
 
83
     public function exportPDF(): StreamedResponse
80
     public function exportPDF(): StreamedResponse
84
     {
81
     {
85
-        return $this->exportService->exportToPdf($this->company, $this->report, endDate: $this->getFilterState('asOfDate'));
82
+        return $this->exportService->exportToPdf($this->company, $this->report, endDate: $this->getFilterState('asOfDate'), activeTab: $this->getActiveTab());
86
     }
83
     }
87
 }
84
 }

+ 26
- 0
app/Filament/Company/Pages/Reports/BaseReportPage.php 查看文件

6
 use App\DTO\ReportDTO;
6
 use App\DTO\ReportDTO;
7
 use App\Filament\Company\Pages\Concerns\HasDeferredFiltersForm;
7
 use App\Filament\Company\Pages\Concerns\HasDeferredFiltersForm;
8
 use App\Filament\Company\Pages\Concerns\HasTableColumnToggleForm;
8
 use App\Filament\Company\Pages\Concerns\HasTableColumnToggleForm;
9
+use App\Filament\Company\Pages\Reports;
9
 use App\Filament\Forms\Components\DateRangeSelect;
10
 use App\Filament\Forms\Components\DateRangeSelect;
10
 use App\Models\Company;
11
 use App\Models\Company;
11
 use App\Services\DateRangeService;
12
 use App\Services\DateRangeService;
62
         $this->fiscalYearEndDate = $this->company->locale->fiscalYearEndDate();
63
         $this->fiscalYearEndDate = $this->company->locale->fiscalYearEndDate();
63
     }
64
     }
64
 
65
 
66
+    public static function shouldRegisterNavigation(): bool
67
+    {
68
+        return false;
69
+    }
70
+
71
+    public static function getSlug(): string
72
+    {
73
+        $prefix = Reports::getSlug() . '/';
74
+
75
+        if (filled(static::$slug)) {
76
+            return $prefix . static::$slug;
77
+        }
78
+
79
+        return $prefix . str(class_basename(static::class))
80
+            ->kebab()
81
+            ->slug();
82
+    }
83
+
84
+    public function getBreadcrumbs(): array
85
+    {
86
+        return [
87
+            Reports::getUrl() => Reports::getNavigationLabel(),
88
+        ];
89
+    }
90
+
65
     protected function loadDefaultDateRange(): void
91
     protected function loadDefaultDateRange(): void
66
     {
92
     {
67
         $flatFields = $this->getFiltersForm()->getFlatFields();
93
         $flatFields = $this->getFiltersForm()->getFlatFields();

+ 5
- 10
app/Filament/Company/Pages/Reports/CashFlowStatement.php 查看文件

4
 
4
 
5
 use App\Contracts\ExportableReport;
5
 use App\Contracts\ExportableReport;
6
 use App\DTO\ReportDTO;
6
 use App\DTO\ReportDTO;
7
+use App\Filament\Company\Pages\Concerns\HasReportTabs;
7
 use App\Services\ExportService;
8
 use App\Services\ExportService;
8
 use App\Services\ReportService;
9
 use App\Services\ReportService;
9
 use App\Support\Column;
10
 use App\Support\Column;
11
 use Filament\Forms\Form;
12
 use Filament\Forms\Form;
12
 use Filament\Support\Enums\Alignment;
13
 use Filament\Support\Enums\Alignment;
13
 use Guava\FilamentClusters\Forms\Cluster;
14
 use Guava\FilamentClusters\Forms\Cluster;
14
-use Livewire\Attributes\Url;
15
 use Symfony\Component\HttpFoundation\StreamedResponse;
15
 use Symfony\Component\HttpFoundation\StreamedResponse;
16
 
16
 
17
 class CashFlowStatement extends BaseReportPage
17
 class CashFlowStatement extends BaseReportPage
18
 {
18
 {
19
-    protected static string $view = 'filament.company.pages.reports.cash-flow-statement';
20
-
21
-    protected static ?string $slug = 'reports/cash-flow-statement';
19
+    use HasReportTabs;
22
 
20
 
23
-    protected static bool $shouldRegisterNavigation = false;
21
+    protected static string $view = 'filament.company.pages.reports.cash-flow-statement';
24
 
22
 
25
     protected ReportService $reportService;
23
     protected ReportService $reportService;
26
 
24
 
27
     protected ExportService $exportService;
25
     protected ExportService $exportService;
28
 
26
 
29
-    #[Url]
30
-    public ?string $activeTab = 'summary';
31
-
32
     public function boot(ReportService $reportService, ExportService $exportService): void
27
     public function boot(ReportService $reportService, ExportService $exportService): void
33
     {
28
     {
34
         $this->reportService = $reportService;
29
         $this->reportService = $reportService;
77
 
72
 
78
     public function exportCSV(): StreamedResponse
73
     public function exportCSV(): StreamedResponse
79
     {
74
     {
80
-        return $this->exportService->exportToCsv($this->company, $this->report, $this->getFilterState('startDate'), $this->getFilterState('endDate'));
75
+        return $this->exportService->exportToCsv($this->company, $this->report, $this->getFilterState('startDate'), $this->getFilterState('endDate'), $this->getActiveTab());
81
     }
76
     }
82
 
77
 
83
     public function exportPDF(): StreamedResponse
78
     public function exportPDF(): StreamedResponse
84
     {
79
     {
85
-        return $this->exportService->exportToPdf($this->company, $this->report, $this->getFilterState('startDate'), $this->getFilterState('endDate'));
80
+        return $this->exportService->exportToPdf($this->company, $this->report, $this->getFilterState('startDate'), $this->getFilterState('endDate'), $this->getActiveTab());
86
     }
81
     }
87
 }
82
 }

+ 5
- 10
app/Filament/Company/Pages/Reports/IncomeStatement.php 查看文件

4
 
4
 
5
 use App\Contracts\ExportableReport;
5
 use App\Contracts\ExportableReport;
6
 use App\DTO\ReportDTO;
6
 use App\DTO\ReportDTO;
7
+use App\Filament\Company\Pages\Concerns\HasReportTabs;
7
 use App\Services\ExportService;
8
 use App\Services\ExportService;
8
 use App\Services\ReportService;
9
 use App\Services\ReportService;
9
 use App\Support\Column;
10
 use App\Support\Column;
11
 use Filament\Forms\Form;
12
 use Filament\Forms\Form;
12
 use Filament\Support\Enums\Alignment;
13
 use Filament\Support\Enums\Alignment;
13
 use Guava\FilamentClusters\Forms\Cluster;
14
 use Guava\FilamentClusters\Forms\Cluster;
14
-use Livewire\Attributes\Url;
15
 use Symfony\Component\HttpFoundation\StreamedResponse;
15
 use Symfony\Component\HttpFoundation\StreamedResponse;
16
 
16
 
17
 class IncomeStatement extends BaseReportPage
17
 class IncomeStatement extends BaseReportPage
18
 {
18
 {
19
-    protected static string $view = 'filament.company.pages.reports.income-statement';
20
-
21
-    protected static ?string $slug = 'reports/income-statement';
19
+    use HasReportTabs;
22
 
20
 
23
-    protected static bool $shouldRegisterNavigation = false;
21
+    protected static string $view = 'filament.company.pages.reports.income-statement';
24
 
22
 
25
     protected ReportService $reportService;
23
     protected ReportService $reportService;
26
 
24
 
27
     protected ExportService $exportService;
25
     protected ExportService $exportService;
28
 
26
 
29
-    #[Url]
30
-    public ?string $activeTab = 'summary';
31
-
32
     public function boot(ReportService $reportService, ExportService $exportService): void
27
     public function boot(ReportService $reportService, ExportService $exportService): void
33
     {
28
     {
34
         $this->reportService = $reportService;
29
         $this->reportService = $reportService;
77
 
72
 
78
     public function exportCSV(): StreamedResponse
73
     public function exportCSV(): StreamedResponse
79
     {
74
     {
80
-        return $this->exportService->exportToCsv($this->company, $this->report, $this->getFilterState('startDate'), $this->getFilterState('endDate'));
75
+        return $this->exportService->exportToCsv($this->company, $this->report, $this->getFilterState('startDate'), $this->getFilterState('endDate'), $this->getActiveTab());
81
     }
76
     }
82
 
77
 
83
     public function exportPDF(): StreamedResponse
78
     public function exportPDF(): StreamedResponse
84
     {
79
     {
85
-        return $this->exportService->exportToPdf($this->company, $this->report, $this->getFilterState('startDate'), $this->getFilterState('endDate'));
80
+        return $this->exportService->exportToPdf($this->company, $this->report, $this->getFilterState('startDate'), $this->getFilterState('endDate'), $this->getActiveTab());
86
     }
81
     }
87
 }
82
 }

+ 0
- 4
app/Filament/Company/Pages/Reports/TrialBalance.php 查看文件

18
 {
18
 {
19
     protected static string $view = 'filament.company.pages.reports.trial-balance';
19
     protected static string $view = 'filament.company.pages.reports.trial-balance';
20
 
20
 
21
-    protected static ?string $slug = 'reports/trial-balance';
22
-
23
-    protected static bool $shouldRegisterNavigation = false;
24
-
25
     protected ReportService $reportService;
21
     protected ReportService $reportService;
26
 
22
 
27
     protected ExportService $exportService;
23
     protected ExportService $exportService;

+ 141
- 46
app/Services/ExportService.php 查看文件

3
 namespace App\Services;
3
 namespace App\Services;
4
 
4
 
5
 use App\Contracts\ExportableReport;
5
 use App\Contracts\ExportableReport;
6
+use App\Contracts\HasSummaryReport;
6
 use App\Models\Company;
7
 use App\Models\Company;
7
 use App\Transformers\CashFlowStatementReportTransformer;
8
 use App\Transformers\CashFlowStatementReportTransformer;
8
 use Barryvdh\Snappy\Facades\SnappyPdf;
9
 use Barryvdh\Snappy\Facades\SnappyPdf;
16
 
17
 
17
 class ExportService
18
 class ExportService
18
 {
19
 {
19
-    public function exportToCsv(Company $company, ExportableReport $report, ?string $startDate = null, ?string $endDate = null): StreamedResponse
20
+    public function exportToCsv(Company $company, ExportableReport $report, ?string $startDate = null, ?string $endDate = null, ?string $activeTab = null): StreamedResponse
20
     {
21
     {
21
         if ($startDate && $endDate) {
22
         if ($startDate && $endDate) {
22
             $formattedStartDate = Carbon::parse($startDate)->toDateString();
23
             $formattedStartDate = Carbon::parse($startDate)->toDateString();
36
             'Content-Disposition' => 'attachment; filename="' . $filename . '"',
37
             'Content-Disposition' => 'attachment; filename="' . $filename . '"',
37
         ];
38
         ];
38
 
39
 
39
-        $callback = function () use ($startDate, $endDate, $report, $company) {
40
+        $callback = function () use ($startDate, $endDate, $report, $company, $activeTab) {
40
             $csv = Writer::createFromStream(fopen('php://output', 'wb'));
41
             $csv = Writer::createFromStream(fopen('php://output', 'wb'));
41
             $csv->setOutputBOM(Bom::Utf8);
42
             $csv->setOutputBOM(Bom::Utf8);
42
 
43
 
53
             $csv->insertOne([$dateLabel]);
54
             $csv->insertOne([$dateLabel]);
54
             $csv->insertOne([]);
55
             $csv->insertOne([]);
55
 
56
 
56
-            $csv->insertOne($report->getHeaders());
57
+            if ($activeTab === 'summary') {
58
+                $this->writeSummaryTableToCsv($csv, $report);
59
+            } else {
60
+                $this->writeDetailedTableToCsv($csv, $report);
61
+            }
62
+        };
57
 
63
 
58
-            foreach ($report->getCategories() as $category) {
59
-                $this->writeDataRowsToCsv($csv, $category->header, $category->data, $report->getColumns());
64
+        return response()->streamDownload($callback, $filename, $headers);
65
+    }
60
 
66
 
61
-                foreach ($category->types ?? [] as $type) {
62
-                    $this->writeDataRowsToCsv($csv, $type->header, $type->data, $report->getColumns());
67
+    /**
68
+     * @throws CannotInsertRecord
69
+     * @throws Exception
70
+     */
71
+    protected function writeSummaryTableToCsv(Writer $csv, ExportableReport $report): void
72
+    {
73
+        /** @var HasSummaryReport $report */
74
+        $csv->insertOne($report->getSummaryHeaders());
63
 
75
 
64
-                    if (filled($type->summary)) {
65
-                        $csv->insertOne($type->summary);
66
-                    }
67
-                }
76
+        foreach ($report->getSummaryCategories() as $category) {
77
+            if (filled($category->header)) {
78
+                $csv->insertOne($category->header);
79
+            }
68
 
80
 
69
-                if (filled($category->summary)) {
70
-                    $csv->insertOne($category->summary);
71
-                }
81
+            foreach ($category->types ?? [] as $type) {
82
+                $csv->insertOne($type->summary);
83
+            }
72
 
84
 
85
+            if (filled($category->summary)) {
86
+                $csv->insertOne($category->summary);
87
+            }
88
+
89
+            if ($category->summary['account_name'] === 'Cost of Goods Sold' && method_exists($report, 'getGrossProfit') && filled($report->getGrossProfit())) {
90
+                $csv->insertOne($report->getGrossProfit());
91
+            }
92
+
93
+            if (filled($category->header)) {
73
                 $csv->insertOne([]);
94
                 $csv->insertOne([]);
74
             }
95
             }
96
+        }
97
+
98
+        if (method_exists($report, 'getSummaryOverviewHeaders') && filled($report->getSummaryOverviewHeaders())) {
99
+            $this->writeSummaryOverviewTableToCsv($csv, $report);
100
+        }
101
+
102
+        if (filled($report->getSummaryOverallTotals())) {
103
+            $csv->insertOne($report->getSummaryOverallTotals());
104
+        }
105
+    }
106
+
107
+    /**
108
+     * @throws CannotInsertRecord
109
+     * @throws Exception
110
+     */
111
+    protected function writeDetailedTableToCsv(Writer $csv, ExportableReport $report): void
112
+    {
113
+        $csv->insertOne($report->getHeaders());
75
 
114
 
76
-            if ($report->getTitle() === 'Cash Flow Statement') {
77
-                $this->writeOverviewTableToCsv($csv, $report);
115
+        foreach ($report->getCategories() as $category) {
116
+            $this->writeDataRowsToCsv($csv, $category->header, $category->data, $report->getColumns());
117
+
118
+            foreach ($category->types ?? [] as $type) {
119
+                $this->writeDataRowsToCsv($csv, $type->header, $type->data, $report->getColumns());
120
+
121
+                if (filled($type->summary)) {
122
+                    $csv->insertOne($type->summary);
123
+                }
78
             }
124
             }
79
 
125
 
80
-            if (filled($report->getOverallTotals())) {
81
-                $csv->insertOne($report->getOverallTotals());
126
+            if (filled($category->summary)) {
127
+                $csv->insertOne($category->summary);
82
             }
128
             }
83
-        };
84
 
129
 
85
-        return response()->streamDownload($callback, $filename, $headers);
130
+            $csv->insertOne([]);
131
+        }
132
+
133
+        if (method_exists($report, 'getOverviewHeaders') && filled($report->getOverviewHeaders())) {
134
+            $this->writeOverviewTableToCsv($csv, $report);
135
+        }
136
+
137
+        if (filled($report->getOverallTotals())) {
138
+            $csv->insertOne($report->getOverallTotals());
139
+        }
86
     }
140
     }
87
 
141
 
88
     /**
142
     /**
89
      * @throws CannotInsertRecord
143
      * @throws CannotInsertRecord
90
      * @throws Exception
144
      * @throws Exception
91
      */
145
      */
92
-    protected function writeOverviewTableToCsv(Writer $csv, ExportableReport $report): void
146
+    protected function writeSummaryOverviewTableToCsv(Writer $csv, ExportableReport $report): void
93
     {
147
     {
94
         /** @var CashFlowStatementReportTransformer $report */
148
         /** @var CashFlowStatementReportTransformer $report */
95
-        $headers = $report->getOverviewHeaders();
149
+        $headers = $report->getSummaryOverviewHeaders();
96
 
150
 
97
         if (filled($headers)) {
151
         if (filled($headers)) {
98
             $csv->insertOne($headers);
152
             $csv->insertOne($headers);
99
         }
153
         }
100
 
154
 
101
-        foreach ($report->getOverview() as $overviewCategory) {
155
+        foreach ($report->getSummaryOverview() as $overviewCategory) {
102
             if (filled($overviewCategory->header)) {
156
             if (filled($overviewCategory->header)) {
103
-                $this->writeDataRowsToCsv($csv, $overviewCategory->header, $overviewCategory->data, $report->getColumns());
157
+                $this->writeDataRowsToCsv($csv, $overviewCategory->header, $overviewCategory->data, $report->getSummaryColumns());
104
             }
158
             }
105
 
159
 
106
             if (filled($overviewCategory->summary)) {
160
             if (filled($overviewCategory->summary)) {
107
                 $csv->insertOne($overviewCategory->summary);
161
                 $csv->insertOne($overviewCategory->summary);
108
             }
162
             }
109
 
163
 
110
-            if ($overviewCategory->header['account_name'] === 'Starting Balance') {
111
-                foreach ($report->getOverviewAlignedWithColumns() as $summaryRow) {
164
+            if ($overviewCategory->summary['account_name'] === 'Starting Balance') {
165
+                foreach ($report->getSummaryOverviewAlignedWithColumns() as $summaryRow) {
112
                     $row = [];
166
                     $row = [];
113
 
167
 
114
-                    foreach ($report->getColumns() as $column) {
168
+                    foreach ($report->getSummaryColumns() as $column) {
115
                         $columnName = $column->getName();
169
                         $columnName = $column->getName();
116
                         $row[] = $summaryRow[$columnName] ?? '';
170
                         $row[] = $summaryRow[$columnName] ?? '';
117
                     }
171
                     }
124
         }
178
         }
125
     }
179
     }
126
 
180
 
127
-    public function exportToPdf(Company $company, ExportableReport $report, ?string $startDate = null, ?string $endDate = null): StreamedResponse
181
+    /**
182
+     * @throws CannotInsertRecord
183
+     * @throws Exception
184
+     */
185
+    protected function writeOverviewTableToCsv(Writer $csv, ExportableReport $report): void
128
     {
186
     {
129
-        if ($startDate && $endDate) {
130
-            $formattedStartDate = Carbon::parse($startDate)->toDateString();
131
-            $formattedEndDate = Carbon::parse($endDate)->toDateString();
132
-            $dateLabel = $formattedStartDate . ' to ' . $formattedEndDate;
133
-        } else {
134
-            $formattedAsOfDate = Carbon::parse($endDate)->toDateString();
135
-            $dateLabel = $formattedAsOfDate;
187
+        /** @var CashFlowStatementReportTransformer $report */
188
+        $headers = $report->getOverviewHeaders();
189
+
190
+        if (filled($headers)) {
191
+            $csv->insertOne($headers);
136
         }
192
         }
137
 
193
 
138
-        $timestamp = Carbon::now()->format('Y-m-d_H-i-s');
194
+        foreach ($report->getOverview() as $overviewCategory) {
195
+            if (filled($overviewCategory->header)) {
196
+                $this->writeDataRowsToCsv($csv, $overviewCategory->header, $overviewCategory->data, $report->getColumns());
197
+            }
139
 
198
 
140
-        $filename = $company->name . ' ' . $report->getTitle() . ' ' . $dateLabel . ' ' . $timestamp . '.pdf';
199
+            if (filled($overviewCategory->summary)) {
200
+                $csv->insertOne($overviewCategory->summary);
201
+            }
141
 
202
 
142
-        $pdf = SnappyPdf::loadView($report->getPdfView(), [
143
-            'company' => $company,
144
-            'report' => $report,
145
-            'startDate' => $startDate ? Carbon::parse($startDate)->toDefaultDateFormat() : null,
146
-            'endDate' => $endDate ? Carbon::parse($endDate)->toDefaultDateFormat() : null,
147
-        ]);
203
+            if ($overviewCategory->header['account_name'] === 'Starting Balance') {
204
+                foreach ($report->getOverviewAlignedWithColumns() as $summaryRow) {
205
+                    $row = [];
148
 
206
 
149
-        return response()->streamDownload(function () use ($pdf) {
150
-            echo $pdf->inline();
151
-        }, $filename);
207
+                    foreach ($report->getColumns() as $column) {
208
+                        $columnName = $column->getName();
209
+                        $row[] = $summaryRow[$columnName] ?? '';
210
+                    }
211
+
212
+                    if (array_filter($row)) {
213
+                        $csv->insertOne($row);
214
+                    }
215
+                }
216
+            }
217
+        }
152
     }
218
     }
153
 
219
 
154
     /**
220
     /**
189
             $csv->insertOne($row);
255
             $csv->insertOne($row);
190
         }
256
         }
191
     }
257
     }
258
+
259
+    public function exportToPdf(Company $company, ExportableReport $report, ?string $startDate = null, ?string $endDate = null, ?string $activeTab = null): StreamedResponse
260
+    {
261
+        if ($startDate && $endDate) {
262
+            $formattedStartDate = Carbon::parse($startDate)->toDateString();
263
+            $formattedEndDate = Carbon::parse($endDate)->toDateString();
264
+            $dateLabel = $formattedStartDate . ' to ' . $formattedEndDate;
265
+        } else {
266
+            $formattedAsOfDate = Carbon::parse($endDate)->toDateString();
267
+            $dateLabel = $formattedAsOfDate;
268
+        }
269
+
270
+        $timestamp = Carbon::now()->format('Y-m-d_H-i-s');
271
+
272
+        $filename = $company->name . ' ' . $report->getTitle() . ' ' . $dateLabel . ' ' . $timestamp . '.pdf';
273
+
274
+        $view = $activeTab === 'summary' ? $report->getSummaryPdfView() : $report->getPdfView();
275
+
276
+        $pdf = SnappyPdf::loadView($view, [
277
+            'company' => $company,
278
+            'report' => $report,
279
+            'startDate' => $startDate ? Carbon::parse($startDate)->toDefaultDateFormat() : null,
280
+            'endDate' => $endDate ? Carbon::parse($endDate)->toDefaultDateFormat() : null,
281
+        ]);
282
+
283
+        return response()->streamDownload(function () use ($pdf) {
284
+            echo $pdf->inline();
285
+        }, $filename);
286
+    }
192
 }
287
 }

+ 6
- 8
app/Transformers/IncomeStatementReportTransformer.php 查看文件

22
         $this->calculateTotals();
22
         $this->calculateTotals();
23
     }
23
     }
24
 
24
 
25
+    public function getSummaryPdfView(): string
26
+    {
27
+        return 'components.company.reports.income-statement-summary-pdf';
28
+    }
29
+
25
     public function getTitle(): string
30
     public function getTitle(): string
26
     {
31
     {
27
         return 'Income Statement';
32
         return 'Income Statement';
96
         $columns = $this->getSummaryColumns();
101
         $columns = $this->getSummaryColumns();
97
 
102
 
98
         foreach ($this->report->categories as $accountCategoryName => $accountCategory) {
103
         foreach ($this->report->categories as $accountCategoryName => $accountCategory) {
99
-            // Header for the main category
100
-            $categoryHeader = [];
101
-
102
-            foreach ($columns as $column) {
103
-                $categoryHeader[$column->getName()] = $column->getName() === 'account_name' ? $accountCategoryName : '';
104
-            }
105
-
106
             // Category-level summary
104
             // Category-level summary
107
             $categorySummary = [];
105
             $categorySummary = [];
108
             foreach ($columns as $column) {
106
             foreach ($columns as $column) {
115
 
113
 
116
             // Add the category summary to the final array
114
             // Add the category summary to the final array
117
             $summaryCategories[$accountCategoryName] = new ReportCategoryDTO(
115
             $summaryCategories[$accountCategoryName] = new ReportCategoryDTO(
118
-                header: $categoryHeader,
116
+                header: [],
119
                 data: [], // No direct accounts are needed here, only summaries
117
                 data: [], // No direct accounts are needed here, only summaries
120
                 summary: $categorySummary,
118
                 summary: $categorySummary,
121
                 types: [] // No types for the income statement
119
                 types: [] // No types for the income statement

+ 5
- 0
app/Transformers/SummaryReportTransformer.php 查看文件

31
             return $headers;
31
             return $headers;
32
         });
32
         });
33
     }
33
     }
34
+
35
+    public function getSummaryPdfView(): string
36
+    {
37
+        return 'components.company.reports.summary-report-pdf';
38
+    }
34
 }
39
 }

+ 59
- 149
resources/views/components/company/reports/account-transactions-report-pdf.blade.php 查看文件

1
-<!DOCTYPE html>
2
-<html lang="en">
3
-<head>
4
-    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
5
-    <meta name="viewport" content="width=device-width, initial-scale=1">
6
-    <title>{{ $report->getTitle() }}</title>
7
-    <style>
8
-        .category-header-row > td {
9
-            background-color: #f3f4f6;
10
-            font-weight: bold;
11
-        }
12
-
13
-        .category-summary-row > td,
14
-        .table-footer-row > td {
15
-            background-color: #ffffff;
16
-            font-weight: bold;
17
-        }
18
-
19
-        .company-name {
20
-            font-size: 1.125rem;
21
-            font-weight: bold;
22
-        }
23
-
24
-        .date-range {
25
-            font-size: 0.875rem;
26
-        }
27
-
28
-        .header {
29
-            color: #374151;
30
-            margin-bottom: 1rem;
31
-        }
32
-
33
-        .header div + div {
34
-            margin-top: 0.5rem;
35
-        }
36
-
37
-        .spacer-row > td {
38
-            height: 0.75rem;
39
-        }
40
-
41
-        .table-body tr {
42
-            background-color: #ffffff;
43
-        }
44
-
45
-        .table-class {
46
-            border-collapse: collapse;
47
-            width: 100%;
48
-        }
49
-
50
-        .table-class td,
51
-        .table-class th {
52
-            border-bottom: 1px solid #d1d5db;
53
-            color: #374151;
54
-            font-size: 0.75rem;
55
-            line-height: 1rem;
56
-            padding: 0.75rem;
57
-        }
58
-
59
-        .table-head {
60
-            display: table-row-group;
61
-        }
62
-
63
-        .text-center {
64
-            text-align: center;
65
-        }
66
-
67
-        .text-left {
68
-            text-align: left;
69
-        }
70
-
71
-        .text-right {
72
-            text-align: right;
73
-        }
74
-
75
-        .title {
76
-            font-size: 1.5rem;
77
-        }
78
-
79
-        .whitespace-normal {
80
-            white-space: normal;
81
-        }
82
-
83
-        .whitespace-nowrap {
84
-            white-space: nowrap;
85
-        }
86
-
87
-        table tfoot {
88
-            display: table-row-group;
89
-        }
90
-    </style>
91
-</head>
92
-<body>
93
-<div class="header">
94
-    <div class="title">{{ $report->getTitle() }}</div>
95
-    <div class="company-name">{{ $company->name }}</div>
96
-    <div class="date-range">Date Range: {{ $startDate }} to {{ $endDate }}</div>
97
-</div>
98
-<table class="table-class">
99
-    <thead class="table-head">
100
-    <tr>
101
-        @foreach($report->getHeaders() as $index => $header)
102
-            <th class="{{ $report->getAlignmentClass($index) }}">
103
-                {{ $header }}
104
-            </th>
105
-        @endforeach
106
-    </tr>
107
-    </thead>
108
-    @foreach($report->getCategories() as $category)
109
-        <tbody>
110
-        <tr class="category-header-row">
111
-            <td colspan="{{ count($report->getHeaders()) }}">
112
-                <div>
113
-                    @foreach($category->header as $headerRow)
114
-                        <div>
115
-                            @foreach($headerRow as $headerValue)
116
-                                @if (!empty($headerValue))
117
-                                    {{ $headerValue }}
118
-                                @endif
119
-                            @endforeach
120
-                        </div>
121
-                    @endforeach
122
-                </div>
123
-            </td>
1
+@extends('components.company.reports.layout')
2
+
3
+@section('content')
4
+    <div class="header">
5
+        <div class="title">{{ $report->getTitle() }}</div>
6
+        <div class="company-name">{{ $company->name }}</div>
7
+        <div class="date-range">Date Range: {{ $startDate }} to {{ $endDate }}</div>
8
+    </div>
9
+    <table class="table-class">
10
+        <thead class="table-head">
11
+        <tr>
12
+            @foreach($report->getHeaders() as $index => $header)
13
+                <th class="{{ $report->getAlignmentClass($index) }}">
14
+                    {{ $header }}
15
+                </th>
16
+            @endforeach
124
         </tr>
17
         </tr>
125
-        @foreach($category->data as $dataIndex => $transaction)
126
-            <tr
127
-                @class([
128
-                    'category-header-row' => $loop->first || $loop->last || $loop->remaining === 1,
129
-                ])>
130
-                @foreach($transaction as $cellIndex => $cell)
131
-                    <td @class([
18
+        </thead>
19
+        @foreach($report->getCategories() as $category)
20
+            <tbody>
21
+            <tr class="category-header-row">
22
+                <td colspan="{{ count($report->getHeaders()) }}">
23
+                    <div>
24
+                        @foreach($category->header as $headerRow)
25
+                            <div>
26
+                                @foreach($headerRow as $headerValue)
27
+                                    @if (!empty($headerValue))
28
+                                        {{ $headerValue }}
29
+                                    @endif
30
+                                @endforeach
31
+                            </div>
32
+                        @endforeach
33
+                    </div>
34
+                </td>
35
+            </tr>
36
+            @foreach($category->data as $dataIndex => $transaction)
37
+                <tr
38
+                    @class([
39
+                        'category-header-row' => $loop->first || $loop->last || $loop->remaining === 1,
40
+                    ])>
41
+                    @foreach($transaction as $cellIndex => $cell)
42
+                        <td @class([
132
                             $report->getAlignmentClass($cellIndex),
43
                             $report->getAlignmentClass($cellIndex),
133
                             'whitespace-normal' => $cellIndex === 'description',
44
                             'whitespace-normal' => $cellIndex === 'description',
134
                             'whitespace-nowrap' => $cellIndex !== 'description',
45
                             'whitespace-nowrap' => $cellIndex !== 'description',
135
                         ])
46
                         ])
136
-                    >
137
-                        @if(is_array($cell) && isset($cell['description']))
138
-                            {{ $cell['description'] }}
139
-                        @else
140
-                            {{ $cell }}
141
-                        @endif
142
-                    </td>
143
-                @endforeach
144
-            </tr>
47
+                        >
48
+                            @if(is_array($cell) && isset($cell['description']))
49
+                                {{ $cell['description'] }}
50
+                            @else
51
+                                {{ $cell }}
52
+                            @endif
53
+                        </td>
54
+                    @endforeach
55
+                </tr>
56
+            @endforeach
57
+            @unless($loop->last)
58
+                <tr class="spacer-row">
59
+                    <td colspan="{{ count($report->getHeaders()) }}"></td>
60
+                </tr>
61
+            @endunless
62
+            </tbody>
145
         @endforeach
63
         @endforeach
146
-        @unless($loop->last)
147
-            <tr class="spacer-row">
148
-                <td colspan="{{ count($report->getHeaders()) }}"></td>
149
-            </tr>
150
-        @endunless
151
-        </tbody>
152
-    @endforeach
153
-</table>
154
-</body>
155
-</html>
64
+    </table>
65
+@endsection

+ 168
- 284
resources/views/components/company/reports/cash-flow-statement-pdf.blade.php 查看文件

1
-<!DOCTYPE html>
2
-<html lang="en">
3
-<head>
4
-    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
5
-    <meta name="viewport" content="width=device-width, initial-scale=1">
6
-    <title>{{ $report->getTitle() }}</title>
7
-    <style>
8
-        .font-bold {
9
-            font-weight: bold;
10
-        }
11
-
12
-        .category-header-row > td,
13
-        .type-header-row > td {
14
-            background-color: #f3f4f6;
15
-            font-weight: bold;
16
-        }
17
-
18
-        .category-summary-row > td,
19
-        .table-footer-row > td,
20
-        .type-summary-row > td {
21
-            background-color: #ffffff;
22
-            font-weight: bold;
23
-        }
24
-
25
-        .cell {
26
-            padding-bottom: 5px;
27
-            position: relative;
28
-        }
29
-
30
-        .company-name {
31
-            font-size: 1.125rem;
32
-            font-weight: bold;
33
-        }
34
-
35
-        .date-range {
36
-            font-size: 0.875rem;
37
-        }
38
-
39
-        .header {
40
-            color: #374151;
41
-            margin-bottom: 1rem;
42
-        }
43
-
44
-        .header div + div {
45
-            margin-top: 0.5rem;
46
-        }
47
-
48
-        .spacer-row > td {
49
-            height: 0.75rem;
50
-        }
51
-
52
-        .table-body tr {
53
-            background-color: #ffffff;
54
-        }
55
-
56
-        .table-class {
57
-            border-collapse: collapse;
58
-            table-layout: fixed;
59
-            width: 100%;
60
-        }
61
-
62
-        .table-class .type-row-indent {
63
-            padding-left: 1.5rem;
64
-        }
65
-
66
-        .table-class td,
67
-        .table-class th {
68
-            border-bottom: 1px solid #d1d5db;
69
-            color: #374151;
70
-            font-size: 0.75rem;
71
-            line-height: 1rem;
72
-            padding: 0.75rem;
73
-        }
74
-
75
-        .table-head {
76
-            display: table-row-group;
77
-        }
78
-
79
-        .text-center {
80
-            text-align: center;
81
-        }
82
-
83
-        .text-left {
84
-            text-align: left;
85
-        }
86
-
87
-        .text-right {
88
-            text-align: right;
89
-        }
90
-
91
-        .title {
92
-            font-size: 1.5rem;
93
-        }
94
-
95
-        .table-class .underline-bold {
96
-            border-bottom: 2px solid #374151;
97
-        }
98
-
99
-        .table-class .underline-thin {
100
-            border-bottom: 1px solid #374151;
101
-        }
102
-
103
-        .whitespace-normal {
104
-            white-space: normal;
105
-        }
106
-
107
-        .whitespace-nowrap {
108
-            white-space: nowrap;
109
-        }
110
-
111
-        table tfoot {
112
-            display: table-row-group;
113
-        }
114
-    </style>
115
-</head>
116
-<body>
117
-<div class="header">
118
-    <div class="title">{{ $report->getTitle() }}</div>
119
-    <div class="company-name">{{ $company->name }}</div>
120
-    @if($startDate && $endDate)
121
-        <div class="date-range">Date Range: {{ $startDate }} to {{ $endDate }}</div>
122
-    @else
123
-        <div class="date-range">As of {{ $endDate }}</div>
124
-    @endif
125
-</div>
126
-<table class="table-class">
127
-    <colgroup>
128
-        @if(array_key_exists('account_code', $report->getHeaders()))
129
-            <col span="1" style="width: 20%;">
130
-            <col span="1" style="width: 55%;">
131
-            <col span="1" style="width: 25%;">
1
+@extends('components.company.reports.layout')
2
+
3
+@section('content')
4
+    <div class="header">
5
+        <div class="title">{{ $report->getTitle() }}</div>
6
+        <div class="company-name">{{ $company->name }}</div>
7
+        @if($startDate && $endDate)
8
+            <div class="date-range">Date Range: {{ $startDate }} to {{ $endDate }}</div>
132
         @else
9
         @else
133
-            <col span="1" style="width: 65%;">
134
-            <col span="1" style="width: 35%;">
10
+            <div class="date-range">As of {{ $endDate }}</div>
135
         @endif
11
         @endif
136
-    </colgroup>
137
-    <thead class="table-head">
138
-    <tr>
139
-        @foreach($report->getHeaders() as $index => $header)
140
-            <th class="{{ $report->getAlignmentClass($index) }}">
141
-                {{ $header }}
142
-            </th>
143
-        @endforeach
144
-    </tr>
145
-    </thead>
146
-    @foreach($report->getCategories() as $category)
147
-        <tbody>
148
-        <tr class="category-header-row">
149
-            @foreach($category->header as $index => $header)
150
-                <td class="{{ $report->getAlignmentClass($index) }}">
12
+    </div>
13
+    <table class="table-class">
14
+        <colgroup>
15
+            @if(array_key_exists('account_code', $report->getHeaders()))
16
+                <col span="1" style="width: 20%;">
17
+                <col span="1" style="width: 55%;">
18
+                <col span="1" style="width: 25%;">
19
+            @else
20
+                <col span="1" style="width: 65%;">
21
+                <col span="1" style="width: 35%;">
22
+            @endif
23
+        </colgroup>
24
+        <thead class="table-head">
25
+        <tr>
26
+            @foreach($report->getHeaders() as $index => $header)
27
+                <th class="{{ $report->getAlignmentClass($index) }}">
151
                     {{ $header }}
28
                     {{ $header }}
152
-                </td>
29
+                </th>
153
             @endforeach
30
             @endforeach
154
         </tr>
31
         </tr>
155
-        @foreach($category->data as $account)
156
-            <tr>
157
-                @foreach($account as $index => $cell)
158
-                    <td @class([
32
+        </thead>
33
+        @foreach($report->getCategories() as $category)
34
+            <tbody>
35
+            <tr class="category-header-row">
36
+                @foreach($category->header as $index => $header)
37
+                    <td class="{{ $report->getAlignmentClass($index) }}">
38
+                        {{ $header }}
39
+                    </td>
40
+                @endforeach
41
+            </tr>
42
+            @foreach($category->data as $account)
43
+                <tr>
44
+                    @foreach($account as $index => $cell)
45
+                        <td @class([
159
                             $report->getAlignmentClass($index),
46
                             $report->getAlignmentClass($index),
160
                             'whitespace-normal' => $index === 'account_name',
47
                             'whitespace-normal' => $index === 'account_name',
161
                             'whitespace-nowrap' => $index !== 'account_name',
48
                             'whitespace-nowrap' => $index !== 'account_name',
162
                         ])
49
                         ])
163
-                    >
164
-                        @if(is_array($cell) && isset($cell['name']))
165
-                            {{ $cell['name'] }}
166
-                        @else
167
-                            {{ $cell }}
168
-                        @endif
169
-                    </td>
170
-                @endforeach
171
-            </tr>
172
-        @endforeach
50
+                        >
51
+                            @if(is_array($cell) && isset($cell['name']))
52
+                                {{ $cell['name'] }}
53
+                            @else
54
+                                {{ $cell }}
55
+                            @endif
56
+                        </td>
57
+                    @endforeach
58
+                </tr>
59
+            @endforeach
173
 
60
 
174
-        <!-- Category Types -->
175
-        @foreach($category->types ?? [] as $type)
176
-            <!-- Type Header -->
177
-            <tr class="type-header-row">
178
-                @foreach($type->header as $index => $header)
179
-                    <td @class([
61
+            <!-- Category Types -->
62
+            @foreach($category->types ?? [] as $type)
63
+                <!-- Type Header -->
64
+                <tr class="type-header-row">
65
+                    @foreach($type->header as $index => $header)
66
+                        <td @class([
180
                             $report->getAlignmentClass($index),
67
                             $report->getAlignmentClass($index),
181
                             'type-row-indent' => $index === 'account_name',
68
                             'type-row-indent' => $index === 'account_name',
182
                         ])
69
                         ])
183
-                    >
184
-                        {{ $header }}
185
-                    </td>
186
-                @endforeach
187
-            </tr>
70
+                        >
71
+                            {{ $header }}
72
+                        </td>
73
+                    @endforeach
74
+                </tr>
188
 
75
 
189
-            <!-- Type Data -->
190
-            @foreach($type->data as $typeRow)
191
-                <tr class="type-data-row">
192
-                    @foreach($typeRow as $index => $cell)
193
-                        <td @class([
76
+                <!-- Type Data -->
77
+                @foreach($type->data as $typeRow)
78
+                    <tr class="type-data-row">
79
+                        @foreach($typeRow as $index => $cell)
80
+                            <td @class([
194
                                 $report->getAlignmentClass($index),
81
                                 $report->getAlignmentClass($index),
195
                                 'whitespace-normal type-row-indent' => $index === 'account_name',
82
                                 'whitespace-normal type-row-indent' => $index === 'account_name',
196
                                 'whitespace-nowrap' => $index !== 'account_name',
83
                                 'whitespace-nowrap' => $index !== 'account_name',
197
                             ])
84
                             ])
85
+                            >
86
+                                @if(is_array($cell) && isset($cell['name']))
87
+                                    {{ $cell['name'] }}
88
+                                @else
89
+                                    {{ $cell }}
90
+                                @endif
91
+                            </td>
92
+                        @endforeach
93
+                    </tr>
94
+                @endforeach
95
+
96
+                <!-- Type Summary -->
97
+                <tr class="type-summary-row">
98
+                    @foreach($type->summary as $index => $cell)
99
+                        <td @class([
100
+                            $report->getAlignmentClass($index),
101
+                            'type-row-indent' => $index === 'account_name',
102
+                        ])
198
                         >
103
                         >
199
-                            @if(is_array($cell) && isset($cell['name']))
200
-                                {{ $cell['name'] }}
201
-                            @else
202
-                                {{ $cell }}
203
-                            @endif
104
+                            {{ $cell }}
204
                         </td>
105
                         </td>
205
                     @endforeach
106
                     @endforeach
206
                 </tr>
107
                 </tr>
207
             @endforeach
108
             @endforeach
208
 
109
 
209
-            <!-- Type Summary -->
210
-            <tr class="type-summary-row">
211
-                @foreach($type->summary as $index => $cell)
110
+            <tr class="category-summary-row">
111
+                @foreach($category->summary as $index => $cell)
212
                     <td @class([
112
                     <td @class([
213
-                            $report->getAlignmentClass($index),
214
-                            'type-row-indent' => $index === 'account_name',
215
-                        ])
113
+                        $report->getAlignmentClass($index),
114
+                        'underline-bold' => $loop->last,
115
+                    ])
216
                     >
116
                     >
217
                         {{ $cell }}
117
                         {{ $cell }}
218
                     </td>
118
                     </td>
219
                 @endforeach
119
                 @endforeach
220
             </tr>
120
             </tr>
221
-        @endforeach
222
 
121
 
223
-        <tr class="category-summary-row">
224
-            @foreach($category->summary as $index => $cell)
225
-                <td @class([
226
-                        'cell',
227
-                        $report->getAlignmentClass($index),
228
-                        'underline-bold' => $loop->last,
229
-                    ])
230
-                >
231
-                    {{ $cell }}
122
+            @unless($loop->last && empty($report->getOverallTotals()))
123
+                <tr class="spacer-row">
124
+                    <td colspan="{{ count($report->getHeaders()) }}"></td>
125
+                </tr>
126
+            @endunless
127
+            </tbody>
128
+        @endforeach
129
+        <tfoot>
130
+        <tr class="table-footer-row">
131
+            @foreach ($report->getOverallTotals() as $index => $total)
132
+                <td class="{{ $report->getAlignmentClass($index) }}">
133
+                    {{ $total }}
232
                 </td>
134
                 </td>
233
             @endforeach
135
             @endforeach
234
         </tr>
136
         </tr>
235
-
236
-        @unless($loop->last && empty($report->getOverallTotals()))
237
-            <tr class="spacer-row">
238
-                <td colspan="{{ count($report->getHeaders()) }}"></td>
239
-            </tr>
240
-        @endunless
241
-        </tbody>
242
-    @endforeach
243
-    <tfoot>
244
-    <tr class="table-footer-row">
245
-        @foreach ($report->getOverallTotals() as $index => $total)
246
-            <td class="{{ $report->getAlignmentClass($index) }}">
247
-                {{ $total }}
248
-            </td>
249
-        @endforeach
250
-    </tr>
251
-    </tfoot>
252
-</table>
253
-
254
-<!-- Second Overview Table -->
255
-<table class="table-class">
256
-    <colgroup>
257
-        @if(array_key_exists('account_code', $report->getHeaders()))
258
-            <col span="1" style="width: 20%;">
259
-            <col span="1" style="width: 55%;">
260
-            <col span="1" style="width: 25%;">
261
-        @else
262
-            <col span="1" style="width: 65%;">
263
-            <col span="1" style="width: 35%;">
264
-        @endif
265
-    </colgroup>
266
-    <thead class="table-head">
267
-    <tr>
268
-        @foreach($report->getOverviewHeaders() as $index => $header)
269
-            <th class="{{ $report->getAlignmentClass($index) }}">
270
-                {{ $header }}
271
-            </th>
272
-        @endforeach
273
-    </tr>
274
-    </thead>
275
-    <!-- Overview Content -->
276
-    @foreach($report->getOverview() as $overviewCategory)
277
-        <tbody>
278
-        <tr class="category-header-row">
279
-            @foreach($overviewCategory->header as $index => $header)
280
-                <td class="{{ $report->getAlignmentClass($index) }}">
137
+        </tfoot>
138
+    </table>
139
+
140
+    <!-- Second Overview Table -->
141
+    <table class="table-class" style="margin-top: 40px;">
142
+        <colgroup>
143
+            @if(array_key_exists('account_code', $report->getHeaders()))
144
+                <col span="1" style="width: 20%;">
145
+                <col span="1" style="width: 55%;">
146
+                <col span="1" style="width: 25%;">
147
+            @else
148
+                <col span="1" style="width: 65%;">
149
+                <col span="1" style="width: 35%;">
150
+            @endif
151
+        </colgroup>
152
+        <thead class="table-head">
153
+        <tr>
154
+            @foreach($report->getOverviewHeaders() as $index => $header)
155
+                <th class="{{ $report->getAlignmentClass($index) }}">
281
                     {{ $header }}
156
                     {{ $header }}
282
-                </td>
157
+                </th>
283
             @endforeach
158
             @endforeach
284
         </tr>
159
         </tr>
285
-        @foreach($overviewCategory->data as $overviewAccount)
286
-            <tr>
287
-                @foreach($overviewAccount as $index => $cell)
288
-                    <td @class([
160
+        </thead>
161
+        <!-- Overview Content -->
162
+        @foreach($report->getOverview() as $overviewCategory)
163
+            <tbody>
164
+            <tr class="category-header-row">
165
+                @foreach($overviewCategory->header as $index => $header)
166
+                    <td class="{{ $report->getAlignmentClass($index) }}">
167
+                        {{ $header }}
168
+                    </td>
169
+                @endforeach
170
+            </tr>
171
+            @foreach($overviewCategory->data as $overviewAccount)
172
+                <tr>
173
+                    @foreach($overviewAccount as $index => $cell)
174
+                        <td @class([
289
                             $report->getAlignmentClass($index),
175
                             $report->getAlignmentClass($index),
290
                             'whitespace-normal' => $index === 'account_name',
176
                             'whitespace-normal' => $index === 'account_name',
291
                             'whitespace-nowrap' => $index !== 'account_name',
177
                             'whitespace-nowrap' => $index !== 'account_name',
292
                         ])
178
                         ])
293
-                    >
294
-                        @if(is_array($cell) && isset($cell['name']))
295
-                            {{ $cell['name'] }}
296
-                        @else
297
-                            {{ $cell }}
298
-                        @endif
179
+                        >
180
+                            @if(is_array($cell) && isset($cell['name']))
181
+                                {{ $cell['name'] }}
182
+                            @else
183
+                                {{ $cell }}
184
+                            @endif
185
+                        </td>
186
+                    @endforeach
187
+                </tr>
188
+            @endforeach
189
+            <!-- Summary Row -->
190
+            <tr class="category-summary-row">
191
+                @foreach($overviewCategory->summary as $index => $summaryCell)
192
+                    <td class="{{ $report->getAlignmentClass($index) }}">
193
+                        {{ $summaryCell }}
299
                     </td>
194
                     </td>
300
                 @endforeach
195
                 @endforeach
301
             </tr>
196
             </tr>
302
-        @endforeach
303
-        <!-- Summary Row -->
304
-        <tr class="category-summary-row">
305
-            @foreach($overviewCategory->summary as $index => $summaryCell)
306
-                <td class="{{ $report->getAlignmentClass($index) }}">
307
-                    {{ $summaryCell }}
308
-                </td>
309
-            @endforeach
310
-        </tr>
311
 
197
 
312
-        @if($overviewCategory->header['account_name'] === 'Starting Balance')
313
-            @foreach($report->getOverviewAlignedWithColumns() as $summaryRow)
314
-                <tr>
315
-                    @foreach($summaryRow as $index => $summaryCell)
316
-                        <td @class([
317
-                                'cell',
198
+            @if($overviewCategory->header['account_name'] === 'Starting Balance')
199
+                @foreach($report->getOverviewAlignedWithColumns() as $summaryRow)
200
+                    <tr>
201
+                        @foreach($summaryRow as $index => $summaryCell)
202
+                            <td @class([
318
                                 $report->getAlignmentClass($index),
203
                                 $report->getAlignmentClass($index),
319
                                 'font-bold' => $loop->parent->last,
204
                                 'font-bold' => $loop->parent->last,
320
                                 'underline-thin' => $loop->parent->remaining === 1 && $index === 'net_movement',
205
                                 'underline-thin' => $loop->parent->remaining === 1 && $index === 'net_movement',
321
                                 'underline-bold' => $loop->parent->last && $index === 'net_movement',
206
                                 'underline-bold' => $loop->parent->last && $index === 'net_movement',
322
                             ])
207
                             ])
323
-                        >
324
-                            {{ $summaryCell }}
325
-                        </td>
326
-                    @endforeach
327
-                </tr>
328
-            @endforeach
329
-        @endif
330
-        </tbody>
331
-    @endforeach
332
-</table>
333
-</body>
334
-</html>
208
+                            >
209
+                                {{ $summaryCell }}
210
+                            </td>
211
+                        @endforeach
212
+                    </tr>
213
+                @endforeach
214
+            @endif
215
+            </tbody>
216
+        @endforeach
217
+    </table>
218
+@endsection

+ 61
- 0
resources/views/components/company/reports/income-statement-summary-pdf.blade.php 查看文件

1
+@extends('components.company.reports.layout')
2
+
3
+@section('content')
4
+    <div class="header">
5
+        <div class="title">{{ $report->getTitle() }}</div>
6
+        <div class="company-name">{{ $company->name }}</div>
7
+        @if($startDate && $endDate)
8
+            <div class="date-range">Date Range: {{ $startDate }} to {{ $endDate }}</div>
9
+        @else
10
+            <div class="date-range">As of {{ $endDate }}</div>
11
+        @endif
12
+    </div>
13
+    <table class="table-class">
14
+        <colgroup>
15
+            <col span="1" style="width: 65%;">
16
+            <col span="1" style="width: 35%;">
17
+        </colgroup>
18
+        <thead class="table-head">
19
+        <tr>
20
+            @foreach($report->getSummaryHeaders() as $index => $header)
21
+                <th class="{{ $report->getAlignmentClass($index) }}">
22
+                    {{ $header }}
23
+                </th>
24
+            @endforeach
25
+        </tr>
26
+        </thead>
27
+        @foreach($report->getSummaryCategories() as $category)
28
+            <tbody>
29
+            <tr>
30
+                @foreach($category->summary as $index => $cell)
31
+                    <td @class([
32
+                        $report->getAlignmentClass($index),
33
+                    ])
34
+                    >
35
+                        {{ $cell }}
36
+                    </td>
37
+                @endforeach
38
+            </tr>
39
+
40
+            @if($category->summary['account_name'] === 'Cost of Goods Sold')
41
+                <tr class="category-header-row">
42
+                    @foreach($report->getGrossProfit() as $grossProfitIndex => $grossProfitCell)
43
+                        <td class="{{ $report->getAlignmentClass($grossProfitIndex) }}">
44
+                            {{ $grossProfitCell }}
45
+                        </td>
46
+                    @endforeach
47
+                </tr>
48
+            @endif
49
+            </tbody>
50
+        @endforeach
51
+        <tfoot>
52
+        <tr class="category-header-row">
53
+            @foreach ($report->getOverallTotals() as $index => $total)
54
+                <td class="{{ $report->getAlignmentClass($index) }}">
55
+                    {{ $total }}
56
+                </td>
57
+            @endforeach
58
+        </tr>
59
+        </tfoot>
60
+    </table>
61
+@endsection

+ 114
- 0
resources/views/components/company/reports/layout.blade.php 查看文件

1
+<!DOCTYPE html>
2
+<html lang="en">
3
+<head>
4
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
5
+    <meta name="viewport" content="width=device-width, initial-scale=1">
6
+    <title>{{ $report->getTitle() }}</title>
7
+    <style>
8
+        .font-bold {
9
+            font-weight: bold;
10
+        }
11
+
12
+        .category-header-row > td,
13
+        .type-header-row > td {
14
+            background-color: #f3f4f6;
15
+            font-weight: bold;
16
+        }
17
+
18
+        .category-summary-row > td,
19
+        .table-footer-row > td,
20
+        .type-summary-row > td {
21
+            background-color: #ffffff;
22
+            font-weight: bold;
23
+        }
24
+
25
+        .company-name {
26
+            font-size: 1.125rem;
27
+            font-weight: bold;
28
+        }
29
+
30
+        .date-range {
31
+            font-size: 0.875rem;
32
+        }
33
+
34
+        .header {
35
+            color: #374151;
36
+            margin-bottom: 1rem;
37
+        }
38
+
39
+        .header div + div {
40
+            margin-top: 0.5rem;
41
+        }
42
+
43
+        .spacer-row > td {
44
+            height: 0.75rem;
45
+        }
46
+
47
+        .table-body tr {
48
+            background-color: #ffffff;
49
+        }
50
+
51
+        .table-class {
52
+            border-collapse: collapse;
53
+            table-layout: fixed;
54
+            width: 100%;
55
+        }
56
+
57
+        .table-class .type-row-indent {
58
+            padding-left: 1.5rem;
59
+        }
60
+
61
+        .table-class td,
62
+        .table-class th {
63
+            border-bottom: 1px solid #d1d5db;
64
+            color: #374151;
65
+            font-size: 0.75rem;
66
+            line-height: 1rem;
67
+            padding: 0.75rem;
68
+        }
69
+
70
+        .table-head {
71
+            display: table-row-group;
72
+        }
73
+
74
+        .text-center {
75
+            text-align: center;
76
+        }
77
+
78
+        .text-left {
79
+            text-align: left;
80
+        }
81
+
82
+        .text-right {
83
+            text-align: right;
84
+        }
85
+
86
+        .title {
87
+            font-size: 1.5rem;
88
+        }
89
+
90
+        .table-class .underline-bold {
91
+            border-bottom: 2px solid #374151;
92
+        }
93
+
94
+        .table-class .underline-thin {
95
+            border-bottom: 1px solid #374151;
96
+        }
97
+
98
+        .whitespace-normal {
99
+            white-space: normal;
100
+        }
101
+
102
+        .whitespace-nowrap {
103
+            white-space: nowrap;
104
+        }
105
+
106
+        table tfoot {
107
+            display: table-row-group;
108
+        }
109
+    </style>
110
+</head>
111
+<body>
112
+@yield('content')
113
+</body>
114
+</html>

+ 94
- 202
resources/views/components/company/reports/report-pdf.blade.php 查看文件

1
-<!DOCTYPE html>
2
-<html lang="en">
3
-<head>
4
-    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
5
-    <meta name="viewport" content="width=device-width, initial-scale=1">
6
-    <title>{{ $report->getTitle() }}</title>
7
-    <style>
8
-        .font-bold {
9
-            font-weight: bold;
10
-        }
11
-
12
-        .category-header-row > td,
13
-        .type-header-row > td {
14
-            background-color: #f3f4f6;
15
-            font-weight: bold;
16
-        }
17
-
18
-        .category-summary-row > td,
19
-        .table-footer-row > td,
20
-        .type-summary-row > td {
21
-            background-color: #ffffff;
22
-            font-weight: bold;
23
-        }
24
-
25
-        .company-name {
26
-            font-size: 1.125rem;
27
-            font-weight: bold;
28
-        }
29
-
30
-        .date-range {
31
-            font-size: 0.875rem;
32
-        }
33
-
34
-        .header {
35
-            color: #374151;
36
-            margin-bottom: 1rem;
37
-        }
38
-
39
-        .header div + div {
40
-            margin-top: 0.5rem;
41
-        }
42
-
43
-        .spacer-row > td {
44
-            height: 0.75rem;
45
-        }
46
-
47
-        .table-body tr {
48
-            background-color: #ffffff;
49
-        }
50
-
51
-        .table-class {
52
-            border-collapse: collapse;
53
-            width: 100%;
54
-        }
55
-
56
-        .table-class .type-row-indent {
57
-            padding-left: 1.5rem;
58
-        }
59
-
60
-        .table-class td,
61
-        .table-class th {
62
-            border-bottom: 1px solid #d1d5db;
63
-            color: #374151;
64
-            font-size: 0.75rem;
65
-            line-height: 1rem;
66
-            padding: 0.75rem;
67
-        }
68
-
69
-        .table-head {
70
-            display: table-row-group;
71
-        }
72
-
73
-        .text-center {
74
-            text-align: center;
75
-        }
76
-
77
-        .text-left {
78
-            text-align: left;
79
-        }
80
-
81
-        .text-right {
82
-            text-align: right;
83
-        }
84
-
85
-        .title {
86
-            font-size: 1.5rem;
87
-        }
88
-
89
-        .table-class .underline-bold {
90
-            border-bottom: 2px solid #374151;
91
-        }
92
-
93
-        .table-class .underline-thin {
94
-            border-bottom: 1px solid #374151;
95
-        }
96
-
97
-        .whitespace-normal {
98
-            white-space: normal;
99
-        }
100
-
101
-        .whitespace-nowrap {
102
-            white-space: nowrap;
103
-        }
104
-
105
-        table tfoot {
106
-            display: table-row-group;
107
-        }
108
-    </style>
109
-</head>
110
-<body>
111
-<div class="header">
112
-    <div class="title">{{ $report->getTitle() }}</div>
113
-    <div class="company-name">{{ $company->name }}</div>
114
-    @if($startDate && $endDate)
115
-        <div class="date-range">Date Range: {{ $startDate }} to {{ $endDate }}</div>
116
-    @else
117
-        <div class="date-range">As of {{ $endDate }}</div>
118
-    @endif
119
-</div>
120
-<table class="table-class">
121
-    <thead class="table-head">
122
-    <tr>
123
-        @foreach($report->getHeaders() as $index => $header)
124
-            <th class="{{ $report->getAlignmentClass($index) }}">
125
-                {{ $header }}
126
-            </th>
127
-        @endforeach
128
-    </tr>
129
-    </thead>
130
-    @foreach($report->getCategories() as $category)
131
-        <tbody>
132
-        <tr class="category-header-row">
133
-            @foreach($category->header as $index => $header)
134
-                <td class="{{ $report->getAlignmentClass($index) }}">
1
+@extends('components.company.reports.layout')
2
+
3
+@section('content')
4
+    <div class="header">
5
+        <div class="title">{{ $report->getTitle() }}</div>
6
+        <div class="company-name">{{ $company->name }}</div>
7
+        @if($startDate && $endDate)
8
+            <div class="date-range">Date Range: {{ $startDate }} to {{ $endDate }}</div>
9
+        @else
10
+            <div class="date-range">As of {{ $endDate }}</div>
11
+        @endif
12
+    </div>
13
+    <table class="table-class">
14
+        <thead class="table-head">
15
+        <tr>
16
+            @foreach($report->getHeaders() as $index => $header)
17
+                <th class="{{ $report->getAlignmentClass($index) }}">
135
                     {{ $header }}
18
                     {{ $header }}
136
-                </td>
19
+                </th>
137
             @endforeach
20
             @endforeach
138
         </tr>
21
         </tr>
139
-        @foreach($category->data as $account)
140
-            <tr>
141
-                @foreach($account as $index => $cell)
142
-                    <td @class([
143
-                            $report->getAlignmentClass($index),
144
-                            'whitespace-normal' => $index === 'account_name',
145
-                            'whitespace-nowrap' => $index !== 'account_name',
146
-                        ])
147
-                    >
148
-                        @if(is_array($cell) && isset($cell['name']))
149
-                            {{ $cell['name'] }}
150
-                        @else
151
-                            {{ $cell }}
152
-                        @endif
153
-                    </td>
154
-                @endforeach
155
-            </tr>
156
-        @endforeach
157
-
158
-        <!-- Category Types -->
159
-        @foreach($category->types ?? [] as $type)
160
-            <!-- Type Header -->
161
-            <tr class="type-header-row">
162
-                @foreach($type->header as $index => $header)
163
-                    <td @class([
164
-                            $report->getAlignmentClass($index),
165
-                            'type-row-indent' => $index === 'account_name',
166
-                        ])
167
-                    >
22
+        </thead>
23
+        @foreach($report->getCategories() as $category)
24
+            <tbody>
25
+            <tr class="category-header-row">
26
+                @foreach($category->header as $index => $header)
27
+                    <td class="{{ $report->getAlignmentClass($index) }}">
168
                         {{ $header }}
28
                         {{ $header }}
169
                     </td>
29
                     </td>
170
                 @endforeach
30
                 @endforeach
171
             </tr>
31
             </tr>
172
-
173
-            <!-- Type Data -->
174
-            @foreach($type->data as $typeRow)
175
-                <tr class="type-data-row">
176
-                    @foreach($typeRow as $index => $cell)
32
+            @foreach($category->data as $account)
33
+                <tr>
34
+                    @foreach($account as $index => $cell)
177
                         <td @class([
35
                         <td @class([
178
-                                $report->getAlignmentClass($index),
179
-                                'whitespace-normal type-row-indent' => $index === 'account_name',
180
-                                'whitespace-nowrap' => $index !== 'account_name',
181
-                            ])
36
+                            $report->getAlignmentClass($index),
37
+                            'whitespace-normal' => $index === 'account_name',
38
+                            'whitespace-nowrap' => $index !== 'account_name',
39
+                        ])
182
                         >
40
                         >
183
                             @if(is_array($cell) && isset($cell['name']))
41
                             @if(is_array($cell) && isset($cell['name']))
184
                                 {{ $cell['name'] }}
42
                                 {{ $cell['name'] }}
190
                 </tr>
48
                 </tr>
191
             @endforeach
49
             @endforeach
192
 
50
 
193
-            <!-- Type Summary -->
194
-            <tr class="type-summary-row">
195
-                @foreach($type->summary as $index => $cell)
196
-                    <td @class([
51
+            <!-- Category Types -->
52
+            @foreach($category->types ?? [] as $type)
53
+                <!-- Type Header -->
54
+                <tr class="type-header-row">
55
+                    @foreach($type->header as $index => $header)
56
+                        <td @class([
197
                             $report->getAlignmentClass($index),
57
                             $report->getAlignmentClass($index),
198
                             'type-row-indent' => $index === 'account_name',
58
                             'type-row-indent' => $index === 'account_name',
199
                         ])
59
                         ])
200
-                    >
60
+                        >
61
+                            {{ $header }}
62
+                        </td>
63
+                    @endforeach
64
+                </tr>
65
+
66
+                <!-- Type Data -->
67
+                @foreach($type->data as $typeRow)
68
+                    <tr class="type-data-row">
69
+                        @foreach($typeRow as $index => $cell)
70
+                            <td @class([
71
+                                $report->getAlignmentClass($index),
72
+                                'whitespace-normal type-row-indent' => $index === 'account_name',
73
+                                'whitespace-nowrap' => $index !== 'account_name',
74
+                            ])
75
+                            >
76
+                                @if(is_array($cell) && isset($cell['name']))
77
+                                    {{ $cell['name'] }}
78
+                                @else
79
+                                    {{ $cell }}
80
+                                @endif
81
+                            </td>
82
+                        @endforeach
83
+                    </tr>
84
+                @endforeach
85
+
86
+                <!-- Type Summary -->
87
+                <tr class="type-summary-row">
88
+                    @foreach($type->summary as $index => $cell)
89
+                        <td @class([
90
+                            $report->getAlignmentClass($index),
91
+                            'type-row-indent' => $index === 'account_name',
92
+                        ])
93
+                        >
94
+                            {{ $cell }}
95
+                        </td>
96
+                    @endforeach
97
+                </tr>
98
+            @endforeach
99
+
100
+            <tr class="category-summary-row">
101
+                @foreach($category->summary as $index => $cell)
102
+                    <td class="{{ $report->getAlignmentClass($index) }}">
201
                         {{ $cell }}
103
                         {{ $cell }}
202
                     </td>
104
                     </td>
203
                 @endforeach
105
                 @endforeach
204
             </tr>
106
             </tr>
205
-        @endforeach
206
 
107
 
207
-        <tr class="category-summary-row">
208
-            @foreach($category->summary as $index => $cell)
108
+            @unless($loop->last && empty($report->getOverallTotals()))
109
+                <tr class="spacer-row">
110
+                    <td colspan="{{ count($report->getHeaders()) }}"></td>
111
+                </tr>
112
+            @endunless
113
+            </tbody>
114
+        @endforeach
115
+        <tfoot>
116
+        <tr class="table-footer-row">
117
+            @foreach ($report->getOverallTotals() as $index => $total)
209
                 <td class="{{ $report->getAlignmentClass($index) }}">
118
                 <td class="{{ $report->getAlignmentClass($index) }}">
210
-                    {{ $cell }}
119
+                    {{ $total }}
211
                 </td>
120
                 </td>
212
             @endforeach
121
             @endforeach
213
         </tr>
122
         </tr>
214
-
215
-        @unless($loop->last && empty($report->getOverallTotals()))
216
-            <tr class="spacer-row">
217
-                <td colspan="{{ count($report->getHeaders()) }}"></td>
218
-            </tr>
219
-        @endunless
220
-        </tbody>
221
-    @endforeach
222
-    <tfoot>
223
-    <tr class="table-footer-row">
224
-        @foreach ($report->getOverallTotals() as $index => $total)
225
-            <td class="{{ $report->getAlignmentClass($index) }}">
226
-                {{ $total }}
227
-            </td>
228
-        @endforeach
229
-    </tr>
230
-    </tfoot>
231
-</table>
232
-</body>
233
-</html>
123
+        </tfoot>
124
+    </table>
125
+@endsection

+ 136
- 0
resources/views/components/company/reports/summary-report-pdf.blade.php 查看文件

1
+@extends('components.company.reports.layout')
2
+
3
+@section('content')
4
+    <div class="header">
5
+        <div class="title">{{ $report->getTitle() }}</div>
6
+        <div class="company-name">{{ $company->name }}</div>
7
+        @if($startDate && $endDate)
8
+            <div class="date-range">Date Range: {{ $startDate }} to {{ $endDate }}</div>
9
+        @else
10
+            <div class="date-range">As of {{ $endDate }}</div>
11
+        @endif
12
+    </div>
13
+    <table class="table-class">
14
+        <colgroup>
15
+            <col span="1" style="width: 65%;">
16
+            <col span="1" style="width: 35%;">
17
+        </colgroup>
18
+        <thead class="table-head">
19
+        <tr>
20
+            @foreach($report->getSummaryHeaders() as $index => $header)
21
+                <th class="{{ $report->getAlignmentClass($index) }}">
22
+                    {{ $header }}
23
+                </th>
24
+            @endforeach
25
+        </tr>
26
+        </thead>
27
+        @foreach($report->getSummaryCategories() as $category)
28
+            <tbody>
29
+            <tr class="category-header-row">
30
+                @foreach($category->header as $index => $header)
31
+                    <td class="{{ $report->getAlignmentClass($index) }}">
32
+                        {{ $header }}
33
+                    </td>
34
+                @endforeach
35
+            </tr>
36
+
37
+            <!-- Category Types -->
38
+            @foreach($category->types ?? [] as $type)
39
+                <!-- Type Summary -->
40
+                <tr>
41
+                    @foreach($type->summary as $index => $cell)
42
+                        <td @class([
43
+                            $report->getAlignmentClass($index),
44
+                        ])
45
+                        >
46
+                            {{ $cell }}
47
+                        </td>
48
+                    @endforeach
49
+                </tr>
50
+            @endforeach
51
+
52
+            <tr class="category-summary-row">
53
+                @foreach($category->summary as $index => $cell)
54
+                    <td @class([
55
+                        $report->getAlignmentClass($index),
56
+                        'underline-bold' => $loop->last && $report->getTitle() === 'Cash Flow Statement',
57
+                    ])
58
+                    >
59
+                        {{ $cell }}
60
+                    </td>
61
+                @endforeach
62
+            </tr>
63
+
64
+            @if($category->summary['account_name'] === 'Cost of Goods Sold')
65
+                <tr class="category-header-row">
66
+                    @foreach($report->getGrossProfit() as $grossProfitIndex => $grossProfitCell)
67
+                        <td class="{{ $report->getAlignmentClass($grossProfitIndex) }}">
68
+                            {{ $grossProfitCell }}
69
+                        </td>
70
+                    @endforeach
71
+                </tr>
72
+            @endif
73
+            </tbody>
74
+        @endforeach
75
+        <tfoot>
76
+        <tr class="table-footer-row">
77
+            @foreach ($report->getOverallTotals() as $index => $total)
78
+                <td class="{{ $report->getAlignmentClass($index) }}">
79
+                    {{ $total }}
80
+                </td>
81
+            @endforeach
82
+        </tr>
83
+        </tfoot>
84
+    </table>
85
+
86
+    <!-- Second Overview Table -->
87
+    @if(method_exists($report, 'getSummaryOverviewHeaders') && filled($report->getSummaryOverviewHeaders()))
88
+        <table class="table-class" style="margin-top: 40px;">
89
+            <colgroup>
90
+                <col span="1" style="width: 65%;">
91
+                <col span="1" style="width: 35%;">
92
+            </colgroup>
93
+            <thead class="table-head">
94
+            <tr>
95
+                @foreach($report->getOverviewHeaders() as $index => $header)
96
+                    <th class="{{ $report->getAlignmentClass($index) }}">
97
+                        {{ $header }}
98
+                    </th>
99
+                @endforeach
100
+            </tr>
101
+            </thead>
102
+            <!-- Overview Content -->
103
+            @foreach($report->getSummaryOverview() as $overviewCategory)
104
+                <tbody>
105
+                <!-- Summary Row -->
106
+                <tr class="category-header-row">
107
+                    @foreach($overviewCategory->summary as $index => $summaryCell)
108
+                        <td class="{{ $report->getAlignmentClass($index) }}">
109
+                            {{ $summaryCell }}
110
+                        </td>
111
+                    @endforeach
112
+                </tr>
113
+
114
+                @if($overviewCategory->summary['account_name'] === 'Starting Balance')
115
+                    @foreach($report->getSummaryOverviewAlignedWithColumns() as $summaryRow)
116
+                        <tr>
117
+                            @foreach($summaryRow as $index => $summaryCell)
118
+                                <td @class([
119
+                                'cell',
120
+                                $report->getAlignmentClass($index),
121
+                                'font-bold' => $loop->parent->last,
122
+                                'underline-thin' => $loop->parent->remaining === 1 && $index === 'net_movement',
123
+                                'underline-bold' => $loop->parent->last && $index === 'net_movement',
124
+                            ])
125
+                                >
126
+                                    {{ $summaryCell }}
127
+                                </td>
128
+                            @endforeach
129
+                        </tr>
130
+                    @endforeach
131
+                @endif
132
+                </tbody>
133
+            @endforeach
134
+        </table>
135
+    @endif
136
+@endsection

+ 1
- 1
resources/views/components/company/tables/reports/income-statement-summary.blade.php 查看文件

15
             @endforeach
15
             @endforeach
16
         </tr>
16
         </tr>
17
 
17
 
18
-        @if($accountCategory->header['account_name'] === 'Cost of Goods Sold')
18
+        @if($accountCategory->summary['account_name'] === 'Cost of Goods Sold')
19
             <tr class="bg-gray-50 dark:bg-white/5">
19
             <tr class="bg-gray-50 dark:bg-white/5">
20
                 @foreach($report->getGrossProfit() as $grossProfitIndex => $grossProfitCell)
20
                 @foreach($report->getGrossProfit() as $grossProfitIndex => $grossProfitCell)
21
                     <x-company.tables.cell :alignment-class="$report->getAlignmentClass($grossProfitIndex)" bold="true">
21
                     <x-company.tables.cell :alignment-class="$report->getAlignmentClass($grossProfitIndex)" bold="true">

+ 2
- 2
resources/views/components/report-tabs.blade.php 查看文件

1
 @props([
1
 @props([
2
     'activeTab' => 'summary',
2
     'activeTab' => 'summary',
3
-    'tabLabels' => ['summary' => 'Summary', 'details' => 'Details'],
3
+    'tabs' => ['summary' => 'Summary', 'details' => 'Details'],
4
 ])
4
 ])
5
 
5
 
6
 <x-filament::tabs>
6
 <x-filament::tabs>
7
-    @foreach ($tabLabels as $tabKey => $label)
7
+    @foreach ($tabs as $tabKey => $label)
8
         <x-filament::tabs.item
8
         <x-filament::tabs.item
9
             :active="$activeTab === $tabKey"
9
             :active="$activeTab === $tabKey"
10
             wire:click="$set('activeTab', '{{ $tabKey }}')"
10
             wire:click="$set('activeTab', '{{ $tabKey }}')"

+ 1
- 1
resources/views/filament/company/pages/reports/balance-sheet.blade.php 查看文件

28
         target-label="Net Assets"
28
         target-label="Net Assets"
29
     />
29
     />
30
 
30
 
31
-    <x-report-tabs :activeTab="$activeTab"/>
31
+    <x-report-tabs :active-tab="$activeTab" :tabs="$this->getTabs()"/>
32
 
32
 
33
     <x-company.tables.container :report-loaded="$this->reportLoaded">
33
     <x-company.tables.container :report-loaded="$this->reportLoaded">
34
         @if($this->report)
34
         @if($this->report)

+ 1
- 1
resources/views/filament/company/pages/reports/cash-flow-statement.blade.php 查看文件

26
         target-label="Net Cash Flow"
26
         target-label="Net Cash Flow"
27
     />
27
     />
28
 
28
 
29
-    <x-report-tabs :activeTab="$activeTab"/>
29
+    <x-report-tabs :active-tab="$activeTab" :tabs="$this->getTabs()"/>
30
 
30
 
31
     <x-company.tables.container :report-loaded="$this->reportLoaded">
31
     <x-company.tables.container :report-loaded="$this->reportLoaded">
32
         @if($this->report)
32
         @if($this->report)

+ 1
- 1
resources/views/filament/company/pages/reports/income-statement.blade.php 查看文件

26
         target-label="Net Earnings"
26
         target-label="Net Earnings"
27
     />
27
     />
28
 
28
 
29
-    <x-report-tabs :activeTab="$activeTab"/>
29
+    <x-report-tabs :active-tab="$activeTab" :tabs="$this->getTabs()"/>
30
 
30
 
31
     <x-company.tables.container :report-loaded="$this->reportLoaded">
31
     <x-company.tables.container :report-loaded="$this->reportLoaded">
32
         @if($this->report)
32
         @if($this->report)

Loading…
取消
儲存