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

344 line
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. $summaryByKeys->put($key, $summaryByKey);
  165. }
  166. // SMS送信実績の取得
  167. $SMSs = SMSSendOrder::getBuilder()
  168. ->where(SMSSendOrder::COL_NAME_CONTRACT_ID, $model->contract_id)
  169. ->whereNotNull(SMSSendOrder::COL_NAME_SEND_DATETIME)
  170. ->where(SMSSendOrder::COL_NAME_SEND_DATETIME, ">=", $from)
  171. ->where(SMSSendOrder::COL_NAME_SEND_DATETIME, "<", $to)
  172. ->where(SMSSendOrder::COL_NAME_DONE, true)
  173. ->groupBy(
  174. ReceiptIssuingOrder::COL_NAME_CONTRACT_ID,
  175. ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY1,
  176. ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY2
  177. )
  178. ->select([
  179. ReceiptIssuingOrder::COL_NAME_CONTRACT_ID,
  180. ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY1,
  181. ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY2,
  182. DB::raw(sprintf("count(*) as count")),
  183. DB::raw(sprintf("sum(%s) as cost", SMSSendOrder::COL_NAME_COST)),
  184. ])
  185. ->get();
  186. $model->sms_send_count = 0;
  187. $model->sms_send_cost = 0;
  188. foreach ($SMSs as $ele) {
  189. $key1 = data_get($ele, SMSSendOrder::COL_NAME_SUMMARY_KEY1);
  190. $key2 = data_get($ele, SMSSendOrder::COL_NAME_SUMMARY_KEY2);
  191. $count = data_get($ele, 'count');
  192. $cost = data_get($ele, 'cost');
  193. $model->sms_send_count += $count;
  194. $model->sms_send_cost += $cost;
  195. $key = $makeKey($key1, $key2);
  196. $summaryByKey = $summaryByKeys->get($key);
  197. if (!$summaryByKey instanceof UseByKeySummary) {
  198. $summaryByKey = $this->getUseByKeyModel($model, $key1, $key2);
  199. $summaryByKeys->put($key, $summaryByKey);
  200. }
  201. $summaryByKey->fill([
  202. UseByKeySummary::COL_NAME_SMS_SEND_COUNT => $count,
  203. UseByKeySummary::COL_NAME_SMS_SEND_COST => $cost,
  204. ]);
  205. }
  206. return $summaryByKeys;
  207. }
  208. /**
  209. * @param UseSummary $model
  210. * @param Collection<UseByKeySummary> $summaryByKeys
  211. * @return void
  212. */
  213. private function save(UseSummary $model, Collection $summaryByKeys)
  214. {
  215. $contract = $model->contract;
  216. if (!$contract) {
  217. throw new Exception("契約不正");
  218. }
  219. $this->outputInfo(
  220. sprintf(
  221. "集計完了:%s 領収証発行依頼:%d件 SMS送信件数:%d件 SMSコスト:%d円",
  222. $contract->name,
  223. $model->receipt_order_count,
  224. $model->sms_send_count,
  225. $model->sms_send_cost,
  226. )
  227. );
  228. $model->save();
  229. $deleteTargets = UseByKeySummary::whereContractId($model->contract_id)
  230. ->whereSummaryYyyymm($this->targetYYYYMM)
  231. ->get();
  232. foreach ($summaryByKeys as $summary) {
  233. if (!($summary instanceof UseByKeySummary)) {
  234. throw new LogicException("モデル不正");
  235. }
  236. $summary->save();
  237. $deleteTargets = $deleteTargets->reject(function ($ele) use ($summary) {
  238. return $ele->summary_key1 === $summary->summary_key1 && $ele->summary_key2 === $summary->summary_key2;
  239. });
  240. }
  241. foreach ($deleteTargets as $ele) {
  242. $ele->delete();
  243. }
  244. }
  245. private function getUseByKeyModel(UseSummary $model, ?string $key1, ?string $key2): UseByKeySummary
  246. {
  247. $query = UseByKeySummary::whereContractId($model->contract_id)
  248. ->whereSummaryYyyymm($this->targetYYYYMM);
  249. if ($key1 === null) {
  250. $query->whereNull(UseByKeySummary::COL_NAME_SUMMARY_KEY1);
  251. } else {
  252. $query->whereSummaryKey1($key1);
  253. }
  254. if ($key2 === null) {
  255. $query->whereNull(UseByKeySummary::COL_NAME_SUMMARY_KEY2);
  256. } else {
  257. $query->whereSummaryKey2($key2);
  258. }
  259. $summary = $query->firstOrNew([
  260. UseByKeySummary::COL_NAME_CONTRACT_ID => $model->contract_id,
  261. UseByKeySummary::COL_NAME_SUMMARY_YYYYMM => $this->targetYYYYMM,
  262. UseByKeySummary::COL_NAME_SUMMARY_KEY1 => $key1,
  263. UseByKeySummary::COL_NAME_SUMMARY_KEY2 => $key2,
  264. ]);
  265. $currentYYYYMM = intval(DateUtil::now()->format('Ym'));
  266. $targetYYYYMM = intval($this->targetYYYYMM);
  267. if ($targetYYYYMM < $currentYYYYMM) {
  268. $summary->is_fixed = true;
  269. }
  270. return $summary;
  271. }
  272. }