領収証発行サービス
No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.

399 líneas
12KB

  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. // 領収証発行依頼件数の取得
  151. $receiptIssuingOrders = ReceiptIssuingOrder::getBuilder()
  152. ->where(ReceiptIssuingOrder::COL_NAME_CONTRACT_ID, $model->contract_id)
  153. ->where(ReceiptIssuingOrder::COL_NAME_ORDER_DATETIME, ">=", $from)
  154. ->where(ReceiptIssuingOrder::COL_NAME_ORDER_DATETIME, "<", $to)
  155. ->groupBy(
  156. ReceiptIssuingOrder::COL_NAME_CONTRACT_ID,
  157. ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY1,
  158. ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY2,
  159. )
  160. ->select([
  161. ReceiptIssuingOrder::COL_NAME_CONTRACT_ID,
  162. ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY1,
  163. ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY2,
  164. DB::raw(sprintf("count(*) as count")),
  165. ])
  166. ->get();
  167. foreach ($receiptIssuingOrders as $ele) {
  168. $key1 = data_get($ele, ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY1);
  169. $key2 = data_get($ele, ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY2);
  170. $count = data_get($ele, 'count');
  171. $model->receipt_order_count += $count;
  172. $key = $makeKey($key1, $key2);
  173. $summaryByKey = $this->getUseByKeyModel($model, $key1, $key2);
  174. $summaryByKey->receipt_order_count = $count;
  175. $summaryByKeys->put($key, $summaryByKey);
  176. }
  177. // 郵送依頼件数の取得
  178. $mailOrders = ReceiptIssuingOrder::getBuilder()
  179. ->where(ReceiptIssuingOrder::COL_NAME_CONTRACT_ID, $model->contract_id)
  180. ->whereNotNull(ReceiptIssuingOrder::COL_NAME_STATUS_ORDER_MAIL_DATETIME)
  181. ->where(ReceiptIssuingOrder::COL_NAME_STATUS_ORDER_MAIL_DATETIME, ">=", $from)
  182. ->where(ReceiptIssuingOrder::COL_NAME_STATUS_ORDER_MAIL_DATETIME, "<", $to)
  183. ->groupBy(
  184. ReceiptIssuingOrder::COL_NAME_CONTRACT_ID,
  185. ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY1,
  186. ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY2,
  187. )
  188. ->select([
  189. ReceiptIssuingOrder::COL_NAME_CONTRACT_ID,
  190. ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY1,
  191. ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY2,
  192. DB::raw(sprintf("count(*) as count")),
  193. ])
  194. ->get();
  195. foreach ($mailOrders as $ele) {
  196. $key1 = data_get($ele, ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY1);
  197. $key2 = data_get($ele, ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY2);
  198. $count = data_get($ele, 'count');
  199. $model->mail_order_count = $count;
  200. $key = $makeKey($key1, $key2);
  201. $summaryByKey = $this->getUseByKeyModel($model, $key1, $key2);
  202. $summaryByKey->mail_order_count += $count;
  203. $summaryByKeys->put($key, $summaryByKey);
  204. }
  205. // SMS送信実績の取得
  206. $SMSs = SMSSendOrder::getBuilder()
  207. ->where(SMSSendOrder::COL_NAME_CONTRACT_ID, $model->contract_id)
  208. ->whereNotNull(SMSSendOrder::COL_NAME_SEND_DATETIME)
  209. ->where(SMSSendOrder::COL_NAME_SEND_DATETIME, ">=", $from)
  210. ->where(SMSSendOrder::COL_NAME_SEND_DATETIME, "<", $to)
  211. ->where(SMSSendOrder::COL_NAME_DONE, true)
  212. ->groupBy(
  213. ReceiptIssuingOrder::COL_NAME_CONTRACT_ID,
  214. ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY1,
  215. ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY2
  216. )
  217. ->select([
  218. ReceiptIssuingOrder::COL_NAME_CONTRACT_ID,
  219. ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY1,
  220. ReceiptIssuingOrder::COL_NAME_SUMMARY_KEY2,
  221. DB::raw(sprintf("count(*) as count")),
  222. DB::raw(sprintf("sum(%s) as cost", SMSSendOrder::COL_NAME_COST)),
  223. ])
  224. ->get();
  225. foreach ($SMSs as $ele) {
  226. $key1 = data_get($ele, SMSSendOrder::COL_NAME_SUMMARY_KEY1);
  227. $key2 = data_get($ele, SMSSendOrder::COL_NAME_SUMMARY_KEY2);
  228. $count = data_get($ele, 'count');
  229. $cost = data_get($ele, 'cost');
  230. $model->sms_send_count = $count;
  231. $model->sms_send_cost = $cost;
  232. $key = $makeKey($key1, $key2);
  233. $summaryByKey = $summaryByKeys->get($key);
  234. if (!$summaryByKey instanceof UseByKeySummary) {
  235. $summaryByKey = $this->getUseByKeyModel($model, $key1, $key2);
  236. $summaryByKeys->put($key, $summaryByKey);
  237. }
  238. $summaryByKey->fill([
  239. UseByKeySummary::COL_NAME_SMS_SEND_COUNT => $count,
  240. UseByKeySummary::COL_NAME_SMS_SEND_COST => $cost,
  241. ]);
  242. }
  243. return $summaryByKeys;
  244. }
  245. /**
  246. * @param UseSummary $model
  247. * @param Collection<UseByKeySummary> $summaryByKeys
  248. * @return void
  249. */
  250. private function save(UseSummary $model, Collection $summaryByKeys)
  251. {
  252. $contract = $model->contract;
  253. if (!$contract) {
  254. throw new Exception("契約不正");
  255. }
  256. $this->outputInfo(
  257. sprintf(
  258. "集計完了:%s 領収証発行依頼:%d件 郵送依頼:%d件 SMS送信件数:%d件 SMSコスト:%d円",
  259. $contract->name,
  260. $model->receipt_order_count,
  261. $model->mail_order_count,
  262. $model->sms_send_count,
  263. $model->sms_send_cost,
  264. )
  265. );
  266. $model->save();
  267. $deleteTargets = UseByKeySummary::whereContractId($model->contract_id)
  268. ->whereSummaryYyyymm($this->targetYYYYMM)
  269. ->get();
  270. foreach ($summaryByKeys as $summary) {
  271. if (!($summary instanceof UseByKeySummary)) {
  272. throw new LogicException("モデル不正");
  273. }
  274. $summary->save();
  275. $deleteTargets = $deleteTargets->reject(function ($ele) use ($summary) {
  276. return $ele->summary_key1 === $summary->summary_key1 && $ele->summary_key2 === $summary->summary_key2;
  277. });
  278. }
  279. foreach ($deleteTargets as $ele) {
  280. $ele->delete();
  281. }
  282. }
  283. private function getUseByKeyModel(UseSummary $model, ?string $key1, ?string $key2): UseByKeySummary
  284. {
  285. $query = UseByKeySummary::whereContractId($model->contract_id)
  286. ->whereSummaryYyyymm($this->targetYYYYMM);
  287. if ($key1 === null) {
  288. $query->whereNull(UseByKeySummary::COL_NAME_SUMMARY_KEY1);
  289. } else {
  290. $query->whereSummaryKey1($key1);
  291. }
  292. if ($key2 === null) {
  293. $query->whereNull(UseByKeySummary::COL_NAME_SUMMARY_KEY2);
  294. } else {
  295. $query->whereSummaryKey2($key2);
  296. }
  297. $summary = $query->firstOrNew([
  298. UseByKeySummary::COL_NAME_CONTRACT_ID => $model->contract_id,
  299. UseByKeySummary::COL_NAME_SUMMARY_YYYYMM => $this->targetYYYYMM,
  300. UseByKeySummary::COL_NAME_SUMMARY_KEY1 => $key1,
  301. UseByKeySummary::COL_NAME_SUMMARY_KEY2 => $key2,
  302. ]);
  303. $currentYYYYMM = intval(DateUtil::now()->format('Ym'));
  304. $targetYYYYMM = intval($this->targetYYYYMM);
  305. if ($targetYYYYMM < $currentYYYYMM) {
  306. $summary->is_fixed = true;
  307. }
  308. // 初期設定
  309. // $summary->receipt_order_count = 0;
  310. // $summary->mail_order_count = 0;
  311. // $summary->sms_send_count = 0;
  312. // $summary->sms_send_cost = 0;
  313. return $summary;
  314. }
  315. }