PurchaseClass.php 70 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700
  1. <?php
  2. namespace bizHd\purchase\classes;
  3. use biz\ghs\classes\GhsClass;
  4. use biz\product\classes\XjClass;
  5. use biz\shop\classes\ShopCapitalClass;
  6. use biz\shop\classes\ShopClass;
  7. use biz\stat\classes\StatCgClass;
  8. use biz\stat\classes\StatCgGhsClass;
  9. use biz\stat\classes\StatOutClass;
  10. use biz\stock\classes\StockClass;
  11. use biz\wx\classes\WxMessageClass;
  12. use bizGhs\admin\classes\AdminClass;
  13. use bizGhs\custom\classes\CustomClass;
  14. use bizGhs\item\classes\CostChangeClass;
  15. use bizGhs\order\classes\OrderClass;
  16. use bizGhs\order\classes\OrderMergeSnClass;
  17. use bizGhs\order\traits\OrderTrait;
  18. use bizGhs\product\classes\ProductClass;
  19. use bizGhs\stock\classes\OnStockRecordClass;
  20. use bizGhs\stock\classes\StockRecordClass;
  21. use bizHd\cg\classes\CgMergeClass;
  22. use bizHd\cg\classes\CgMergeSnClass;
  23. use bizHd\cg\models\CgMerge;
  24. use bizHd\cg\classes\CgRefundClass;
  25. use bizHd\cg\classes\CgTreeClass;
  26. use bizHd\cg\classes\CgXjClass;
  27. use bizHd\shop\classes\MainClass;
  28. use bizHd\stat\classes\StatIncomeClass;
  29. use common\components\dict;
  30. use common\components\imgUtil;
  31. use common\components\IntraCityExpress;
  32. use common\components\mapUtil;
  33. use common\components\noticeUtil;
  34. use common\components\orderSn;
  35. use common\components\stringUtil;
  36. use common\components\util;
  37. use bizHd\base\classes\BaseClass;
  38. use common\components\lakala\Lakala;
  39. use Yii;
  40. use yii\helpers\ArrayHelper;
  41. class PurchaseClass extends BaseClass
  42. {
  43. use OrderTrait;
  44. public static $baseFile = '\bizHd\purchase\models\Purchase';
  45. //订单状态 1待付款,待确认,2待配送,3配送中,4已完成,5取消,失败
  46. const STATUS_UN_PAY = 1;
  47. const STATUS_UN_SEND = 2;
  48. const STATUS_SENDING = 3;
  49. const STATUS_COMPLETE = 4;
  50. const STATUS_CANCEL = 5;
  51. //未知
  52. const DEBT_UNKNOWN = 0;
  53. //欠款
  54. const DEBT_YES = 1;
  55. //没有欠款
  56. const DEBT_NO = 2;
  57. //配送,这里的数值是错的,请参考dict.php的数值
  58. const GET_TYPE_SEND = 1;
  59. //自取,这里的数值是错的,请参考dict.php的数值
  60. const GET_TYPE_SELF_GET = 2;
  61. const REFUND_NO = 1;
  62. const REFUND_YES = 2;
  63. //更新同一供货商、同一天的多张采购单合并 $update 0 添加 1 修改(写法对齐 OrderClass::updateMergeOrder)
  64. public static function updateMergeCg($cg, $update = 0)
  65. {
  66. $date = substr($cg->payTime, 0, 10);
  67. $ghsId = $cg->ghsId ?? 0;
  68. $mainId = $cg->mainId ?? 0;
  69. $cgId = $cg->id ?? 0;
  70. //当前零售商 + 供货商 + 账单日 下的采购单
  71. $startTime = $date . ' 00:00:00';
  72. $endTime = $date . ' 23:59:59';
  73. $dayOrders = self::getAllByCondition([
  74. 'mainId' => $mainId,
  75. 'ghsId' => $ghsId,
  76. 'payTime' => ['between', [$startTime, $endTime]]
  77. ], 'id ASC', 'id');
  78. if (empty($dayOrders)) {
  79. return false;
  80. }
  81. $ids = array_column($dayOrders, 'id');
  82. $count = count($ids);
  83. if ($update == 0) {
  84. if ($count <= 1) {
  85. //新增,如果当天只有一个订单,不需要操作
  86. return false;
  87. }
  88. $hasMerge = CgMergeSnClass::getByCondition(['ghsId' => $ghsId, 'mergeDate' => $date], true);
  89. if (!empty($hasMerge)) {
  90. $hasMergeId = $hasMerge->id ?? 0;
  91. $cg->mergeId = $hasMergeId;
  92. $cg->save();
  93. CgMergeClass::add(['mergeId' => $hasMergeId, 'cgId' => $cgId, 'mergeDate' => $date, 'ghsId' => $ghsId, 'mainId' => $mainId]);
  94. self::syncCgMergeNumForMergeId($hasMergeId);
  95. self::syncMergeRelationByCgIds([$cgId]);
  96. } else {
  97. self::rebuildCgMergeGroupForDay($ids, $date, $ghsId, $mainId);
  98. }
  99. } else {
  100. //修改账单日:payTime 已在调用方改为新日 $date
  101. $oldMergeId = (int)($cg->mergeId ?? 0);
  102. if ($oldMergeId > 0) {
  103. $oldMergeSn = CgMergeSnClass::getById($oldMergeId, true);
  104. if (empty($oldMergeSn)) {
  105. $cg->mergeId = 0;
  106. $cg->mergeNum = 0;
  107. $cg->save();
  108. } else {
  109. $oldDate = $oldMergeSn->mergeDate ?? '';
  110. if ($oldDate == $date) {
  111. return false;
  112. }
  113. $oldMergeList = CgMergeClass::getAllByCondition(['mergeId' => $oldMergeId], null, '*', null, true);
  114. if (count($oldMergeList) <= 2) {
  115. $oldMergeSn->delete();
  116. $del = [];
  117. foreach ($oldMergeList as $mergeRow) {
  118. $del[] = $mergeRow->cgId;
  119. $mergeRow->delete();
  120. }
  121. self::updateByIds($del, ['mergeId' => 0, 'mergeNum' => 0]);
  122. } else {
  123. $cg->mergeId = 0;
  124. $cg->mergeNum = 0;
  125. $cg->save();
  126. CgMergeClass::deleteByCondition(['mergeId' => $oldMergeId, 'cgId' => $cgId]);
  127. self::syncCgMergeNumForMergeId($oldMergeId);
  128. }
  129. }
  130. }
  131. $hasNewMerge = CgMergeSnClass::getByCondition(['ghsId' => $ghsId, 'mergeDate' => $date], true);
  132. if (!empty($hasNewMerge)) {
  133. $newMergeId = $hasNewMerge->id ?? 0;
  134. $cg->mergeId = $newMergeId;
  135. $cg->save();
  136. CgMergeClass::add(['mergeId' => $newMergeId, 'cgId' => $cgId, 'mergeDate' => $date, 'ghsId' => $ghsId, 'mainId' => $mainId]);
  137. self::syncCgMergeNumForMergeId($newMergeId);
  138. self::syncMergeRelationByCgIds([$cgId]);
  139. } else {
  140. self::rebuildCgMergeGroupForDay($ids, $date, $ghsId, $mainId);
  141. }
  142. }
  143. return true;
  144. }
  145. /**
  146. * 指定零售商、供货商、某日多采购单:清理旧明细后新建合并主档,回写 mergeId,并写入 xhCgMerge。
  147. *
  148. * @param int[] $ids 当日同供货商采购单 id 列表
  149. */
  150. private static function rebuildCgMergeGroupForDay(array $ids, $date, $ghsId, $mainId)
  151. {
  152. if (!empty($ids)) {
  153. CgMerge::deleteAll(['cgId' => $ids]);
  154. }
  155. $mergeData = ['mergeDate' => $date, 'ghsId' => $ghsId, 'mainId' => $mainId];
  156. $mergeId = orderSn::getCgMergeId($mergeData);
  157. $mergeNum = count($ids);
  158. self::updateByIds($ids, ['mergeId' => $mergeId, 'mergeNum' => $mergeNum]);
  159. $addData = [];
  160. foreach ($ids as $currentId) {
  161. $addData[] = ['mergeId' => $mergeId, 'cgId' => $currentId, 'mergeDate' => $date, 'ghsId' => $ghsId, 'mainId' => $mainId];
  162. }
  163. CgMergeClass::batchAdd($addData);
  164. self::syncMergeRelationByCgIds($ids);
  165. }
  166. public static function syncMergeRelationByOrderIds($orderIds)
  167. {
  168. if (empty($orderIds)) {
  169. return false;
  170. }
  171. $orders = OrderClass::getByIds($orderIds, null, null, 'id,purchaseId');
  172. if (empty($orders)) {
  173. return false;
  174. }
  175. $cgIds = [];
  176. foreach ($orders as $order) {
  177. $purchaseId = $order['purchaseId'] ?? 0;
  178. if (!empty($purchaseId)) {
  179. $cgIds[] = $purchaseId;
  180. }
  181. }
  182. return self::syncMergeRelationByCgIds($cgIds);
  183. }
  184. public static function syncMergeRelationByCgIds($cgIds)
  185. {
  186. if (empty($cgIds)) {
  187. return false;
  188. }
  189. $cgIds = array_values(array_unique(array_filter($cgIds)));
  190. if (empty($cgIds)) {
  191. return false;
  192. }
  193. $cgs = self::getByIds($cgIds, null, null, 'id,saleId,mergeId');
  194. if (empty($cgs)) {
  195. return false;
  196. }
  197. foreach ($cgs as $cg) {
  198. $cgId = $cg['id'] ?? 0;
  199. $orderId = $cg['saleId'] ?? 0;
  200. $cgMergeId = $cg['mergeId'] ?? 0;
  201. if (empty($cgId) || empty($orderId) || empty($cgMergeId)) {
  202. continue;
  203. }
  204. $order = OrderClass::getById($orderId, false, 'id,mergeId');
  205. $ghsMergeId = $order['mergeId'] ?? 0;
  206. if (empty($ghsMergeId)) {
  207. continue;
  208. }
  209. $cgMergeSn = CgMergeSnClass::getById($cgMergeId, false, 'id,salt');
  210. $ghsMergeSn = OrderMergeSnClass::getById($ghsMergeId, false, 'id,salt');
  211. $cgSalt = $cgMergeSn['salt'] ?? '';
  212. $ghsSalt = $ghsMergeSn['salt'] ?? '';
  213. if (empty($cgSalt) || empty($ghsSalt)) {
  214. continue;
  215. }
  216. CgMergeSnClass::updateById($cgMergeId, [
  217. 'ghsMergeId' => $ghsMergeId,
  218. 'ghsSalt' => $ghsSalt,
  219. ]);
  220. OrderMergeSnClass::updateById($ghsMergeId, [
  221. 'cgMergeId' => $cgMergeId,
  222. 'cgSalt' => $cgSalt,
  223. ]);
  224. }
  225. return true;
  226. }
  227. /** 按 xhCgMerge 明细数量,回写该合并组内所有采购单的 mergeNum */
  228. private static function syncCgMergeNumForMergeId($mergeId)
  229. {
  230. $mergeId = (int)$mergeId;
  231. if ($mergeId <= 0) {
  232. return;
  233. }
  234. $rows = CgMergeClass::getAllByCondition(['mergeId' => $mergeId], null, 'cgId', null, true);
  235. $mergeNum = count($rows);
  236. if ($mergeNum === 0) {
  237. return;
  238. }
  239. $cgIds = [];
  240. foreach ($rows as $row) {
  241. $cgIds[] = (int)(is_object($row) ? ($row->cgId ?? 0) : ($row['cgId'] ?? 0));
  242. }
  243. $cgIds = array_values(array_filter($cgIds));
  244. if (!empty($cgIds)) {
  245. self::updateByIds($cgIds, ['mergeNum' => $mergeNum]);
  246. }
  247. }
  248. //获取打包费
  249. public static function getKmPackCost($mainId)
  250. {
  251. $cost = dict::getDict('kmPackCost');
  252. if (getenv('YII_ENV') == 'production') {
  253. //云漫梦金鹏 徐记花卉 康多鲜 旭海 打包费0元
  254. if (in_array($mainId, [10728, 20528, 10866, 38657, 41121])) {
  255. $cost = dict::getDict('kmPackCost2');
  256. }
  257. //花瀚鲜花供应链
  258. if (in_array($mainId, [50, 14499])) {
  259. $cost = dict::getDict('kmPackCost3');
  260. }
  261. } else {
  262. }
  263. return $cost;
  264. }
  265. //订单发货主方法 ssh 20231119
  266. public static function orderSend($cg, $params, $allowRepeat)
  267. {
  268. $respond = self::orderFh($cg, $params, $allowRepeat);
  269. $orderId = $cg->saleId ?? 0;
  270. $orderInfo = OrderClass::getById($orderId, true);
  271. if (empty($orderInfo)) {
  272. util::fail('没有找到销售单信息');
  273. }
  274. OrderClass::orderFh($orderInfo, $params, $allowRepeat);
  275. return $respond;
  276. }
  277. public static function cancelFh($cg, $params)
  278. {
  279. $cg->status = 2;
  280. $cg->save();
  281. $orderSn = $cg->orderSn ?? '';
  282. $itemList = PurchaseItemClass::getAllByCondition(['orderSn' => $orderSn], null, '*', null, true);
  283. if (!empty($itemList)) {
  284. $sjId = $cg->sjId ?? 0;
  285. $shopId = $cg->shopId ?? 0;
  286. $mainId = $cg->mainId ?? 0;
  287. $ghsName = $cg->ghsName ?? '';
  288. foreach ($itemList as $key => $val) {
  289. $productId = $val->productId ?? 0;
  290. $itemId = $val->itemId ?? 0;
  291. $num = $val->xhNum ?? 0;
  292. $product = ProductClass::getLockById($productId);
  293. if (empty($product)) {
  294. util::fail('没有找到花材呢');
  295. }
  296. //减少路上库存
  297. $onRecord = [];
  298. $onRecord['sjId'] = $sjId;
  299. $onRecord['shopId'] = $shopId;
  300. $onRecord['mainId'] = $mainId;
  301. $onRecord['orderSn'] = $orderSn;
  302. $onRecord['productId'] = $productId;
  303. $onRecord['itemId'] = $itemId;
  304. $onRecord['relateName'] = $ghsName;
  305. $onRecord['itemNum'] = $num;
  306. $oldOnStock = $product->onStock ?? 0;
  307. $onRecord['oldStock'] = $oldOnStock;
  308. $newOnStock = bcsub($oldOnStock, $num);
  309. $onRecord['newStock'] = $newOnStock;
  310. $onRecord['ptStyle'] = 1;
  311. OnStockRecordClass::cancelCg($onRecord);
  312. ProductClass::decreaseOnStockByItemNum($productId, $num);
  313. }
  314. }
  315. }
  316. /**
  317. * 职责:采购单/订单发货子方法
  318. * 入参:$cg:采购实体模型, $params:物流等发货参数, $allowRepeat:是否允许重复发货, $notice:是否发送发货通知
  319. * 返回:更新后的采购实体模型
  320. * 副作用:会变更订单状态,增加在途(路上)库存并记录在途库存日志
  321. * 关键边界:使用商品ID升序排队悲观锁防死锁,支持乱序对单
  322. */
  323. public static function orderFh($cg, $params, $allowRepeat = false, $notice = false)
  324. {
  325. if ($notice) {
  326. $noticeText = json_encode(['id' => $cg->id]);
  327. $noticeKey = "ghsKdNoticeHd";
  328. Yii::$app->redis->executeCommand('LPUSH', [$noticeKey, $noticeText]);
  329. }
  330. if ($cg->status == 3) {
  331. if (!$allowRepeat) {
  332. util::fail('订单已发货');
  333. } else {
  334. return true;
  335. }
  336. }
  337. $fhType = $params['fhType'] ?? 0;
  338. if (!is_numeric($fhType)) {
  339. util::fail('请选择发货类型');
  340. }
  341. if ($fhType == 1) {
  342. $fhWl = $params['fhWl'] ?? '';
  343. $fhWlNo = $params['fhWlNo'] ?? '';
  344. if (empty($fhWl)) {
  345. util::fail('请填写物流名称');
  346. }
  347. if (empty($fhWlNo)) {
  348. util::fail('请填写物流编号');
  349. }
  350. $cg->fhWl = $fhWl;
  351. $cg->fhWlNo = $fhWlNo;
  352. }
  353. $cg->fhType = $fhType;
  354. $cg->status = 3;
  355. $cg->fhTime = date("Y-m-d H:i:s");
  356. $cg->save();
  357. $orderSn = $cg->orderSn ?? '';
  358. $itemList = PurchaseItemClass::getAllByCondition(['orderSn' => $orderSn], null, '*', null, true);
  359. if (!empty($itemList)) {
  360. $sjId = $cg->sjId ?? 0;
  361. $shopId = $cg->shopId ?? 0;
  362. $mainId = $cg->mainId ?? 0;
  363. $ghsName = $cg->ghsName ?? '';
  364. // 复杂分支/关键逻辑:优化。防高并发死锁,同时保留原始客户对单顺序。
  365. // 步骤1:临时提取出所有商品 ID,进行去重和升序排序。
  366. // 步骤2:按照升序加锁商品,避免并发不同请求由于顺序不一致引起交叉死锁。
  367. // 步骤3:在主循环里依旧使用原始的 $itemList 顺序处理,100%保留对单及展现顺序。
  368. $productIds = [];
  369. foreach ($itemList as $val) {
  370. $pId = $val->productId ?? 0;
  371. if ($pId > 0) {
  372. $productIds[] = (int)$pId;
  373. }
  374. }
  375. if (!empty($productIds)) {
  376. $productIds = array_unique($productIds);
  377. sort($productIds);
  378. foreach ($productIds as $pId) {
  379. ProductClass::getLockById($pId);
  380. }
  381. }
  382. foreach ($itemList as $key => $val) {
  383. $productId = $val->productId ?? 0;
  384. $itemId = $val->itemId ?? 0;
  385. $num = $val->xhNum ?? 0;
  386. $product = ProductClass::getLockById($productId);
  387. if (empty($product)) {
  388. util::fail('没有找到花材呢');
  389. }
  390. //增加路上库存
  391. $onRecord = [];
  392. $onRecord['sjId'] = $sjId;
  393. $onRecord['shopId'] = $shopId;
  394. $onRecord['mainId'] = $mainId;
  395. $onRecord['orderSn'] = $orderSn;
  396. $onRecord['productId'] = $productId;
  397. $onRecord['itemId'] = $itemId;
  398. $onRecord['relateName'] = $ghsName;
  399. $onRecord['itemNum'] = $num;
  400. $oldOnStock = $product->onStock ?? 0;
  401. $onRecord['oldStock'] = $oldOnStock;
  402. $newOnStock = bcadd($oldOnStock, $num);
  403. $onRecord['newStock'] = $newOnStock;
  404. $onRecord['ptStyle'] = 1;
  405. OnStockRecordClass::addPurchaseOrderRecord($onRecord);
  406. ProductClass::addOnStockByItemNum($productId, $num);
  407. }
  408. }
  409. return $cg;
  410. }
  411. //确认收货 ssh 20231119
  412. public static function confirmTake($cg)
  413. {
  414. //订单如果没有发货,则先发货
  415. if ($cg->status == self::STATUS_UN_SEND) {
  416. $cg = self::orderSend($cg, [], true);
  417. }
  418. // 构建“冲销”函数的参数 -- 变动前的花材存库数据
  419. $purchaseItems = PurchaseItemClass::getAllByCondition(['orderSn' => $cg->orderSn], null, "productId");
  420. $productIds = ArrayHelper::getColumn($purchaseItems, 'productId'); //$productIds 是本次入库涉及的花材ID数组
  421. $oldStocks = [];
  422. foreach ($productIds as $productId) {
  423. $product = ProductClass::getById($productId, true);
  424. $oldStocks[$productId] = $product;
  425. }
  426. self::takeToPutIn($cg);
  427. $shop = ShopClass::getLockById($cg->shopId);
  428. if (empty($shop)) {
  429. util::fail('没有找到门店14');
  430. }
  431. $adminId = $shop->adminId;
  432. $mainId = $shop->mainId;
  433. PurchaseClass::rollbackProductStock($oldStocks, $shop, $adminId, $mainId, '系统');
  434. }
  435. /**
  436. * 职责:采购单/订单确认收货并入库操作
  437. * 入参:$cg:采购实体模型
  438. * 返回:无(失败抛异常)
  439. * 副作用:更新销售单和采购单状态为已收货/已完成,增加花材库存并记录库存流水,减少在途(路上)库存并更新商品成本单价
  440. * 关键边界:使用商品ID升序排队悲观锁防死锁,支持乱序对单
  441. */
  442. public static function takeToPutIn($cg)
  443. {
  444. if ($cg->status != 3) {
  445. util::fail('订单不是待收货状态哦,编号:cg' . $cg->id);
  446. }
  447. $saleId = $cg->saleId ?? 0;
  448. $order = OrderClass::getById($saleId, true);
  449. if (empty($order)) {
  450. util::fail('没有找到订单');
  451. }
  452. if ($order->status != 3) {
  453. util::fail('订单不是待收货状态,订单状态:' . $order->status . ',订单ID:' . $saleId);
  454. }
  455. $order->status = 4;
  456. $order->save();
  457. //加库存 lqh 2021-05-10
  458. $orderSn = $cg->orderSn;
  459. $sjId = $cg->sjId ?? 0;
  460. $shopId = $cg->shopId ?? 0;
  461. $mainId = $cg->mainId ?? 0;
  462. $ghsName = $cg->ghsName ?? '';
  463. $purchaseItem = PurchaseItemClass::getAllByCondition(['orderSn' => $orderSn], null, "*");
  464. // 复杂分支/关键逻辑:优化。防高并发死锁,同时保留原始客户对单顺序。
  465. // 步骤1:从商品详情列表中临时提取出所有商品 ID,进行去重和升序排序。
  466. // 步骤2:按照升序加锁商品,避免并发不同请求由于顺序不一致引起交叉死锁。
  467. // 步骤3:在主循环里依旧使用原始的 $purchaseItem 顺序处理,100%保留对单及展现顺序。
  468. $productIds = [];
  469. foreach ($purchaseItem as $v) {
  470. $pId = $v['productId'] ?? 0;
  471. if ($pId > 0) {
  472. $productIds[] = (int)$pId;
  473. }
  474. }
  475. if (!empty($productIds)) {
  476. $productIds = array_unique($productIds);
  477. sort($productIds);
  478. foreach ($productIds as $pId) {
  479. ProductClass::getLockById($pId);
  480. }
  481. }
  482. foreach ($purchaseItem as $v) {
  483. //小单位库存不考虑入库 ssh 202310
  484. $itemNum = $v['itemNum'];
  485. if ($itemNum > 0) {
  486. $productId = $v['productId'];
  487. $stockInfo = ProductClass::addStockByItemNum($v['productId'], $itemNum);
  488. //记录库存流水
  489. $recordData = [];
  490. $recordData['sjId'] = $cg->sjId;
  491. $recordData['shopId'] = $cg->shopId;
  492. $recordData['mainId'] = $cg->mainId;
  493. $recordData['itemId'] = $v['itemId'] ?? 0;
  494. $recordData['itemNum'] = $itemNum;
  495. $recordData['oldStock'] = $stockInfo['oldStock'];
  496. $recordData['newStock'] = $stockInfo['newStock'];
  497. $recordData['productId'] = $v['productId'];
  498. $recordData['orderSn'] = $orderSn;
  499. $recordData['relateName'] = $cg->ghsName ?? '';
  500. StockRecordClass::addPurchaseOrderRecord($recordData);
  501. $product = ProductClass::getLockById($productId);
  502. if (empty($product)) {
  503. util::fail('没有找到花材哈');
  504. }
  505. //减少路上库存
  506. $onRecord = [];
  507. $onRecord['sjId'] = $sjId;
  508. $onRecord['shopId'] = $shopId;
  509. $onRecord['mainId'] = $mainId;
  510. $onRecord['orderSn'] = $orderSn;
  511. $onRecord['productId'] = $productId;
  512. $onRecord['itemId'] = $v['itemId'] ?? 0;
  513. $onRecord['relateName'] = $ghsName;
  514. $onRecord['itemNum'] = $itemNum;
  515. $oldOnStock = $product->onStock ?? 0;
  516. $onRecord['oldStock'] = $oldOnStock;
  517. $onRecord['newStock'] = bcsub($oldOnStock, $itemNum);
  518. $onRecord['ptStyle'] = 1;
  519. OnStockRecordClass::addInStockOrderRecord($onRecord);
  520. ProductClass::decreaseOnStockByItemNum($productId, $itemNum);
  521. $unitPrice = $v['xhUnitPrice'] ?? 0;
  522. $product->cost = $unitPrice;
  523. $product->save();
  524. $ptItemId = $product->itemId ?? 0;
  525. $mainId = $product->mainId ?? 0;
  526. $staffId = $cg->shopAdminId ?? 0;
  527. $staffName = $cg->shopAdminName ?? '';
  528. //成本变动记录
  529. $changeData = [
  530. 'ptStyle' => 2,
  531. 'sjId' => 0,
  532. 'mainId' => $mainId,
  533. 'itemId' => $productId,
  534. 'ptItemId' => $ptItemId,
  535. 'cost' => $unitPrice,
  536. 'staffId' => $staffId,
  537. 'staffName' => $staffName,
  538. 'name' => $product->name ?? '',
  539. 'cover' => $product->cover ?? '',
  540. 'targetId' => $cg->id ?? 0,
  541. 'event' => '采购',
  542. ];
  543. CostChangeClass::addData($changeData);
  544. }
  545. }
  546. $cg->status = 4;
  547. $cg->stockChange = 1;
  548. $cg->save();
  549. }
  550. // 库存回滚为入库前的状态
  551. public static function rollbackProductStock($oldStocks, $shop, $adminId, $mainId, $staffName)
  552. {
  553. $itemInfo = [];
  554. foreach ($oldStocks as $productId => $product) {
  555. $ratio = $product->ratio;
  556. $formNum = ProductClass::formatStock($product->stock, $ratio); //花材的库存转换
  557. $itemInfo[] = [
  558. 'productId' => $productId,
  559. 'itemNum' => $product->stock, // 回滚为入库前的库存
  560. 'itemId' => $product->itemId,
  561. 'bigNum' => $formNum['bigNum'],
  562. 'smallNum' => $formNum['smallNum'],
  563. 'price' => $product->price,
  564. ];
  565. }
  566. $data = [
  567. 'sjId' => $shop->sjId,
  568. 'shopId' => $shop->id,
  569. 'adminId' => $adminId,
  570. 'mainId' => $mainId,
  571. 'staffName' => $staffName,
  572. 'itemInfo' => $itemInfo,
  573. 'pdType' => 'rollbackStock',
  574. 'remark' => '',
  575. ];
  576. \bizGhs\order\classes\CheckOrderClass::opOrder($data, false, false, false);
  577. }
  578. // 指定花材新增库存
  579. public static function increaseProductStock($itemDatas, $shop, $adminId, $mainId, $staffName)
  580. {
  581. $itemInfo = [];
  582. foreach ($itemDatas as $productId => $arr) {
  583. $addNum = $arr['addNum']; //新增的数值
  584. $product = $arr['product'];
  585. $ratio = $product->ratio;
  586. $newStock = bcadd($product->stock, $addNum, 2);
  587. $formNum = ProductClass::formatStock($newStock, $ratio); //花材的库存转换
  588. $itemInfo[] = [
  589. 'productId' => $productId,
  590. 'itemNum' => $newStock, // 新库存数
  591. 'itemId' => $product->itemId,
  592. 'bigNum' => $formNum['bigNum'],
  593. 'smallNum' => $formNum['smallNum'],
  594. 'price' => $product->price,
  595. ];
  596. }
  597. $data = [
  598. 'sjId' => $shop->sjId,
  599. 'shopId' => $shop->id,
  600. 'adminId' => $adminId,
  601. 'mainId' => $mainId,
  602. 'staffName' => $staffName,
  603. 'itemInfo' => $itemInfo,
  604. 'pdType' => 'rollbackStock',
  605. 'remark' => '',
  606. ];
  607. \bizGhs\order\classes\CheckOrderClass::opOrder($data, false, false, false);
  608. }
  609. public static function notSameCity($shop, $ghsShop)
  610. {
  611. return true;
  612. $shopLat = $shop->lat ?? '';
  613. $shopLong = $shop->long ?? '';
  614. if (empty($shopLat) || empty($shopLong)) {
  615. util::fail('请完善门店地址');
  616. }
  617. $ghsShopLat = $ghsShop->lat ?? '';
  618. $ghsShopLong = $ghsShop->long ?? '';
  619. $distance = mapUtil::calculateDistance($shopLong, $shopLat, $ghsShopLong, $ghsShopLat);
  620. $formatDistance = ceil($distance / 1000);
  621. if ($formatDistance > 150) {
  622. return false;
  623. }
  624. return true;
  625. }
  626. //计算距离和运费【另外还有个地方要同步修改,关键词calc_freight_distance】 ssh 20221003
  627. public static function getDistanceFee($shop, $ghsShop, $weight, $custom)
  628. {
  629. $ability = ShopClass::hasIntraCity($ghsShop);
  630. $openIntraCity = $ability['openIntraCity'] ?? 0;
  631. if ($openIntraCity == 0) {
  632. //没有开启同城配送能力,不计算运费和距离
  633. return ['distance' => 0, 'showDistance' => 0, 'fee' => 0];
  634. }
  635. $shopLat = $shop->lat ?? '';
  636. $shopLong = $shop->long ?? '';
  637. if (empty($shopLat) || empty($shopLong)) {
  638. util::fail('请完善你的收花地址');
  639. }
  640. $address = $shop->address ?? '';
  641. if (empty($address)) {
  642. util::fail('请完善收花地址');
  643. }
  644. if ($weight <= 0) {
  645. util::fail('花材没有重量');
  646. }
  647. $wxStoreId = $ability['wxStoreId'] ?? '';
  648. $packageNum = 1;
  649. $price = 300;
  650. $cargo = [
  651. 'cargo_name' => '鲜花花材', // 商品名称 -- $order 中没有,要在 goodsInfo 中取
  652. 'cargo_weight' => intval($weight * 1000), // 单位:克 -- 把千克转换为克
  653. 'cargo_price' => intval($price * 100), // 单位:分 -- 把元转换为分
  654. 'cargo_type' => IntraCityExpress::GOODS_TYPE_FLOWER,
  655. 'cargo_num' => $packageNum
  656. ];
  657. $customName = $custom->name ?? '';
  658. if (empty($customName)) {
  659. util::fail('客户名称是空的');
  660. }
  661. $customMobile = $custom->mobile ?? '';
  662. if (empty($customMobile)) {
  663. util::fail('客户手机缺失');
  664. }
  665. //根据环境变量判断是否使用沙盒环境
  666. // $useSandBox = getenv('YII_ENV') == 'production' ? 0 : 1;
  667. $orderData = [
  668. 'wx_store_id' => $wxStoreId,
  669. 'user_name' => $customName,
  670. 'user_phone' => $customMobile,
  671. 'user_lng' => $shopLong,
  672. 'user_lat' => $shopLat,
  673. 'user_address' => $address,
  674. 'cargo' => $cargo,
  675. // 'use_sandbox' => $useSandBox,//正式环境不能传这个参数,会报错
  676. ];
  677. //多处有用到这个方法,需修改请同步修改,搜索关键词 intra_city_express_pre_add
  678. $result = IntraCityExpress::preAddOrder($orderData);
  679. if ($result['errcode'] !== 0) {
  680. $errMsg = $result['errmsg'] ?? '';
  681. noticeUtil::push('运费查询失败原因:' . $errMsg . '参数:' . $result['errcode'], '15280215347');
  682. util::fail('运费查询失败,编号1160');
  683. }
  684. $distance = $result['distance'] ?? 0;
  685. $showDistance = sprintf("%.1f", ($distance / 1000));
  686. $fee = $result['est_fee'] ?? 0;
  687. $totalFee = sprintf("%.1f", ($fee / 100));
  688. //noticeUtil::push("批发订单,获取运费:{$totalFee} 重量:{$weight} 距离:{$showDistance} 客户名:{$customMobile} 手机号 {$customMobile} 经纬 {$shopLong} {$shopLat} 金额:{$price} 数量:{$packageNum}", '15280215347');
  689. return ['distance' => $distance, 'showDistance' => $showDistance, 'fee' => $totalFee];
  690. }
  691. //到货之后 ssh 20220321
  692. public static function arrival($cg, $data)
  693. {
  694. if ($cg->stockChange == 1) {
  695. util::fail('您已操作过了');
  696. }
  697. $sendCost = isset($data['sendCost']) && is_numeric($data['sendCost']) ? $data['sendCost'] : 0;
  698. $packCost = isset($data['packCost']) && is_numeric($data['packCost']) ? $data['packCost'] : 0;
  699. $cg->sendCost = $sendCost;
  700. $cg->packCost = $packCost;
  701. $cg->save();
  702. $sjId = $data['sjId'] ?? 0;
  703. $shopId = $data['shopId'] ?? 0;
  704. //product [{"productId":"3674","num":0,"price":1}]
  705. $product = $data['product'] ?? '';
  706. if (empty($product)) {
  707. util::fail('请填写成本价和到货数量');
  708. }
  709. $ghsName = $cg->ghsName ?? '';
  710. $orderSn = $cg->orderSn ?? '';
  711. $list = PurchaseItemClass::getAllByCondition(['orderSn' => $orderSn], null, '*', null, true);
  712. if (empty($list)) {
  713. util::fail('没有找到花材哦');
  714. }
  715. $weight = 0;
  716. $itemPrice = 0;
  717. foreach ($list as $key => $val) {
  718. $productId = $val->productId ?? 0;
  719. if (isset($product[$productId]) == false) {
  720. util::fail('没有找到对应采购花材');
  721. }
  722. $num = $product[$productId]['num'] ?? 0;
  723. $price = $product[$productId]['price'] ?? 0;
  724. if ($price <= 0) {
  725. util::fail('花材金额需要大于0');
  726. }
  727. $totalPrice = bcmul($num, $price, 2);
  728. $itemPrice = bcadd($itemPrice, $totalPrice, 2);
  729. $val->price = $price;
  730. $val->itemNum = $num;
  731. $val->preTotalPrice = $totalPrice;
  732. $val->totalPrice = $totalPrice;
  733. $val->xhNum = $num;
  734. $val->xhUnitPrice = $price;
  735. $val->xhPrice = $totalPrice;
  736. $val->save();
  737. $unitWeight = $val->unitWeight ?? 0;
  738. $currentWeight = bcmul($num, $unitWeight, 2);
  739. $weight = bcadd($weight, $currentWeight, 2);
  740. $unitType = $val->xhUnitType ?? 0;
  741. $bigNum = 0;
  742. $smallNum = 0;
  743. if ($unitType == dict::getDict('unitType', 'big')) {
  744. $bigNum = $num;
  745. } else {
  746. $smallNum = $num;
  747. }
  748. //增加库存
  749. $option = ['relateName' => $ghsName];
  750. StockClass::purchaseItem($orderSn, $sjId, $shopId, $productId, $bigNum, $smallNum, $option);
  751. }
  752. $cg->itemPrice = $itemPrice;
  753. $sendCost = $cg->sendCost ?? 0;
  754. $currentPrice = bcadd($itemPrice, $sendCost, 2);
  755. $packCost = $cg->packCost ?? 0;
  756. $currentPrice = bcadd($currentPrice, $packCost, 2);
  757. $kiloFee = $data['kiloFee'] ?? 0;
  758. $miniKilo = $data['miniKilo'] ?? 0;
  759. $calcWeight = $weight > $miniKilo ? $weight : $miniKilo;
  760. $allKiloFee = bcmul($calcWeight, $kiloFee, 2);
  761. $currentPrice = bcadd($currentPrice, $allKiloFee, 2);
  762. $cg->prePrice = $currentPrice;
  763. $cg->orderPrice = $currentPrice;
  764. $cg->actPrice = $currentPrice;
  765. $cg->realPrice = $currentPrice;
  766. $cg->stockChange = 1;
  767. $cg->save();
  768. $mainId = $cg->mainId ?? 0;
  769. $sjId = $cg->sjId ?? 0;
  770. $shopId = $cg->shopId ?? 0;
  771. $cgId = $cg->id ?? 0;
  772. $saleRefundId = $data['saleRefundId'] ?? 0;
  773. $shop = ShopClass::getLockById($shopId);
  774. if (empty($shop)) {
  775. util::fail('没有找到零售店的门店');
  776. }
  777. $main = MainClass::getLockById($mainId);
  778. if (empty($main)) {
  779. util::fail('没有main信息12');
  780. }
  781. $main->cgUnSend -= 1;
  782. $main->cgFinish += 1;
  783. $main->save();
  784. $cgRefund = null;
  785. $bookPrice = $cg->bookPrice ?? 0;
  786. if ($bookPrice == $currentPrice) {
  787. return ['refund' => $cgRefund];
  788. } elseif ($bookPrice > $currentPrice) {
  789. //退款
  790. $refundPrice = bcsub($bookPrice, $currentPrice, 2);
  791. $book = 1;
  792. $refundOrderSn = orderSn::getCgRefundSn();
  793. $refundType = CgRefundClass::REFUND_TYPE_MONEY;
  794. $refundData = [
  795. 'book' => $book,
  796. 'mainId' => $mainId,
  797. 'orderSn' => $refundOrderSn,
  798. 'cgId' => $cgId,
  799. 'status' => CgRefundClass::STATUS_COMPLETE,
  800. 'shopAdminId' => 0,
  801. 'shopAdminName' => '',
  802. 'refundType' => $refundType,
  803. 'refundPrice' => $refundPrice,
  804. 'saleRefundId' => $saleRefundId,
  805. 'sjId' => $sjId,
  806. 'shopId' => $shopId,
  807. ];
  808. $cgRefund = CgRefundClass::add($refundData, true);
  809. //门店收入增加
  810. $currentTotalIncome = bcadd($main->totalIncome, $refundPrice, 2);
  811. $main->totalIncome = $currentTotalIncome;
  812. $main->save();
  813. //供货商退款,向供货商采购总金额减少
  814. StatCgClass::replace($main, $shop, $refundPrice, 0, false);
  815. //供货商退款,按供货商分别采购的金额减少
  816. StatCgGhsClass::hdCgGhsReplace(false, $cg, $refundPrice, 0);
  817. $currentType = dict::getDict('capitalType', 'hdBookRefund', 'id');
  818. $sjId = $cg->sjId ?? 0;
  819. $shopId = $cg->shopId ?? 0;
  820. $event = "预订单{$orderSn}多退少补({$ghsName})";
  821. $capitalData = [
  822. 'capitalType' => $currentType,
  823. 'io' => 1,
  824. 'totalIncome' => $currentTotalIncome,
  825. 'payWay' => 0,
  826. 'amount' => $refundPrice,
  827. 'sjId' => $sjId,
  828. 'shopId' => $shopId,
  829. 'event' => $event,
  830. 'mainId' => $mainId,
  831. ];
  832. ShopCapitalClass::addCapital($capitalData);
  833. //收入统计
  834. StatIncomeClass::updateOrInsert($main, $shop, $refundPrice);
  835. $ghsId = $cg->ghsId ?? 0;
  836. $ghs = GhsClass::getLockById($ghsId);
  837. if (empty($ghs)) {
  838. util::fail('没有找到供货商');
  839. }
  840. $ghsShopId = $ghs->shopId ?? 0;
  841. $ghsShop = ShopClass::getById($ghsShopId, true);
  842. if (empty($ghsShop)) {
  843. util::fail('没有找到供货商的门店');
  844. }
  845. $refundFee = bcmul($refundPrice, 100);
  846. $merchantPrivateKeyPath = Yii::getAlias("@vendor/lakala") . '/production/api_private_key.pem';
  847. $lklCertificatePath = Yii::getAlias("@vendor/lakala") . '/production/lkl-apigw-v1.cer';
  848. $params = [
  849. 'appid' => 'OP00002119',
  850. 'serial_no' => '018b08cfddbd',
  851. 'merchant_no' => $ghsShop->lklSjNo,
  852. 'term_no' => $ghsShop->lklScanTermNo,
  853. 'merchantPrivateKeyPath' => $merchantPrivateKeyPath,
  854. 'lklCertificatePath' => $lklCertificatePath,
  855. ];
  856. $laResource = new Lakala($params);
  857. $refundReason = '';
  858. $aliParams = [
  859. 'refundSn' => $refundOrderSn,
  860. 'orderSn' => $orderSn,
  861. 'refundAmount' => $refundFee,
  862. 'refundReason' => $refundReason,
  863. ];
  864. $response = $laResource->refund($aliParams);
  865. if (isset($response['code']) == false || $response['code'] != 'BBS00000') {
  866. $errMsg = $response['msg'] ?? '';
  867. noticeUtil::push('退款失败了哦!请注意:' . $errMsg . ' 金额:' . $refundFee, '15280215347');
  868. util::fail('退款失败');
  869. }
  870. $refundTradeNo = isset($response['resp_data']['trade_no']) ? $response['resp_data']['trade_no'] : '';
  871. $cgRefund->thirdRefundNo = $refundTradeNo;
  872. $cgRefund->save();
  873. $cg->refundPrice = $refundPrice;
  874. $cg->tkPrice = bcadd($cg->tkPrice, $refundPrice, 2);
  875. $cg->refund = self::REFUND_YES;
  876. $cg->refundLog = 1;
  877. $cg->save();
  878. } else {
  879. //欠款
  880. $currentDebt = bcsub($currentPrice, $bookPrice, 2);
  881. $cg->debtPrice = $currentDebt;
  882. $cg->remainDebtPrice = $currentDebt;
  883. $cg->debt = PurchaseClass::DEBT_YES;
  884. $cg->save();
  885. $payWay = dict::getDict('payWay', 'debtPay');
  886. // 净余额已在 OrderClass::arrival → CustomClass::cgDebtAmountAdd 成对扣过,此处只记花店支出
  887. //支出统计
  888. StatOutClass::updateOrInsert($main, $shop, $currentDebt);
  889. //支出流水
  890. $capitalType = dict::getDict('capitalType', 'hdBookFinalPay', 'id');
  891. $sjId = $cg->sjId ?? 0;
  892. $shopId = $cg->shopId ?? 0;
  893. $currentExpend = bcadd($main->totalExpend, $currentDebt, 2);
  894. $main->totalExpend = $currentExpend;
  895. $main->save();
  896. $event = "预订单{$orderSn}补尾款欠款";
  897. $capitalData = [
  898. 'capitalType' => $capitalType,
  899. 'io' => 0,
  900. 'totalExpend' => $currentExpend,
  901. 'payWay' => $payWay,
  902. 'amount' => $currentDebt,
  903. 'sjId' => $sjId,
  904. 'shopId' => $shopId,
  905. 'event' => $event,
  906. 'mainId' => $mainId,
  907. ];
  908. ShopCapitalClass::addCapital($capitalData);
  909. //补款给供货商,采购总金额增加
  910. StatCgClass::replace($main, $shop, $currentDebt, 0, true);
  911. //补款给供货商,按供货商分别统计采购量增加
  912. StatCgGhsClass::hdCgGhsReplace(true, $cg, $currentDebt, 0);
  913. }
  914. return ['refund' => $cgRefund];
  915. }
  916. //添加采购 ssh 2021.1.18
  917. public static function addPurchase($data, $ghs)
  918. {
  919. $shopId = $data['shopId'] ?? 0;
  920. $sjId = $data['sjId'] ?? 0;
  921. $mainId = $data['mainId'] ?? 0;
  922. $ghsId = $data['ghsId'] ?? 0;
  923. $book = $data['book'] ?? 0;
  924. $shopAdminId = $data['shopAdminId'] ?? 0;
  925. $customId = $data['customId'] ?? 0;
  926. $snData = ['shopId' => $shopId, 'mainId' => $mainId, 'ghsId' => $ghsId, 'customId' => $customId];
  927. $orderSn = orderSn::getPurchaseSn($snData);
  928. $data['orderSn'] = $orderSn;
  929. $prePrice = 0;
  930. $live = $ghs['live'] ?? 1;
  931. $sendTimeWant = !empty($data['sendTimeWant']) ? $data['sendTimeWant'] : date("Y-m-d");
  932. if ($sendTimeWant < date("Y-m-d")) {
  933. util::fail('您选了过去时间');
  934. }
  935. $data['sendTimeWant'] = $sendTimeWant;
  936. if (empty($data['product'])) {
  937. util::fail('请选择花材17');
  938. }
  939. $productList = $data['product'];
  940. $productList = self::mergeItemInfo($productList);
  941. $data['kindNum'] = count($productList); //多少种花材
  942. //传过来的是供货商的productId,需要转换为零售端的productId
  943. //数据格式 [{"productId":"113","bigNum":1,"smallNum":0,"classId":"-2","hdProductId":1}]
  944. if ($live == 1) {
  945. //花店真实向批发店采购花材,因为拿的供货商的ID,所以需要转化花店的id
  946. $productList = ProductClass::toggleProductList($productList, $shopId, $sjId, $mainId, $ghs);
  947. $ghsProductData = self::getProductMapData($productList, "ghsProductId");
  948. } else {
  949. //虚拟采购用的自己的ID所以不需要转化
  950. $ghsProductData = [];
  951. }
  952. $hdProductData = self::getProductMapData($productList, "productId");
  953. $giveLevel = $ghs['giveLevel'] ?? 0;
  954. $priceMap = CustomClass::$levelPriceKeyMap;
  955. //这个参数应该是要作废的,但下面加的参数是有用的,多处要同步修改,关键词 abandon_add_price_map ,ssh 20260116
  956. $addPriceMap = CustomClass::$levelAddPriceKeyMap;
  957. //但这边加的参数是有用的,给指定客户涨价的
  958. $addPriceMap['risePercent'] = $ghs['risePercent'] ?? 0;
  959. //累计满减
  960. $totalReachDiscount = 0;
  961. //计算价格= 各个productId的price*num(bigNum,smallNum) + sendCost(配送费)
  962. $bigNum = 0; //总共多少扎
  963. $smallNum = 0; //总共多少支
  964. $totalWeight = 0;
  965. foreach ($productList as $key => $product) {
  966. $currentBigNum = $product['bigNum'] ?? 0;
  967. $currentSmallNum = $product['smallNum'] ?? 0;
  968. $bigNum += $product['bigNum'] ?? 0;
  969. $smallNum += $product['smallNum'] ?? 0;
  970. $productList[$key]['orderSn'] = $orderSn;
  971. if ($live == 1) {
  972. $ghsProductId = $product['ghsProductId'];
  973. $hdProductId = $product['productId'];
  974. } else {
  975. $hdProductId = $product['productId'] ?? 0;
  976. $ghsProductId = 0;
  977. }
  978. $unitWeight = $hdProductData[$hdProductId]['weight'] ?? 0;
  979. $variety = $hdProductData[$hdProductId]['variety'] ?? 0;
  980. $ratio = $hdProductData[$hdProductId]['ratio'] ?? 0;
  981. $ratioType = $hdProductData[$hdProductId]['ratioType'] ?? 0;
  982. $itemId = $hdProductData[$hdProductId]['itemId'] ?? 0;
  983. $rank = $hdProductData[$hdProductId]['rank'] ?? 'B';
  984. $stock = $hdProductData[$hdProductId]['stock'] ?? 0;
  985. $kind = $hdProductData[$hdProductId]['kind'] ?? 0;
  986. $stockFormat = ProductClass::formatStock($stock, $ratio);
  987. $limitKey = ProductClass::LIMIT_BUY_KEY . $ghsProductId; // 注意:要使用批发的花材id
  988. $hasNum = Yii::$app->redis->executeCommand('HGET', [$limitKey, $customId]);
  989. $hasBuyNum = $hasNum ?: 0;
  990. $itemNum = ProductClass::mergeItemNum($product['bigNum'], $product['smallNum'], $ratio);
  991. $hasBuyNum = bcadd($itemNum, $hasBuyNum, 2); // 已购买总数量
  992. $weight = $hdProductData[$hdProductId]['weight'] ?? 0;
  993. $currentWeight = bcmul($weight, $itemNum, 2);
  994. $totalWeight = bcadd($currentWeight, $totalWeight, 2);
  995. //1、供货商开单改价 2、花店向虚拟供货商采购录入价格
  996. $userPrice = isset($product['userPrice']) && $product['userPrice'] > 0 ? $product['userPrice'] : 0;
  997. $currentGhsProduct = $ghsProductData[$ghsProductId] ?? [];
  998. if (!empty($currentGhsProduct)) {
  999. //用批发店的花材名称
  1000. $itemName = $currentGhsProduct['name'] ?? '';
  1001. $cover = $currentGhsProduct['cover'] ?? '';
  1002. //如果零售店自己下预订单,预付0.1元,有两个地方要同步修改,搜索关键词 pre0.1
  1003. if ($book == 1 && $shopAdminId > 0) {
  1004. $price = 0.1;
  1005. } else {
  1006. //用批发店花材的价格
  1007. $price = ProductClass::getFinalPrice($currentGhsProduct, $giveLevel, $priceMap, $addPriceMap, $userPrice, $itemNum, $hasBuyNum);
  1008. }
  1009. $thisPrice = $price;
  1010. $reachNum = $currentGhsProduct['reachNum'] ?? 0;
  1011. if ($reachNum > 0) {
  1012. if ($itemNum >= $reachNum) {
  1013. $reachNumDiscount = $currentGhsProduct['reachNumDiscount'] ?? 0;
  1014. $currentReachDiscount = bcmul($reachNumDiscount, $itemNum, 2);
  1015. $totalReachDiscount = bcadd($totalReachDiscount, $currentReachDiscount, 2);
  1016. }
  1017. }
  1018. } else {
  1019. $currentHdProduct = $hdProductData[$hdProductId] ?? [];
  1020. if (empty($currentHdProduct)) {
  1021. util::fail('花材有问题哦');
  1022. }
  1023. $itemName = $currentHdProduct['name'] ?? '';
  1024. $cover = $currentHdProduct['cover'] ?? '';
  1025. $price = $userPrice;
  1026. $thisPrice = $userPrice;
  1027. }
  1028. if ($currentSmallNum > 0) {
  1029. $productTotalPrice = bcmul($currentSmallNum, $price, 2);
  1030. } else {
  1031. $productTotalPrice = bcmul($currentBigNum, $price, 2);
  1032. }
  1033. $itemInfo = [
  1034. 'itemName' => $itemName,
  1035. 'itemCover' => $cover,
  1036. 'bigNum' => $product['bigNum'] ?? 0,
  1037. 'smallNum' => $product['smallNum'] ?? 0,
  1038. 'bigNumStock' => $stockFormat['bigNum'],
  1039. 'smallNumStock' => $stockFormat['smallNum'],
  1040. 'itemUnit' => $ratio ? 2 : 1,
  1041. 'ratio' => $ratio,
  1042. 'rank' => $rank,
  1043. 'bigUnit' => $hdProductData[$hdProductId]['bigUnit'],
  1044. 'smallUnit' => $hdProductData[$hdProductId]['smallUnit'],
  1045. ];
  1046. if ($itemNum <= 0) {
  1047. util::fail($itemName . ' 数量要大于零');
  1048. }
  1049. $productList[$key]['itemInfo'] = json_encode($itemInfo);
  1050. $productList[$key]['variety'] = $variety;
  1051. $productList[$key]['itemId'] = $itemId;
  1052. $productList[$key]['preItemNum'] = $itemNum;
  1053. $productList[$key]['itemNum'] = $itemNum;
  1054. $productList[$key]['bigNum'] = $product['bigNum'] ?? 0;
  1055. $productList[$key]['smallNum'] = $product['smallNum'] ?? 0;
  1056. $productList[$key]['price'] = $thisPrice;
  1057. $productList[$key]['ratio'] = $ratio;
  1058. $productList[$key]['ratioType'] = $ratioType;
  1059. $productList[$key]['unitWeight'] = $unitWeight;
  1060. $productList[$key]['totalPrice'] = $productTotalPrice;
  1061. $productList[$key]['preTotalPrice'] = $productTotalPrice;
  1062. $productList[$key]['productId'] = $hdProductId;
  1063. $productList[$key]['ghsProductId'] = $ghsProductId;
  1064. $productList[$key]['smallPrice'] = $price;
  1065. $productList[$key]['mainId'] = $mainId;
  1066. $productList[$key]['ghsId'] = $ghsId;
  1067. $productList[$key]['name'] = $itemName;
  1068. $productList[$key]['cover'] = $cover;
  1069. $productList[$key]['kind'] = $kind;
  1070. $currentBigUnit = $hdProductData[$hdProductId]['bigUnit'] ?? '';
  1071. $currentSmallUnit = $hdProductData[$hdProductId]['smallUnit'] ?? '';
  1072. if ($currentBigNum > 0) {
  1073. $xhNum = $currentBigNum;
  1074. $xhUnitName = $currentBigUnit;
  1075. $xhUnitType = dict::getDict('unitType', 'big');
  1076. } else {
  1077. $xhNum = $currentSmallNum;
  1078. $xhUnitName = $currentSmallUnit;
  1079. $xhUnitType = dict::getDict('unitType', 'small');
  1080. }
  1081. $itemPrice = $price;
  1082. $price = bcmul($itemPrice, $xhNum, 2);
  1083. //客户下单时会出现同时选了扎和支,很奇怪,前端没有找到原因,后端限制
  1084. if ($currentBigNum > 0 && $currentSmallNum > 0) {
  1085. util::fail($itemName . " {$currentBigUnit}和{$currentSmallUnit}只能选一种");
  1086. }
  1087. $productList[$key]['xhNum'] = $xhNum;
  1088. $productList[$key]['xhUnitName'] = $xhUnitName;
  1089. $productList[$key]['xhUnitType'] = $xhUnitType;
  1090. $productList[$key]['xhUnitPrice'] = $itemPrice;
  1091. $productList[$key]['xhPrice'] = $price;
  1092. $productList[$key]['xhPreNum'] = $xhNum;
  1093. $productList[$key]['xhPreUnitName'] = $xhUnitName;
  1094. $productList[$key]['xhPreUnitType'] = $xhUnitType;
  1095. $productList[$key]['xhPreUnitPrice'] = $itemPrice;
  1096. $productList[$key]['xhPrePrice'] = $price;
  1097. $prePrice = bcadd($prePrice, $productTotalPrice, 2);
  1098. }
  1099. $data['itemPrice'] = $prePrice;
  1100. $sendCost = $data['sendCost'] ?? 0.00;
  1101. $prePrice = bcadd($prePrice, $sendCost, 2);
  1102. $packCost = $data['packCost'] ?? 0;
  1103. $prePrice = bcadd($prePrice, $packCost, 2);
  1104. $data['weight'] = $totalWeight;
  1105. //预订单需要增加服务费
  1106. if ($book == 1) {
  1107. $kiloFee = $ghs['kiloFee'] ?? 0;
  1108. $miniKilo = $ghs['miniKilo'] ?? 0;
  1109. $calcWeight = max($totalWeight, $miniKilo);
  1110. $allKiloFee = bcmul($calcWeight, $kiloFee, 2);
  1111. $prePrice = bcadd($prePrice, $allKiloFee, 2);
  1112. $data['allKiloFee'] = $allKiloFee;
  1113. $data['kiloFee'] = $kiloFee;
  1114. $data['miniKilo'] = $miniKilo;
  1115. $data['stockChange'] = 0;
  1116. }
  1117. $actPrice = $prePrice;
  1118. if (isset($data['modifyPrice'])) {
  1119. $modifyPrice = $data['modifyPrice'];
  1120. if ($modifyPrice < $prePrice) {
  1121. //优惠金额
  1122. $data['discountAmount'] = bcsub($prePrice, $modifyPrice, 2);
  1123. $data['discountType'] = dict::getDict('discountType', 'discount');
  1124. }
  1125. if ($modifyPrice > $prePrice) {
  1126. //人工和资材费用
  1127. $data['labourCost'] = bcsub($modifyPrice, $prePrice, 2);
  1128. }
  1129. $actPrice = $modifyPrice;
  1130. if ($book == 1) {
  1131. $actPrice = 0;
  1132. }
  1133. } else {
  1134. //客户自己下单才有整单满减,门店帮忙开单没有,,多个地方要同步修改,关键词 meet_amount_num_reduce
  1135. if ($book == 0) {
  1136. $ghsShopId = $ghs['shopId'] ?? 0;
  1137. $ghsShop = ShopClass::getById($ghsShopId, true);
  1138. if (!empty($ghsShop)) {
  1139. $meetNum = $ghsShop->meetNum ?? 0;
  1140. $meetAmount = $ghsShop->meetAmount ?? 0;
  1141. $cutStyle = $ghsShop->cutStyle ?? 0;
  1142. $cutOption = $ghsShop->cutAmount ?? 0;
  1143. if ($cutStyle == 0) {
  1144. $cutAmount = $cutOption;
  1145. } else {
  1146. $miniOption = bcsub(1, $cutOption, 2);
  1147. $cutAmount = bcmul($actPrice, $miniOption, 2);
  1148. }
  1149. if ($cutAmount > 0 && $meetNum > 0 && $meetAmount > 0) {
  1150. if ($bigNum >= $meetNum && $data['itemPrice'] >= $meetAmount) {
  1151. if ($cutAmount < $actPrice) {
  1152. $actPrice = bcsub($actPrice, $cutAmount, 2);
  1153. $data['orderReachDiscountPrice'] = $cutAmount;
  1154. } else {
  1155. noticeUtil::push('订单金额不足满减金额,ghsShopId:' . $ghsShopId, '15280215347');
  1156. }
  1157. }
  1158. }
  1159. }
  1160. }
  1161. }
  1162. //如果有设置指定客户涨价百分之几,然后又设置了涨价的幅度。【这个功能主要用于客户不收他运费,门店不赚钱,收他运费客户不乐意,所以把运费加到花材里】,多处同步修改,关键词 rise_percent_rang
  1163. if (isset($data['itemPrice'])) {
  1164. if (isset($data['orderReachDiscountPrice'])) {
  1165. $originActPrice = bcadd($actPrice, $data['orderReachDiscountPrice'], 2);
  1166. } else {
  1167. $originActPrice = $actPrice;
  1168. }
  1169. if ($originActPrice >= $data['itemPrice']) {
  1170. if (isset($ghs['risePercent']) && $ghs['risePercent'] > 0) {
  1171. if (isset($ghs['riseRange']) && $ghs['riseRange'] > 0) {
  1172. $risePercent = $ghs['risePercent'];
  1173. $riseRange = $ghs['riseRange'];
  1174. // 计算涨价金额:涨价后金额 * (涨价百分比 / (100 + 涨价百分比))
  1175. $riseAmount = bcmul($data['itemPrice'], bcdiv($risePercent, bcadd(100, $risePercent, 0), 4), 2);
  1176. if ($riseAmount > $riseRange) {
  1177. //应该退回的金额
  1178. $backAmount = bcsub($riseAmount, $riseRange, 2);
  1179. $actPrice = bcsub($actPrice, $backAmount, 2);
  1180. if (isset($data['orderReachDiscountPrice'])) {
  1181. if ($data['orderReachDiscountPrice'] < $backAmount) {
  1182. //前面整单满减的,就不减了,所以这里面加回来
  1183. $actPrice = bcadd($actPrice, $data['orderReachDiscountPrice'], 2);
  1184. //改成减多加钱里面的
  1185. $data['orderReachDiscountPrice'] = $backAmount;
  1186. }
  1187. } else {
  1188. $data['orderReachDiscountPrice'] = $backAmount;
  1189. }
  1190. }
  1191. }
  1192. }
  1193. }
  1194. }
  1195. //这个字段记录每个花材满多少扎减掉的金额,并汇总金额,区别于整单满减的 $cutAmount。保存起来应该是在哪里有用,$totalReachDiscount在这边不影响actPrice,估计因为取花材的价格里::getFinalPrice()已经使用了优惠后的价格,有多处,关键词 reachDiscountPrice_explain ssh 20260116
  1196. $data['reachDiscountPrice'] = $totalReachDiscount;
  1197. $data['prePrice'] = $prePrice;
  1198. $data['bookPrice'] = $actPrice;
  1199. $data['orderPrice'] = $actPrice;
  1200. $data['actPrice'] = $actPrice;
  1201. $data['realPrice'] = $actPrice;
  1202. $data['bigNum'] = $bigNum;
  1203. $data['smallNum'] = $smallNum;
  1204. $data['discount'] = 1;
  1205. $data['status'] = self::STATUS_UN_PAY;
  1206. $data['ghsShopId'] = $ghs['shopId'] ?? 0;
  1207. //采购单有效期
  1208. $currentTime = time();
  1209. $validTime = dict::getDict('order_pay_has_time');
  1210. $current = $currentTime + $validTime;
  1211. $data['deadline'] = $data['deadline'] ?? date("Y-m-d H:i:s", $current);
  1212. if ($book == 1) {
  1213. $fiveTime = bcmul(86400, 5);
  1214. $bookDeadline = bcadd($currentTime, $fiveTime);
  1215. $data['deadline'] = date("Y-m-d H:i:s", $bookDeadline);
  1216. }
  1217. //采购单自动记为欠款单时间
  1218. $localConfirmTime = dict::getDict('local_gys_kd_auto_set_debt_time');
  1219. $currentSet = $currentTime + $localConfirmTime;
  1220. $data['autoSetTime'] = $data['autoSetTime'] ?? date("Y-m-d H:i:s", $currentSet);
  1221. $data['salt'] = $data['salt'] ?? stringUtil::charsShuffleLowerCase(10);
  1222. //保存订单
  1223. $respond = self::add($data, true);
  1224. foreach ($productList as $productVal) {
  1225. //增加采购单的花材
  1226. $returnItem = PurchaseItemClass::add($productVal, true);
  1227. $productId = $productVal['productId'];
  1228. $unitPrice = $returnItem->price ?? 0;
  1229. $currentId = $returnItem->id ?? 0;
  1230. $currentPtItemId = $returnItem->itemId ?? 0;
  1231. if (!empty($data['xj'][$currentPtItemId])) {
  1232. $xjArr = $data['xj'][$currentPtItemId];
  1233. if (is_array($xjArr)) {
  1234. $ids = array_column($xjArr, 'id');
  1235. $sjInfo = XjClass::getByIds($ids, null, 'id');
  1236. $xjData = [];
  1237. foreach ($xjArr as $xjItem) {
  1238. $id = $xjItem['id'] ?? 0;
  1239. $num = $xjItem['num'] ?? 0;
  1240. $name = $sjInfo[$id]['name'] ?? '';
  1241. $cover = $sjInfo[$id]['cover'] ?? '';
  1242. $xjData[] = ['xjId' => $id, 'name' => $name, 'ptItemId' => $currentPtItemId,
  1243. 'num' => $num, 'price' => $unitPrice, 'itemId' => $currentId, 'cover' => $cover];
  1244. }
  1245. CgXjClass::batchAdd($xjData);
  1246. $returnItem->variety = 1;
  1247. $returnItem->save();
  1248. }
  1249. }
  1250. if (!empty($data['treeData'][$currentPtItemId])) {
  1251. $myTreeData = $data['treeData'][$currentPtItemId];
  1252. foreach ($myTreeData as $treeItem) {
  1253. $treeNum = $treeItem['num'] ?? 0;
  1254. $treeParams = [
  1255. 'orderSn' => $orderSn,
  1256. 'num' => $treeNum,
  1257. 'productId' => $productId,
  1258. ];
  1259. CgTreeClass::add($treeParams);
  1260. $dishNum = count($myTreeData);
  1261. $returnItem->dishNum = $dishNum;
  1262. $returnItem->save();
  1263. }
  1264. }
  1265. }
  1266. $shop = ShopClass::getLockById($shopId);
  1267. if (empty($shop)) {
  1268. util::fail('没有找到门店59 ' . $shopId);
  1269. }
  1270. $mainId = $shop->mainId ?? 0;
  1271. $main = MainClass::getLockById($mainId);
  1272. if (empty($main)) {
  1273. util::fail('没有main信息13');
  1274. }
  1275. $main->totalPurchaseOrder += 1;
  1276. $main->cgUnPay += 1;
  1277. $main->save();
  1278. return $respond;
  1279. }
  1280. //添加采购 ssh 2021.1.18
  1281. public static function updatePurchase($purchase, $data)
  1282. {
  1283. util::fail('已删除');
  1284. }
  1285. //组合数据 ssh 2021.1.25
  1286. public static function groupPurchase($list, $showButton = false, $showProduct = false)
  1287. {
  1288. $ghsIds = array_unique(array_filter(array_column($list, 'ghsId')));
  1289. $ghsInfo = GhsClass::getGhsByIds($ghsIds);
  1290. $adminIds = array_unique(array_filter(array_column($list, 'adminId')));
  1291. $adminInfo = AdminClass::getAdminByIds($adminIds, null, 'id');
  1292. $time = time();
  1293. foreach ($list as $key => $val) {
  1294. //剩余支付时间
  1295. $remainPayTime = 0;
  1296. $deadline = $val['deadline'] ?? '';
  1297. if (!empty($deadline)) {
  1298. $deadTime = strtotime($deadline);
  1299. if (($deadTime - $time - 30) > 0) {
  1300. $remainPayTime = $deadTime - $time;
  1301. $remainPayTime = $remainPayTime - 30;
  1302. }
  1303. }
  1304. $list[$key]['remainPayTime'] = $remainPayTime;
  1305. $adminId = $val['adminId'] ?? 0;
  1306. $list[$key]['adminName'] = $adminInfo[$adminId]['name'] ?? '';
  1307. $ghsId = $val['ghsId'] ?? 0;
  1308. $list[$key]['supplierName'] = $ghsInfo[$ghsId]['name'] ?? '';
  1309. $list[$key]['supplierAvatar'] = $ghsInfo[$ghsId]['smallLogoUrl'] ?? '';
  1310. $list[$key]['supplierMobile'] = $ghsInfo[$ghsId]['mobile'] ?? '';
  1311. $list[$key]['supplierAddress'] = $ghsInfo[$ghsId]['address'] ?? '';
  1312. $list[$key]['debtAmount'] = $ghsInfo[$ghsId]['debtAmount'] ?? '';
  1313. // 供货商卡片:统一为净余额(与 AccountMoneyClass 一致,appVersion=3 时接口 balance 已是净额)
  1314. $balance = $ghsInfo[$ghsId]['balance'] ?? 0;
  1315. $debtAmount = $ghsInfo[$ghsId]['debtAmount'] ?? 0;
  1316. $balanceMerged = $ghsInfo[$ghsId]['balanceMerged'] ?? 0;
  1317. $ghsSalt = $ghsInfo[$ghsId]['salt'] ?? '';
  1318. $net = \bizGhs\custom\classes\AccountMoneyClass::calcNetBalance($balance, $debtAmount, !empty($balanceMerged));
  1319. $remainDebtAmount = bccomp($net, '0', 2) < 0 ? bcmul($net, '-1', 2) : '0.00';
  1320. if (bccomp($net, '0', 2) > 0) {
  1321. $remainDebtAmount = bcsub('0', $net, 2);
  1322. }
  1323. $list[$key]['remainDebtAmount'] = floatval($remainDebtAmount);
  1324. $list[$key]['inMyGhsBalance'] = floatval($net);
  1325. $list[$key]['balance'] = floatval($net);
  1326. $list[$key]['displayBalance'] = floatval($net);
  1327. $list[$key]['ghsSalt'] = $ghsSalt;
  1328. $list[$key]['lat'] = $ghsInfo[$ghsId]['lat'] ?? '';
  1329. $list[$key]['long'] = $ghsInfo[$ghsId]['long'] ?? '';
  1330. $list[$key]['supplierId'] = $ghsInfo[$ghsId]['id'] ?? '';
  1331. if ($showButton) {
  1332. $list[$key]['buttonList'] = [
  1333. ['type' => 'pay', 'show' => 1, 'disable' => 1],
  1334. ['type' => 'debtPay', 'show' => 1, 'disable' => 0],
  1335. ];
  1336. }
  1337. $list[$key]['sendProgress'] = [];
  1338. if ($showProduct) {
  1339. $orderSn = $val['orderSn'];
  1340. $orderItem = PurchaseItemClass::getOrderItemDetail($orderSn);
  1341. $itemData = [];
  1342. $xjData = [];
  1343. if ($orderItem) {
  1344. foreach ($orderItem as $v) {
  1345. $info = json_decode($v['itemInfo'], true);
  1346. $tmp = $info;
  1347. unset($v['itemInfo']);
  1348. if (!isset($tmp['rank'])) {
  1349. $tmp['rank'] = 'B';
  1350. }
  1351. $tmp['name'] = $tmp['itemName'] ?? '';
  1352. $tmp = array_merge($tmp, $v);
  1353. $shortCover = $info['itemCover'] ?? '';
  1354. $tmp['cover'] = imgUtil::groupImg($shortCover) . "?x-oss-process=image/resize,m_fill,h_130,w_130";
  1355. if (isset($tmp['variety']) && $tmp['variety'] == 1) {
  1356. $currentItemId = $tmp['id'] ?? 0;
  1357. $xjList = CgXjClass::getAllByCondition(['itemId' => $currentItemId], null, '*');
  1358. if (!empty($xjList)) {
  1359. foreach ($xjList as $xjKey => $xj) {
  1360. $xjBase = $tmp;
  1361. $num = $xj['num'] ?? 0;
  1362. $name = $xj['name'] ?? '';
  1363. $cover = $xj['cover'] ?? '';
  1364. $price = $xj['price'] ?? '';
  1365. $totalPrice = bcmul($num, $price, 2);
  1366. if ($num > 0) {
  1367. $xjBase['cover'] = imgUtil::groupImg($cover) . "?x-oss-process=image/resize,m_fill,h_130,w_130";
  1368. $xjBase['name'] = $name;
  1369. $xjBase['itemNum'] = floatval($num);
  1370. $xjBase['bigNum'] = floatval($num);
  1371. $xjBase['smallNum'] = 0;
  1372. $xjBase['price'] = $price;
  1373. $xjBase['totalPrice'] = $totalPrice;
  1374. $xjBase['xhNum'] = floatval($num);
  1375. $xjAddList[] = $xjBase;
  1376. }
  1377. $xjData[] = $xjBase;
  1378. }
  1379. } else {
  1380. $itemData[] = $tmp;
  1381. }
  1382. } else {
  1383. $itemData[] = $tmp;
  1384. }
  1385. }
  1386. if (!empty($xjData)) {
  1387. $itemData = array_merge($itemData, $xjData);
  1388. }
  1389. }
  1390. $list[$key]['product'] = $itemData;
  1391. }
  1392. }
  1393. return $list;
  1394. }
  1395. //订单取消,变更状态,占用库存释放 ssh 2021.5.8
  1396. public static function cancel($id)
  1397. {
  1398. $purchase = PurchaseClass::getLockById($id);
  1399. $purchase->status = self::STATUS_CANCEL;
  1400. $purchase->cancelTime = date("Y-m-d H:i:s");
  1401. $purchase->save();
  1402. $shopId = $purchase->shopId;
  1403. $orderSn = $purchase->orderSn ?? '';
  1404. $shop = ShopClass::getLockById($shopId);
  1405. if (empty($shop)) {
  1406. util::fail('没有找到门店60');
  1407. }
  1408. $mainId = $shop->mainId ?? 0;
  1409. $main = MainClass::getLockById($mainId);
  1410. if (empty($main)) {
  1411. util::fail('没有main信息14');
  1412. }
  1413. $main->cgUnPay -= 1;
  1414. $main->cgCancel += 1;
  1415. $main->save();
  1416. $ghsId = $purchase->ghsId;
  1417. $ghs = GhsClass::getById($ghsId, true);
  1418. if (empty($ghs)) {
  1419. util::fail('没有找到供货商');
  1420. }
  1421. $ghsShopId = $ghs->shopId ?? 0;
  1422. $ghsShop = ShopClass::getById($ghsShopId, true);
  1423. if (empty($ghsShop)) {
  1424. util::fail('没有找到供货商门店');
  1425. }
  1426. $merchantPrivateKeyPath = Yii::getAlias("@vendor/lakala") . '/production/api_private_key.pem';
  1427. $lklCertificatePath = Yii::getAlias("@vendor/lakala") . '/production/lkl-apigw-v1.cer';
  1428. $params = [
  1429. 'appid' => 'OP00002119',
  1430. 'serial_no' => '018b08cfddbd',
  1431. 'merchant_no' => $ghsShop->lklSjNo,
  1432. 'term_no' => $ghsShop->lklScanTermNo,
  1433. 'merchantPrivateKeyPath' => $merchantPrivateKeyPath,
  1434. 'lklCertificatePath' => $lklCertificatePath,
  1435. ];
  1436. $laResource = new Lakala($params);
  1437. $closeParams = ['orderSn' => $orderSn];
  1438. $response = $laResource->close($closeParams);
  1439. if (!isset($response['code']) || $response['code'] != 'BBS00000') {
  1440. $msg = $response['msg'] ?? '';
  1441. $code = $response['code'] ?? 0;
  1442. //主扫(手机上微信下单拉取微信支付)调用这个方法有效,如果收银机上扫客户收款码的,调用这个方法无效,具体说明看 https://o.lakala.com/#/home/document/detail?id=115 所以在这个取消方法,暂时不考虑关单成功与否 ssh 20250517
  1443. //noticeUtil::push('花店采购 ' . $orderSn . ' 关单没有成功哦' . $msg . ' ' . $code, '15280215347');
  1444. }
  1445. //收银台关单
  1446. $closeParams = ['orderSn' => $orderSn];
  1447. $response = $laResource->cashClose($closeParams);
  1448. if (!isset($response['code']) || $response['code'] != '000000') {
  1449. $msg = $response['msg'] ?? '';
  1450. $code = $response['code'] ?? 0;
  1451. //聚合收银台(APP发起的微信和支付支付)调用这个方法有效,说明 https://o.lakala.com/#/home/document/detail?id=722,其他情况调用无法,所以在这个取消方法,暂时不考虑关单成功与否 ssh 20250517
  1452. //noticeUtil::push('花店采购 ' . $orderSn . ' 收银台关单的提示:' . $msg . ' ' . $code, '15280215347');
  1453. }
  1454. return $purchase;
  1455. }
  1456. //采购统计2021-05-08
  1457. //采购数,采购金额统计(根据时间,今日,昨天,7天,30天)
  1458. public static function statOrderInfo($shopId, $statType)
  1459. {
  1460. $start = '';
  1461. $end = '';
  1462. switch ($statType) {
  1463. case "today":
  1464. $start = date('Y-m-d');
  1465. $end = $start;
  1466. break;
  1467. case "yesterday" :
  1468. $start = date('Y-m-d', strtotime("-1 day"));
  1469. $end = $start;
  1470. break;
  1471. case "seven":
  1472. $start = date('Y-m-d', strtotime("-6 day"));
  1473. $end = date('Y-m-d');
  1474. break;
  1475. case "thirty":
  1476. $start = date('Y-m-d', strtotime("-29 day"));
  1477. $end = date('Y-m-d');
  1478. break;
  1479. default:
  1480. }
  1481. $where = [];
  1482. $where['shopId'] = $shopId;
  1483. $where['addTime'] = ['between', [$start, $end]];
  1484. $res = self::getAllByCondition($where, null, "id,actPrice");
  1485. $totalAmount = 0;
  1486. $totalNum = count($res);
  1487. foreach ($res as $v) {
  1488. $totalAmount = bcadd($totalAmount, $v['actPrice'], 2);
  1489. }
  1490. return [
  1491. 'totalAmount' => $totalAmount,
  1492. 'totalNum' => $totalNum,
  1493. ];
  1494. }
  1495. //采购单完成 ssh 2021.5.15
  1496. public static function complete($purchase)
  1497. {
  1498. $purchase->status = self::STATUS_COMPLETE;
  1499. $purchase->save();
  1500. $shopId = $purchase->shopId;
  1501. $shop = ShopClass::getLockById($shopId);
  1502. if (empty($shop)) {
  1503. util::fail('没有找到门店61');
  1504. }
  1505. $mainId = $shop->mainId ?? 0;
  1506. $main = MainClass::getLockById($mainId);
  1507. if (empty($main)) {
  1508. util::fail('没有main信息15');
  1509. }
  1510. $main->cgSending -= 1;
  1511. $main->cgFinish += 1;
  1512. $main->save();
  1513. /********订单完成后加库存 lqh 2021-05-10*********/
  1514. $orderSn = $purchase->orderSn;
  1515. $purchaseItem = PurchaseItemClass::getAllByCondition(['orderSn' => $orderSn], null, "*");
  1516. foreach ($purchaseItem as $v) {
  1517. $stockInfo = ProductClass::addStockByItemNum($v['productId'], $v['itemNum']);
  1518. //记录库存流水
  1519. $recordData = [];
  1520. $recordData['sjId'] = $purchase->sjId;
  1521. $recordData['shopId'] = $purchase->shopId;
  1522. $recordData['mainId'] = $purchase->mainId;
  1523. $recordData['itemId'] = $v['itemId'] ?? 0;
  1524. $recordData['itemNum'] = $v['itemNum'];
  1525. $recordData['oldStock'] = $stockInfo['oldStock'];
  1526. $recordData['newStock'] = $stockInfo['newStock'];
  1527. $recordData['productId'] = $v['productId'];
  1528. $recordData['orderSn'] = $orderSn;
  1529. StockRecordClass::addPurchaseOrderRecord($recordData);
  1530. }
  1531. }
  1532. //订单信息变更为配送中 ssh 2021.5.15
  1533. public static function sending($purchaseId)
  1534. {
  1535. $purchase = PurchaseClass::getById($purchaseId, true);
  1536. if (empty($purchase)) {
  1537. util::fail('没有采购单信息');
  1538. }
  1539. $purchase->status = PurchaseClass::STATUS_SENDING;
  1540. $purchase->save();
  1541. $shopId = $purchase->shopId;
  1542. $shop = ShopClass::getLockById($shopId);
  1543. if (empty($shop)) {
  1544. util::fail('没有找到门店62');
  1545. }
  1546. $shop->cgUnSend -= 1;
  1547. $shop->cgSending += 1;
  1548. $shop->save();
  1549. //自动完成订单时不需要通知
  1550. $noNeedWxNotice = isset(Yii::$app->params['noNeedWxNotice']) ? Yii::$app->params['noNeedWxNotice'] : 0;
  1551. if ($noNeedWxNotice == 0) {
  1552. WxMessageClass::cgOrderHasSend($shop, $purchase);
  1553. }
  1554. }
  1555. public static function valid($cg, $shopId)
  1556. {
  1557. if (isset($cg->shopId) == false || $cg->shopId != $shopId) {
  1558. util::fail('无法访问,编号1255');
  1559. }
  1560. }
  1561. }