領収証発行サービス
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.

346 lines
10KB

  1. <?php
  2. namespace App\Console\Commands;
  3. use App\Logic\SMS\SMSManager;
  4. use App\Models\Contract;
  5. use App\Models\ReceiptIssuingOrder;
  6. use App\Models\SMSSendOrder;
  7. use App\Models\UseByKeySummary;
  8. use App\Models\UseSummary;
  9. use App\Util\DateUtil;
  10. use App\Util\DBUtil;
  11. use Exception;
  12. use Illuminate\Support\Carbon;
  13. use Illuminate\Support\Collection;
  14. use Illuminate\Support\Facades\DB;
  15. use LogicException;
  16. class SummaryUse extends BaseCommand
  17. {
  18. const COMMAND = "summary:use {--contractId=} {--yyyymm=}";
  19. /**
  20. * The name and signature of the console command.
  21. *
  22. * @var string
  23. */
  24. protected $signature = self::COMMAND;
  25. /**
  26. * The console command description.
  27. *
  28. * @var string
  29. */
  30. protected $description = '利用実績を集計する';
  31. static public function getCommand()
  32. {
  33. return self::COMMAND;
  34. }
  35. private string $targetYYYYMM = "";
  36. private ?Carbon $targetDate = null;
  37. /**
  38. * Create a new command instance.
  39. *
  40. * @return void
  41. */
  42. public function __construct(private SMSManager $manager)
  43. {
  44. parent::__construct();
  45. }
  46. /**
  47. * Execute the console command.
  48. *
  49. * @return int
  50. */
  51. public function service(): int
  52. {
  53. $this->setTargetYYYYMM();
  54. $this->outputInfo(sprintf("対象集計年月: %s", $this->targetYYYYMM));
  55. $db = DBUtil::instance();
  56. try {
  57. $db->beginTransaction();
  58. foreach ($this->getTargetContractIds() as $contractId) {
  59. $this->handleTarget($contractId);
  60. }
  61. $db->commit();
  62. } catch (Exception $e) {
  63. $db->rollBack();
  64. throw $e;
  65. }
  66. return self::RESULTCODE_SUCCESS;
  67. }
  68. private function handleTarget(string $contractId)
  69. {
  70. // 集計対象のモデルを取得
  71. $model = $this->getModel($contractId);
  72. // 集計
  73. $summaryByKeys = $this->summary($model);
  74. // 保存
  75. $this->save($model, $summaryByKeys);
  76. }
  77. private function getTargetContractIds(): array
  78. {
  79. if ($this->option('contractId')) {
  80. return [$this->option('contractId')];
  81. }
  82. return Contract::pluck(Contract::COL_NAME_ID)->toArray();
  83. }
  84. private function setTargetYYYYMM()
  85. {
  86. $yyyyMM = $this->option('yyyymm');
  87. // デフォルト判定
  88. if ($yyyyMM === null) {
  89. // デフォルトは先月とする
  90. $this->targetDate = DateUtil::now()->addMonth(-1)->setDay(1)->setTime(0, 0);
  91. $this->targetYYYYMM = $this->targetDate->format('Ym');
  92. return;
  93. }
  94. // フォーマットチェック
  95. if (!preg_match("/^\d{6}$/", $yyyyMM)) {
  96. throw new Exception('YYYYMM不正');
  97. }
  98. // 日付チェック
  99. $tmpDate = Carbon::createFromFormat('Ym', $yyyyMM);
  100. if (!$tmpDate->isValid()) {
  101. throw new Exception('YYYYMM 日付不正');
  102. }
  103. $this->targetDate = $tmpDate->setDay(1)->setTime(0, 0);;
  104. $this->targetYYYYMM = $this->targetDate->format('Ym');;
  105. }
  106. private function getModel(string $contractId): UseSummary
  107. {
  108. $model = UseSummary::whereContractId($contractId)
  109. ->whereSummaryYyyymm($this->targetYYYYMM)
  110. ->firstOrNew([
  111. UseSummary::COL_NAME_CONTRACT_ID => $contractId,
  112. UseSummary::COL_NAME_SUMMARY_YYYYMM => $this->targetYYYYMM,
  113. ]);
  114. $currentYYYYMM = intval(DateUtil::now()->format('Ym'));
  115. $targetYYYYMM = intval($this->targetYYYYMM);
  116. if ($targetYYYYMM < $currentYYYYMM) {
  117. $model->is_fixed = true;
  118. }
  119. return $model;
  120. }
  121. /**
  122. * Undocumented function
  123. *
  124. * @param UseSummary $model
  125. * @return Collection<string,UseByKeySummary>
  126. */
  127. private function summary(UseSummary $model): Collection
  128. {
  129. $from = $this->targetDate->clone()->setTime(0, 0);
  130. $to = $this->targetDate->clone()->addMonth()->setTime(0, 0);
  131. /**
  132. * @var Collection<string,UseByKeySummary>
  133. */
  134. $summaryByKeys = collect();
  135. $makeKey = function (?string $key1, ?string $key2) {
  136. return sprintf("%s-%s", $key1 ?? "NULL", $key2 ?? "NULL");
  137. };
  138. $targetOrders = ReceiptIssuingOrder::getBuilder()
  139. ->where(ReceiptIssuingOrder::COL_NAME_CONTRACT_ID, $model->contract_id)
  140. ->where(ReceiptIssuingOrder::COL_NAME_ORDER_DATETIME, ">=", $from)
  141. ->where(ReceiptIssuingOrder::COL_NAME_ORDER_DATETIME, "<", $to);
  142. // 領収証発行依頼件数の取得
  143. $receiptIssuingOrders = DB::table($targetOrders, 'target_orders')
  144. ->groupBy(
  145. ReceiptIssuingOrder::COL_NAME_CONTRACT_ID,
  146. ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY1,
  147. ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY2,
  148. )
  149. ->select([
  150. ReceiptIssuingOrder::COL_NAME_CONTRACT_ID,
  151. ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY1,
  152. ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY2,
  153. DB::raw(sprintf("count(*) as count")),
  154. ])
  155. ->get();
  156. $model->receipt_order_count = 0;
  157. foreach ($receiptIssuingOrders as $ele) {
  158. $key1 = data_get($ele, ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY1);
  159. $key2 = data_get($ele, ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY2);
  160. $count = data_get($ele, 'count');
  161. $model->receipt_order_count += $count;
  162. $key = $makeKey($key1, $key2);
  163. $summaryByKey = $this->getUseByKeyModel($model, $key1, $key2);
  164. $summaryByKey->receipt_order_count += $count;
  165. $summaryByKeys->put($key, $summaryByKey);
  166. }
  167. // SMS送信実績の取得
  168. $SMSs = SMSSendOrder::getBuilder()
  169. ->where(SMSSendOrder::COL_NAME_CONTRACT_ID, $model->contract_id)
  170. ->whereNotNull(SMSSendOrder::COL_NAME_SEND_DATETIME)
  171. ->where(SMSSendOrder::COL_NAME_SEND_DATETIME, ">=", $from)
  172. ->where(SMSSendOrder::COL_NAME_SEND_DATETIME, "<", $to)
  173. ->where(SMSSendOrder::COL_NAME_DONE, true)
  174. ->groupBy(
  175. ReceiptIssuingOrder::COL_NAME_CONTRACT_ID,
  176. ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY1,
  177. ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY2
  178. )
  179. ->select([
  180. ReceiptIssuingOrder::COL_NAME_CONTRACT_ID,
  181. ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY1,
  182. ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY2,
  183. DB::raw(sprintf("count(*) as count")),
  184. DB::raw(sprintf("sum(%s) as cost", SMSSendOrder::COL_NAME_COST)),
  185. ])
  186. ->get();
  187. $model->sms_send_count = 0;
  188. $model->sms_send_cost = 0;
  189. foreach ($SMSs as $ele) {
  190. $key1 = data_get($ele, SMSSendOrder::COL_NAME_SUMMARY_KEY1);
  191. $key2 = data_get($ele, SMSSendOrder::COL_NAME_SUMMARY_KEY2);
  192. $count = data_get($ele, 'count');
  193. $cost = data_get($ele, 'cost');
  194. $model->sms_send_count += $count;
  195. $model->sms_send_cost += $cost;
  196. $key = $makeKey($key1, $key2);
  197. $summaryByKey = $summaryByKeys->get($key);
  198. if (!$summaryByKey instanceof UseByKeySummary) {
  199. $summaryByKey = $this->getUseByKeyModel($model, $key1, $key2);
  200. $summaryByKeys->put($key, $summaryByKey);
  201. }
  202. $summaryByKey->fill([
  203. UseByKeySummary::COL_NAME_SMS_SEND_COUNT => $count,
  204. UseByKeySummary::COL_NAME_SMS_SEND_COST => $cost,
  205. ]);
  206. }
  207. return $summaryByKeys;
  208. }
  209. /**
  210. * @param UseSummary $model
  211. * @param Collection<UseByKeySummary> $summaryByKeys
  212. * @return void
  213. */
  214. private function save(UseSummary $model, Collection $summaryByKeys)
  215. {
  216. $contract = $model->contract;
  217. if (!$contract) {
  218. throw new Exception("契約不正");
  219. }
  220. $this->outputInfo(
  221. sprintf(
  222. "集計完了:%s 領収証発行依頼:%d件 SMS送信件数:%d件 SMSコスト:%d円",
  223. $contract->name,
  224. $model->receipt_order_count,
  225. $model->sms_send_count,
  226. $model->sms_send_cost,
  227. )
  228. );
  229. $model->save();
  230. $deleteTargets = UseByKeySummary::whereContractId($model->contract_id)
  231. ->whereSummaryYyyymm($this->targetYYYYMM)
  232. ->get();
  233. foreach ($summaryByKeys as $summary) {
  234. if (!($summary instanceof UseByKeySummary)) {
  235. throw new LogicException("モデル不正");
  236. }
  237. $summary->save();
  238. $deleteTargets = $deleteTargets->reject(function ($ele) use ($summary) {
  239. return $ele->summary_key1 === $summary->summary_key1 && $ele->summary_key2 === $summary->summary_key2;
  240. });
  241. }
  242. foreach ($deleteTargets as $ele) {
  243. $ele->delete();
  244. }
  245. }
  246. private function getUseByKeyModel(UseSummary $model, ?string $key1, ?string $key2): UseByKeySummary
  247. {
  248. $query = UseByKeySummary::whereContractId($model->contract_id)
  249. ->whereSummaryYyyymm($this->targetYYYYMM);
  250. if ($key1 === null) {
  251. $query->whereNull(UseByKeySummary::COL_NAME_SUMMARY_KEY1);
  252. } else {
  253. $query->whereSummaryKey1($key1);
  254. }
  255. if ($key2 === null) {
  256. $query->whereNull(UseByKeySummary::COL_NAME_SUMMARY_KEY2);
  257. } else {
  258. $query->whereSummaryKey2($key2);
  259. }
  260. $summary = $query->firstOrNew([
  261. UseByKeySummary::COL_NAME_CONTRACT_ID => $model->contract_id,
  262. UseByKeySummary::COL_NAME_SUMMARY_YYYYMM => $this->targetYYYYMM,
  263. UseByKeySummary::COL_NAME_SUMMARY_KEY1 => $key1,
  264. UseByKeySummary::COL_NAME_SUMMARY_KEY2 => $key2,
  265. ]);
  266. $currentYYYYMM = intval(DateUtil::now()->format('Ym'));
  267. $targetYYYYMM = intval($this->targetYYYYMM);
  268. if ($targetYYYYMM < $currentYYYYMM) {
  269. $summary->is_fixed = true;
  270. }
  271. return $summary;
  272. }
  273. }