OwnerFeeTotalService.php 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509
  1. <?php
  2. namespace App\Services;
  3. use App\OrderIssue;
  4. use App\Owner;
  5. use App\OwnerBillReport;
  6. use App\OwnerPriceOperation;
  7. use App\OwnerPriceOperationItem;
  8. use App\OwnerStoreOutFeeReport;
  9. use App\Traits\ServiceAppAop;
  10. use App\OwnerFeeTotal;
  11. use App\Traits\SettlementBillServiceTrait;
  12. use Illuminate\Database\Eloquent\Builder;
  13. class OwnerFeeTotalService
  14. {
  15. const TYPE = '总费用';
  16. use ServiceAppAop;
  17. use SettlementBillServiceTrait;
  18. protected $modelClass = OwnerFeeTotal::class;
  19. /**
  20. * 生成统计数据
  21. * @param string|null $counting_month string 统计月份默认为上一个月
  22. * @param array $ownerIds
  23. */
  24. public function record(string $counting_month = null, array $ownerIds = [])
  25. {
  26. if (is_null($counting_month)) {
  27. $counting_month = now()->subMonth()->startOfMonth()->toDateString();
  28. }
  29. /**
  30. * 仓储。入库,出库
  31. * 1. 查到所有货主
  32. * 2. 遍历货主
  33. * 3. 获得货主下的计费模型,将计费模型里面的数据转换成“描述”
  34. * 4. 查询相关表,获得指定计费模型下的费用总和(含税费)
  35. *
  36. * 配送,加工,系统使用,杂项
  37. * 1. 查询对应表的总和即可,不需关联计费模型
  38. *
  39. * 理赔
  40. * 1. 暂不明确
  41. *
  42. * 优惠
  43. * 1. 一个字段,在结算管理-账单却认下添加一个优惠输入的字段
  44. *
  45. * 税率
  46. * 1. 计算得出:总税费/总金额
  47. */
  48. //仓储:ownerStoragePriceModels。入库,出库:ownerPriceOperations
  49. $builder = Owner::query()
  50. ->with([
  51. "ownerStoragePriceModels.unit:id,name",
  52. "ownerStoragePriceModels.timeUnit:id,name",
  53. "ownerPriceOperations" => function ($query) {
  54. /** @var Builder $query */
  55. $query->with(["items" => function ($query) {
  56. /** @var Builder $query */
  57. $query->with(['unit'])->orderByRaw("CASE strategy WHEN '起步' THEN 1 WHEN '默认' THEN 2 WHEN '特征' THEN 3 END");
  58. }]);
  59. }])
  60. ->where('deleted_at', '>=', now()->subMonth()->startOfMonth())
  61. ->orWhereNull('deleted_at');
  62. if (!empty($ownerIds)) {
  63. $builder->whereIn('id', $ownerIds);
  64. }
  65. $owners = $builder->get();
  66. foreach ($owners as $owner) {
  67. $information = $this->buildInformation($owner, $counting_month);
  68. $ownerFeeTotal = [];
  69. $ownerFeeTotal['information'] = $information;
  70. //快递费
  71. $ownerFeeTotal = $this->buildExpressFee($owner, $counting_month, $ownerFeeTotal);
  72. //物流费
  73. $ownerFeeTotal = $this->buildLogisticFee($owner, $counting_month, $ownerFeeTotal);
  74. //加工
  75. $ownerFeeTotal = $this->buildProcessFee($owner, $counting_month, $ownerFeeTotal);
  76. //系统使用费
  77. $ownerFeeTotal = $this->buildSystemFee($counting_month, $owner, $ownerFeeTotal);
  78. //杂项费
  79. $ownerFeeTotal = $this->buildSundryFee($owner, $counting_month, $ownerFeeTotal);
  80. //理赔
  81. $ownerFeeTotal = $this->buildIndemnityFee($counting_month, $owner, $ownerFeeTotal);
  82. // 包材费
  83. $ownerFeeTotal = $this->buildPackingMaterialFee($owner, $counting_month, $ownerFeeTotal);
  84. // 卸货费
  85. $ownerFeeTotal = $this->buildUnloadFee($owner, $counting_month, $ownerFeeTotal);
  86. //总费用 和 总税费
  87. list($totalFee, $totalTaxFee, $ownerFeeTotal) = $this->calTotalFeeAndTotalTaxFee($ownerFeeTotal);
  88. //税率
  89. $ownerFeeTotal = $this->calTaxRate($totalTaxFee, $totalFee, $ownerFeeTotal);
  90. $insertData = $this->buildInsertData($owner, $counting_month, $ownerFeeTotal);
  91. $this->createOrUpdate($owner, $counting_month, $insertData);
  92. }
  93. }
  94. public function get(array $kvPairs)
  95. {
  96. return OwnerFeeTotal::query()
  97. ->where('owner_id', $kvPairs['owner_id'])
  98. ->where('counting_month', $kvPairs['counting_month'])
  99. ->firstOr(function (){
  100. return new OwnerFeeTotal();
  101. });
  102. }
  103. /**
  104. * 出库入库描述
  105. * @param $ownerPriceOperation
  106. * @return array
  107. */
  108. private function buildPriceRemarks($ownerPriceOperation): array
  109. {
  110. //起步: 3 件 / 2.7000元 (满减单价: 0-19999 单(2.7元) , 20000-49999 单(2.5元) , 50000-99999 单(2元) , 100000+ 单(1.6元) )
  111. //默认续费: 1 件 / 0.5000元 (满减单价: 0-19999 单(0.5元) , 20000-49999 单(0.4元) , 50000-99999 单(0.3元) , 100000+ 单(0.2元) )
  112. $discount_counts = explode(',', $ownerPriceOperation->discount_count);
  113. $priceRemarks = [];
  114. foreach ($ownerPriceOperation->items as $operationItem) {
  115. $discount_prices = explode(',', $operationItem->discount_price);
  116. $strategy = $operationItem->strategy == '起步' ? '起步' : '默认续费';
  117. $unit_name = $operationItem->unit->name ?? '个';
  118. $priceRemark = "{$strategy}: {$operationItem->amount} {$unit_name}/{$operationItem->unit_price}元";
  119. if (!empty($discount_prices)) {
  120. $priceRemark .= "(满减单价:";
  121. for ($i = 0; $i < count($discount_counts) - 1; $i++) {
  122. $next_discount_count = $discount_counts[$i + 1] ?? '+';
  123. $discount_count = $discount_counts[$i] ?? '';
  124. $discount_price = $discount_prices[$i] ?? '';
  125. $priceRemark .= "{$discount_count}-{$next_discount_count} {$unit_name} {$discount_price}元,";
  126. }
  127. $priceRemark .= ")";
  128. }
  129. $priceRemarks[] = $priceRemark;
  130. }
  131. return $priceRemarks;
  132. }
  133. /**
  134. * @param $owner
  135. * @param $counting_month
  136. * @return array|array[]
  137. */
  138. private function buildInformation($owner, $counting_month): array
  139. {
  140. //获取特征信息
  141. $features = app("FeatureService")->getMapArray();
  142. OwnerPriceOperation::$features = $features;
  143. OwnerPriceOperationItem::$features = $features;
  144. foreach ($owner->ownerPriceOperations as &$operation) {
  145. $operation["featureFormat"] = $operation->featureFormat;
  146. $operation["isRejected"] = $operation->type_mark === 0;
  147. foreach ($operation->items as &$item) {
  148. $item["featureFormat"] = $item->featureFormat;
  149. if ($item["strategy"] == "起步") $item["type"] = $item["amount"] ? 0 : 1;
  150. }
  151. }
  152. $information = [
  153. //仓储
  154. 'storageFee' => [],
  155. //入库
  156. 'storeFee' => [],
  157. //出库
  158. 'storeOutFee' => [],
  159. ];
  160. //仓储
  161. foreach ($owner->ownerStoragePriceModels as $ownerStoragePriceModel) {
  162. /**@var $areaFeeService SettlementBillsAreaFeeService */
  163. $areaFeeService = app('SettlementBillsAreaFeeService');
  164. $remark = "起租面积:{$ownerStoragePriceModel->minimum_area},{$ownerStoragePriceModel->price[0]}元/{$ownerStoragePriceModel->unit->name}/{$ownerStoragePriceModel->timeUnit->name}";
  165. $information['storageFee'][] = [
  166. 'name' => $ownerStoragePriceModel->name,
  167. 'remark' => $remark,
  168. 'id' => $ownerStoragePriceModel->id,
  169. 'fee' => $areaFeeService->getTotalFee($owner->id, $counting_month)->storage_fee ?? 0,
  170. 'tax_fee' => $areaFeeService->getTotalFee($owner->id, $counting_month)->storage_tax_fee ?? 0
  171. ];
  172. }
  173. $ownerPriceOperationsGrouped = $owner->ownerPriceOperations->groupBy('operation_type');
  174. //入库
  175. $ownerStoreFeeReports = \App\OwnerStoreFeeReport::query()
  176. ->selectRaw("
  177. sum(fee) as fee,
  178. sum(tax_fee) as tax_fee,
  179. model_id
  180. ")
  181. ->where('counting_month', $counting_month)
  182. ->where('owner_id', $owner->id)
  183. ->groupBy('model_id')
  184. ->get();
  185. $ownerStoreFeeReportsGroupByModelId = $ownerStoreFeeReports->groupBy('model_id')->toArray();
  186. foreach ($ownerPriceOperationsGrouped['入库'] ?? [] as $ownerPriceOperationsGroupedItem) {
  187. try {
  188. $fee = $ownerStoreFeeReportsGroupByModelId[$ownerPriceOperationsGroupedItem->id][0]['fee'];
  189. } catch (\Exception $e) {
  190. $fee = $ownerStoreFeeReportsGroupByModelId[$ownerPriceOperationsGroupedItem->target_id][0]['fee'] ?? 0;
  191. $ownerPriceOperationsGroupedItem = OwnerPriceOperation::query()->with(['items'])->find($ownerPriceOperationsGroupedItem->target_id);
  192. }
  193. if ($fee > 0) {
  194. $information['storeFee'][] = [
  195. 'name' => $ownerPriceOperationsGroupedItem->name,
  196. 'remark' => $this->buildPriceRemarks($ownerPriceOperationsGroupedItem),
  197. 'id' => $ownerPriceOperationsGroupedItem->id,
  198. 'fee' => $fee,
  199. 'tax_fee' => $ownerStoreFeeReportsGroupByModelId[$ownerPriceOperationsGroupedItem->id][0]['tax_fee'] ?? 0,
  200. ];
  201. }
  202. }
  203. //出库
  204. $ownerStoreOutFeeReports = OwnerStoreOutFeeReport::query()
  205. ->selectRaw("
  206. sum(fee) as fee,
  207. sum(tax_fee) as tax_fee,
  208. model_id
  209. ")
  210. ->where('counting_month', $counting_month)
  211. ->where('owner_id', $owner->id)
  212. ->groupBy('model_id')
  213. ->get();
  214. $ownerStoreOutFeeReportsGroupByModelId = $ownerStoreOutFeeReports->groupBy('model_id')->toArray();
  215. foreach ($ownerPriceOperationsGrouped['出库'] ?? [] as $ownerPriceOperationsGroupedItem) {
  216. try {
  217. $fee = $ownerStoreOutFeeReportsGroupByModelId[$ownerPriceOperationsGroupedItem->id][0]['fee'];
  218. } catch (\Exception $e) {
  219. $fee = $ownerStoreOutFeeReportsGroupByModelId[$ownerPriceOperationsGroupedItem->target_id][0]['fee'] ?? 0;
  220. $ownerPriceOperationsGroupedItem = OwnerPriceOperation::query()->with(['items'])->find($ownerPriceOperationsGroupedItem->target_id);
  221. }
  222. if ($fee > 0) {
  223. $information['storeOutFee'][] = [
  224. 'name' => $ownerPriceOperationsGroupedItem->name,
  225. 'remark' => $this->buildPriceRemarks($ownerPriceOperationsGroupedItem),
  226. 'id' => $ownerPriceOperationsGroupedItem->id,
  227. 'fee' => $fee,
  228. 'tax_fee' => $ownerStoreOutFeeReportsGroupByModelId[$ownerPriceOperationsGroupedItem->id][0]['tax_fee'] ?? 0,
  229. ];
  230. }
  231. }
  232. return $information;
  233. }
  234. /**
  235. * @param $owner
  236. * @param string|null $counting_month
  237. * @param array $ownerFeeTotal
  238. * @return array
  239. */
  240. private function buildExpressFee($owner, ?string $counting_month, array $ownerFeeTotal): array
  241. {
  242. /**@var $expressFeeService OwnerLogisticFeeReportService */
  243. $expressFeeService = app('OwnerLogisticFeeReportService');
  244. $expressFeeTotal = $expressFeeService->getTotalFee($owner->id, $counting_month);
  245. $ownerFeeTotal ['expressFee'] = [
  246. 'fee' => $expressFeeTotal->fee ?? 0,
  247. 'tax_fee' => $expressFeeTotal->tax_fee ?? 0,
  248. ];
  249. return $ownerFeeTotal;
  250. }
  251. /**
  252. * @param $owner
  253. * @param string|null $counting_month
  254. * @param array $ownerFeeTotal
  255. * @return array
  256. */
  257. private function buildLogisticFee($owner, ?string $counting_month, array $ownerFeeTotal): array
  258. {
  259. /**@var $waybillSettlementBillService OwnerWaybillSettlementBillService */
  260. $waybillSettlementBillService = app('OwnerWaybillSettlementBillService');
  261. $logisticFeeTotal = $waybillSettlementBillService->getTotalFee($owner->id, $counting_month);
  262. $ownerFeeTotal ['logisticFee'] = [
  263. 'fee' => $logisticFeeTotal->fee ?? 0,
  264. 'tax_fee' => $logisticFeeTotal->tax_fee ?? 0,
  265. ];
  266. return $ownerFeeTotal;
  267. }
  268. /**
  269. * @param $owner
  270. * @param string|null $counting_month
  271. * @param array $ownerFeeTotal
  272. * @return array
  273. */
  274. private function buildProcessFee($owner, ?string $counting_month, array $ownerFeeTotal): array
  275. {
  276. /**@var $ownerProcessSettlementBillService OwnerProcessSettlementBillService */
  277. $ownerProcessSettlementBillService = app('OwnerProcessSettlementBillService');
  278. $processFeeTotal = $ownerProcessSettlementBillService->getTotalFee($owner->id, $counting_month);
  279. $ownerFeeTotal ['processFee'] = [
  280. 'fee' => $processFeeTotal->fee ?? 0,
  281. 'tax_fee' => $processFeeTotal->tax_fee ?? 0,
  282. ];
  283. return $ownerFeeTotal;
  284. }
  285. /**
  286. * @param string|null $counting_month
  287. * @param $owner
  288. * @param array $ownerFeeTotal
  289. * @return array
  290. */
  291. private function buildSystemFee(?string $counting_month, $owner, array $ownerFeeTotal): array
  292. {
  293. $ownerBillReport = OwnerBillReport::query()
  294. ->where('counting_month', $counting_month)
  295. ->where('owner_id', $owner->id)
  296. ->first();
  297. $ownerFeeTotal ['systemFee'] = [
  298. 'fee' => $ownerBillReport->other_fee ?? 0,
  299. 'tax_fee' => $ownerBillReport->other_tax_fee ?? 0,
  300. ];
  301. return $ownerFeeTotal;
  302. }
  303. /**
  304. * @param $owner
  305. * @param string|null $counting_month
  306. * @param array $ownerFeeTotal
  307. * @return array
  308. */
  309. private function buildSundryFee($owner, ?string $counting_month, array $ownerFeeTotal): array
  310. {
  311. /**@var $ownerSundryFeeDetailService OwnerSundryFeeDetailService */
  312. $ownerSundryFeeDetailService = app('OwnerSundryFeeDetailService');
  313. $sundryFeeTotals = $ownerSundryFeeDetailService->getTotalFee($owner->id, $counting_month);
  314. if (!$sundryFeeTotals->isEmpty()) {
  315. foreach ($sundryFeeTotals as $sundryFeeTotal) {
  316. $ownerFeeTotal ['sundryFee'][] = [
  317. 'type' => $sundryFeeTotal->type,
  318. 'fee' => $sundryFeeTotal->fee,
  319. ];
  320. }
  321. } else {
  322. $ownerFeeTotal ['sundryFee'] = [];
  323. }
  324. return $ownerFeeTotal;
  325. }
  326. /**
  327. * @param string|null $counting_month
  328. * @param $owner
  329. * @param array $ownerFeeTotal
  330. * @return array
  331. */
  332. private function buildIndemnityFee(?string $counting_month, $owner, array $ownerFeeTotal): array
  333. {
  334. list($start, $end) = $this->getStartAndEnd($counting_month);
  335. $indemnityFee = OrderIssue::query()
  336. ->selectRaw("sum(baoshi_indemnity_money) as fee,owner_id,order_id")
  337. ->leftJoin('orders', 'order_issues.order_id', '=', 'orders.id')
  338. ->where('owner_id', $owner->id)
  339. ->whereBetween('order_issues.created_at', [$start, $end])
  340. ->whereBetween('orders.created_at', [$start, $end])
  341. ->first();
  342. $ownerFeeTotal ['indemnityFee'] = [
  343. 'fee' => $indemnityFee->fee ?? 0,
  344. ];
  345. return $ownerFeeTotal;
  346. }
  347. /**
  348. * @param $owner
  349. * @param string|null $counting_month
  350. * @param array $ownerFeeTotal
  351. * @return array
  352. */
  353. private function buildPackingMaterialFee($owner, ?string $counting_month, array $ownerFeeTotal): array
  354. {
  355. /**@var $packingMaterialFeeService OwnerProcurementSettlementBillService */
  356. $packingMaterialFeeService = app('OwnerProcurementSettlementBillService');
  357. $packingMaterialFee = $packingMaterialFeeService->getTotalFee($owner->id, $counting_month);
  358. $ownerFeeTotal ['packingMaterialFee'] = [
  359. 'fee' => $packingMaterialFee ?? 0,
  360. ];
  361. return $ownerFeeTotal;
  362. }
  363. /**
  364. * @param $owner
  365. * @param string|null $counting_month
  366. * @param array $ownerFeeTotal
  367. * @return array
  368. */
  369. private function buildUnloadFee($owner, ?string $counting_month, array $ownerFeeTotal): array
  370. {
  371. /**@var $unloadService OwnerDischargeTaskSettlementBillService */
  372. $unloadService = app('OwnerDischargeTaskSettlementBillService');
  373. $unloadFee = $unloadService->getTotalFee($owner->id, $counting_month);
  374. $ownerFeeTotal ['unloadFee'] = [
  375. 'fee' => $unloadFee ?? 0,
  376. ];
  377. return $ownerFeeTotal;
  378. }
  379. /**
  380. * @param array $ownerFeeTotal
  381. * @return array
  382. */
  383. private function calTotalFeeAndTotalTaxFee(array $ownerFeeTotal): array
  384. {
  385. $totalFee = 0;
  386. $totalTaxFee = 0;
  387. foreach ($ownerFeeTotal as $key => $ownerFeeTotalItem) {
  388. if ($key == 'information') {
  389. list($totalFee, $totalTaxFee) = $this->calInformationTotalFeeAndTotalTaxFee($ownerFeeTotalItem, $totalFee, $totalTaxFee);
  390. } else if ($key == 'sundryFee') {
  391. foreach ($ownerFeeTotalItem as $sundryFeeItem) {
  392. $totalFee += $sundryFeeItem['fee'] ?? 0;
  393. }
  394. } else {
  395. $totalFee += $ownerFeeTotalItem['fee'] ?? 0;
  396. $totalTaxFee += $ownerFeeTotalItem['tax_fee'] ?? 0;
  397. }
  398. }
  399. $ownerFeeTotal['totalFee'] = $totalFee;
  400. $ownerFeeTotal['totalTaxFee'] = $totalTaxFee;
  401. return array($totalFee, $totalTaxFee, $ownerFeeTotal);
  402. }
  403. /**
  404. * @param $totalTaxFee
  405. * @param $totalFee
  406. * @param $ownerFeeTotal
  407. * @return mixed
  408. */
  409. private function calTaxRate($totalTaxFee, $totalFee, $ownerFeeTotal)
  410. {
  411. try {
  412. $taxRate = ($totalTaxFee / $totalFee) * 100 ?? 0;
  413. } catch (\Exception $e) {
  414. $taxRate = 0;
  415. }
  416. $ownerFeeTotal['taxRate'] = $taxRate;
  417. return $ownerFeeTotal;
  418. }
  419. /**
  420. * @param $owner
  421. * @param string|null $counting_month
  422. * @param $ownerFeeTotal
  423. * @return array
  424. */
  425. private function buildInsertData($owner, ?string $counting_month, $ownerFeeTotal): array
  426. {
  427. return [
  428. 'owner_id' => $owner->id,
  429. 'counting_month' => $counting_month,
  430. 'information' => $ownerFeeTotal['information'],
  431. 'fee' => $ownerFeeTotal['totalFee'],
  432. 'logistic_fee' => $ownerFeeTotal['logisticFee']['fee'],
  433. 'logistic_tax_fee' => $ownerFeeTotal['logisticFee']['tax_fee'],
  434. 'express_fee' => $ownerFeeTotal['expressFee']['fee'],
  435. 'express_tax_fee' => $ownerFeeTotal['expressFee']['tax_fee'],
  436. 'process_fee' => $ownerFeeTotal['processFee']['fee'],
  437. 'process_tax_fee' => $ownerFeeTotal['processFee']['tax_fee'],
  438. 'system_fee' => $ownerFeeTotal['systemFee']['fee'],
  439. 'system_tax_fee' => $ownerFeeTotal['systemFee']['tax_fee'],
  440. 'sundry_information' => $ownerFeeTotal['sundryFee'],
  441. 'packing_material_fee' => $ownerFeeTotal['packingMaterialFee']['fee'],
  442. 'unload_fee' => $ownerFeeTotal['unloadFee']['fee'],
  443. 'tax_rate' => $ownerFeeTotal['taxRate'],
  444. 'indemnity_fee' => $ownerFeeTotal['indemnityFee']['fee'],
  445. ];
  446. }
  447. /**
  448. * @param $ownerFeeTotalItem
  449. * @param $totalFee
  450. * @param $totalTaxFee
  451. * @return array|int[]
  452. */
  453. private function calInformationTotalFeeAndTotalTaxFee($ownerFeeTotalItem, $totalFee, $totalTaxFee): array
  454. {
  455. foreach ($ownerFeeTotalItem as $ownerFeeTotalItemOne) {
  456. foreach ($ownerFeeTotalItemOne as $feeItem) {
  457. $totalFee += $feeItem['fee'] ?? 0;
  458. $totalTaxFee += $feeItem['tax_fee'] ?? 0;
  459. }
  460. }
  461. return array($totalFee, $totalTaxFee);
  462. }
  463. /**
  464. * @param $owner
  465. * @param string|null $counting_month
  466. * @param array $insertData
  467. */
  468. private function createOrUpdate($owner, ?string $counting_month, array $insertData): void
  469. {
  470. if (OwnerFeeTotal::query()->where('owner_id', $owner->id)->where('counting_month', $counting_month)->exists()) {
  471. OwnerFeeTotal::query()
  472. ->where('owner_id', $owner->id)
  473. ->where('counting_month', $counting_month)
  474. ->update($insertData);
  475. } else {
  476. OwnerFeeTotal::query()
  477. ->where('owner_id', $owner->id)
  478. ->where('counting_month', $counting_month)
  479. ->create($insertData);
  480. }
  481. }
  482. }