| @@ -9,7 +9,7 @@ abstract class FromKintoneController extends WebController | |||||
| public function __construct() | public function __construct() | ||||
| { | { | ||||
| parent::__construct(); | parent::__construct(); | ||||
| $this->middleware('formKintone'); | |||||
| $this->middleware('fromKintone'); | |||||
| } | } | ||||
| protected function makeResponse() | protected function makeResponse() | ||||
| @@ -0,0 +1,39 @@ | |||||
| <?php | |||||
| namespace App\Http\Controllers\Web\Receipt; | |||||
| use App\Http\Controllers\Web\FromKintoneController; | |||||
| use App\Jobs\SeasonTicketContract\Selection\NoticeToCandidates; | |||||
| use App\Logic\ReceiptManager; | |||||
| use Illuminate\Http\JsonResponse; | |||||
| use Illuminate\Http\Request; | |||||
| class ReceiptCreateController extends FromKintoneController | |||||
| { | |||||
| public function name(): string | |||||
| { | |||||
| return "領収証作成"; | |||||
| } | |||||
| public function description(): string | |||||
| { | |||||
| return "領収証を作成する"; | |||||
| } | |||||
| public function __construct(protected ReceiptCreateParams $param) | |||||
| { | |||||
| parent::__construct(); | |||||
| } | |||||
| protected function run(Request $request): JsonResponse | |||||
| { | |||||
| $param = $this->param; | |||||
| $manager = new ReceiptManager(); | |||||
| $manager->create($param->recordNo); | |||||
| return $this->successResponse(); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,19 @@ | |||||
| <?php | |||||
| namespace App\Http\Controllers\Web\Receipt; | |||||
| use App\Http\Controllers\Web\BaseParam; | |||||
| /** | |||||
| * @property int[] $recordNo | |||||
| */ | |||||
| class ReceiptCreateParams extends BaseParam | |||||
| { | |||||
| public function rules(): array | |||||
| { | |||||
| return [ | |||||
| 'record_no' => $this->array(), | |||||
| 'record_no.*' => $this->numeric(true), | |||||
| ]; | |||||
| } | |||||
| } | |||||
| @@ -64,6 +64,6 @@ class Kernel extends HttpKernel | |||||
| 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, | 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, | ||||
| 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class, | 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class, | ||||
| 'formKintone' => \App\Http\Middleware\FromKintoneMiddleware::class, | |||||
| 'fromKintone' => \App\Http\Middleware\FromKintoneMiddleware::class, | |||||
| ]; | ]; | ||||
| } | } | ||||
| @@ -0,0 +1,13 @@ | |||||
| <?php | |||||
| namespace App\Kintone\Models\DropDown\PaymentPlan; | |||||
| abstract class PaymentType | |||||
| { | |||||
| const SEASON_TICKET = "定期料金"; | |||||
| const A保証金 = "保証金"; | |||||
| const B証明証郵送代 = "証明証郵送代"; | |||||
| const C事務手数料 = "事務手数料"; | |||||
| const D延滞金 = "延滞金"; | |||||
| const E余剰金 = "余剰金"; | |||||
| } | |||||
| @@ -2,14 +2,30 @@ | |||||
| namespace App\Kintone\Models; | namespace App\Kintone\Models; | ||||
| use Illuminate\Support\Carbon; | |||||
| /** | /** | ||||
| * アプリ名 入金予定・結果 | * アプリ名 入金予定・結果 | ||||
| * @property int SEASON_TICKET_CONTRACT_RECORD_NO | |||||
| * @property string parkingName | |||||
| * @property int parkingName | |||||
| * @property int customerCode | |||||
| * @property string paymentType | |||||
| * @property string paymentMethod | |||||
| * @property int targetYear | |||||
| * @property int targetMonth | |||||
| * @property int targetTermMonth | |||||
| * @property Carbon appropriationDate | |||||
| * @property int appropriationAmount | |||||
| * @property int remainingAmount | |||||
| */ | */ | ||||
| class PaymentPlan extends KintoneModel | class PaymentPlan extends KintoneModel | ||||
| { | { | ||||
| const CONFIG_KEY = "KINTONE_APP_PAYMENT_PLAN"; | const CONFIG_KEY = "KINTONE_APP_PAYMENT_PLAN"; | ||||
| const FIELD_SEASON_TICKET_CONTRACT_RECORD_NO = "contract_record_number"; | const FIELD_SEASON_TICKET_CONTRACT_RECORD_NO = "contract_record_number"; | ||||
| const FIELD_PARKING_NAME = "parking_name"; | |||||
| const FIELD_CUSTOMER_CODE = "customer_code"; | |||||
| const FIELD_PAYMENT_TYPE = "payment_type"; | const FIELD_PAYMENT_TYPE = "payment_type"; | ||||
| const FIELD_PAYMENT_METHOD = "payment_method"; | const FIELD_PAYMENT_METHOD = "payment_method"; | ||||
| const FIELD_TARGET_YEAR = "target_year"; | const FIELD_TARGET_YEAR = "target_year"; | ||||
| @@ -24,16 +40,18 @@ class PaymentPlan extends KintoneModel | |||||
| protected const FIELDS = [ | protected const FIELDS = [ | ||||
| ...parent::FIELDS, | ...parent::FIELDS, | ||||
| self::FIELD_SEASON_TICKET_CONTRACT_RECORD_NO => FieldType::NUMBER, | self::FIELD_SEASON_TICKET_CONTRACT_RECORD_NO => FieldType::NUMBER, | ||||
| self::FIELD_PARKING_NAME => FieldType::SINGLE_LINE_TEXT, | |||||
| self::FIELD_CUSTOMER_CODE => FieldType::NUMBER, | |||||
| self::FIELD_PAYMENT_TYPE => FieldType::DROP_DOWN, | self::FIELD_PAYMENT_TYPE => FieldType::DROP_DOWN, | ||||
| self::FIELD_PAYMENT_METHOD => FieldType::DROP_DOWN, | self::FIELD_PAYMENT_METHOD => FieldType::DROP_DOWN, | ||||
| self::FIELD_TARGET_YEAR => FieldType::SINGLE_LINE_TEXT, | |||||
| self::FIELD_TARGET_MONTH => FieldType::SINGLE_LINE_TEXT, | |||||
| self::FIELD_TARGET_TERM_MONTH => FieldType::SINGLE_LINE_TEXT, | |||||
| self::FIELD_TARGET_YEAR => FieldType::NUMBER, | |||||
| self::FIELD_TARGET_MONTH => FieldType::NUMBER, | |||||
| self::FIELD_TARGET_TERM_MONTH => FieldType::NUMBER, | |||||
| self::FIELD_PAYMENT_PLAN_DATE => FieldType::DATE, | self::FIELD_PAYMENT_PLAN_DATE => FieldType::DATE, | ||||
| self::FIELD_PAYMENT_PLAN_AMOUNT => FieldType::SINGLE_LINE_TEXT, | |||||
| self::FIELD_PAYMENT_PLAN_AMOUNT => FieldType::NUMBER, | |||||
| self::FIELD_APPROPRIATION_DATE => FieldType::DATE, | self::FIELD_APPROPRIATION_DATE => FieldType::DATE, | ||||
| self::FIELD_APPROPRIATION_AMOUNT => FieldType::SINGLE_LINE_TEXT, | |||||
| self::FIELD_REMAINING_AMOUNT => FieldType::SINGLE_LINE_TEXT, | |||||
| self::FIELD_APPROPRIATION_AMOUNT => FieldType::NUMBER, | |||||
| self::FIELD_REMAINING_AMOUNT => FieldType::NUMBER, | |||||
| ]; | ]; | ||||
| protected const FIELD_NAMES = [ | protected const FIELD_NAMES = [ | ||||
| @@ -43,20 +61,22 @@ class PaymentPlan extends KintoneModel | |||||
| self::FIELD_PAYMENT_TYPE => 'payment_type', | self::FIELD_PAYMENT_TYPE => 'payment_type', | ||||
| self::FIELD_PAYMENT_METHOD => 'payment_method', | self::FIELD_PAYMENT_METHOD => 'payment_method', | ||||
| self::FIELD_PAYMENT_PLAN_DATE => 'payment_plan_date', | self::FIELD_PAYMENT_PLAN_DATE => 'payment_plan_date', | ||||
| self::FIELD_TARGET_YEAR => 'target_year', | |||||
| self::FIELD_TARGET_MONTH => 'target_month', | |||||
| ]; | ]; | ||||
| protected function toArrayCustom(): array | protected function toArrayCustom(): array | ||||
| { | { | ||||
| return [ | |||||
| 'payment_status' => $this->donePayment ? "支払済み" : "未払い", | |||||
| ]; | |||||
| } | |||||
| public function donePayment(): bool | |||||
| { | |||||
| $paymentDate = $this->getDate(self::FIELD_APPROPRIATION_DATE); | $paymentDate = $this->getDate(self::FIELD_APPROPRIATION_DATE); | ||||
| $remainingAmount = $this->getNumber(self::FIELD_REMAINING_AMOUNT); | $remainingAmount = $this->getNumber(self::FIELD_REMAINING_AMOUNT); | ||||
| $paymentStatus = "未払い"; | |||||
| if (!!$paymentDate && $remainingAmount === 0) { | |||||
| $paymentStatus = "支払済み"; | |||||
| } | |||||
| return [ | |||||
| 'payment_status' => $paymentStatus, | |||||
| ]; | |||||
| return !!$paymentDate && $remainingAmount === 0; | |||||
| } | } | ||||
| } | } | ||||
| @@ -2,50 +2,100 @@ | |||||
| namespace App\Kintone\Models; | namespace App\Kintone\Models; | ||||
| use App\Kintone\Models\SubTable\SeasonTicketContractReserve\TargetRoom; | |||||
| use App\Kintone\Models\PaymentPlan as ModelsPaymentPlan; | |||||
| use App\Kintone\Models\SubTable\Receipt\PaymentPlan; | |||||
| use App\Kintone\Models\SubTable\Receipt\ReceiptDetail; | |||||
| use App\Kintone\Models\SubTable\Receipt\TaxDetail; | |||||
| use Illuminate\Support\Carbon; | |||||
| use Illuminate\Support\Collection; | use Illuminate\Support\Collection; | ||||
| /** | /** | ||||
| * アプリ名 領収証 | * アプリ名 領収証 | ||||
| * @property string receiptNo | * @property string receiptNo | ||||
| * @property Carbon receiptDate | |||||
| * @property string invoiceNo | |||||
| * @property string receiptIssure | |||||
| * @property string receiptName | |||||
| * @property int customerCode | * @property int customerCode | ||||
| * @property string customerName | * @property string customerName | ||||
| * @property string parkingName | |||||
| * @property string taxType | |||||
| * @property Collection<int, ReceiptDetail> receiptDetail | |||||
| * @property Collection<int, TaxDetail> taxDetail | |||||
| * @property string receiptCustomerName | * @property string receiptCustomerName | ||||
| * @property string receiptPurpose | |||||
| * @property int receiptTotalAmount | * @property int receiptTotalAmount | ||||
| * @property Collection<int, PaymentPlan> paymentPlans | |||||
| */ | */ | ||||
| class Receipt extends KintoneModel | class Receipt extends KintoneModel | ||||
| { | { | ||||
| const CONFIG_KEY = "KINTONE_APP_RECEIPT"; | const CONFIG_KEY = "KINTONE_APP_RECEIPT"; | ||||
| const FIELD_RECEIPT_NO = "領収証番号"; | const FIELD_RECEIPT_NO = "領収証番号"; | ||||
| const FIELD_RECEIPT_DATE = "発行日"; | |||||
| const FIELD_INVOICE_NO = "インボイス登録番号"; | |||||
| const FIELD_RECEIPT_ISSURE = "発行事業者名"; | |||||
| const FIELD_RECEIPT_NAME = "領収証名称"; | |||||
| const FIELD_CUSTOMER_CODE = "顧客コード"; | const FIELD_CUSTOMER_CODE = "顧客コード"; | ||||
| const FIELD_CUSTOMER_NAME = "顧客名"; | const FIELD_CUSTOMER_NAME = "顧客名"; | ||||
| const FIELD_TAX_TYPE = "税区分"; | |||||
| const FIELD_RECEIPT_DETAIL = "テーブル_明細"; | |||||
| const FIELD_RECEIPT_DETAIL_NAME = "明細_名称"; | |||||
| const FIELD_RECEIPT_DETAIL_PARKING_NAME = "明細_駐車場名"; | |||||
| const FIELD_RECEIPT_DETAIL_TARGET_MONTH = "明細_対象月"; | |||||
| const FIELD_RECEIPT_DETAIL_MEMO = "明細_備考"; | |||||
| const FIELD_RECEIPT_DETAIL_UNIT_PRICE = "明細_単価"; | |||||
| const FIELD_RECEIPT_DETAIL_QUANTITY = "明細_数量"; | |||||
| const FIELD_RECEIPT_DETAIL_AMOUNT = "明細_金額"; | |||||
| const FIELD_RECEIPT_DETAIL_TAX_RATE = "明細_税率"; | |||||
| const FIELD_TAX_DETAIL = "テーブル_内税明細"; | |||||
| const FIELD_TAX_DETAIL_TAX_RATE = "内税明細_税率"; | |||||
| const FIELD_TAX_DETAIL_TOTAL_AMOUNT = "内税明細_税込金額合計"; | |||||
| const FIELD_TAX_DETAIL_TAX_AMOUNT = "内税明細_消費税_内税"; | |||||
| const FIELD_RECEIPT_CUSTOMER_NAME = "宛名"; | const FIELD_RECEIPT_CUSTOMER_NAME = "宛名"; | ||||
| const FIELD_RECEIPT_PURPOSE = "但し書き"; | |||||
| const FIELD_RECEIPT_TOTAL_AMOUNT = "合計"; | const FIELD_RECEIPT_TOTAL_AMOUNT = "合計"; | ||||
| const FIELD_PAYMENT_PLANS = "テーブル_入金実績"; | |||||
| const FIELD_PAYMENT_PLANS_RECORD_NO = "入金実績_入金予定レコード番号"; | |||||
| const FIELD_PAYMENT_PLANS_PAYMENT_PURPOSE = "入金実績_支払種別"; | |||||
| const FIELD_PAYMENT_PLANS_DATE = "入金実績_入金日"; | |||||
| const FIELD_PAYMENT_PLANS_AMOUNT = "入金実績_入金額"; | |||||
| const FIELD_PAYMENT_PLANS_TARGET_YEAR = "入金実績支払対象_利用_年"; | |||||
| const FIELD_PAYMENT_PLANS_TARGET_MONTH = "入金実績支払対象_利用_月"; | |||||
| const FIELD_RECEIPT_PDF_FILE = "領収証PDF"; | const FIELD_RECEIPT_PDF_FILE = "領収証PDF"; | ||||
| protected const FIELDS = [ | protected const FIELDS = [ | ||||
| ...parent::FIELDS, | ...parent::FIELDS, | ||||
| self::FIELD_RECEIPT_NO => FieldType::SINGLE_LINE_TEXT, | self::FIELD_RECEIPT_NO => FieldType::SINGLE_LINE_TEXT, | ||||
| self::FIELD_RECEIPT_DATE => FieldType::DATE, | |||||
| self::FIELD_INVOICE_NO => FieldType::SINGLE_LINE_TEXT, | |||||
| self::FIELD_RECEIPT_ISSURE => FieldType::MULTI_LINE_TEXT, | |||||
| self::FIELD_RECEIPT_NAME => FieldType::SINGLE_LINE_TEXT, | |||||
| self::FIELD_CUSTOMER_CODE => FieldType::NUMBER, | self::FIELD_CUSTOMER_CODE => FieldType::NUMBER, | ||||
| self::FIELD_CUSTOMER_NAME => FieldType::SINGLE_LINE_TEXT, | self::FIELD_CUSTOMER_NAME => FieldType::SINGLE_LINE_TEXT, | ||||
| self::FIELD_TAX_TYPE => FieldType::DROP_DOWN, | |||||
| self::FIELD_RECEIPT_DETAIL => FieldType::SUBTABLE, | |||||
| self::FIELD_TAX_DETAIL => FieldType::SUBTABLE, | |||||
| self::FIELD_RECEIPT_CUSTOMER_NAME => FieldType::SINGLE_LINE_TEXT, | self::FIELD_RECEIPT_CUSTOMER_NAME => FieldType::SINGLE_LINE_TEXT, | ||||
| self::FIELD_RECEIPT_PURPOSE => FieldType::SINGLE_LINE_TEXT, | |||||
| self::FIELD_RECEIPT_TOTAL_AMOUNT => FieldType::NUMBER, | self::FIELD_RECEIPT_TOTAL_AMOUNT => FieldType::NUMBER, | ||||
| self::FIELD_RECEIPT_PDF_FILE => FieldType::FILE, | self::FIELD_RECEIPT_PDF_FILE => FieldType::FILE, | ||||
| self::FIELD_PAYMENT_PLANS => FieldType::SUBTABLE, | |||||
| ]; | ]; | ||||
| protected const FIELD_NAMES = [ | protected const FIELD_NAMES = [ | ||||
| ...parent::FIELD_NAMES, | ...parent::FIELD_NAMES, | ||||
| ]; | ]; | ||||
| protected const SUB_TABLES = [ | |||||
| self::FIELD_RECEIPT_DETAIL => ReceiptDetail::class, | |||||
| self::FIELD_TAX_DETAIL => TaxDetail::class, | |||||
| self::FIELD_PAYMENT_PLANS => PaymentPlan::class, | |||||
| ]; | |||||
| protected const RELATIONS = [ | protected const RELATIONS = [ | ||||
| Customer::class, | Customer::class, | ||||
| PaymentPlan::class, | |||||
| ModelsPaymentPlan::class, | |||||
| ]; | ]; | ||||
| } | } | ||||
| @@ -0,0 +1,37 @@ | |||||
| <?php | |||||
| namespace App\Kintone\Models\SubTable\Receipt; | |||||
| use App\Kintone\Models\Receipt; | |||||
| use App\Kintone\Models\SubTable\SubTableData; | |||||
| use App\Util\DateUtil; | |||||
| use Illuminate\Support\Carbon; | |||||
| class PaymentPlan extends SubTableData | |||||
| { | |||||
| public int $recordNo; | |||||
| public string $paymentPurpose; | |||||
| public ?Carbon $date; | |||||
| public int $amount; | |||||
| public int $targetYear; | |||||
| public int $targetMonth; | |||||
| public function __construct(array $data = []) | |||||
| { | |||||
| $this->recordNo = intval(data_get($data, Receipt::FIELD_PAYMENT_PLANS_RECORD_NO, 0)); | |||||
| $this->paymentPurpose = data_get($data, Receipt::FIELD_PAYMENT_PLANS_PAYMENT_PURPOSE, ""); | |||||
| $this->date = DateUtil::parse(data_get($data, Receipt::FIELD_PAYMENT_PLANS_DATE, null)); | |||||
| $this->amount = intval(data_get($data, Receipt::FIELD_PAYMENT_PLANS_AMOUNT, 0)); | |||||
| $this->targetYear = intval(data_get($data, Receipt::FIELD_PAYMENT_PLANS_TARGET_YEAR, 0)); | |||||
| $this->targetMonth = intval(data_get($data, Receipt::FIELD_PAYMENT_PLANS_TARGET_MONTH, 0)); | |||||
| parent::__construct($data); | |||||
| } | |||||
| public function toArray(): array | |||||
| { | |||||
| return [ | |||||
| Receipt::FIELD_PAYMENT_PLANS_RECORD_NO => $this->recordNo, | |||||
| ]; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,48 @@ | |||||
| <?php | |||||
| namespace App\Kintone\Models\SubTable\Receipt; | |||||
| use App\Kintone\Models\Receipt; | |||||
| use App\Kintone\Models\SubTable\SubTableData; | |||||
| class ReceiptDetail extends SubTableData | |||||
| { | |||||
| public string $name = ""; | |||||
| public string $parkingName = ""; | |||||
| public string $targetMonth = ""; | |||||
| public string $memo = ""; | |||||
| public int $unitPrice = 0; | |||||
| public int $quantity = 0; | |||||
| public int $amount = 0; | |||||
| public int $taxRate = 0; | |||||
| public function __construct(array $data = []) | |||||
| { | |||||
| $this->name = data_get($data, Receipt::FIELD_RECEIPT_DETAIL_NAME, ""); | |||||
| $this->parkingName = data_get($data, Receipt::FIELD_RECEIPT_DETAIL_PARKING_NAME, ""); | |||||
| $this->targetMonth = data_get($data, Receipt::FIELD_RECEIPT_DETAIL_TARGET_MONTH, ""); | |||||
| $this->memo = data_get($data, Receipt::FIELD_RECEIPT_DETAIL_MEMO, ""); | |||||
| $this->unitPrice = intval(data_get($data, Receipt::FIELD_RECEIPT_DETAIL_UNIT_PRICE, 0)); | |||||
| $this->quantity = intval(data_get($data, Receipt::FIELD_RECEIPT_DETAIL_QUANTITY, 0)); | |||||
| $this->amount = intval(data_get($data, Receipt::FIELD_RECEIPT_DETAIL_AMOUNT, 0)); | |||||
| $this->taxRate = intval(data_get($data, Receipt::FIELD_RECEIPT_DETAIL_TAX_RATE, 0)); | |||||
| parent::__construct($data); | |||||
| } | |||||
| public function toArray(): array | |||||
| { | |||||
| return [ | |||||
| Receipt::FIELD_RECEIPT_DETAIL_NAME => $this->name, | |||||
| Receipt::FIELD_RECEIPT_DETAIL_PARKING_NAME => $this->parkingName, | |||||
| Receipt::FIELD_RECEIPT_DETAIL_TARGET_MONTH => $this->targetMonth, | |||||
| Receipt::FIELD_RECEIPT_DETAIL_MEMO => $this->memo, | |||||
| Receipt::FIELD_RECEIPT_DETAIL_UNIT_PRICE => $this->unitPrice, | |||||
| Receipt::FIELD_RECEIPT_DETAIL_QUANTITY => $this->quantity, | |||||
| Receipt::FIELD_RECEIPT_DETAIL_AMOUNT => $this->amount, | |||||
| Receipt::FIELD_RECEIPT_DETAIL_TAX_RATE => $this->taxRate, | |||||
| ]; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,33 @@ | |||||
| <?php | |||||
| namespace App\Kintone\Models\SubTable\Receipt; | |||||
| use App\Kintone\Models\Receipt; | |||||
| use App\Kintone\Models\SubTable\SubTableData; | |||||
| class TaxDetail extends SubTableData | |||||
| { | |||||
| public int $taxRate = 0; | |||||
| public int $totalAmount = 0; | |||||
| public int $taxAmount = 0; | |||||
| public function __construct(array $data = []) | |||||
| { | |||||
| $this->taxRate = intval(data_get($data, Receipt::FIELD_TAX_DETAIL_TAX_RATE, 0)); | |||||
| $this->totalAmount = intval(data_get($data, Receipt::FIELD_TAX_DETAIL_TOTAL_AMOUNT, 0)); | |||||
| $this->taxAmount = intval(data_get($data, Receipt::FIELD_TAX_DETAIL_TAX_AMOUNT, 0)); | |||||
| parent::__construct($data); | |||||
| } | |||||
| public function toArray(): array | |||||
| { | |||||
| return [ | |||||
| Receipt::FIELD_TAX_DETAIL_TAX_RATE => $this->taxRate, | |||||
| Receipt::FIELD_TAX_DETAIL_TOTAL_AMOUNT => $this->totalAmount, | |||||
| Receipt::FIELD_TAX_DETAIL_TAX_AMOUNT => $this->taxAmount, | |||||
| ]; | |||||
| } | |||||
| } | |||||
| @@ -2,8 +2,19 @@ | |||||
| namespace App\Logic; | namespace App\Logic; | ||||
| use App\Exceptions\AppCommonException; | |||||
| use App\Exceptions\GeneralErrorMessageException; | |||||
| use App\Files\PDF\Receipt as ReceiptReceipt; | use App\Files\PDF\Receipt as ReceiptReceipt; | ||||
| use App\Kintone\Models\Customer; | |||||
| use App\Kintone\Models\DropDown\PaymentPlan\PaymentType; | |||||
| use App\Kintone\Models\PaymentPlan; | |||||
| use App\Kintone\Models\Receipt; | use App\Kintone\Models\Receipt; | ||||
| use App\Kintone\Models\SubTable\Receipt\PaymentPlan as ReceiptPaymentPlan; | |||||
| use App\Kintone\Models\SubTable\Receipt\ReceiptDetail; | |||||
| use App\Kintone\Models\SubTable\Receipt\TaxDetail; | |||||
| use App\Util\DateUtil; | |||||
| use App\Util\TaxUtil; | |||||
| use Illuminate\Support\Collection; | |||||
| use PDF; | use PDF; | ||||
| class ReceiptManager | class ReceiptManager | ||||
| @@ -19,16 +30,154 @@ class ReceiptManager | |||||
| } | } | ||||
| } | } | ||||
| private function load(int $recordNo) | |||||
| public function getReceipt(): Receipt | |||||
| { | { | ||||
| $this->receipt = Receipt::find($recordNo); | |||||
| return $this->receipt; | |||||
| } | } | ||||
| public function getReceipt(): Receipt | |||||
| public function create(array $paymentPlanRecordNoList) | |||||
| { | { | ||||
| return $this->receipt; | |||||
| // 支払予定データの取得 | |||||
| $payments = $this->getPayments($paymentPlanRecordNoList); | |||||
| $this->checkPayments($payments, count($paymentPlanRecordNoList)); | |||||
| // 顧客取得 | |||||
| $customer = Customer::findByCustomerCode($payments->first()->customerCode); | |||||
| $receipt = $this->receipt; | |||||
| $receipt->receiptNo = $this->getReceiptNo(); | |||||
| $receipt->receiptDate = DateUtil::now(); | |||||
| $receipt->invoiceNo = config("business.invoiceNo", ""); | |||||
| $receipt->receiptName = $this->getReceiptName($payments); | |||||
| $receipt->customerCode = $customer->customerCode; | |||||
| $receipt->taxType = "内税"; | |||||
| $receipt->receiptCustomerName = $customer->customerName; | |||||
| $receipt->receiptTotalAmount = 0; | |||||
| // 明細------------ | |||||
| foreach ($payments as $payment) { | |||||
| $detail = new ReceiptDetail(); | |||||
| $detail->name = $payment->paymentType; | |||||
| $detail->parkingName = $payment->parkingName; | |||||
| $detail->unitPrice = $payment->appropriationAmount; | |||||
| $detail->quantity = 1; | |||||
| $detail->amount = $detail->unitPrice * $detail->quantity; | |||||
| $detail->targetMonth = $payment->targetMonth ? sprintf("%d月分", $payment->targetMonth) : ""; | |||||
| $detail->taxRate = config("business.taxRate.normal", 0); | |||||
| $detail->memo = "memo"; | |||||
| $receipt->receiptDetail->push($detail); | |||||
| $receipt->receiptTotalAmount += $payment->appropriationAmount; | |||||
| } | |||||
| // 内税明細-------------- | |||||
| foreach ($receipt->receiptDetail as $detail) { | |||||
| $tax = $receipt->taxDetail->first(function (TaxDetail $ele) use ($detail) { | |||||
| return $ele->taxRate === $detail->taxRate; | |||||
| }); | |||||
| if ($tax === null) { | |||||
| $tax = new TaxDetail(); | |||||
| $tax->taxRate = $detail->taxRate; | |||||
| $receipt->taxDetail->push($tax); | |||||
| } | |||||
| if ($tax instanceof TaxDetail) { | |||||
| $tax->totalAmount += $detail->amount; | |||||
| } | |||||
| } | |||||
| // 税率の昇順に並び替え | |||||
| $receipt->taxDetail->sort(function (TaxDetail $a, TaxDetail $b) { | |||||
| return $a->taxRate < $b->taxRate ? -1 : 1; | |||||
| }); | |||||
| // 内税計算 | |||||
| foreach ($receipt->taxDetail as $detail) { | |||||
| $detail->taxAmount = TaxUtil::calcInnerTaxAmount($detail->totalAmount, $detail->taxRate); | |||||
| } | |||||
| // 入金実績--------------- | |||||
| foreach ($payments as $payment) { | |||||
| $detail = new ReceiptPaymentPlan(); | |||||
| $detail->recordNo = $payment->getRecordId(); | |||||
| $receipt->paymentPlans->push($detail); | |||||
| } | |||||
| $receipt->save(); | |||||
| return $this; | |||||
| } | |||||
| private function getPayments(array $paymentPlanRecordNoList) | |||||
| { | |||||
| $query = PaymentPlan::getQuery()->whereIn(PaymentPlan::FIELD_RECORD_NO, $paymentPlanRecordNoList); | |||||
| $ret = PaymentPlan::getAccess()->all($query); | |||||
| return $ret; | |||||
| } | |||||
| /** | |||||
| * @param Collection<int, PaymentPlan> $payments | |||||
| * @return void | |||||
| */ | |||||
| private function checkPayments(Collection $payments, int $expectCount) | |||||
| { | |||||
| // データチェック | |||||
| if ($payments->isEmpty()) { | |||||
| throw new AppCommonException("支払予定検索0件"); | |||||
| } | |||||
| if ($payments->count() !== $expectCount) { | |||||
| throw new AppCommonException(sprintf("件数不正 %d件 期待値:%d件 ", $payments->count(), $expectCount)); | |||||
| } | |||||
| $customerCode = $payments->first()->customerCode; | |||||
| foreach ($payments as $payment) { | |||||
| if (!$payment->donePayment()) { | |||||
| throw new AppCommonException(sprintf("支払済みでない支払 %d ", $payment->getRecordId())); | |||||
| } | |||||
| if ($payment->customerCode !== $customerCode) { | |||||
| throw new AppCommonException(sprintf("単一の顧客コードのみ設定可能 %d or %d", $customerCode, $payment->customerCode)); | |||||
| } | |||||
| } | |||||
| } | |||||
| /** | |||||
| * @param Collection<int, PaymentPlan> $payments | |||||
| * @return void | |||||
| */ | |||||
| private function getReceiptName(Collection $payments): string | |||||
| { | |||||
| $seasonTicketCount = 0; | |||||
| $otherCount = 0; | |||||
| $otherName = ""; | |||||
| foreach ($payments as $payment) { | |||||
| if ($payment->paymentType === PaymentType::SEASON_TICKET) { | |||||
| $seasonTicketCount++; | |||||
| } else { | |||||
| $otherCount++; | |||||
| $otherName = $payment->paymentType; | |||||
| } | |||||
| } | |||||
| if (0 < $seasonTicketCount && $otherCount === 0) { | |||||
| return "定期料金"; | |||||
| } | |||||
| if (0 < $seasonTicketCount && 0 < $otherCount) { | |||||
| return "定期料金ほか"; | |||||
| } | |||||
| return $otherName; | |||||
| } | } | ||||
| private function load(int $recordNo) | |||||
| { | |||||
| $this->receipt = Receipt::find($recordNo); | |||||
| } | |||||
| public function makePdf(): ReceiptReceipt | public function makePdf(): ReceiptReceipt | ||||
| { | { | ||||
| $pdf = PDF::loadView("pdf/receipt", $this->getPdfData()) | $pdf = PDF::loadView("pdf/receipt", $this->getPdfData()) | ||||
| @@ -67,4 +216,22 @@ class ReceiptManager | |||||
| 'receiptPurpose' => $this->receipt->receiptPurpose, | 'receiptPurpose' => $this->receipt->receiptPurpose, | ||||
| ]; | ]; | ||||
| } | } | ||||
| private function getReceiptNo(): string | |||||
| { | |||||
| /** | |||||
| * 申請番号を発番する。重複チェックを一定回数行う。 | |||||
| */ | |||||
| for ($i = 0; $i < 10; $i++) { | |||||
| $no = sprintf("%s-R%06d", DateUtil::now()->format('Ymd'), rand(1, 999999)); | |||||
| $check = Receipt::getAccess()->some(Receipt::getQuery()->where(Receipt::FIELD_RECEIPT_NO, $no)); | |||||
| if ($check->isEmpty()) { | |||||
| return $no; | |||||
| } | |||||
| } | |||||
| throw new AppCommonException('申請番号取得失敗'); | |||||
| } | |||||
| } | } | ||||
| @@ -0,0 +1,13 @@ | |||||
| <?php | |||||
| namespace App\Util; | |||||
| class TaxUtil | |||||
| { | |||||
| public static function calcInnerTaxAmount(int $amount, int $rate): int | |||||
| { | |||||
| $taxRate = $rate / 100; | |||||
| return intval(floor($amount / (1 + $taxRate) * $taxRate)); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,32 @@ | |||||
| <?php | |||||
| use App\Kintone\KintoneConfiguration; | |||||
| return [ | |||||
| /* | |||||
| |-------------------------------------------------------------------------- | |||||
| | 税率 | |||||
| |-------------------------------------------------------------------------- | |||||
| | | |||||
| | | |||||
| */ | |||||
| 'taxRate' => [ | |||||
| 'normal' => 10, | |||||
| 'reduce' => 8, | |||||
| ], | |||||
| /* | |||||
| |-------------------------------------------------------------------------- | |||||
| | インボイス番号 | |||||
| |-------------------------------------------------------------------------- | |||||
| | | |||||
| | | |||||
| */ | |||||
| 'invoiceNo' => 'T7130005012806', | |||||
| ]; | |||||
| @@ -15,6 +15,7 @@ use App\Util\RouteHelper; | |||||
| RouteHelper::post('/email/send', App\Http\Controllers\Web\Email\EmailSendController::class); | RouteHelper::post('/email/send', App\Http\Controllers\Web\Email\EmailSendController::class); | ||||
| RouteHelper::post('/season-ticket-contract-selection/notice-to-candidates', App\Http\Controllers\Web\SeasonTicketContract\Selection\NoticeToCandidatesController::class); | RouteHelper::post('/season-ticket-contract-selection/notice-to-candidates', App\Http\Controllers\Web\SeasonTicketContract\Selection\NoticeToCandidatesController::class); | ||||
| RouteHelper::post('/receipt/create', App\Http\Controllers\Web\Receipt\ReceiptCreateController::class); | |||||
| // ------FOR WEBHOOK------------------- | // ------FOR WEBHOOK------------------- | ||||