diff --git a/app/Files/PDF/PDFFile.php b/app/Files/PDF/PDFFile.php new file mode 100644 index 0000000..a118fed --- /dev/null +++ b/app/Files/PDF/PDFFile.php @@ -0,0 +1,47 @@ +appFileName; + } + + public function setAppFileName(string $fileName) + { + $this->appFileName = $fileName; + return $this; + } +} diff --git a/app/Files/PDF/Receipt.php b/app/Files/PDF/Receipt.php new file mode 100644 index 0000000..f5d371a --- /dev/null +++ b/app/Files/PDF/Receipt.php @@ -0,0 +1,21 @@ +getFileTypeName(), $this->uuid, $this->getFileExtension()); } + public function getAppFileName() + { + return $this->getFileName(); + } + public function getId(): string { return $this->uuid; @@ -93,16 +105,25 @@ class TmpFile public function put(string $content) { Storage::put($this->getPath(), $content); + $this->content = $content; + return $this; } - public function get() + public function load() { - return Storage::get($this->getPath()); + $this->content = Storage::get($this->getPath()); + return $this; } public function append(string $content) { Storage::append($this->getPath(), $content); + $this->content .= $content; + return $this; + } + public function get(): string + { + return $this->content; } public function download(string $name = "download") @@ -122,6 +143,7 @@ class TmpFile if ($ret) info(sprintf("ファイル削除:%s ", $this->getFullPath())); return; } else { + $this->content = ""; DeleteFile::dispatch($this) ->delay($delay); return; diff --git a/app/Kintone/KintoneAccess.php b/app/Kintone/KintoneAccess.php index 9c3e860..21a3710 100644 --- a/app/Kintone/KintoneAccess.php +++ b/app/Kintone/KintoneAccess.php @@ -4,6 +4,7 @@ namespace App\Kintone; use App\Exceptions\AppCommonException; use App\Exceptions\ConfigException; +use App\Files\TmpFile; use App\Kintone\Models\KintoneModel; use Exception; use Illuminate\Database\Eloquent\ModelNotFoundException; @@ -403,16 +404,25 @@ class KintoneAccess /** * ファイルアップロード * - * @param UploadedFile $file + * @param UploadedFile|TmpFile $file * @return string fileKey */ - public function filePut(UploadedFile $file): string + public function filePut(UploadedFile|TmpFile $file): string { - $content = file_get_contents($file); + $content = ""; + $sendFileName = ""; + if ($file instanceof UploadedFile) { + $content = file_get_contents($file); + $sendFileName = sprintf("file.%s", $file->extension()); + } else if ($file instanceof TmpFile) { + $content = $file->get(); + $sendFileName = $file->getAppFileName(); + } + $response = Http::withHeaders([ "X-Cybozu-API-Token" => $this->apiToken, ]) - ->attach("file", $content, sprintf("file.%s", $file->extension())) + ->attach("file", $content, $sendFileName) ->post($this->getFileUrl()); if ($response->failed()) { diff --git a/app/Kintone/Models/KintoneModel.php b/app/Kintone/Models/KintoneModel.php index 9f675ed..90ba194 100644 --- a/app/Kintone/Models/KintoneModel.php +++ b/app/Kintone/Models/KintoneModel.php @@ -2,15 +2,18 @@ namespace App\Kintone\Models; -use App\Exceptions\AppCommonException; use App\Exceptions\ConfigException; +use App\Files\TmpFile; use App\Kintone\File; use App\Kintone\KintoneAccess; use App\Kintone\KintoneAccessStore; use App\Kintone\KintoneRecordQuery; +use App\Kintone\Models\SubTable\SubTableData; use App\Util\DateUtil; +use Illuminate\Http\UploadedFile; use Illuminate\Support\Arr; use Illuminate\Support\Carbon; +use Illuminate\Support\Collection; use Illuminate\Support\Str; use LogicException; use stdClass; @@ -89,6 +92,13 @@ abstract class KintoneModel { return static::getAccess()->find($recordId); } + /** + * @return static + */ + public static function first(?KintoneRecordQuery $query = null) + { + return static::getAccess()->first($query); + } public static function getDropDownOptions(string $fieldCode): array { @@ -112,8 +122,12 @@ abstract class KintoneModel protected ?stdClass $dataOrigin = null; protected stdClass $data; + const FIELD_RECORD_NO = "レコード番号"; + protected const FIELDS = []; + protected const SUB_TABLES = []; + protected const FIELD_NAMES = []; protected const RELATIONS = []; @@ -148,36 +162,15 @@ abstract class KintoneModel public function set(string $fieldCode, $value) { - $field = Arr::get(static::FIELDS, $fieldCode); - if ($field instanceof FieldType) { - $this->setData($fieldCode, $field, $value); - } else if (is_array($field)) { - $this->setTable($fieldCode, $value); + $type = $this->getFieldType($fieldCode); + if ($type) { + $this->setData($fieldCode, $value); + data_set($this->changed, $fieldCode, true); } - - data_set($this->changed, $fieldCode, true); return $this; } - private function setTable(string $fieldCode, array $table) - { - foreach ($table as $index => $row) { - $row = data_get($row, "value"); - $this->setTableRow($fieldCode, $index, $row); - } - } - - private function setTableRow(string $fieldCode, int $index, array $row) - { - foreach ($row as $columnFieldCode => $column) { - $value = $column["value"]; - $type = static::FIELDS[$fieldCode][$columnFieldCode]; - $insertKey = sprintf("%s.%d.%s", $fieldCode, $index, $columnFieldCode); - $this->setData($insertKey, $type, $value); - } - } - - private function setData(string $path, FieldType $type, $value) + private function setData(string $path, $value) { data_set($this->data, $path, $value); @@ -207,43 +200,73 @@ abstract class KintoneModel continue; } - $type = FieldType::tryFrom($type); - if ($type === null) continue; + data_set($this->data, $fieldCode, $this->readData($ele, $fieldCode)); + } + + if ($this->recordId === null) { + throw new LogicException(sprintf("レコード番号取得失敗 :%s", static::class)); + } + + $ret = $this->setDataCustom($data); + if ($ret) { + $this->clean(); + } + return $ret; + } + private function readData(array $data, string $fieldCode = "") + { + $type = data_get($data, "type"); + $value = data_get($data, "value"); + if ($type === null || $value === null) { + return null; + } + + $type = FieldType::tryFrom($type); + if ($type !== null) { if (in_array($type, [FieldType::DATETIME, FieldType::DATE])) { if ($value) { - data_set($this->data, $fieldCode, DateUtil::parse($value)); + return DateUtil::parse($value); } else { - data_set($this->data, $fieldCode, null); + return null; } - continue; + } + if ($type === FieldType::NUMBER) { + return intval($value); } if ($type === FieldType::FILE) { $ret = []; foreach ($value as $f) { $ret[] = new File($f); } - data_set($this->data, $fieldCode, $ret); - continue; + return $ret; } - if ($type === FieldType::SUBTABLE) { - continue; + if ($type === FieldType::SUBTABLE && $this->getFieldType($fieldCode)) { + $ret = collect(); + foreach ($value as $v) { + $rowRet = + $rowArray = data_get($v, 'value'); + foreach ($rowArray as $subFieldCode => $ele) { + $rowRet[$subFieldCode] = $this->readData($ele); + } + $ret->push($this->getSubTableData($fieldCode, $rowRet)); + } + return $ret; } - - // 以外はそのまま格納 - data_set($this->data, $fieldCode, $value); + return $value; } - if ($this->recordId === null) { - throw new LogicException(sprintf("レコード番号取得失敗 :%s", static::class)); - } + return null; + } - $ret = $this->setDataCustom($data); - if ($ret) { - $this->clean(); + private function getSubTableData(string $fieldCode, array $data): SubTableData + { + $className = data_get(static::SUB_TABLES, $fieldCode, ""); + if (class_exists($className)) { + return new $className($data); } - return $ret; + throw new LogicException("キントーン設定不備"); } @@ -273,28 +296,54 @@ abstract class KintoneModel } $path = sprintf("%s.value", $fieldCode); - if ($type === FieldType::DATETIME) { - $data = $this->getDate($fieldCode); - if ($data) { - data_set($ret, $path, $data->toIso8601ZuluString()); - } else { - data_set($ret, $path, ""); - } - continue; + data_set($ret, $path, $this->getApiLayoutValue($fieldCode)); + } + + return array_merge($ret, $this->getApiLayoutCustom()); + } + + private function getApiLayoutValue(string $fieldCode) + { + + $ret = []; + $data = $this->get($fieldCode); + + $type = $this->getFieldType($fieldCode); + + if ($type === FieldType::DATETIME) { + $data = $this->getDate($fieldCode); + if ($data) { + return $data->toIso8601ZuluString(); + } else { + return ""; } - if ($type === FieldType::DATE) { - $data = $this->getDate($fieldCode); - if ($data) { - data_set($ret, $path, $data->toDateString()); - } else { - data_set($ret, $path, ""); + } + if ($type === FieldType::DATE) { + $data = $this->getDate($fieldCode); + if ($data) { + return $data->toDateString(); + } else { + return ""; + } + } + + if ($data instanceof Collection) { + $ret = []; + foreach ($data as $ele) { + if ($ele instanceof SubTableData) { + $obj = []; + $obj['value'] = $ele->toArray(); + $ret[] = $obj; } - continue; } - data_set($ret, $path, data_get($this->data, $fieldCode)); + return $ret; } + return data_get($this->data, $fieldCode); + } - return array_merge($ret, $this->getApiLayoutCustom()); + private function getFieldType(string $fieldCode): ?FieldType + { + return data_get(static::FIELDS, $fieldCode); } public function get(string $key) @@ -346,6 +395,46 @@ abstract class KintoneModel return $this->createdAt; } + /** + * @param string $fieldCode + * @param Collection $files + * @param bool $override + * @return static + */ + public function setFiles(string $fieldCode, Collection $files, bool $override = true): static + { + if ($override) { + $this->set($fieldCode, []); + } + foreach ($files as $file) { + $this->addFile($fieldCode, $file); + } + return $this; + } + + public function addFile(string $fieldCode, UploadedFile|TmpFile $file) + { + if ($file instanceof UploadedFile) { + $name = sprintf("image_%d.%s", Str::uuid(), $file->extension()); + $contentType = $file->getClientMimeType(); + } else { + $name = $file->getAppFileName(); + $contentType = $file->getMimeType(); + } + $access = $this->getAccess(); + + $field = $this->get($fieldCode); + + array_push($field, [ + 'fileKey' => $access->filePut($file), + 'name' => $name, + 'contentType' => $contentType, + ]); + + $this->set($fieldCode, $field); + return $this; + } + public function save() { $access = static::getAccess(); diff --git a/app/Kintone/Models/Receipt.php b/app/Kintone/Models/Receipt.php new file mode 100644 index 0000000..eb9170d --- /dev/null +++ b/app/Kintone/Models/Receipt.php @@ -0,0 +1,51 @@ + FieldType::SINGLE_LINE_TEXT, + self::FIELD_CUSTOMER_CODE => FieldType::NUMBER, + self::FIELD_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_PDF_FILE => FieldType::FILE, + ]; + + protected const FIELD_NAMES = [ + ...parent::FIELD_NAMES, + ]; + + protected const RELATIONS = [ + Customer::class, + PaymentPlan::class, + ]; +} diff --git a/app/Kintone/Models/SubTable/SubTableData.php b/app/Kintone/Models/SubTable/SubTableData.php new file mode 100644 index 0000000..30cf80c --- /dev/null +++ b/app/Kintone/Models/SubTable/SubTableData.php @@ -0,0 +1,12 @@ +load($recordNo); + } else { + $this->receipt = new Receipt(); + } + } + + private function load(int $recordNo) + { + $this->receipt = Receipt::find($recordNo); + } + + public function getReceipt(): Receipt + { + return $this->receipt; + } + + public function makePdf(): ReceiptReceipt + { + $pdf = PDF::loadView("pdf/receipt", $this->getPdfData()) + // ->setPaper("A4") + ->setOption('page-width', 210) + ->setOption('page-height', 148) + ->setOrientation("Portrait") + ->setOption('encoding', 'utf-8'); + + + $file = new ReceiptReceipt(); + $file->setAppFileName($this->makeFileName($file)) + ->put($pdf->output()); + + return $file; + } + + private function makeFileName(ReceiptReceipt $file) + { + return sprintf( + "領収証_%s_%s_%s.%s", + $this->receipt->receiptNo, + $this->receipt->customerName, + $this->receipt->receiptPurpose, + $file->getFileExtension(), + ); + } + + private function getPdfData() + { + return [ + 'receiptDate' => "2023/10/17", + 'receiptCustomerName' => $this->receipt->receiptCustomerName, + 'receiptTotalAmount' => number_format($this->receipt->receiptTotalAmount), + 'taxTotalAmount' => number_format(100), + 'receiptPurpose' => $this->receipt->receiptPurpose, + ]; + } +} diff --git a/resources/views/pdf/receipt.blade.php b/resources/views/pdf/receipt.blade.php new file mode 100644 index 0000000..9f5f2ff --- /dev/null +++ b/resources/views/pdf/receipt.blade.php @@ -0,0 +1,34 @@ + + + + + + 領収証 + + + + +
+ 領収証 +
+
{{ $receiptCustomerName }}様
+
領収日 {{ $receiptDate }}
+
金額{{ $receiptTotalAmount }}(税込)
+
税率10% ¥{{ $taxTotalAmount }}
+
+

+ 但し {{ $receiptPurpose }} として +

+

+ 上記正に領収いたしました +

+
+
+

一般財団法人 京都市都市整備公社

+

〒600-8421

+

京都市下京区小道通烏丸西入童侍者町167番

+

登録番号 T7 1300 0501 2806

+
+ + + \ No newline at end of file diff --git a/resources/views/pdf/receipt.css b/resources/views/pdf/receipt.css new file mode 100644 index 0000000..a0a82e8 --- /dev/null +++ b/resources/views/pdf/receipt.css @@ -0,0 +1,67 @@ + +body { + position: relative; + font-size: 5mm +} + +body div { + position: absolute; +} + +#title { + top : 10mm; + left: 100mm; + font-size: 10mm; + font-weight: bold; + letter-spacing: 10mm; +} + +.receiptCustomerName { + top : 30mm; +} +#receiptCustomerName { + left: 100mm; +} +#receiptDate { + left: 150mm; +} + +#receiptTotalAmount { + border-top: solid 1px; + border-bottom: solid 1px; + padding-top: 2mm;; + padding-bottom: 2mm; + + top: 50mm; + left: 20mm; + width: 170mm; +} + +#taxTotalAmount { + border-bottom: solid 1px; + padding-bottom: 2mm; + + top: 65mm; + left: 140mm; + width: 50mm; +} + +#receiptPurpose { + top: 70mm; + left: 20mm; +} + +#company { + top: 130mm; + left: 150mm; + font-size: 4mm; +} +#company p { + margin: 1mm; +} +#company .name { + font-size: 5mm; +} + + +