| @@ -8,6 +8,7 @@ use App\Models\Email; | |||||
| use App\Models\EmailAttachment; | use App\Models\EmailAttachment; | ||||
| use App\Models\User; | use App\Models\User; | ||||
| use App\Util\DateUtil; | use App\Util\DateUtil; | ||||
| use App\Util\UrlUtil; | |||||
| use Illuminate\Bus\Queueable; | use Illuminate\Bus\Queueable; | ||||
| use Illuminate\Database\Eloquent\Collection; | use Illuminate\Database\Eloquent\Collection; | ||||
| use Illuminate\Mail\Mailable; | use Illuminate\Mail\Mailable; | ||||
| @@ -180,24 +181,6 @@ abstract class BaseEmailer extends Mailable | |||||
| */ | */ | ||||
| protected function getAppUrl(array|string $path, array $query = []): string | protected function getAppUrl(array|string $path, array $query = []): string | ||||
| { | { | ||||
| $elements = [config("app.url")]; | |||||
| if (is_array($path)) { | |||||
| $elements = array_merge($elements, $path); | |||||
| } else { | |||||
| $elements[] = $path; | |||||
| } | |||||
| $url = implode("/", $elements); | |||||
| if (!!$query) { | |||||
| $url .= "?"; | |||||
| $queryStrList = []; | |||||
| foreach ($query as $key => $value) { | |||||
| $queryStrList[] = sprintf("%s=%s", $key, $value); | |||||
| } | |||||
| $url .= implode("&", $queryStrList); | |||||
| } | |||||
| return $url; | |||||
| return UrlUtil::getAppUrl($path, $query); | |||||
| } | } | ||||
| } | } | ||||
| @@ -0,0 +1,49 @@ | |||||
| <?php | |||||
| namespace App\Http\Controllers\Web\Receipt; | |||||
| use App\Exceptions\AppCommonException; | |||||
| use App\Http\Controllers\Web\WebController; | |||||
| use App\Logic\ReceiptManager; | |||||
| use Auth; | |||||
| use Illuminate\Http\Request; | |||||
| use Illuminate\Http\Response; | |||||
| class ReceiptDownloadController extends WebController | |||||
| { | |||||
| public function name(): string | |||||
| { | |||||
| return "領収証PDF取得"; | |||||
| } | |||||
| public function description(): string | |||||
| { | |||||
| return "領収証PDFを取得する"; | |||||
| } | |||||
| public function __construct(protected ReceiptDownloadParam $param) | |||||
| { | |||||
| parent::__construct(); | |||||
| $this->middleware('auth:sanctum'); | |||||
| } | |||||
| protected function run(Request $request): Response | |||||
| { | |||||
| $param = $this->param; | |||||
| $manager = new ReceiptManager($param->recordNo); | |||||
| if ($manager->getReceipt()->receiptNo !== $param->receiptNo) { | |||||
| throw new AppCommonException("パラメータ不正"); | |||||
| } | |||||
| $customerCode = $manager->getReceipt()->customerCode; | |||||
| $customerCodeAuth = Auth::user()->kintone_customer_code; | |||||
| if ($customerCode != $customerCodeAuth) { | |||||
| throw new AppCommonException("パラメータ不正"); | |||||
| } | |||||
| return $manager->getPdf(); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,20 @@ | |||||
| <?php | |||||
| namespace App\Http\Controllers\Web\Receipt; | |||||
| use App\Http\Controllers\Web\BaseParam; | |||||
| /** | |||||
| * @property int recordNo | |||||
| * @property string receiptNo | |||||
| */ | |||||
| class ReceiptDownloadParam extends BaseParam | |||||
| { | |||||
| function rules(): array | |||||
| { | |||||
| return [ | |||||
| 'record_no' => $this->str(), | |||||
| 'receipt_no' => $this->str(), | |||||
| ]; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,46 @@ | |||||
| <?php | |||||
| namespace App\Http\Controllers\Web\Receipt; | |||||
| use App\Http\Controllers\Web\WebController; | |||||
| use App\Kintone\Repositories\ReceiptRepository; | |||||
| use App\Kintone\Repositories\SeasonTicketContractRepository; | |||||
| use Illuminate\Http\JsonResponse; | |||||
| use Illuminate\Http\Request; | |||||
| use Illuminate\Support\Facades\Auth; | |||||
| class ReceiptsController extends WebController | |||||
| { | |||||
| public function name(): string | |||||
| { | |||||
| return "領収証一覧取得"; | |||||
| } | |||||
| public function description(): string | |||||
| { | |||||
| return "領収証の一覧を取得する"; | |||||
| } | |||||
| public function __construct(protected ReceiptsParam $param) | |||||
| { | |||||
| parent::__construct(); | |||||
| $this->middleware('auth:sanctum'); | |||||
| } | |||||
| protected function run(Request $request): JsonResponse | |||||
| { | |||||
| $user = Auth::user(); | |||||
| $list = ReceiptRepository::get($user->kintone_customer_code); | |||||
| $result = []; | |||||
| foreach ($list as $ele) { | |||||
| $result[] = $ele->toArray(); | |||||
| } | |||||
| return $this->successResponse($result); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,9 @@ | |||||
| <?php | |||||
| namespace App\Http\Controllers\Web\Receipt; | |||||
| use App\Http\Controllers\Web\NoneParams; | |||||
| class ReceiptsParam extends NoneParams | |||||
| { | |||||
| } | |||||
| @@ -2,10 +2,12 @@ | |||||
| namespace App\Kintone\Models; | namespace App\Kintone\Models; | ||||
| use App\Http\Controllers\Web\Receipt\ReceiptDownloadController; | |||||
| use App\Kintone\Models\PaymentPlan as ModelsPaymentPlan; | use App\Kintone\Models\PaymentPlan as ModelsPaymentPlan; | ||||
| use App\Kintone\Models\SubTable\Receipt\PaymentPlan; | use App\Kintone\Models\SubTable\Receipt\PaymentPlan; | ||||
| use App\Kintone\Models\SubTable\Receipt\ReceiptDetail; | use App\Kintone\Models\SubTable\Receipt\ReceiptDetail; | ||||
| use App\Kintone\Models\SubTable\Receipt\TaxDetail; | use App\Kintone\Models\SubTable\Receipt\TaxDetail; | ||||
| use App\Util\RouteHelper; | |||||
| use Illuminate\Support\Carbon; | use Illuminate\Support\Carbon; | ||||
| use Illuminate\Support\Collection; | use Illuminate\Support\Collection; | ||||
| @@ -86,6 +88,9 @@ class Receipt extends KintoneModel | |||||
| protected const FIELD_NAMES = [ | protected const FIELD_NAMES = [ | ||||
| ...parent::FIELD_NAMES, | ...parent::FIELD_NAMES, | ||||
| self::FIELD_RECEIPT_DATE => 'receipt_date', | |||||
| self::FIELD_RECEIPT_NAME => 'receipt_name', | |||||
| self::FIELD_RECEIPT_TOTAL_AMOUNT => 'total_amount', | |||||
| ]; | ]; | ||||
| protected const SUB_TABLES = [ | protected const SUB_TABLES = [ | ||||
| @@ -98,4 +103,11 @@ class Receipt extends KintoneModel | |||||
| Customer::class, | Customer::class, | ||||
| ModelsPaymentPlan::class, | ModelsPaymentPlan::class, | ||||
| ]; | ]; | ||||
| protected function toArrayCustom(): array | |||||
| { | |||||
| return [ | |||||
| 'pdf_url' => RouteHelper::getPath(ReceiptDownloadController::class, ['receipt_no' => $this->receiptNo, 'record_no' => $this->getRecordId()]), | |||||
| ]; | |||||
| } | |||||
| } | } | ||||
| @@ -0,0 +1,28 @@ | |||||
| <?php | |||||
| namespace App\Kintone\Repositories; | |||||
| use App\Kintone\Models\Receipt; | |||||
| use Illuminate\Support\Collection; | |||||
| class ReceiptRepository | |||||
| { | |||||
| /** | |||||
| * キーは車室情報管理のレコード番号 | |||||
| * @param string $customerCode | |||||
| * @return Collection<int, ReceiptRepositoryData> | |||||
| */ | |||||
| static function get(int $customerCode): Collection | |||||
| { | |||||
| $ret = collect(); | |||||
| $query = Receipt::getQuery()->where(Receipt::FIELD_CUSTOMER_CODE, $customerCode)->orderByDesc(Receipt::FIELD_RECEIPT_DATE); | |||||
| $all = Receipt::getAccess()->all($query); | |||||
| $all->map(function (Receipt $receipt) use ($ret) { | |||||
| $ret->push(new ReceiptRepositoryData($receipt)); | |||||
| }); | |||||
| return $ret; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,20 @@ | |||||
| <?php | |||||
| namespace App\Kintone\Repositories; | |||||
| use App\Kintone\Models\Receipt; | |||||
| class ReceiptRepositoryData | |||||
| { | |||||
| public function __construct( | |||||
| public Receipt $receipt, | |||||
| ) { | |||||
| } | |||||
| public function toArray(): array | |||||
| { | |||||
| return [ | |||||
| ...$this->receipt->toArray(), | |||||
| ]; | |||||
| } | |||||
| } | |||||
| @@ -181,9 +181,9 @@ class ReceiptManager | |||||
| public function makePdf(): ReceiptReceipt | public function makePdf(): ReceiptReceipt | ||||
| { | { | ||||
| $pdf = PDF::loadView("pdf/receipt", $this->getPdfData()) | $pdf = PDF::loadView("pdf/receipt", $this->getPdfData()) | ||||
| // ->setPaper("A4") | |||||
| ->setOption('page-width', 210) | |||||
| ->setOption('page-height', 148) | |||||
| ->setPaper("A4") | |||||
| // ->setOption('page-width', 210) | |||||
| // ->setOption('page-height', 148) | |||||
| ->setOrientation("Portrait") | ->setOrientation("Portrait") | ||||
| ->setOption('encoding', 'utf-8'); | ->setOption('encoding', 'utf-8'); | ||||
| @@ -195,6 +195,23 @@ class ReceiptManager | |||||
| return $file; | return $file; | ||||
| } | } | ||||
| public function getPdf() | |||||
| { | |||||
| $pdf = PDF::loadView("pdf/receipt", $this->getPdfData()) | |||||
| ->setPaper("A4") | |||||
| // ->setOption('page-width', 210) | |||||
| // ->setOption('page-height', 148) | |||||
| ->setOrientation("Portrait") | |||||
| ->setOption('encoding', 'utf-8'); | |||||
| return $pdf->inline(); | |||||
| } | |||||
| public function getHtml() | |||||
| { | |||||
| return response()->view("pdf/receipt", ["forHtml" => true, ...$this->getPdfData()]); | |||||
| } | |||||
| private function makeFileName(ReceiptReceipt $file) | private function makeFileName(ReceiptReceipt $file) | ||||
| { | { | ||||
| return sprintf( | return sprintf( | ||||
| @@ -209,14 +226,23 @@ class ReceiptManager | |||||
| private function getPdfData() | private function getPdfData() | ||||
| { | { | ||||
| return [ | return [ | ||||
| 'receiptDate' => "2023/10/17", | |||||
| 'receiptDate' => $this->receipt->receiptDate->format('Y/m/d'), | |||||
| 'receiptCustomerName' => $this->receipt->receiptCustomerName, | 'receiptCustomerName' => $this->receipt->receiptCustomerName, | ||||
| 'receiptTotalAmount' => number_format($this->receipt->receiptTotalAmount), | 'receiptTotalAmount' => number_format($this->receipt->receiptTotalAmount), | ||||
| 'taxTotalAmount' => number_format(100), | |||||
| 'receiptPurpose' => $this->receipt->receiptPurpose, | |||||
| 'taxTotalAmount' => $this->getTaxAmount10(), | |||||
| 'detail' => $this->receipt->receiptDetail, | |||||
| ]; | ]; | ||||
| } | } | ||||
| private function getTaxAmount10(): int | |||||
| { | |||||
| $receipt = $this->receipt->taxDetail->first(function (TaxDetail $tax) { | |||||
| return $tax->taxRate === 10; | |||||
| }); | |||||
| return $receipt ? $receipt->taxAmount : 0; | |||||
| } | |||||
| private function getReceiptNo(): string | private function getReceiptNo(): string | ||||
| { | { | ||||
| @@ -34,6 +34,11 @@ class RouteHelper | |||||
| return $routeName; | return $routeName; | ||||
| } | } | ||||
| static public function getPath(string $controllerClassName, array $param = []) | |||||
| { | |||||
| return route(self::routeName($controllerClassName), $param); | |||||
| } | |||||
| static public function webRoute(string $route) | static public function webRoute(string $route) | ||||
| { | { | ||||
| return Str::replaceFirst('/api', '', $route); | return Str::replaceFirst('/api', '', $route); | ||||
| @@ -0,0 +1,36 @@ | |||||
| <?php | |||||
| namespace App\Util; | |||||
| class UrlUtil | |||||
| { | |||||
| /** | |||||
| * 画面のURLを生成する | |||||
| * | |||||
| * @param array|string $path | |||||
| * @return string | |||||
| */ | |||||
| public static function getAppUrl(array|string $path, array $query = []): string | |||||
| { | |||||
| $elements = [config("app.url")]; | |||||
| if (is_array($path)) { | |||||
| $elements = array_merge($elements, $path); | |||||
| } else { | |||||
| $elements[] = $path; | |||||
| } | |||||
| $url = implode("/", $elements); | |||||
| if (!!$query) { | |||||
| $url .= "?"; | |||||
| $queryStrList = []; | |||||
| foreach ($query as $key => $value) { | |||||
| $queryStrList[] = sprintf("%s=%s", $key, $value); | |||||
| } | |||||
| $url .= implode("&", $queryStrList); | |||||
| } | |||||
| return $url; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,32 @@ | |||||
| .flex { | |||||
| display: flex; | |||||
| display: -webkit-box; | |||||
| } | |||||
| .flex-start { | |||||
| display: flex; | |||||
| display: -webkit-box; | |||||
| justify-content: start; | |||||
| -webkit-box-pack: start; | |||||
| } | |||||
| .flex-center { | |||||
| display: flex; | |||||
| display: -webkit-box; | |||||
| justify-content: center; | |||||
| -webkit-box-pack: center; | |||||
| } | |||||
| .flex-end { | |||||
| display: flex; | |||||
| display: -webkit-box; | |||||
| justify-content: end; | |||||
| -webkit-box-pack: end; | |||||
| } | |||||
| .flex-space-between { | |||||
| display: flex; | |||||
| display: -webkit-box; | |||||
| justify-content: space-between; | |||||
| -webkit-box-pack: justify; | |||||
| } | |||||
| @@ -4,30 +4,98 @@ | |||||
| <head> | <head> | ||||
| <meta charset="utf-8"> | <meta charset="utf-8"> | ||||
| <title>領収証</title> | <title>領収証</title> | ||||
| <link rel="stylesheet" href="{{ resource_path('views/pdf/receipt.css') }}"> | |||||
| <link rel="stylesheet" href="{{ resource_path('views/pdf/common.css') }}" /> | |||||
| <link rel="stylesheet" href="{{ resource_path('views/pdf/receipt.css') }}" /> | |||||
| </head> | </head> | ||||
| <body> | <body> | ||||
| <div id="title"> | |||||
| 領収証 | |||||
| </div> | |||||
| <div id="receiptCustomerName" class="receiptCustomerName">{{ $receiptCustomerName }}様</div> | |||||
| <div id="receiptDate" class="receiptCustomerName">領収日 {{ $receiptDate }}</div> | |||||
| <div id="receiptTotalAmount">金額{{ $receiptTotalAmount }}(税込)</div> | |||||
| <div id="taxTotalAmount">税率10% ¥{{ $taxTotalAmount }}</div> | |||||
| <div id="receiptPurpose"> | |||||
| <p> | |||||
| 但し {{ $receiptPurpose }} として | |||||
| </p> | |||||
| <p> | |||||
| 上記正に領収いたしました | |||||
| </p> | |||||
| </div> | |||||
| <div id="company"> | |||||
| <p class="name">一般財団法人 京都市都市整備公社</p> | |||||
| <p class="zipCode">〒600-8421</p> | |||||
| <p class="address1">京都市下京区小道通烏丸西入童侍者町167番</p> | |||||
| <p class="invoiceNo">登録番号 T7 1300 0501 2806</p> | |||||
| <div id="content"> | |||||
| <div id="title" class="flex-center"> | |||||
| <div> | |||||
| 領収書 | |||||
| </div> | |||||
| </div> | |||||
| <div id="receiptCustomerName" class="flex-end"> | |||||
| <div> | |||||
| {{ $receiptCustomerName }}様 | |||||
| </div> | |||||
| <div id="receiptDate"> | |||||
| 領収日 {{ $receiptDate }} | |||||
| </div> | |||||
| </div> | |||||
| <div id="receiptTotalAmount" class="flex-space-between"> | |||||
| <div> | |||||
| 金額 | |||||
| </div> | |||||
| <div> | |||||
| ¥{{ $receiptTotalAmount }}- | |||||
| </div> | |||||
| <div style="font-weight: normal;"> | |||||
| (税込) | |||||
| </div> | |||||
| </div> | |||||
| <div id="taxTotalAmount" class="flex-end"> | |||||
| <div class="flex-space-between"> | |||||
| <div> | |||||
| 税率10% | |||||
| </div> | |||||
| <div> | |||||
| ¥{{ number_format($taxTotalAmount) }} | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| <div id="detailTable"> | |||||
| <table> | |||||
| <thead> | |||||
| <th class="parkingName">駐車場名</th> | |||||
| <th class="amount">金額</th> | |||||
| <th class="targetMonth">対象月</th> | |||||
| <th class="memo">備考</th> | |||||
| </thead> | |||||
| <tbody> | |||||
| @foreach($detail as $ele) | |||||
| <tr> | |||||
| <td>{{ $ele->parkingName }}</td> | |||||
| <td>¥{{ number_format($ele->amount) }}</td> | |||||
| <td>{{ sprintf("%d月分", $ele->targetMonth) }}</td> | |||||
| <td>{{ $ele->memo }}</td> | |||||
| </tr> | |||||
| @endforeach | |||||
| </tbody> | |||||
| </table> | |||||
| </div> | |||||
| <div style="margin-top: 10mm;"> | |||||
| 上記正に領収いたしました。 | |||||
| </div> | |||||
| <div id="company" class="flex-end"> | |||||
| <div> | |||||
| <p class="name">一般財団法人 京都市都市整備公社</p> | |||||
| <p class="zipCode">〒600-8421</p> | |||||
| <p class="address1">京都市下京区小道通烏丸西入童侍者町167番</p> | |||||
| <p class="invoiceNo">登録番号 T7 1300 0501 2806</p> | |||||
| </div> | |||||
| <img id="stamp" width="50mm" height="50mm" src="{{ resource_path('views/pdf/stamp.png') }}" /> | |||||
| </div> | |||||
| </div> | </div> | ||||
| </body> | </body> | ||||
| @@ -1,67 +1,109 @@ | |||||
| body { | body { | ||||
| position: relative; | |||||
| font-size: 5mm | |||||
| font-size: 3.5mm | |||||
| } | } | ||||
| body div { | |||||
| position: absolute; | |||||
| #content { | |||||
| padding: 20mm; | |||||
| padding-top: 10mm | |||||
| } | } | ||||
| #title { | #title { | ||||
| top : 10mm; | |||||
| left: 100mm; | |||||
| font-size: 10mm; | |||||
| font-size: 7mm; | |||||
| font-weight: bold; | font-weight: bold; | ||||
| letter-spacing: 10mm; | |||||
| letter-spacing: 5mm; | |||||
| } | } | ||||
| .receiptCustomerName { | |||||
| top : 30mm; | |||||
| } | |||||
| #receiptCustomerName { | #receiptCustomerName { | ||||
| left: 100mm; | |||||
| font-size: 5mm; | |||||
| padding-top: 10mm; | |||||
| } | } | ||||
| #receiptDate { | #receiptDate { | ||||
| left: 150mm; | |||||
| margin-left: 50mm; | |||||
| } | } | ||||
| #receiptTotalAmount { | #receiptTotalAmount { | ||||
| border-top: solid 1px; | border-top: solid 1px; | ||||
| border-bottom: solid 1px; | border-bottom: solid 1px; | ||||
| padding-top: 2mm;; | |||||
| padding-top: 2mm; | |||||
| padding-bottom: 2mm; | padding-bottom: 2mm; | ||||
| margin-top: 10mm; | |||||
| margin-bottom: 2mm; | |||||
| font-size: 5mm; | |||||
| font-weight: bold; | |||||
| width: 150mm; | |||||
| top: 50mm; | |||||
| left: 20mm; | |||||
| width: 170mm; | |||||
| } | |||||
| #receiptTotalAmount :nth-of-type(1) { | |||||
| margin-left: 10mm; | |||||
| } | |||||
| #receiptTotalAmount :nth-last-of-type(1) { | |||||
| margin-right: 6mm; | |||||
| } | } | ||||
| #taxTotalAmount { | #taxTotalAmount { | ||||
| border-bottom: solid 1px; | |||||
| padding-bottom: 2mm; | |||||
| width: 150mm; | |||||
| } | |||||
| #taxTotalAmount>div { | |||||
| width: 60mm; | |||||
| border-bottom: solid 0.5px; | |||||
| } | |||||
| top: 65mm; | |||||
| left: 140mm; | |||||
| width: 50mm; | |||||
| #taxTotalAmount>div :nth-of-type(1) { | |||||
| margin-left: 10mm; | |||||
| } | } | ||||
| #receiptPurpose { | |||||
| top: 70mm; | |||||
| left: 20mm; | |||||
| #taxTotalAmount>div :nth-of-type(2) { | |||||
| margin-right: 10mm; | |||||
| } | } | ||||
| #detailTable { | |||||
| margin-top: 5mm; | |||||
| } | |||||
| #detailTable table { | |||||
| border-collapse: collapse; | |||||
| /* セルの線を重ねる */ | |||||
| width: 150mm; | |||||
| text-align: left; | |||||
| } | |||||
| #detailTable th, | |||||
| td { | |||||
| border: solid 0.5px; | |||||
| /* 枠線指定 */ | |||||
| } | |||||
| #detailTable th.amount { | |||||
| width: 20mm; | |||||
| } | |||||
| #detailTable th.targetMonth { | |||||
| width: 15mm; | |||||
| } | |||||
| #company { | #company { | ||||
| top: 130mm; | |||||
| left: 150mm; | |||||
| font-size: 4mm; | font-size: 4mm; | ||||
| margin-top: 5mm; | |||||
| } | } | ||||
| #company p { | #company p { | ||||
| margin: 1mm; | margin: 1mm; | ||||
| } | } | ||||
| #company .name { | #company .name { | ||||
| font-size: 5mm; | font-size: 5mm; | ||||
| } | } | ||||
| #stamp { | |||||
| top: 100mm; | |||||
| } | |||||
| @@ -36,6 +36,10 @@ RouteHelper::post('/season-ticket-contract/entry/cancel', App\Http\Controllers\W | |||||
| RouteHelper::get('/season-ticket-contract/selection/info', App\Http\Controllers\Web\SeasonTicketContract\Selection\SelectionInfoController::class); | RouteHelper::get('/season-ticket-contract/selection/info', App\Http\Controllers\Web\SeasonTicketContract\Selection\SelectionInfoController::class); | ||||
| RouteHelper::post('/season-ticket-contract/selection/entry', App\Http\Controllers\Web\SeasonTicketContract\Selection\EntryController::class); | RouteHelper::post('/season-ticket-contract/selection/entry', App\Http\Controllers\Web\SeasonTicketContract\Selection\EntryController::class); | ||||
| RouteHelper::get('/receipts', App\Http\Controllers\Web\Receipt\ReceiptsController::class); | |||||
| RouteHelper::get('/receipt/download', App\Http\Controllers\Web\Receipt\ReceiptDownloadController::class); | |||||
| RouteHelper::get('/faq', App\Http\Controllers\Web\FAQ\FAQsController::class); | RouteHelper::get('/faq', App\Http\Controllers\Web\FAQ\FAQsController::class); | ||||
| RouteHelper::get('/faq/genres', App\Http\Controllers\Web\FAQ\FAQGenresController::class); | RouteHelper::get('/faq/genres', App\Http\Controllers\Web\FAQ\FAQGenresController::class); | ||||
| RouteHelper::post('/ask', App\Http\Controllers\Web\FAQ\AskController::class); | RouteHelper::post('/ask', App\Http\Controllers\Web\FAQ\AskController::class); | ||||