Stock.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523
  1. <?php
  2. /**
  3. * Niushop商城系统 - 团队十年电商经验汇集巨献!
  4. * =========================================================
  5. * Copy right 2019-2029 上海牛之云网络科技有限公司, 保留所有权利。
  6. * ----------------------------------------------
  7. * 官方网址: https://www.niushop.com
  8. * 这不是一个自由软件!您只能在不用于商业目的的前提下对程序代码进行修改和使用。
  9. * 任何企业和个人不允许对程序代码以任何形式任何目的再发布。
  10. * =========================================================
  11. */
  12. namespace addon\stock\shop\controller;
  13. use addon\stock\model\stock\Allot;
  14. use addon\stock\model\stock\Document;
  15. use addon\stock\model\stock\Inventory;
  16. use addon\stock\model\stock\Stock as StockModel;
  17. use addon\stock\model\Store;
  18. use app\model\goods\GoodsCategory as GoodsCategoryModel;
  19. use app\shop\controller\BaseShop;
  20. /**
  21. * 库存管理
  22. * Class Goods
  23. */
  24. class Stock extends BaseShop
  25. {
  26. /**
  27. * 库存列表
  28. * @return mixed
  29. */
  30. public function manage()
  31. {
  32. if (request()->isAjax()) {
  33. $page = input('page', 1);
  34. $page_size = input('page_size', PAGE_LIST_ROWS);
  35. $search = input('search_text', '');//名称或编码
  36. $category_id = input('category_id', '');//todo 也可以多选
  37. $min_stock = input('min_stock', 0);
  38. $max_stock = input('max_stock', 0);
  39. $store_id = input('store_id', 0);
  40. $field = 'gs.stock,gs.*,g.unit';
  41. $join = array (
  42. [ 'goods g', 'g.goods_id = gs.goods_id', 'left' ],
  43. );
  44. $condition = [];
  45. $condition[] = [ 'gs.site_id', '=', $this->site_id ];
  46. $condition[] = [ 'g.is_delete', '=', 0 ];
  47. $condition[] = [ 'g.goods_class', 'in', [ 1, 6 ] ];
  48. if (!empty($search)) {
  49. $condition[] = [ 'gs.sku_name|sku_no', 'like', '%' . $search . '%' ];
  50. }
  51. if (!empty($category_id)) {
  52. $condition[] = [ 'g.category_id', 'like', '%,' . $category_id . ',%' ];
  53. }
  54. if ($store_id > 0) {
  55. $stock_alias = 'sgs.real_stock';
  56. $join[] = [
  57. 'store_goods_sku sgs',
  58. 'sgs.sku_id = gs.sku_id and (sgs.store_id is null or sgs.store_id = ' . $store_id . ')',
  59. 'left'
  60. ];
  61. $field .= ',sgs.stock, sgs.real_stock';
  62. } else {
  63. $stock_alias = 'gs.real_stock';
  64. $join[] = [
  65. 'store_goods_sku sgs',
  66. 'sgs.sku_id = gs.sku_id or sgs.sku_id is null',
  67. 'left'
  68. ];
  69. $field .= ',gs.stock, sum(sgs.real_stock) as real_stock';
  70. $group = 'gs.sku_id';
  71. }
  72. if ($min_stock > 0 && $max_stock > 0) {
  73. $condition[] = [ $stock_alias, 'between', [ $min_stock, $max_stock ] ];
  74. } else if ($min_stock > 0 && $max_stock == 0) {
  75. $condition[] = [ $stock_alias, '>=', $min_stock ];
  76. } else if ($min_stock == 0 && $max_stock > 0) {
  77. $condition[] = [ $stock_alias, '<=', $max_stock ];
  78. }
  79. $goods_model = new \app\model\goods\Goods();
  80. $res = $goods_model->getGoodsSkuPageList($condition, $page, $page_size, 'g.create_time desc', $field, 'gs', $join, $group ?? null);
  81. return $res;
  82. } else {
  83. //获取一级商品分类
  84. $goods_category_model = new GoodsCategoryModel();
  85. $condition = [
  86. [ 'pid', '=', 0 ],
  87. [ 'site_id', '=', $this->site_id ]
  88. ];
  89. $goods_category_list = $goods_category_model->getCategoryList($condition, 'category_id,category_name,level,commission_rate')[ 'data' ];
  90. $this->assign('goods_category_list', $goods_category_list);
  91. $this->assign('store_list', ( new Store )->getStoreList($this->site_id)[ 'data' ] ?? []);
  92. return $this->fetch('stock/manage');
  93. }
  94. }
  95. /**
  96. * 入库管理(应该豁免盘点)
  97. * @return mixed
  98. */
  99. public function storage()
  100. {
  101. if (request()->isAjax()) {
  102. $document_model = new Document();
  103. $page = input('page', 1);
  104. $page_size = input('page_size', PAGE_LIST_ROWS);
  105. $store_id = !empty(input('store_id', 0)) ? input('store_id', 0) : $this->store_id;
  106. $condition = array (
  107. [ 'site_id', '=', $this->site_id ],
  108. [ 'type', '=', 'input' ]
  109. );
  110. if ($store_id > 0) {
  111. $condition[] = [ 'store_id', 'in', $store_id ];
  112. }
  113. $result = $document_model->getDocumentPageList($condition, $page, $page_size, 'create_time desc');
  114. return $result;
  115. } else {
  116. $this->assign('store_list', ( new Store )->getStoreList($this->site_id)[ 'data' ] ?? []);
  117. return $this->fetch('stock/storage');
  118. }
  119. }
  120. /**
  121. * 创建入库单
  122. */
  123. public function stockin()
  124. {
  125. if (request()->isAjax()) {
  126. $stock_json = input('stock_json', '');
  127. $stock_array = json_decode($stock_json, true);
  128. $store_id = input('store_id', 0);
  129. $document_model = new Document();
  130. $result = $document_model->addPurchase([
  131. 'site_id' => $this->site_id,
  132. 'store_id' => $store_id,
  133. 'user_info' => $this->user_info,
  134. 'goods_sku_list' => $stock_array,
  135. ]);
  136. return $result;
  137. } else {
  138. $this->assign('store_list', ( new Store )->getStoreList($this->site_id)[ 'data' ] ?? []);
  139. return $this->fetch('stock/stockin');
  140. }
  141. }
  142. /**
  143. * 入库单详情
  144. */
  145. public function inputDetail()
  146. {
  147. $document_id = intval(input('document_id', 0));
  148. $document_model = new Document();
  149. $condition = array (
  150. [ 'site_id', '=', $this->site_id ],
  151. [ 'document_id', '=', $document_id ],
  152. [ 'type', '=', 'input' ]
  153. );
  154. $document_detail = $document_model->getDocumentInfo($condition)[ 'data' ] ?? [];
  155. $this->assign('document_detail', $document_detail);
  156. return $this->fetch('stock/input_detail');
  157. }
  158. /**
  159. * 出库管理(应该豁免盘点)
  160. * @return mixed
  161. */
  162. public function wastage()
  163. {
  164. if (request()->isAjax()) {
  165. $document_model = new Document();
  166. $page = input('page', 1);
  167. $page_size = input('page_size', PAGE_LIST_ROWS);
  168. $store_id = !empty(input('store_id', 0)) ? input('store_id', 0) : $this->store_id;
  169. $condition = array (
  170. [ 'site_id', '=', $this->site_id ],
  171. [ 'type', '=', 'output' ]
  172. );
  173. if ($store_id > 0) {
  174. $condition[] = [ 'store_id', 'in', $store_id ];
  175. }
  176. $result = $document_model->getDocumentPageList($condition, $page, $page_size, 'create_time desc');
  177. return $result;
  178. } else {
  179. $this->assign('store_list', ( new Store )->getStoreList($this->site_id)[ 'data' ] ?? []);
  180. return $this->fetch('stock/wastage');
  181. }
  182. }
  183. /**
  184. * 创建出库单
  185. */
  186. public function stockout()
  187. {
  188. if (request()->isAjax()) {
  189. $stock_json = input('stock_json', '');
  190. $store_id = input('store_id', 0);
  191. $stock_array = json_decode($stock_json, true);
  192. $document_model = new Document();
  193. $document_params = array (
  194. 'site_id' => $this->site_id,
  195. 'store_id' => $store_id,
  196. 'user_info' => $this->user_info,
  197. 'goods_sku_list' => $stock_array,
  198. 'is_out_stock' => 1
  199. );
  200. $result = $document_model->addOtherOutput($document_params);
  201. return $result;
  202. } else {
  203. $this->assign('store_list', ( new Store )->getStoreList($this->site_id)[ 'data' ] ?? []);
  204. return $this->fetch('stock/stockout');
  205. }
  206. }
  207. /**
  208. * 出库单详情
  209. */
  210. public function outputDetail()
  211. {
  212. $document_id = input('document_id', 0);
  213. $document_model = new Document();
  214. $condition = array (
  215. [ 'site_id', '=', $this->site_id ],
  216. [ 'document_id', '=', $document_id ],
  217. [ 'type', '=', 'output' ]
  218. );
  219. $document_detail = $document_model->getDocumentInfo($condition)[ 'data' ] ?? [];
  220. $this->assign('document_detail', $document_detail);
  221. return $this->fetch('stock/output_detail');
  222. }
  223. /**
  224. * 库存盘点
  225. * @return mixed
  226. */
  227. public function check()
  228. {
  229. if (request()->isAjax()) {
  230. $page = input('page', 1);
  231. $page_size = input('page_size', PAGE_LIST_ROWS);
  232. $store_id = !empty(input('store_id', 0)) ? input('store_id', 0) : $this->store_id;
  233. $inventory_no = input('inventory_no', '');
  234. $condition = array (
  235. [ 'site_id', '=', $this->site_id ],
  236. );
  237. if ($store_id > 0) {
  238. $condition[] = [ 'store_id', 'in', $store_id ];
  239. }
  240. if ($inventory_no) {
  241. $condition[] = [ 'inventory_no', '=', $inventory_no ];
  242. }
  243. $inventory_model = new Inventory();
  244. $list = $inventory_model->getInventoryPageList($condition, $page, $page_size, 'create_time desc');
  245. return $list;
  246. } else {
  247. $this->assign('store_list', ( new Store )->getStoreList($this->site_id)[ 'data' ] ?? []);
  248. return $this->fetch('stock/check');
  249. }
  250. }
  251. /**
  252. * 库存盘点详情
  253. */
  254. public function inventoryDetail()
  255. {
  256. $inventory_no = input('inventory_no', 0);
  257. $inventory_model = new Inventory();
  258. $condition = array (
  259. [ 'site_id', '=', $this->site_id ],
  260. [ 'inventory_no', '=', $inventory_no ]
  261. );
  262. $inventory_detail = $inventory_model->getInventoryInfo($condition)[ 'data' ] ?? [];
  263. if (empty($inventory_detail)) {
  264. $this->error("盘点单不存在");
  265. }
  266. $this->assign('inventory_detail', $inventory_detail);
  267. return $this->fetch('stock/inventory_detail');
  268. }
  269. /**
  270. *新增盘点记录
  271. * @return mixed
  272. */
  273. public function addCheck()
  274. {
  275. if (request()->isAjax()) {
  276. $inventory_model = new Inventory();
  277. $store_id = input('store_id', 0);
  278. $stock_list = input('stock_json', '');//商品库存映照json {{'sku_id':1, 'stock' : 10, 'cost_price':10,'source':'来源'}}
  279. $stock_list = json_decode($stock_list, true);
  280. $params = array (
  281. 'site_id' => $this->site_id,
  282. 'store_id' => $store_id,
  283. 'sku_list' => $stock_list,
  284. 'user_info' => $this->user_info,
  285. );
  286. $result = $inventory_model->addInventory($params);
  287. return $result;
  288. } else {
  289. $this->assign('store_list', ( new Store )->getStoreList($this->site_id)[ 'data' ] ?? []);
  290. return $this->fetch('stock/add_check');
  291. }
  292. }
  293. /**
  294. * 库存流水
  295. */
  296. public function records()
  297. {
  298. $sku_id = input('sku_id', 0);
  299. $document_model = new Document();
  300. if (request()->isAjax()) {
  301. $page = input('page', 1);
  302. $page_size = input('page_size', PAGE_LIST_ROWS);
  303. $type = input('type', '');
  304. $start_time = input('start_time', 0);
  305. $end_time = input('end_time', 0);
  306. $store_id = input('store_id', 0);
  307. $condition = array (
  308. [ 'dg.site_id', '=', $this->site_id ],
  309. );
  310. if ($sku_id > 0) {
  311. $condition[] = [ 'dg.goods_sku_id', '=', $sku_id ];
  312. }
  313. if (!empty($type)) {
  314. $condition[] = [ 'dt.key', '=', $type ];
  315. }
  316. if ($store_id > 0) {
  317. $condition[] = [ 'd.store_id', '=', $store_id ];
  318. }
  319. //注册时间
  320. if ($start_time != '' && $end_time != '') {
  321. $condition[] = [ 'dg.create_time', 'between', [ strtotime($start_time), strtotime($end_time) ] ];
  322. } else if ($start_time != '' && $end_time == '') {
  323. $condition[] = [ 'dg.create_time', '>=', strtotime($start_time) ];
  324. } else if ($start_time == '' && $end_time != '') {
  325. $condition[] = [ 'dg.create_time', '<=', strtotime($end_time) ];
  326. }
  327. $result = $document_model->getDocumentGoodsPageList($condition, $page, $page_size, 'dg.create_time desc');
  328. return $result;
  329. } else {
  330. $type_list = $document_model->getDocumentTypeList()[ 'data' ] ?? [];
  331. $this->assign('store_list', ( new Store )->getStoreList($this->site_id)[ 'data' ] ?? []);
  332. $this->assign('type_list', $type_list);
  333. $this->assign('sku_id', $sku_id);
  334. return $this->fetch('stock/records');
  335. }
  336. }
  337. /**
  338. * 商品规格列表(仅作临时用)
  339. */
  340. public function getSkuList()
  341. {
  342. $search = input('search', '');
  343. $goods_id = input('goods_id', '');
  344. $store_id = input('store_id', 0);
  345. $stock_model = new StockModel();
  346. $condition = array (
  347. [ 'gs.site_id', '=', $this->site_id ],
  348. [ 'g.goods_class', 'in', [ 1, 6 ] ],
  349. [ 'g.is_delete', '=', 0 ]
  350. );
  351. if (!empty($search)) {
  352. $condition[] = [ 'gs.sku_name|gs.sku_no', 'like', '%' . $search . '%' ];
  353. }
  354. if (!empty($goods_id)) {
  355. $condition[] = [ 'g.goods_id', '=', $goods_id ];
  356. }
  357. if ($store_id > 0) {
  358. //查询商品支持门店(支持当前门店或全部)
  359. $condition[] = [ 'g.sale_store', 'like', [ "%," . $store_id . ",%", "%all%" ], 'or' ];
  360. // $condition[] = ['sgs.store_id', '=', $store_id];
  361. }
  362. $sku_list = $stock_model->getStoreGoodsSkuList($condition, 'gs.*,sgs.stock,sgs.real_stock,sgs.price,sgs.cost_price', 'gs.create_time desc', $store_id);
  363. return $sku_list;
  364. }
  365. /**
  366. * 更新库存
  367. */
  368. public function skuInput()
  369. {
  370. $params = array (
  371. 'site_id' => $this->site_id,
  372. 'sku_id' => input('sku_id', 0),
  373. 'goods_id' => input('goods_id', 0),
  374. 'store_id' => input('store_id', 0),
  375. 'key' => input('key', 0),
  376. 'remark' => input('remark', 0),
  377. 'goods_num' => input('goods_num', 0),
  378. 'goods_price' => input('goods_price', 0),
  379. 'user_info' => $this->user_info,
  380. 'time' => input('time', 0),
  381. );
  382. $stock_model = new StockModel();
  383. $result = $stock_model->changeStock($params);
  384. return $result;
  385. }
  386. /**
  387. * 调拨单
  388. * @return mixed
  389. */
  390. public function allocate()
  391. {
  392. if (request()->isAjax()) {
  393. $allot_model = new Allot();
  394. $page = input('page', 1);
  395. $page_size = input('page_size', PAGE_LIST_ROWS);
  396. $output_store_id = input('output_store_id', 0);
  397. $input_store_id = input('input_store_id', 0);
  398. $allot_no = input('allot_no', '');
  399. $condition = array (
  400. [ 'site_id', '=', $this->site_id ],
  401. );
  402. if ($output_store_id > 0) {
  403. $condition[] = [ 'output_store_id', '=', $output_store_id ];
  404. }
  405. if ($input_store_id > 0) {
  406. $condition[] = [ 'input_store_id', '=', $input_store_id ];
  407. }
  408. if ($allot_no) {
  409. $condition[] = [ 'allot_no', '=', $allot_no ];
  410. }
  411. $result = $allot_model->getStockAllotPageList($condition, $page, $page_size, 'create_time desc');
  412. return $result;
  413. } else {
  414. $this->assign('store_list', ( new Store )->getStoreList($this->site_id)[ 'data' ] ?? []);
  415. return $this->fetch('stock/allocate');
  416. }
  417. }
  418. /**
  419. * 调拨记录
  420. * @return mixed
  421. */
  422. public function allotrecords()
  423. {
  424. $allot_id = input('allot_id', 0);
  425. $allot_model = new Allot();
  426. if (request()->isAjax()) {
  427. $page = input('page', 1);
  428. $page_size = input('page_size', PAGE_LIST_ROWS);
  429. $output_store_id = input('output_store_id', 0);
  430. $input_store_id = input('input_store_id', 0);
  431. $allot_no = input('allot_no', '');
  432. $condition = array (
  433. [ 'site_id', '=', $this->site_id ],
  434. );
  435. if ($output_store_id > 0) {
  436. $condition[] = [ 'output_store_id', '=', $output_store_id ];
  437. }
  438. if ($input_store_id > 0) {
  439. $condition[] = [ 'input_store_id', '=', $input_store_id ];
  440. }
  441. if ($allot_no > 0) {
  442. $condition[] = [ 'allot_no', '=', $allot_no ];
  443. }
  444. $result = $allot_model->getStockAllotPageList($condition, $page, $page_size, 'create_time desc');
  445. return $result;
  446. } else {
  447. $condition = array (
  448. [ 'site_id', '=', $this->site_id ],
  449. [ 'allot_id', '=', $allot_id ],
  450. );
  451. $detail = $allot_model->getAllotInfo($condition)[ 'data' ] ?? [];
  452. $this->assign('detail', $detail);
  453. return $this->fetch('stock/allot_records');
  454. }
  455. }
  456. /**
  457. * 创建调拨单
  458. */
  459. public function addAllocate()
  460. {
  461. if (request()->isAjax()) {
  462. $allot_model = new Allot();
  463. $data = [
  464. 'output_store_id' => input('output_store_id', 0),
  465. 'input_store_id' => input('input_store_id', 0),
  466. 'remark' => input('remark', ''),
  467. 'goods_sku_list' => input('goods_sku_list', '') ? json_decode(input('goods_sku_list'), true) : [],
  468. 'allot_time' => input('allot_time', '') ? date_to_time(input('allot_time')) : 0,
  469. 'operater' => $this->uid,
  470. 'site_id' => $this->site_id
  471. ];
  472. $result = $allot_model->addAllot($data);
  473. return $result;
  474. } else {
  475. $this->assign('store_list', ( new Store )->getStoreList($this->site_id)[ 'data' ] ?? []);
  476. $this->assign('start_time', date('Y-m-d'));
  477. $this->assign('default_time', date('Y-m-d H:i:s'));
  478. return $this->fetch('stock/add_allocate');
  479. }
  480. }
  481. }