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

408 lines
13KB

  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=} {--current}";
  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. if ($this->option('current')) {
  90. // Currentの場合は当月を対象とする
  91. $this->targetDate = DateUtil::now()->setDay(1)->setTime(0, 0);
  92. $this->targetYYYYMM = $this->targetDate->format('Ym');
  93. return;
  94. } else {
  95. // デフォルトは先月とする
  96. $this->targetDate = DateUtil::now()->addMonth(-1)->setDay(1)->setTime(0, 0);
  97. $this->targetYYYYMM = $this->targetDate->format('Ym');
  98. return;
  99. }
  100. }
  101. // フォーマットチェック
  102. if (!preg_match("/^\d{6}$/", $yyyyMM)) {
  103. throw new Exception('YYYYMM不正');
  104. }
  105. // 日付チェック
  106. $tmpDate = Carbon::createFromFormat('Ym', $yyyyMM);
  107. if (!$tmpDate->isValid()) {
  108. throw new Exception('YYYYMM 日付不正');
  109. }
  110. $this->targetDate = $tmpDate->setDay(1)->setTime(0, 0);;
  111. $this->targetYYYYMM = $this->targetDate->format('Ym');;
  112. }
  113. private function getModel(string $contractId): UseSummary
  114. {
  115. $model = UseSummary::whereContractId($contractId)
  116. ->whereSummaryYyyymm($this->targetYYYYMM)
  117. ->firstOrNew([
  118. UseSummary::COL_NAME_CONTRACT_ID => $contractId,
  119. UseSummary::COL_NAME_SUMMARY_YYYYMM => $this->targetYYYYMM,
  120. ]);
  121. $currentYYYYMM = intval(DateUtil::now()->format('Ym'));
  122. $targetYYYYMM = intval($this->targetYYYYMM);
  123. if ($targetYYYYMM < $currentYYYYMM) {
  124. $model->is_fixed = true;
  125. }
  126. // 初期設定
  127. $model->receipt_order_count = 0;
  128. $model->mail_order_count = 0;
  129. $model->sms_send_count = 0;
  130. $model->sms_send_cost = 0;
  131. return $model;
  132. }
  133. /**
  134. * Undocumented function
  135. *
  136. * @param UseSummary $model
  137. * @return Collection<string,UseByKeySummary>
  138. */
  139. private function summary(UseSummary $model): Collection
  140. {
  141. $from = $this->targetDate->clone()->setTime(0, 0);
  142. $to = $this->targetDate->clone()->addMonth()->setTime(0, 0);
  143. /**
  144. * @var Collection<string,UseByKeySummary>
  145. */
  146. $summaryByKeys = collect();
  147. $makeKey = function (?string $key1, ?string $key2) {
  148. return sprintf("%s-%s", $key1 ?? "NULL", $key2 ?? "NULL");
  149. };
  150. $targetOrders = ReceiptIssuingOrder::getBuilder()
  151. ->where(ReceiptIssuingOrder::COL_NAME_CONTRACT_ID, $model->contract_id)
  152. ->where(ReceiptIssuingOrder::COL_NAME_ORDER_DATETIME, ">=", $from)
  153. ->where(ReceiptIssuingOrder::COL_NAME_ORDER_DATETIME, "<", $to);
  154. $targetMailOrders = ReceiptIssuingOrder::getBuilder()
  155. ->where(ReceiptIssuingOrder::COL_NAME_CONTRACT_ID, $model->contract_id)
  156. ->whereNotNull(ReceiptIssuingOrder::COL_NAME_STATUS_ORDER_MAIL_DATETIME)
  157. ->where(ReceiptIssuingOrder::COL_NAME_STATUS_ORDER_MAIL_DATETIME, ">=", $from)
  158. ->where(ReceiptIssuingOrder::COL_NAME_STATUS_ORDER_MAIL_DATETIME, "<", $to);
  159. // 領収証発行依頼件数の取得
  160. $receiptIssuingOrders = ReceiptIssuingOrder::getBuilder()
  161. ->where(ReceiptIssuingOrder::COL_NAME_CONTRACT_ID, $model->contract_id)
  162. ->where(ReceiptIssuingOrder::COL_NAME_ORDER_DATETIME, ">=", $from)
  163. ->where(ReceiptIssuingOrder::COL_NAME_ORDER_DATETIME, "<", $to)
  164. ->groupBy(
  165. ReceiptIssuingOrder::COL_NAME_CONTRACT_ID,
  166. ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY1,
  167. ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY2,
  168. )
  169. ->select([
  170. ReceiptIssuingOrder::COL_NAME_CONTRACT_ID,
  171. ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY1,
  172. ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY2,
  173. DB::raw(sprintf("count(*) as count")),
  174. ])
  175. ->get();
  176. foreach ($receiptIssuingOrders as $ele) {
  177. $key1 = data_get($ele, ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY1);
  178. $key2 = data_get($ele, ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY2);
  179. $count = data_get($ele, 'count');
  180. $model->receipt_order_count += $count;
  181. $key = $makeKey($key1, $key2);
  182. $summaryByKey = $this->getUseByKeyModel($model, $key1, $key2);
  183. $summaryByKey->receipt_order_count += $count;
  184. $summaryByKeys->put($key, $summaryByKey);
  185. }
  186. // 郵送依頼件数の取得
  187. $mailOrders = ReceiptIssuingOrder::getBuilder()
  188. ->where(ReceiptIssuingOrder::COL_NAME_CONTRACT_ID, $model->contract_id)
  189. ->whereNotNull(ReceiptIssuingOrder::COL_NAME_STATUS_ORDER_MAIL_DATETIME)
  190. ->where(ReceiptIssuingOrder::COL_NAME_STATUS_ORDER_MAIL_DATETIME, ">=", $from)
  191. ->where(ReceiptIssuingOrder::COL_NAME_STATUS_ORDER_MAIL_DATETIME, "<", $to)
  192. ->groupBy(
  193. ReceiptIssuingOrder::COL_NAME_CONTRACT_ID,
  194. ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY1,
  195. ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY2,
  196. )
  197. ->select([
  198. ReceiptIssuingOrder::COL_NAME_CONTRACT_ID,
  199. ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY1,
  200. ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY2,
  201. DB::raw(sprintf("count(*) as count")),
  202. ])
  203. ->get();
  204. foreach ($mailOrders as $ele) {
  205. $key1 = data_get($ele, ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY1);
  206. $key2 = data_get($ele, ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY2);
  207. $count = data_get($ele, 'count');
  208. $model->mail_order_count += $count;
  209. $key = $makeKey($key1, $key2);
  210. $summaryByKey = $this->getUseByKeyModel($model, $key1, $key2);
  211. $summaryByKey->mail_order_count += $count;
  212. $summaryByKeys->put($key, $summaryByKey);
  213. }
  214. // SMS送信実績の取得
  215. $SMSs = SMSSendOrder::getBuilder()
  216. ->where(SMSSendOrder::COL_NAME_CONTRACT_ID, $model->contract_id)
  217. ->whereNotNull(SMSSendOrder::COL_NAME_SEND_DATETIME)
  218. ->where(SMSSendOrder::COL_NAME_SEND_DATETIME, ">=", $from)
  219. ->where(SMSSendOrder::COL_NAME_SEND_DATETIME, "<", $to)
  220. ->where(SMSSendOrder::COL_NAME_DONE, true)
  221. ->groupBy(
  222. ReceiptIssuingOrder::COL_NAME_CONTRACT_ID,
  223. ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY1,
  224. ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY2
  225. )
  226. ->select([
  227. ReceiptIssuingOrder::COL_NAME_CONTRACT_ID,
  228. ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY1,
  229. ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY2,
  230. DB::raw(sprintf("count(*) as count")),
  231. DB::raw(sprintf("sum(%s) as cost", SMSSendOrder::COL_NAME_COST)),
  232. ])
  233. ->get();
  234. foreach ($SMSs as $ele) {
  235. $key1 = data_get($ele, SMSSendOrder::COL_NAME_SUMMARY_KEY1);
  236. $key2 = data_get($ele, SMSSendOrder::COL_NAME_SUMMARY_KEY2);
  237. $count = data_get($ele, 'count');
  238. $cost = data_get($ele, 'cost');
  239. $model->sms_send_count += $count;
  240. $model->sms_send_cost += $cost;
  241. $key = $makeKey($key1, $key2);
  242. $summaryByKey = $summaryByKeys->get($key);
  243. if (!$summaryByKey instanceof UseByKeySummary) {
  244. $summaryByKey = $this->getUseByKeyModel($model, $key1, $key2);
  245. $summaryByKeys->put($key, $summaryByKey);
  246. }
  247. $summaryByKey->fill([
  248. UseByKeySummary::COL_NAME_SMS_SEND_COUNT => $count,
  249. UseByKeySummary::COL_NAME_SMS_SEND_COST => $cost,
  250. ]);
  251. }
  252. return $summaryByKeys;
  253. }
  254. /**
  255. * @param UseSummary $model
  256. * @param Collection<UseByKeySummary> $summaryByKeys
  257. * @return void
  258. */
  259. private function save(UseSummary $model, Collection $summaryByKeys)
  260. {
  261. $contract = $model->contract;
  262. if (!$contract) {
  263. throw new Exception("契約不正");
  264. }
  265. $this->outputInfo(
  266. sprintf(
  267. "集計完了:%s 領収証発行依頼:%d件 郵送依頼:%d件 SMS送信件数:%d件 SMSコスト:%d円",
  268. $contract->name,
  269. $model->receipt_order_count,
  270. $model->mail_order_count,
  271. $model->sms_send_count,
  272. $model->sms_send_cost,
  273. )
  274. );
  275. $model->save();
  276. $deleteTargets = UseByKeySummary::whereContractId($model->contract_id)
  277. ->whereSummaryYyyymm($this->targetYYYYMM)
  278. ->get();
  279. foreach ($summaryByKeys as $summary) {
  280. if (!($summary instanceof UseByKeySummary)) {
  281. throw new LogicException("モデル不正");
  282. }
  283. $summary->save();
  284. $deleteTargets = $deleteTargets->reject(function ($ele) use ($summary) {
  285. return $ele->summary_key1 === $summary->summary_key1 && $ele->summary_key2 === $summary->summary_key2;
  286. });
  287. }
  288. foreach ($deleteTargets as $ele) {
  289. $ele->delete();
  290. }
  291. }
  292. private function getUseByKeyModel(UseSummary $model, ?string $key1, ?string $key2): UseByKeySummary
  293. {
  294. $query = UseByKeySummary::whereContractId($model->contract_id)
  295. ->whereSummaryYyyymm($this->targetYYYYMM);
  296. if ($key1 === null) {
  297. $query->whereNull(UseByKeySummary::COL_NAME_SUMMARY_KEY1);
  298. } else {
  299. $query->whereSummaryKey1($key1);
  300. }
  301. if ($key2 === null) {
  302. $query->whereNull(UseByKeySummary::COL_NAME_SUMMARY_KEY2);
  303. } else {
  304. $query->whereSummaryKey2($key2);
  305. }
  306. $summary = $query->firstOrNew([
  307. UseByKeySummary::COL_NAME_CONTRACT_ID => $model->contract_id,
  308. UseByKeySummary::COL_NAME_SUMMARY_YYYYMM => $this->targetYYYYMM,
  309. UseByKeySummary::COL_NAME_SUMMARY_KEY1 => $key1,
  310. UseByKeySummary::COL_NAME_SUMMARY_KEY2 => $key2,
  311. ]);
  312. $currentYYYYMM = intval(DateUtil::now()->format('Ym'));
  313. $targetYYYYMM = intval($this->targetYYYYMM);
  314. if ($targetYYYYMM < $currentYYYYMM) {
  315. $summary->is_fixed = true;
  316. }
  317. // 初期設定
  318. $summary->receipt_order_count = 0;
  319. $summary->mail_order_count = 0;
  320. $summary->sms_send_count = 0;
  321. $summary->sms_send_cost = 0;
  322. return $summary;
  323. }
  324. }