StoreOrderInvoiceServices.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2016~2023 https://www.crmeb.com All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
  8. // +----------------------------------------------------------------------
  9. // | Author: CRMEB Team <admin@crmeb.com>
  10. // +----------------------------------------------------------------------
  11. declare (strict_types=1);
  12. namespace app\services\order;
  13. use app\jobs\OrderInvoiceJob;
  14. use app\services\BaseServices;
  15. use app\services\serve\ServeServices;
  16. use crmeb\exceptions\AdminException;
  17. use crmeb\exceptions\ApiException;
  18. use app\dao\order\StoreOrderInvoiceDao;
  19. use app\services\user\UserInvoiceServices;
  20. use think\facade\Log;
  21. /**
  22. * Class StoreOrderInvoiceServices
  23. * @package app\services\order
  24. */
  25. class StoreOrderInvoiceServices extends BaseServices
  26. {
  27. /**
  28. * LiveAnchorServices constructor.
  29. * @param StoreOrderInvoiceDao $dao
  30. */
  31. public function __construct(StoreOrderInvoiceDao $dao)
  32. {
  33. $this->dao = $dao;
  34. }
  35. public function chart(array $where)
  36. {
  37. $where['is_pay'] = 1;
  38. $where['is_del'] = 0;
  39. //全部
  40. $data['all'] = (string)$this->dao->count($where);
  41. //待开
  42. $where['type'] = 1;
  43. $data['noOpened'] = (string)$this->dao->count($where);
  44. //已开
  45. $where['type'] = 2;
  46. $data['opened'] = (string)$this->dao->count($where);
  47. //退款
  48. $where['type'] = 3;
  49. $data['refund'] = (string)$this->dao->count($where);
  50. $data['elec_invoice'] = (int)sys_config('elec_invoice', 0);
  51. return $data;
  52. }
  53. /**
  54. * 后台获取开票列表
  55. * @param array $where
  56. * @return array
  57. * @throws \think\db\exception\DataNotFoundException
  58. * @throws \think\db\exception\DbException
  59. * @throws \think\db\exception\ModelNotFoundException
  60. */
  61. public function getList(array $where)
  62. {
  63. [$page, $list] = $this->getPageValue();
  64. $field = 'id as invoice_id,order_id,header_type,type,name,duty_number,drawer_phone,email,tell,address,bank,card_number,is_invoice,invoice_number,remark as invoice_reamrk,invoice_time,add_time as invoice_add_time,unique_num,invoice_num,invoice_type,invoice_serial_number,red_invoice_num';
  65. $where['is_pay'] = 1;
  66. $where['is_del'] = 0;
  67. $list = $this->dao->getList($where, $field, ['order' => function ($query) {
  68. $query->field('id,order_id,pay_price,add_time,real_name,user_phone,status,refund_status');
  69. }], 'add_time desc', $page, $list);
  70. foreach ($list as &$item) {
  71. $item['id'] = $item['order']['id'] ?? 0;
  72. $item['order_id'] = $item['order']['order_id'] ?? '';
  73. $item['pay_price'] = $item['order']['pay_price'] ?? 0.00;
  74. $item['real_name'] = $item['order']['real_name'] ?? '';
  75. $item['user_phone'] = $item['order']['user_phone'] ?? '';
  76. $item['status'] = $item['order']['status'] ?? '';
  77. $item['refund_status'] = $item['order']['refund_status'] ?? 0;
  78. $item['add_time'] = date('Y-m-d H:i:s', $item['order']['add_time'] ?? $item['invoice_add_time'] ?? time());
  79. $item['invoice_add_time'] = date('Y-m-d H:i:s', $item['invoice_add_time']);
  80. }
  81. $count = $this->dao->count($where);
  82. return compact('list', 'count');
  83. }
  84. /**
  85. * 前端获取开票列表(带商品信息)
  86. * @param $where
  87. * @return array
  88. */
  89. public function getOrderInvoiceList(array $where)
  90. {
  91. [$page, $list] = $this->getPageValue();
  92. $where['is_pay'] = 1;
  93. $where['is_del'] = 0;
  94. $list = $this->dao->getList($where, '*', ['order'], 'add_time desc', $page, $list);
  95. /** @var StoreOrderServices $storeOrderServices */
  96. $storeOrderServices = app()->make(StoreOrderServices::class);
  97. foreach ($list as &$item) {
  98. if (isset($item['order']) && $item['order']) {
  99. $item['order'] = $storeOrderServices->tidyOrder($item['order'], true);
  100. if (isset($item['order']['_status']['_type']) && $item['order']['_status']['_type'] == 3) {
  101. foreach ($item['order']['cartInfo'] ?: [] as $key => $product) {
  102. $item['order']['cartInfo'][$key]['add_time'] = isset($product['add_time']) ? date('Y-m-d H:i', (int)$product['add_time']) : '时间错误';
  103. }
  104. }
  105. }
  106. }
  107. return $list;
  108. }
  109. /**
  110. * 订单申请开票
  111. * @param int $uid
  112. * @param $order_id
  113. * @param int $invoice_id
  114. * @return mixed
  115. * @throws \think\db\exception\DataNotFoundException
  116. * @throws \think\db\exception\DbException
  117. * @throws \think\db\exception\ModelNotFoundException
  118. */
  119. public function makeUp(int $uid, $order_id, int $invoice_id)
  120. {
  121. if (!$order_id) throw new AdminException(100100);
  122. if (!$invoice_id) throw new AdminException(410325);
  123. /** @var StoreOrderServices $storeOrderServices */
  124. $storeOrderServices = app()->make(StoreOrderServices::class);
  125. /** @var UserInvoiceServices $userInvoiceServices */
  126. $userInvoiceServices = app()->make(UserInvoiceServices::class);
  127. $order = $storeOrderServices->getOne(['order_id|id' => $order_id, 'is_del' => 0]);
  128. if (!$order) {
  129. throw new AdminException(410173);
  130. }
  131. //检测再带查询
  132. $invoice = $userInvoiceServices->checkInvoice($invoice_id, $uid);
  133. if ($this->dao->getOne(['order_id' => $order['id'], 'uid' => $uid])) {
  134. throw new AdminException(410249);
  135. }
  136. if ($order['refund_status'] == 2) {
  137. throw new AdminException(410226);
  138. }
  139. if ($order['refund_status'] == 1) {
  140. throw new AdminException(410250);
  141. }
  142. unset($invoice['id'], $invoice['add_time']);
  143. $data = [];
  144. $data['category'] = 'order';
  145. $data['order_id'] = $order['id'];
  146. $data['invoice_id'] = $invoice_id;
  147. $data['add_time'] = time();
  148. $data['is_pay'] = $order['paid'] == 1 ? 1 : 0;
  149. $data = array_merge($data, $invoice);
  150. if (!$re = $this->dao->save($data)) {
  151. throw new AdminException(410251);
  152. }
  153. if (sys_config('elec_invoice', 1) == 1 && sys_config('auto_invoice', 1) == 1 && $data['is_pay'] == 1) {
  154. //自动开票
  155. OrderInvoiceJob::dispatchSecs(10, 'autoInvoice', [$re->id]);
  156. }
  157. //自定义事件-申请开票
  158. event('CustomEventListener', ['order_invoice', [
  159. 'uid' => $uid,
  160. 'order_id' => $order_id,
  161. 'phone' => $order['user_phone'],
  162. 'invoice_id' => $re->id,
  163. 'add_time' => date('Y-m-d H:i:s'),
  164. ]]);
  165. return ['id' => $re->id];
  166. }
  167. public function setInvoice(int $id, array $data)
  168. {
  169. $orderInvoice = $this->dao->get($id);
  170. if (!$orderInvoice) {
  171. throw new AdminException(100026);
  172. }
  173. if ($data['is_invoice'] == 1) {
  174. $data['invoice_time'] = time();
  175. }
  176. if (!$this->dao->update($id, $data, 'id')) {
  177. throw new AdminException(100015);
  178. }
  179. return true;
  180. }
  181. /**
  182. * 拆分订单同步拆分申请开票记录
  183. * @param int $oid
  184. * @return bool
  185. * @throws \think\db\exception\DataNotFoundException
  186. * @throws \think\db\exception\DbException
  187. * @throws \think\db\exception\ModelNotFoundException
  188. */
  189. public function splitOrderInvoice(int $oid)
  190. {
  191. /** @var StoreOrderServices $storeOrderServices */
  192. $storeOrderServices = app()->make(StoreOrderServices::class);
  193. $orderInfo = $storeOrderServices->getOne(['id' => $oid, 'is_del' => 0]);
  194. if (!$orderInfo) {
  195. throw new AdminException(410173);
  196. }
  197. $pid = $orderInfo['pid'] > 0 ? $orderInfo['pid'] : $orderInfo['id'];
  198. //查询开票记录
  199. $orderInvoice = $this->dao->get(['order_id' => $oid]);
  200. //查询子订单
  201. $spliteOrder = $storeOrderServices->getColumn(['pid' => $pid, 'is_system_del' => 0], 'id,order_id');
  202. if ($spliteOrder && $orderInvoice) {
  203. $data = $orderInvoice->toArray();
  204. unset($data['id']);
  205. $data['add_time'] = strtotime($data['add_time']);
  206. $data_all = [];
  207. foreach ($spliteOrder as $order) {
  208. if (!$this->dao->count(['order_id' => $order['id']])) {
  209. $data['order_id'] = $order['id'];
  210. $data_all[] = $data;
  211. }
  212. }
  213. if ($data_all) {
  214. $this->transaction(function () use ($data_all, $orderInvoice, $orderInfo) {
  215. $this->dao->saveAll($data_all);
  216. if ($orderInfo['pid'] <= 0) {
  217. $this->dao->update(['id' => $orderInvoice['id']], ['is_del' => 1]);
  218. }
  219. });
  220. }
  221. }
  222. return true;
  223. }
  224. /**
  225. * 开具发票
  226. * @param $id
  227. * @return bool
  228. * @throws \ReflectionException
  229. * @throws \think\db\exception\DataNotFoundException
  230. * @throws \think\db\exception\DbException
  231. * @throws \think\db\exception\ModelNotFoundException
  232. * @author wuhaotian
  233. * @email 442384644@qq.com
  234. * @date 2024/5/15
  235. */
  236. public function invoiceIssuance($id)
  237. {
  238. if (sys_config('elec_invoice', 1) != 1) {
  239. return app('json')->fail('电子发票功能未开启,请在一号通中开启并且在商城后台一号通配置中开启');
  240. }
  241. $info = $this->dao->getOne(['id' => $id]);
  242. $orderInfo = app()->make(StoreOrderServices::class)->get($info['order_id']);
  243. $cartInfo = app()->make(StoreOrderCartInfoServices::class)->getOrderCartInfo($orderInfo['id']);
  244. $data = [];
  245. $data['is_tax_inclusive'] = 1;
  246. $data['account_name'] = $info['name'];
  247. $data['bank_name'] = '';
  248. $data['bank_account'] = '';
  249. $data['telephone'] = $info['drawer_phone'];
  250. $data['company_address'] = '';
  251. $data['drawer'] = '';
  252. $data['amount'] = $orderInfo['pay_price'];
  253. if ($info['header_type'] == 1) {
  254. $data['invoice_type'] = 82;
  255. $data['tax_id'] = '';
  256. $data['is_enterprise'] = 0;
  257. } else {
  258. $data['invoice_type'] = 81;
  259. $data['tax_id'] = $info['duty_number'];
  260. $data['is_enterprise'] = 1;
  261. }
  262. $goods = [];
  263. foreach ($cartInfo as $item) {
  264. $goods[] = [
  265. 'cate_id' => sys_config('elec_invoice_cate', 1799),
  266. 'store_name' => $item['cart_info']['productInfo']['store_name'],
  267. 'unit_price' => bcadd($item['cart_info']['sum_price'], $item['cart_info']['postage_price'], 2),
  268. 'num' => $item['cart_info']['cart_num'],
  269. 'tax_rate' => sys_config('elec_invoice_tax_rate', 13),
  270. ];
  271. }
  272. $data['goods'] = $goods;
  273. try {
  274. $invoice = app()->make(ServeServices::class)->invoice();
  275. $res = $invoice->invoiceIssuance($orderInfo['order_id'], $data);
  276. if ($res['status'] == 200) {
  277. $this->dao->update($id, [
  278. 'is_invoice' => 1,
  279. 'unique_num' => $orderInfo['order_id'],
  280. 'invoice_num' => $res['data']['invoice_num'],
  281. 'invoice_type' => $res['data']['invoice_type'],
  282. 'invoice_serial_number' => $res['data']['invoice_serial_number'],
  283. 'invoice_time' => time()
  284. ]);
  285. }
  286. } catch (\Exception $e) {
  287. Log::error('自动开具发票失败,失败原因:' . $e->getMessage());
  288. }
  289. return true;
  290. }
  291. /**
  292. * 未开发票自动开具电子发票
  293. * @return bool
  294. * @author wuhaotian
  295. * @email 442384644@qq.com
  296. * @date 2024/5/15
  297. */
  298. public function autoInvoice()
  299. {
  300. if (sys_config('elec_invoice', 1) != 1) {
  301. return true;
  302. }
  303. if (sys_config('auto_invoice', 1) == 1) {
  304. $list = $this->dao->getColumn([
  305. ['is_pay', '=', 1],
  306. ['is_invoice', '=', 1],
  307. ['unique_num', '=', ''],
  308. ['is_del', '=', 0],
  309. ['add_time', '<', time() - 60],
  310. ], 'id');
  311. if ($list) {
  312. foreach ($list as $item) {
  313. //自动开票
  314. OrderInvoiceJob::dispatchSecs(10, 'autoInvoice', [$item]);
  315. }
  316. }
  317. }
  318. return true;
  319. }
  320. /**
  321. * 退款订单自动冲红
  322. * @return bool
  323. * @author wuhaotian
  324. * @email 442384644@qq.com
  325. * @date 2024/5/15
  326. */
  327. public function autoInvoiceRed()
  328. {
  329. if (sys_config('elec_invoice', 1) != 1) {
  330. return true;
  331. }
  332. if (sys_config('auto_invoice', 1) == 1) {
  333. $list = $this->dao->getColumn([
  334. ['is_pay', '=', 1],
  335. ['is_invoice', '=', 1],
  336. ['unique_num', '<>', ''],
  337. ['red_invoice_num', '=', ''],
  338. ['is_del', '=', 1],
  339. ['add_time', '<', time() - 60],
  340. ], 'id');
  341. if ($list) {
  342. foreach ($list as $item) {
  343. //自动冲红
  344. OrderInvoiceJob::dispatchSecs(10, 'autoInvoiceRed', [$item]);
  345. }
  346. }
  347. }
  348. return true;
  349. }
  350. /**
  351. * 负数发票开具
  352. * @param $id
  353. * @return bool
  354. * @throws \think\db\exception\DataNotFoundException
  355. * @throws \think\db\exception\DbException
  356. * @throws \think\db\exception\ModelNotFoundException
  357. * @author wuhaotian
  358. * @email 442384644@qq.com
  359. * @date 2024/5/16
  360. */
  361. public function redInvoiceIssuance($id)
  362. {
  363. $invoiceInfo = $this->dao->get($id);
  364. if ($invoiceInfo['is_pay'] == 0 || $invoiceInfo['is_invoice'] == 0 || $invoiceInfo['unique_num'] == '') {
  365. throw new AdminException('发票状态有误,请检查');
  366. }
  367. $invoice = app()->make(ServeServices::class)->invoice();
  368. $redInfo = $invoice->applyRedInvoice(['invoice_num' => $invoiceInfo['invoice_num'], 'apply_type' => '01']);
  369. if ($redInfo['status'] != 200) throw new AdminException('申请红字发票失败,请检查');
  370. $res = $invoice->redInvoiceIssuance(['invoice_num' => $invoiceInfo['invoice_num'], 'red_number' => $redInfo['data']['red_number']]);
  371. if ($res['status'] != 200) throw new AdminException('开具负数发票失败,请检查');
  372. $this->dao->update($id, ['red_invoice_num' => $redInfo['data']['red_number']]);
  373. return true;
  374. }
  375. }