You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

264 lines
8.2KB

  1. <?php
  2. namespace App\Logic;
  3. use App\Exceptions\AppCommonException;
  4. use App\Exceptions\GeneralErrorMessageException;
  5. use App\Files\PDF\Receipt as ReceiptReceipt;
  6. use App\Kintone\Models\Customer;
  7. use App\Kintone\Models\DropDown\PaymentPlan\PaymentType;
  8. use App\Kintone\Models\PaymentPlan;
  9. use App\Kintone\Models\Receipt;
  10. use App\Kintone\Models\SubTable\Receipt\PaymentPlan as ReceiptPaymentPlan;
  11. use App\Kintone\Models\SubTable\Receipt\ReceiptDetail;
  12. use App\Kintone\Models\SubTable\Receipt\TaxDetail;
  13. use App\Util\DateUtil;
  14. use App\Util\TaxUtil;
  15. use Illuminate\Support\Collection;
  16. use PDF;
  17. class ReceiptManager
  18. {
  19. private ?Receipt $receipt = null;
  20. public function __construct(?int $recordNo = null)
  21. {
  22. if ($recordNo) {
  23. $this->load($recordNo);
  24. } else {
  25. $this->receipt = new Receipt();
  26. }
  27. }
  28. public function getReceipt(): Receipt
  29. {
  30. return $this->receipt;
  31. }
  32. public function create(array $paymentPlanRecordNoList)
  33. {
  34. // 支払予定データの取得
  35. $payments = $this->getPayments($paymentPlanRecordNoList);
  36. $this->checkPayments($payments, count($paymentPlanRecordNoList));
  37. // 顧客取得
  38. $customer = Customer::findByCustomerCode($payments->first()->customerCode);
  39. $receipt = $this->receipt;
  40. $receipt->receiptNo = $this->getReceiptNo();
  41. $receipt->receiptDate = DateUtil::now();
  42. $receipt->invoiceNo = config("business.invoiceNo", "");
  43. $receipt->receiptName = $this->getReceiptName($payments);
  44. $receipt->customerCode = $customer->customerCode;
  45. $receipt->taxType = "内税";
  46. $receipt->receiptCustomerName = $customer->customerName;
  47. $receipt->receiptTotalAmount = 0;
  48. // 明細------------
  49. foreach ($payments as $payment) {
  50. $detail = new ReceiptDetail();
  51. $detail->name = $payment->paymentType;
  52. $detail->parkingName = $payment->parkingName;
  53. $detail->unitPrice = $payment->appropriationAmount;
  54. $detail->quantity = 1;
  55. $detail->amount = $detail->unitPrice * $detail->quantity;
  56. $detail->targetMonth = $payment->targetMonth ? sprintf("%d月分", $payment->targetMonth) : "";
  57. $detail->taxRate = config("business.taxRate.normal", 0);
  58. $detail->memo = "memo";
  59. $receipt->receiptDetail->push($detail);
  60. $receipt->receiptTotalAmount += $payment->appropriationAmount;
  61. }
  62. // 内税明細--------------
  63. foreach ($receipt->receiptDetail as $detail) {
  64. $tax = $receipt->taxDetail->first(function (TaxDetail $ele) use ($detail) {
  65. return $ele->taxRate === $detail->taxRate;
  66. });
  67. if ($tax === null) {
  68. $tax = new TaxDetail();
  69. $tax->taxRate = $detail->taxRate;
  70. $receipt->taxDetail->push($tax);
  71. }
  72. if ($tax instanceof TaxDetail) {
  73. $tax->totalAmount += $detail->amount;
  74. }
  75. }
  76. // 税率の昇順に並び替え
  77. $receipt->taxDetail->sort(function (TaxDetail $a, TaxDetail $b) {
  78. return $a->taxRate < $b->taxRate ? -1 : 1;
  79. });
  80. // 内税計算
  81. foreach ($receipt->taxDetail as $detail) {
  82. $detail->taxAmount = TaxUtil::calcInnerTaxAmount($detail->totalAmount, $detail->taxRate);
  83. }
  84. // 入金実績---------------
  85. foreach ($payments as $payment) {
  86. $detail = new ReceiptPaymentPlan();
  87. $detail->recordNo = $payment->getRecordId();
  88. $receipt->paymentPlans->push($detail);
  89. }
  90. $receipt->save();
  91. return $this;
  92. }
  93. private function getPayments(array $paymentPlanRecordNoList)
  94. {
  95. $query = PaymentPlan::getQuery()->whereIn(PaymentPlan::FIELD_RECORD_NO, $paymentPlanRecordNoList);
  96. $ret = PaymentPlan::getAccess()->all($query);
  97. return $ret;
  98. }
  99. /**
  100. * @param Collection<int, PaymentPlan> $payments
  101. * @return void
  102. */
  103. private function checkPayments(Collection $payments, int $expectCount)
  104. {
  105. // データチェック
  106. if ($payments->isEmpty()) {
  107. throw new AppCommonException("支払予定検索0件");
  108. }
  109. if ($payments->count() !== $expectCount) {
  110. throw new AppCommonException(sprintf("件数不正 %d件 期待値:%d件 ", $payments->count(), $expectCount));
  111. }
  112. $customerCode = $payments->first()->customerCode;
  113. foreach ($payments as $payment) {
  114. if (!$payment->donePayment()) {
  115. throw new AppCommonException(sprintf("支払済みでない支払 %d ", $payment->getRecordId()));
  116. }
  117. if ($payment->customerCode !== $customerCode) {
  118. throw new AppCommonException(sprintf("単一の顧客コードのみ設定可能 %d or %d", $customerCode, $payment->customerCode));
  119. }
  120. }
  121. }
  122. /**
  123. * @param Collection<int, PaymentPlan> $payments
  124. * @return void
  125. */
  126. private function getReceiptName(Collection $payments): string
  127. {
  128. $seasonTicketCount = 0;
  129. $otherCount = 0;
  130. $otherName = "";
  131. foreach ($payments as $payment) {
  132. if ($payment->paymentType === PaymentType::SEASON_TICKET) {
  133. $seasonTicketCount++;
  134. } else {
  135. $otherCount++;
  136. $otherName = $payment->paymentType;
  137. }
  138. }
  139. if (0 < $seasonTicketCount && $otherCount === 0) {
  140. return "定期料金";
  141. }
  142. if (0 < $seasonTicketCount && 0 < $otherCount) {
  143. return "定期料金ほか";
  144. }
  145. return $otherName;
  146. }
  147. private function load(int $recordNo)
  148. {
  149. $this->receipt = Receipt::find($recordNo);
  150. }
  151. public function makePdf(): ReceiptReceipt
  152. {
  153. $pdf = PDF::loadView("pdf/receipt", $this->getPdfData())
  154. ->setPaper("A4")
  155. // ->setOption('page-width', 210)
  156. // ->setOption('page-height', 148)
  157. ->setOrientation("Portrait")
  158. ->setOption('encoding', 'utf-8');
  159. $file = new ReceiptReceipt();
  160. $file->setAppFileName($this->makeFileName($file))
  161. ->put($pdf->output());
  162. return $file;
  163. }
  164. public function getPdf()
  165. {
  166. $pdf = PDF::loadView("pdf/receipt", $this->getPdfData())
  167. ->setPaper("A4")
  168. // ->setOption('page-width', 210)
  169. // ->setOption('page-height', 148)
  170. ->setOrientation("Portrait")
  171. ->setOption('encoding', 'utf-8');
  172. return $pdf->inline();
  173. }
  174. public function getHtml()
  175. {
  176. return response()->view("pdf/receipt", ["forHtml" => true, ...$this->getPdfData()]);
  177. }
  178. private function makeFileName(ReceiptReceipt $file)
  179. {
  180. return sprintf(
  181. "領収証_%s_%s_%s.%s",
  182. $this->receipt->receiptNo,
  183. $this->receipt->customerName,
  184. $this->receipt->receiptPurpose,
  185. $file->getFileExtension(),
  186. );
  187. }
  188. private function getPdfData()
  189. {
  190. return [
  191. 'receiptDate' => $this->receipt->receiptDate->format('Y/m/d'),
  192. 'receiptCustomerName' => $this->receipt->receiptCustomerName,
  193. 'receiptTotalAmount' => number_format($this->receipt->receiptTotalAmount),
  194. 'taxTotalAmount' => $this->getTaxAmount10(),
  195. 'detail' => $this->receipt->receiptDetail,
  196. ];
  197. }
  198. private function getTaxAmount10(): int
  199. {
  200. $receipt = $this->receipt->taxDetail->first(function (TaxDetail $tax) {
  201. return $tax->taxRate === 10;
  202. });
  203. return $receipt ? $receipt->taxAmount : 0;
  204. }
  205. private function getReceiptNo(): string
  206. {
  207. /**
  208. * 申請番号を発番する。重複チェックを一定回数行う。
  209. */
  210. for ($i = 0; $i < 10; $i++) {
  211. $no = sprintf("%s-R%06d", DateUtil::now()->format('Ymd'), rand(1, 999999));
  212. $check = Receipt::getAccess()->some(Receipt::getQuery()->where(Receipt::FIELD_RECEIPT_NO, $no));
  213. if ($check->isEmpty()) {
  214. return $no;
  215. }
  216. }
  217. throw new AppCommonException('申請番号取得失敗');
  218. }
  219. }