StatSaleClass.php 108 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110
  1. <?php
  2. namespace bizGhs\stat\classes;
  3. use biz\stat\classes\StatStockInClass;
  4. use biz\stat\classes\StatStockOutClass;
  5. use bizGhs\cg\classes\CgRefundClass;
  6. use bizGhs\cg\classes\CgPurchaseNextDayClass;
  7. use bizGhs\custom\classes\CustomClass;
  8. use bizGhs\custom\classes\DistClass;
  9. use bizGhs\expend\classes\ExpendClass;
  10. use bizGhs\item\classes\ItemClassClass;
  11. use bizGhs\order\classes\OrderItemClass;
  12. use bizGhs\order\classes\PurchaseOrderClass;
  13. use bizGhs\order\classes\PurchaseOrderItemClass;
  14. use bizGhs\order\classes\RefundOrderClass;
  15. use bizGhs\order\classes\NextDayRefundClass;
  16. use bizGhs\order\classes\StockWastageOrderClass;
  17. use bizGhs\product\classes\ProductClass;
  18. use bizGhs\staff\classes\SalaryClass;
  19. use bizHd\goods\classes\PlantCgClass;
  20. use bizHd\order\classes\OrderClass as HdOrderClass;
  21. use bizHd\order\classes\OrderItemClass as HdOrderItemClass;
  22. use bizGhs\order\classes\OrderClass;
  23. use bizHd\purchase\classes\PurchaseClass;
  24. use bizHd\refund\classes\HdRefundClass;
  25. use bizHd\refund\classes\HdNextDayRefundClass;
  26. use common\components\arrayUtil;
  27. use common\components\dateUtil;
  28. use common\components\dict;
  29. use common\components\util;
  30. use Yii;
  31. use bizGhs\base\classes\BaseClass;
  32. class StatSaleClass extends BaseClass
  33. {
  34. public static $baseFile = '\bizGhs\stat\models\StatSale';
  35. public static function wastage($mainId)
  36. {
  37. $get = Yii::$app->request->get();
  38. $where = [];
  39. $where['mainId'] = $mainId;
  40. $searchTime = isset($get['searchTime']) && !empty($get['searchTime']) ? $get['searchTime'] : 'today';
  41. $startTime = $get['startTime'] ?? '';
  42. $endTime = $get['endTime'] ?? '';
  43. $period = dateUtil::formatTime($searchTime, $startTime, $endTime, true);
  44. $start = $period['startTime'];
  45. $end = $period['endTime'];
  46. $currentStartDate = date('Y-m-d', strtotime($start));
  47. $currentEndDate = date('Y-m-d', strtotime($end));
  48. $currentStartTime = $currentStartDate . ' 00:00:00';
  49. $currentEndTime = $currentEndDate . ' 23:59:59';
  50. $where['addTime'] = ['between', [$currentStartTime, $currentEndTime]];
  51. $list = StockWastageOrderClass::getAllByCondition($where, null, '*');
  52. $totalPrice = 0;
  53. $totalCost = 0;
  54. if (!empty($list)) {
  55. foreach ($list as $order) {
  56. $price = $order['price'] ?? 0;
  57. $totalPrice = bcadd($totalPrice, $price, 2);
  58. $cost = $order['cost'] ?? 0;
  59. $totalCost = bcadd($totalCost, $cost, 2);
  60. }
  61. }
  62. $totalPrice = floatval($totalPrice);
  63. return ['totalPrice' => $totalPrice, 'totalCost' => $totalCost];
  64. }
  65. //结账跟踪 ssh 20230503
  66. public static function OrderClear($mainId)
  67. {
  68. $get = Yii::$app->request->get();
  69. $where = ['mainId' => $mainId, 'status' => ['in', [OrderClass::ORDER_STATUS_UN_SEND, OrderClass::ORDER_STATUS_SENDING, OrderClass::ORDER_STATUS_COMPLETE]]];
  70. $timeStyle = $get['timeStyle'] ?? 0;
  71. if (isset($get['searchTime']) && !empty($get['searchTime'])) {
  72. $searchTime = $get['searchTime'];
  73. $startTime = $get['startTime'] ?? '';
  74. $endTime = $get['endTime'] ?? '';
  75. $period = dateUtil::formatTime($searchTime, $startTime, $endTime, true);
  76. $start = $period['startTime'];
  77. $end = $period['endTime'];
  78. $currentStartDate = date('Y-m-d', strtotime($start));
  79. $currentEndDate = date('Y-m-d', strtotime($end));
  80. $currentStartTime = $currentStartDate . ' 00:00:00';
  81. $currentEndTime = $currentEndDate . ' 23:59:59';
  82. if ($timeStyle == 0) {
  83. $where['payTime'] = ['between', [$currentStartTime, $currentEndTime]];
  84. } else {
  85. $where['clearTime'] = ['between', [$currentStartTime, $currentEndTime]];
  86. }
  87. } else {
  88. $payTime = 'today';
  89. $startTime = $get['startTime'] ?? '';
  90. $endTime = $get['endTime'] ?? '';
  91. $period = dateUtil::formatTime($payTime, $startTime, $endTime, true);
  92. $start = $period['startTime'];
  93. $end = $period['endTime'];
  94. $currentStartDate = date('Y-m-d', strtotime($start));
  95. $currentEndDate = date('Y-m-d', strtotime($end));
  96. $currentStartTime = $currentStartDate . ' 00:00:00';
  97. $currentEndTime = $currentEndDate . ' 23:59:59';
  98. $where['payTime'] = ['between', [$currentStartTime, $currentEndTime]];
  99. }
  100. $customId = $get['customId'] ?? 0;
  101. if (!empty($customId)) {
  102. $where['customId'] = $customId;
  103. }
  104. $unClear = $get['unClear'] ?? 0;
  105. if ($unClear == 1) {
  106. $where['clearId'] = 0;
  107. $where['remainDebtPrice>'] = 0;
  108. }
  109. $ghsOrderList = OrderClass::getAllByCondition($where, null, '*');
  110. return ['list' => $ghsOrderList];
  111. }
  112. /**
  113. * 客户业绩:按客户汇总笔数/扎数/金额/成本/利润,供 ghsApp customSale、/stat-kh-cg/profile 使用。
  114. * totalCount 为总扎数(含隔天售后扣减),与各行 count 口径一致。
  115. */
  116. public static function statCustomCg($mainId)
  117. {
  118. $get = Yii::$app->request->get();
  119. $where = [];
  120. $where['mainId'] = $mainId;
  121. $searchTime = !empty($get['searchTime']) ? $get['searchTime'] : 'today';
  122. $startTime = $get['startTime'] ?? '';
  123. $endTime = $get['endTime'] ?? '';
  124. $period = dateUtil::formatTime($searchTime, $startTime, $endTime, true);
  125. $start = $period['startTime'];
  126. $end = $period['endTime'];
  127. $where['time'] = ['between', [$start, $end]];
  128. $currentStartDate = date('Y-m-d', strtotime($start));
  129. $currentEndDate = date('Y-m-d', strtotime($end));
  130. $currentStartTime = $currentStartDate . ' 00:00:00';
  131. $currentEndTime = $currentEndDate . ' 23:59:59';
  132. $where = ['mainId' => $mainId, 'status' => ['in', [OrderClass::ORDER_STATUS_UN_SEND, OrderClass::ORDER_STATUS_SENDING, OrderClass::ORDER_STATUS_COMPLETE]]];
  133. $where['payTime'] = ['between', [$currentStartTime, $currentEndTime]];
  134. $customList = CustomClass::getAllByCondition(['ownMainId' => $mainId], null, 'id,name,py', 'id');
  135. $arr = [];
  136. $ghsOrderList = OrderClass::getAllByCondition($where, null, '*');
  137. $totalAmount = 0;
  138. $totalNum = 0;
  139. $totalCount = 0;
  140. $totalProfit = 0;
  141. $totalCost = 0;
  142. if (!empty($ghsOrderList)) {
  143. foreach ($ghsOrderList as $ghsKey => $ghsOrder) {
  144. $actPrice = $ghsOrder['actPrice'] ?? 0;
  145. if ($actPrice <= 0) {
  146. continue;
  147. }
  148. $cost = $ghsOrder['remainCost'] ?? 0;
  149. $profit = bcsub($actPrice, $cost, 2);
  150. $orderPrice = $ghsOrder['orderPrice'] ?? 0;
  151. $tkPrice = $ghsOrder['tkPrice'] ?? 0;
  152. $customId = $ghsOrder['customId'] ?? 0;
  153. $status = $ghsOrder['status'] ?? 0;
  154. $book = $ghsOrder['book'] ?? 0;
  155. //扎数
  156. $count = $ghsOrder['itemNum'] ?? 0;
  157. if ($status == 1 || $status == 5) {
  158. continue;
  159. }
  160. if ($book == 1 && $status == 2) {
  161. continue;
  162. }
  163. $customName = $customList[$customId]['name'] ?? '';
  164. $py = $customList[$customId]['py'] ?? '';
  165. $totalNum++;
  166. $totalCount = bcadd($totalCount, $count, 2);
  167. $totalAmount = bcadd($totalAmount, $actPrice, 2);
  168. $totalProfit = bcadd($totalProfit, $profit, 2);
  169. $totalCost = bcadd($totalCost, $cost, 2);
  170. if (isset($arr[$customId])) {
  171. $arr[$customId]['num']++;
  172. $arr[$customId]['amount'] = bcadd($arr[$customId]['amount'], $actPrice, 2);
  173. $arr[$customId]['count'] = bcadd($arr[$customId]['count'], $count, 2);
  174. $arr[$customId]['orderPrice'] = bcadd($arr[$customId]['orderPrice'], $orderPrice, 2);
  175. $arr[$customId]['tkPrice'] = bcadd($arr[$customId]['tkPrice'], $tkPrice, 2);
  176. $arr[$customId]['cost'] = bcadd($arr[$customId]['cost'], $cost, 2);
  177. $arr[$customId]['profit'] = bcadd($arr[$customId]['profit'], $profit, 2);
  178. } else {
  179. $arr[$customId] = [
  180. 'customId' => $customId,
  181. 'customName' => $customName,
  182. 'py' => $py,
  183. 'source' => 'ghs',
  184. 'num' => 1,
  185. 'count' => $count,
  186. 'amount' => $actPrice,
  187. 'levelName' => '',
  188. 'orderPrice' => $orderPrice,
  189. 'tkPrice' => $tkPrice,
  190. 'cost' => $cost,
  191. 'profit' => $profit,
  192. ];
  193. }
  194. }
  195. }
  196. // 零售开单:与批发共用客户业绩;key 用 hd_{id},名称加「零售-」前缀
  197. $productCostMap = ProductClass::getAllByCondition(['mainId' => $mainId], null, 'id,cost,avCost', 'id');
  198. $hdWhere = [
  199. 'mainId' => $mainId,
  200. 'status' => ['in', [HdOrderClass::ORDER_STATUS_UN_SEND, HdOrderClass::ORDER_STATUS_SENDING, HdOrderClass::ORDER_STATUS_COMPLETE]],
  201. 'payTime' => ['between', [$currentStartTime, $currentEndTime]],
  202. ];
  203. $hdOrderList = HdOrderClass::getAllByCondition($hdWhere, null, '*');
  204. if (!empty($hdOrderList)) {
  205. foreach ($hdOrderList as $hdOrder) {
  206. $forward = intval($hdOrder['forward'] ?? 0);
  207. if ($forward === 1) {
  208. // 售后付款属支出,不进客户业绩
  209. continue;
  210. }
  211. $status = intval($hdOrder['status'] ?? 0);
  212. if ($status === HdOrderClass::ORDER_STATUS_UN_PAY || $status === HdOrderClass::ORDER_STATUS_CANCEL) {
  213. continue;
  214. }
  215. $mainPay = bcadd((string)($hdOrder['mainPay'] ?? '0'), '0', 2);
  216. $tkPrice = bcadd((string)($hdOrder['tkPrice'] ?? '0'), '0', 2);
  217. $cash = bcadd((string)($hdOrder['cash'] ?? '0'), '0', 2);
  218. // 当天退款已从 mainPay 口径扣 tkPrice;现金另计(对齐渠道收入)
  219. $actPrice = bcadd(bcsub($mainPay, $tkPrice, 2), $cash, 2);
  220. if (bccomp($actPrice, '0', 2) <= 0) {
  221. continue;
  222. }
  223. $customId = intval($hdOrder['customId'] ?? 0);
  224. $rowKey = HdNextDayRefundClass::customStatKey($customId);
  225. $rawName = trim((string)($hdOrder['customName'] ?? ''));
  226. $orderSn = $hdOrder['orderSn'] ?? '';
  227. // 扎数、成本:按支付日净销量(隔天部分 nextRefundNum 留到通过日扣)
  228. $count = '0.00';
  229. $cost = '0.00';
  230. $orderItemList = HdOrderItemClass::getAllByCondition(['orderSn' => $orderSn], null, '*', null, true);
  231. if (!empty($orderItemList)) {
  232. foreach ($orderItemList as $orderItem) {
  233. $remainNum = HdNextDayRefundClass::payDayRemainNum(
  234. $orderItem->num ?? 0,
  235. $orderItem->refundNum ?? 0,
  236. $orderItem->nextRefundNum ?? 0
  237. );
  238. $count = bcadd($count, $remainNum, 2);
  239. $productId = intval($orderItem->itemId ?? 0);
  240. $unitCost = $orderItem->cost ?? null;
  241. if ($unitCost === null || $unitCost === '') {
  242. $unitCost = $productCostMap[$productId]['avCost']
  243. ?? ($productCostMap[$productId]['cost'] ?? 0);
  244. }
  245. $cost = bcadd($cost, bcmul($remainNum, (string)$unitCost, 2), 2);
  246. }
  247. }
  248. $profit = bcsub($actPrice, $cost, 2);
  249. $totalNum++;
  250. $totalCount = bcadd($totalCount, $count, 2);
  251. $totalAmount = bcadd($totalAmount, $actPrice, 2);
  252. $totalProfit = bcadd($totalProfit, $profit, 2);
  253. $totalCost = bcadd($totalCost, $cost, 2);
  254. if (isset($arr[$rowKey])) {
  255. $arr[$rowKey]['num']++;
  256. $arr[$rowKey]['amount'] = bcadd($arr[$rowKey]['amount'], $actPrice, 2);
  257. $arr[$rowKey]['count'] = bcadd($arr[$rowKey]['count'], $count, 2);
  258. $arr[$rowKey]['orderPrice'] = bcadd($arr[$rowKey]['orderPrice'], $mainPay, 2);
  259. $arr[$rowKey]['tkPrice'] = bcadd($arr[$rowKey]['tkPrice'], $tkPrice, 2);
  260. $arr[$rowKey]['cost'] = bcadd($arr[$rowKey]['cost'], $cost, 2);
  261. $arr[$rowKey]['profit'] = bcadd($arr[$rowKey]['profit'], $profit, 2);
  262. } else {
  263. $arr[$rowKey] = [
  264. 'customId' => $customId,
  265. 'customName' => HdNextDayRefundClass::displayCustomName($rawName, $customId),
  266. 'py' => '',
  267. 'source' => 'hd',
  268. 'num' => 1,
  269. 'count' => $count,
  270. 'amount' => $actPrice,
  271. 'levelName' => '',
  272. 'orderPrice' => $mainPay,
  273. 'tkPrice' => $tkPrice,
  274. 'cost' => $cost,
  275. 'profit' => $profit,
  276. ];
  277. }
  278. }
  279. }
  280. // 隔天售后:按通过日扣金额/扎数;无当日开单也建负行(笔数不减)
  281. // ghs 批发客户用原 customId;hd 零售用 hd_{id} 隔离,避免与 xhGhsCustom 撞号
  282. $fwdMap = NextDayRefundClass::sumAmountGroupByCustom($mainId, $currentStartTime, $currentEndTime);
  283. $qtyMap = NextDayRefundClass::sumItemQtyByCustom($mainId, $currentStartTime, $currentEndTime);
  284. $cidKeys = array_unique(array_merge(array_keys($fwdMap), array_keys($qtyMap)));
  285. foreach ($cidKeys as $cid) {
  286. $amt = $fwdMap[$cid] ?? '0';
  287. $qty = $qtyMap[$cid] ?? '0';
  288. if (bccomp((string)$amt, '0', 2) == 0 && bccomp((string)$qty, '0', 2) == 0) {
  289. continue;
  290. }
  291. if (!isset($arr[$cid])) {
  292. $arr[$cid] = [
  293. 'customId' => $cid,
  294. 'customName' => $customList[$cid]['name'] ?? '',
  295. 'py' => $customList[$cid]['py'] ?? '',
  296. 'source' => 'ghs',
  297. 'num' => 0,
  298. 'count' => '0.00',
  299. 'amount' => '0.00',
  300. 'levelName' => '',
  301. 'orderPrice' => 0,
  302. 'tkPrice' => 0,
  303. 'cost' => 0,
  304. 'profit' => '0.00',
  305. ];
  306. }
  307. $arr[$cid]['amount'] = bcsub((string)$arr[$cid]['amount'], (string)$amt, 2);
  308. $arr[$cid]['profit'] = bcsub((string)$arr[$cid]['profit'], (string)$amt, 2);
  309. $arr[$cid]['count'] = bcsub((string)($arr[$cid]['count'] ?? 0), (string)$qty, 2);
  310. $totalAmount = bcsub((string)$totalAmount, (string)$amt, 2);
  311. $totalProfit = bcsub((string)$totalProfit, (string)$amt, 2);
  312. $totalCount = bcsub((string)$totalCount, (string)$qty, 2);
  313. }
  314. // hd 零售隔天:独立 key +「零售-」客户名
  315. $hdAmtMap = HdNextDayRefundClass::sumAmountGroupByCustom($mainId, $currentStartTime, $currentEndTime);
  316. $hdQtyMap = HdNextDayRefundClass::sumItemQtyByCustom($mainId, $currentStartTime, $currentEndTime);
  317. $hdCids = array_unique(array_merge(array_keys($hdAmtMap), array_keys($hdQtyMap)));
  318. foreach ($hdCids as $cid) {
  319. $amtInfo = $hdAmtMap[$cid] ?? null;
  320. $qtyInfo = $hdQtyMap[$cid] ?? null;
  321. $amt = is_array($amtInfo) ? ($amtInfo['amount'] ?? '0') : '0';
  322. $qty = is_array($qtyInfo) ? ($qtyInfo['num'] ?? '0') : '0';
  323. if (bccomp((string)$amt, '0', 2) == 0 && bccomp((string)$qty, '0', 2) == 0) {
  324. continue;
  325. }
  326. $rowKey = HdNextDayRefundClass::customStatKey($cid);
  327. $rawName = '';
  328. $py = '';
  329. if (is_array($amtInfo)) {
  330. $rawName = $amtInfo['customName'] ?? '';
  331. $py = $amtInfo['py'] ?? '';
  332. }
  333. if ($rawName === '' && is_array($qtyInfo)) {
  334. $rawName = $qtyInfo['customName'] ?? '';
  335. if ($py === '') {
  336. $py = $qtyInfo['py'] ?? '';
  337. }
  338. }
  339. if (!isset($arr[$rowKey])) {
  340. $arr[$rowKey] = [
  341. 'customId' => $cid,
  342. 'customName' => HdNextDayRefundClass::displayCustomName($rawName, $cid),
  343. 'py' => $py,
  344. 'source' => 'hd',
  345. 'num' => 0,
  346. 'count' => '0.00',
  347. 'amount' => '0.00',
  348. 'levelName' => '',
  349. 'orderPrice' => 0,
  350. 'tkPrice' => 0,
  351. 'cost' => 0,
  352. 'profit' => '0.00',
  353. ];
  354. }
  355. $arr[$rowKey]['amount'] = bcsub((string)$arr[$rowKey]['amount'], (string)$amt, 2);
  356. $arr[$rowKey]['profit'] = bcsub((string)$arr[$rowKey]['profit'], (string)$amt, 2);
  357. $arr[$rowKey]['count'] = bcsub((string)($arr[$rowKey]['count'] ?? 0), (string)$qty, 2);
  358. $totalAmount = bcsub((string)$totalAmount, (string)$amt, 2);
  359. $totalProfit = bcsub((string)$totalProfit, (string)$amt, 2);
  360. $totalCount = bcsub((string)$totalCount, (string)$qty, 2);
  361. }
  362. if (!empty($arr)) {
  363. $arr = arrayUtil::arraySort($arr, 'profit');
  364. }
  365. $totalAmount = floatval($totalAmount);
  366. return ['list' => $arr, 'totalAmount' => $totalAmount, 'totalNum' => $totalNum, 'totalCount' => floatval($totalCount), 'totalCost' => $totalCost, 'totalProfit' => $totalProfit];
  367. }
  368. /**
  369. * 经营利润总表:收入 − 支出。
  370. * 采购隔天售后:不改「花材采购」入库口径;通过日单独「采购隔天退货」(退货并退款+仅退款合计)计入收入。
  371. */
  372. public static function profile($mainId)
  373. {
  374. //收入:批发开单、零售开单(含门店、美团和微信客服)、出库、采购隔天退货
  375. //支出:采购、调拨入库、批发退款、零售退款、店租、水电、伙食
  376. $get = Yii::$app->request->get();
  377. $where = [];
  378. $where['mainId'] = $mainId;
  379. $searchTime = isset($get['searchTime']) && !empty($get['searchTime']) ? $get['searchTime'] : 'today';
  380. $startTime = $get['startTime'] ?? '';
  381. $endTime = $get['endTime'] ?? '';
  382. $period = dateUtil::formatTime($searchTime, $startTime, $endTime, true);
  383. $start = $period['startTime'];
  384. $end = $period['endTime'];
  385. $where['time'] = ['between', [$start, $end]];
  386. $currentStartDate = date('Y-m-d', strtotime($start));
  387. $currentEndDate = date('Y-m-d', strtotime($end));
  388. $currentStartTime = $currentStartDate . ' 00:00:00';
  389. $currentEndTime = $currentEndDate . ' 23:59:59';
  390. //出库
  391. $stockOut = StatStockOutClass::getSum($where);
  392. //采购入库
  393. $cgWhere = ['mainId' => $mainId, 'status' => 4];
  394. $cgWhere['entryTime'] = ['between', [$currentStartTime, $currentEndTime]];
  395. $cgList = PurchaseOrderClass::getAllByCondition($cgWhere, null, '*');
  396. $cg = 0;
  397. if (!empty($cgList)) {
  398. foreach ($cgList as $cgKey => $cgItem) {
  399. $actPrice = $cgItem['actPrice'] ?? 0;
  400. $paidPrice = $cgItem['paidPrice'] ?? 0;
  401. if ($actPrice <= 0) {
  402. // 如果$actPrice=0,表示这个采购单已经全部退款,不需要再统计成本,本来$paidPrice要同步清零,但没有,这个问题由徐州邂逅花坊马总,提出的需求发现
  403. $cg = bcadd($cg, 0, 2);
  404. } else {
  405. $total = bcadd($actPrice, $paidPrice, 2);
  406. //物流费用不算成本。意思是说,长短途运费+提货费+本地运费,这四项和打包费一起,参与到平摊花材运费成本的计算,但那四项不参与总的采购成本登记,因为,有另外地方会独立登记运费成本。
  407. //$paidPrice 字段说明,此字段表示收货方已付款金额,包括 $localCharge本地运费、 $pickCharge提货费、 $shortCharge短途运费(到付时算,如果不是到付,不算在$paidPrice里)、$longCharge长途运费(到付时算,如果不是到付,不算在$paidPrice里)
  408. //请搜索关键词 cg_item_cost ,有多处相似情况要考虑
  409. if (isset($cgItem['isCost']) && $cgItem['isCost'] == 0) {
  410. $shortCharge = $cgItem['shortCharge'] ?? 0;
  411. $longCharge = $cgItem['longCharge'] ?? 0;
  412. $pickCharge = $cgItem['pickCharge'] ?? 0;
  413. $localCharge = $cgItem['localCharge'] ?? 0;
  414. $a1 = bcadd($shortCharge, $longCharge, 2);
  415. $a2 = bcadd($pickCharge, $localCharge, 2);
  416. $a = bcadd($a1, $a2, 2);
  417. //注意下面是减号 bcsub
  418. $total = bcsub($total, $a, 2);
  419. }
  420. $cg = bcadd($cg, $total, 2);
  421. }
  422. }
  423. }
  424. $cg = floatval($cg);
  425. $lsCgWhere = ['mainId' => $mainId, 'status' => 4, 'belongCost' => 1];
  426. $lsCgWhere['payTime'] = ['between', [$currentStartTime, $currentEndTime]];
  427. $lsCgList = PurchaseClass::getAllByCondition($lsCgWhere, null, '*');
  428. $lsCgAmount = 0;
  429. if (!empty($lsCgList)) {
  430. foreach ($lsCgList as $lsCgItem) {
  431. $actPrice = $lsCgItem['actPrice'] ?? 0;
  432. $lsCgAmount = bcadd($lsCgAmount, $actPrice, 2);
  433. }
  434. }
  435. $lsCgAmount = floatval($lsCgAmount);
  436. $lzWhere = ['mainId' => $mainId, 'status' => 3];
  437. $lzWhere['entryTime'] = ['between', [$currentStartTime, $currentEndTime]];
  438. $lzCgList = PlantCgClass::getAllByCondition($lzWhere, null, '*');
  439. $lzCgAmount = 0;
  440. if (!empty($lzCgList)) {
  441. foreach ($lzCgList as $lzInfo) {
  442. $actPrice = $lzInfo['actPrice'] ?? 0;
  443. $lzCgAmount = bcadd($actPrice, $lzCgAmount, 2);
  444. }
  445. }
  446. $lzCgAmount = floatval($lzCgAmount);
  447. //调拨入库
  448. $stockIn = StatStockInClass::getSum($where);
  449. $totalOrderNum = 0;
  450. //批发开单
  451. $pfIncome = 0;
  452. $pfSendCost = 0;
  453. $pfPack = 0;
  454. $where = ['mainId' => $mainId, 'status' => ['in', [OrderClass::ORDER_STATUS_UN_SEND, OrderClass::ORDER_STATUS_SENDING, OrderClass::ORDER_STATUS_COMPLETE]]];
  455. $where['payTime'] = ['between', [$currentStartTime, $currentEndTime]];
  456. $ghsOrderList = OrderClass::getAllByCondition($where, null, '*');
  457. if (!empty($ghsOrderList)) {
  458. foreach ($ghsOrderList as $ghsKey => $ghsOrder) {
  459. $actPrice = $ghsOrder['actPrice'] ?? 0;
  460. $sendCost = $ghsOrder['sendCost'] ?? 0;
  461. $refundSendCost = $ghsOrder['refundSendCost'] ?? 0;
  462. $remainSendCost = bcsub($sendCost, $refundSendCost, 2);
  463. $packCost = $ghsOrder['packCost'] ?? 0;
  464. $refundPackCost = $ghsOrder['refundPackCost'] ?? 0;
  465. $remainPackCost = bcsub($packCost, $refundPackCost, 2);
  466. $pfIncome = bcadd($pfIncome, $actPrice, 2);
  467. $pfSendCost = bcadd($pfSendCost, $remainSendCost, 2);
  468. $pfPack = bcadd($pfPack, $remainPackCost, 2);
  469. $totalOrderNum++;
  470. }
  471. }
  472. // 隔天售后扣减批发收入(笔数不变)
  473. $pfIncome = bcsub(
  474. (string)$pfIncome,
  475. NextDayRefundClass::sumAmountByMainAndTime($mainId, $currentStartTime, $currentEndTime),
  476. 2
  477. );
  478. // 采购隔天售后(退货并退款+仅退款):通过日合计,单列收入,不改花材采购
  479. $cgNextDayRefund = CgPurchaseNextDayClass::sumAmountByMainAndTime(
  480. $mainId,
  481. $currentStartTime,
  482. $currentEndTime
  483. );
  484. //零售开单
  485. $payCodeIncome = 0;
  486. $lsAfterSale = 0;
  487. $lsGiveBalance = 0;
  488. $lsIncome = 0;
  489. $lsSendCost = 0;
  490. $lsServiceFee = 0;
  491. $hdWhere = ['mainId' => $mainId, 'status' => ['in', [HdOrderClass::ORDER_STATUS_UN_SEND, HdOrderClass::ORDER_STATUS_SENDING, HdOrderClass::ORDER_STATUS_COMPLETE]]];
  492. $hdWhere['payTime'] = ['between', [$currentStartTime, $currentEndTime]];
  493. $hdOrderList = HdOrderClass::getAllByCondition($hdWhere, null, '*');
  494. foreach ($hdOrderList as $hdKey => $hdOrder) {
  495. $actPrice = $hdOrder['mainPay'] ?? 0;
  496. $onlinePay = $hdOrder['onlinePay'] ?? 1;
  497. $repeat = $hdOrder['repeat'] ?? 0;
  498. $forward = $hdOrder['forward'] ?? 0;
  499. if ($hdOrder['tkPrice'] && $hdOrder['tkPrice'] > 0) {
  500. $actPrice = bcsub($actPrice, $hdOrder['tkPrice'], 2);
  501. }
  502. if ($forward == 1) {
  503. //零售售后付款
  504. $lsAfterSale = bcadd($lsAfterSale, $actPrice, 2);
  505. } else {
  506. $cash = $hdOrder['cash'] ?? 0;
  507. $serviceFee = $hdOrder['serviceFee'] ?? 0;
  508. $sendCost = $hdOrder['sendCost'] ?? 0;
  509. $lsIncome = bcadd($actPrice, $lsIncome, 2);
  510. $lsIncome = bcadd($lsIncome, $cash, 2);
  511. //如果$actPrice<=0,则退款取消先不计算成本
  512. if ($actPrice > 0) {
  513. $lsSendCost = bcadd($lsSendCost, $sendCost, 2);
  514. $lsServiceFee = bcadd($lsServiceFee, $serviceFee, 2);
  515. }
  516. $totalOrderNum++;
  517. if ($onlinePay == 2 && floatval($cash) <= 0 && $repeat == 1) {
  518. $payCodeIncome = bcadd($payCodeIncome, $actPrice, 2);
  519. $payCodeIncome = floatval($payCodeIncome);
  520. }
  521. $giveBalance = $hdOrder['giveBalance'] ?? 0;
  522. $giveBalanceTk = $hdOrder['giveBalanceTk'] ?? 0;
  523. $remainGiveBalance = bcsub($giveBalance, $giveBalanceTk, 2);
  524. $lsGiveBalance = bcadd($lsGiveBalance, $remainGiveBalance, 2);
  525. }
  526. }
  527. // 隔天售后扣减零售收入(当天退款已在 tkPrice 扣过;隔天记 nextDayTkPrice,按通过日扣)
  528. $lsIncome = bcsub(
  529. (string)$lsIncome,
  530. HdNextDayRefundClass::sumAmountByMainAndTime($mainId, $currentStartTime, $currentEndTime),
  531. 2
  532. );
  533. $condition = ['mainId' => $mainId, 'addTime' => ['between', [$currentStartTime, $currentEndTime]],];
  534. $salary = 0;
  535. $salaryList = SalaryClass::getAllByCondition($condition, null, '*', null, true);
  536. if (!empty($salaryList)) {
  537. foreach ($salaryList as $item) {
  538. $salary = bcadd($salary, $item->amount, 2);
  539. }
  540. }
  541. $condition = [
  542. 'mainId' => $mainId,
  543. 'payTime' => ['between', [$currentStartTime, $currentEndTime]],
  544. 'delStatus' => 0,
  545. ];
  546. $expendList = ExpendClass::getAllByCondition($condition, null, '*', null, true);
  547. $expendTypeMap = dict::getDict('expendTypeMap');
  548. $expendAmount = [];
  549. if (!empty($expendList)) {
  550. foreach ($expendList as $item) {
  551. $type = $item->type ?? 0;
  552. $amount = $item->amount ?? 0;
  553. $name = $expendTypeMap[$type] ?? '暂无';
  554. if (!isset($expendAmount[$type]['amount'])) {
  555. $expendAmount[$type]['amount'] = 0;
  556. }
  557. $expendAmount[$type]['name'] = $name;
  558. $expendAmount[$type]['amount'] = floatval(bcadd($expendAmount[$type]['amount'], $amount, 2));
  559. }
  560. }
  561. $income = [
  562. ['name' => '批发', 'id' => 'pf', 'amount' => floatval($pfIncome)],
  563. ['name' => '零售', 'id' => 'ls', 'amount' => floatval($lsIncome)],
  564. ['name' => '调拨出库', 'id' => 'allot', 'amount' => floatval($stockOut)],
  565. // 采购隔天售后冲回(含退货并退款、仅退款);后续若细分再拆行
  566. ['name' => '采购隔天退货', 'id' => 'cgNextDay', 'amount' => floatval($cgNextDayRefund)],
  567. ];
  568. $expend = [
  569. ['name' => '批发运费', 'id' => 'pfSendCost', 'amount' => floatval($pfSendCost)],
  570. ['name' => '批发包装费', 'id' => 'pfSendCost', 'amount' => floatval($pfPack)],
  571. ['name' => '零售运费', 'id' => 'lsSendCost', 'amount' => floatval($lsSendCost)],
  572. ['name' => '零售售后付款', 'id' => 'lsSendCost', 'amount' => floatval($lsAfterSale)],
  573. ['name' => '美团手续费', 'id' => 'mtFee', 'amount' => floatval($lsServiceFee)],
  574. ['name' => '花材采购', 'id' => 'cg', 'amount' => floatval($cg)],
  575. ['name' => '绿植资材采购', 'id' => 'cg', 'amount' => floatval($lzCgAmount)],
  576. ['name' => '零售花材采购', 'id' => 'cg', 'amount' => floatval($lsCgAmount)],
  577. ['name' => '调拨入库', 'id' => 'allot', 'amount' => floatval($stockIn)],
  578. ['name' => '员工工资', 'id' => 'salary', 'amount' => floatval($salary)],
  579. ['name' => '收款码收入', 'id' => 'salary', 'amount' => floatval($payCodeIncome)],
  580. ['name' => '零售充值赠送', 'id' => 'salary', 'amount' => floatval($lsGiveBalance)],
  581. ];
  582. if (!empty($expendAmount)) {
  583. foreach ($expendAmount as $it) {
  584. $expend[] = $it;
  585. }
  586. }
  587. $balance = 0;
  588. foreach ($income as $it) {
  589. $amount = $it['amount'] ?? 0;
  590. $balance = bcadd($amount, $balance, 2);
  591. }
  592. foreach ($expend as $it) {
  593. $amount = $it['amount'] ?? 0;
  594. $balance = bcsub($balance, $amount, 2);
  595. }
  596. return ['income' => $income, 'expend' => $expend, 'balance' => floatval($balance), 'totalOrderNum' => $totalOrderNum];
  597. }
  598. //按花材统计收入 ssh 20231111
  599. public static function incomeOutItem($mainId)
  600. {
  601. $get = Yii::$app->request->get();
  602. $export = $get['export'] ?? 0;
  603. $where = [];
  604. $where['mainId'] = $mainId;
  605. $searchTime = isset($get['searchTime']) && !empty($get['searchTime']) ? $get['searchTime'] : 'today';
  606. $startTime = $get['startTime'] ?? '';
  607. $endTime = $get['endTime'] ?? '';
  608. $period = dateUtil::formatTime($searchTime, $startTime, $endTime, true);
  609. $start = $period['startTime'];
  610. $end = $period['endTime'];
  611. $where['time'] = ['between', [$start, $end]];
  612. $currentStartDate = date('Y-m-d', strtotime($start));
  613. $currentEndDate = date('Y-m-d', strtotime($end));
  614. $currentStartTime = $currentStartDate . ' 00:00:00';
  615. $currentEndTime = $currentEndDate . ' 23:59:59';
  616. $s = strtotime($currentStartTime);
  617. $e = strtotime($currentEndTime);
  618. $p = bcsub($e, $s);
  619. $xx = bcmul(86400, 90);
  620. if ($p > $xx) {
  621. util::fail('查询时间不能超过三个月,编号666');
  622. }
  623. $productList = ProductClass::getAllByCondition(['mainId' => $mainId], null, 'id,name', 'id');
  624. if (empty($productList)) {
  625. util::fail('没有找到花材');
  626. }
  627. $arr = [];
  628. //打包费和运费
  629. $packFreight = 0;
  630. $onlyRefundCg = 0;
  631. $onlyRefund = 0;
  632. $totalDiscountAmount = 0;
  633. $totalLabourCost = 0;
  634. //采购入库
  635. $tbCgIncome = [];
  636. $tbCgIncomeStaff = [];
  637. $cgWhere = ['mainId' => $mainId, 'status' => 4];
  638. $cgWhere['entryTime'] = ['between', [$currentStartTime, $currentEndTime]];
  639. $cgList = PurchaseOrderClass::getAllByCondition($cgWhere, null, '*');
  640. if (!empty($cgList)) {
  641. foreach ($cgList as $cgKey => $cgItem) {
  642. $packingCharge = $cgItem['packingCharge'] ?? 0;
  643. $shortCharge = $cgItem['shortCharge'] ?? 0;
  644. $longCharge = $cgItem['longCharge'] ?? 0;
  645. $pickCharge = $cgItem['pickCharge'] ?? 0;
  646. $localCharge = $cgItem['localCharge'] ?? 0;
  647. $packFreight = bcadd($packFreight, $packingCharge, 2);
  648. $packFreight = bcadd($packFreight, $shortCharge, 2);
  649. $packFreight = bcadd($packFreight, $longCharge, 2);
  650. $packFreight = bcadd($packFreight, $pickCharge, 2);
  651. $packFreight = bcadd($packFreight, $localCharge, 2);
  652. $tkPrice = $cgItem['tkPrice'] ?? 0;
  653. $orderSn = $cgItem['orderSn'] ?? '';
  654. $cgItemInfoList = PurchaseOrderItemClass::getAllByCondition(['orderSn' => $orderSn], null, '*', null, true);
  655. if (!empty($cgItemInfoList)) {
  656. foreach ($cgItemInfoList as $cgData) {
  657. $itemNum = $cgData->itemNum ?? 0;
  658. $productId = $cgData->productId ?? 0;
  659. $refundNum = $cgData->refundNum ?? 0;
  660. $remainNum = bcsub($itemNum, $refundNum);
  661. $bigPrice = $cgData->bigPrice ?? 0;
  662. $amount = bcmul($remainNum, $bigPrice, 2);
  663. $itemName = $productList[$productId]['name'] ?? '已删除';
  664. $arr[$productId]['name'] = $itemName;
  665. $arr[$productId]['id'] = $productId;
  666. if (isset($arr[$productId]['expend'])) {
  667. $arr[$productId]['expend'] = bcadd($amount, $arr[$productId]['expend'], 2);
  668. $arr[$productId]['outNum'] = bcadd($arr[$productId]['outNum'], $remainNum, 2);
  669. } else {
  670. $arr[$productId]['expend'] = $amount;
  671. $arr[$productId]['outNum'] = $remainNum;
  672. $arr[$productId]['inNum'] = 0;
  673. $arr[$productId]['income'] = 0;
  674. }
  675. }
  676. if ($tkPrice > 0) {
  677. $cgRefundOrderList = CgRefundClass::getAllByCondition(['relateOrderSn' => $orderSn], null, '*', null, true);
  678. if (!empty($cgRefundOrderList)) {
  679. foreach ($cgRefundOrderList as $cgRefundOrder) {
  680. if ($cgRefundOrder->refundType == 2) {
  681. $onlyRefundCg = bcadd($cgRefundOrder->refundPrice, $onlyRefundCg, 2);
  682. }
  683. }
  684. }
  685. }
  686. }
  687. }
  688. }
  689. //批发开单
  690. $pfIncome = 0;
  691. $tbSaleIncome = [];
  692. $tbSaleIncomeStaff = [];
  693. $where = ['mainId' => $mainId, 'status' => ['in', [OrderClass::ORDER_STATUS_UN_SEND, OrderClass::ORDER_STATUS_SENDING, OrderClass::ORDER_STATUS_COMPLETE]]];
  694. $where['payTime'] = ['between', [$currentStartTime, $currentEndTime]];
  695. $ghsOrderList = OrderClass::getAllByCondition($where, null, '*');
  696. if (!empty($ghsOrderList)) {
  697. foreach ($ghsOrderList as $ghsKey => $ghsOrder) {
  698. $book = $ghsOrder['book'] ?? 0;
  699. $status = $ghsOrder['status'] ?? 0;
  700. if ($book == 1 && $status == 2) {
  701. //预订单待发货的不算收入
  702. continue;
  703. }
  704. if ($status == 0 || $status == 5) {
  705. continue;
  706. }
  707. $shopAdminId = $ghsOrder['shopAdminId'] ?? 0;
  708. $shopAdminName = $ghsOrder['shopAdminName'] ?? '';
  709. $orderSn = $ghsOrder['orderSn'] ?? '';
  710. $actPrice = $ghsOrder['actPrice'] ?? 0;
  711. $tkPrice = $ghsOrder['tkPrice'] ?? 0;
  712. $pfIncome = bcadd($pfIncome, $actPrice, 2);
  713. $labourCost = $ghsOrder['labourCost'] ?? 0;
  714. $discountAmount = $ghsOrder['discountAmount'] ?? 0;
  715. $totalLabourCost = bcadd($labourCost, $totalLabourCost, 2);
  716. $totalDiscountAmount = bcadd($totalDiscountAmount, $discountAmount, 2);
  717. $orderItemList = OrderItemClass::getAllByCondition(['orderSn' => $orderSn], null, '*', null, true);
  718. if (!empty($orderItemList)) {
  719. foreach ($orderItemList as $orderItem) {
  720. $refundNum = $orderItem->refundNum ?? 0;
  721. $name = $orderItem->name ?? '';
  722. $productId = $orderItem->productId ?? 0;
  723. $num = $orderItem->xhNum ?? 0;
  724. // 支付日净销量:当天已退=refundNum-nextRefundNum
  725. $remainNum = NextDayRefundClass::payDayRemainNum(
  726. $num,
  727. $refundNum,
  728. $orderItem->nextRefundNum ?? 0
  729. );
  730. $xhUnitPrice = $orderItem->xhUnitPrice ?? 0;
  731. $amount = bcmul($remainNum, $xhUnitPrice, 2);
  732. $itemName = $productList[$productId]['name'] ?? '已删除';
  733. $arr[$productId]['name'] = $itemName;
  734. $arr[$productId]['id'] = $productId;
  735. if (isset($arr[$productId]['income'])) {
  736. $arr[$productId]['income'] = bcadd($amount, $arr[$productId]['income'], 2);
  737. $arr[$productId]['inNum'] = bcadd($arr[$productId]['inNum'], $remainNum, 2);
  738. } else {
  739. $arr[$productId]['expend'] = 0;
  740. $arr[$productId]['outNum'] = 0;
  741. $arr[$productId]['inNum'] = $remainNum;
  742. $arr[$productId]['income'] = $amount;
  743. }
  744. //单独统计
  745. $belongCost = $orderItem->belongCost ?? 0;
  746. if ($belongCost == 1) {
  747. if (isset($tbSaleIncome[$productId])) {
  748. $tbSaleIncome[$productId]['num'] = bcadd($tbSaleIncome[$productId]['num'], $remainNum);
  749. $tbSaleIncome[$productId]['price'] = bcadd($tbSaleIncome[$productId]['price'], $amount, 2);
  750. } else {
  751. $tbSaleIncome[$productId] = ['name' => $name, 'num' => $remainNum, 'price' => $amount];
  752. }
  753. if (isset($tbSaleIncomeStaff[$productId]) && isset($tbSaleIncomeStaff[$productId][$shopAdminId])) {
  754. $tbSaleIncomeStaff[$productId][$shopAdminId]['num'] = bcadd($tbSaleIncomeStaff[$productId][$shopAdminId]['num'], $remainNum);
  755. $tbSaleIncomeStaff[$productId][$shopAdminId]['price'] = bcadd($tbSaleIncomeStaff[$productId][$shopAdminId]['price'], $amount, 2);
  756. } else {
  757. $tbSaleIncomeStaff[$productId][$shopAdminId] = ['name' => $name, 'num' => $remainNum, 'price' => $amount, 'staffName' => $shopAdminName];
  758. }
  759. }
  760. }
  761. if ($tkPrice > 0) {
  762. $refundList = RefundOrderClass::getAllByCondition(['relateOrderSn' => $orderSn], null, '*', null, true);
  763. if (!empty($refundList)) {
  764. foreach ($refundList as $refund) {
  765. if ($refund->refundType == 1) {
  766. //退货退款前面已经考虑,这里不再处理 OK
  767. } else {
  768. // 隔天仅退款记在通过日,这里跳过避免与下方 sumMoneyOnly 重复
  769. if (intval($refund->sameDay ?? RefundOrderClass::SAME_DAY_YES) === RefundOrderClass::SAME_DAY_NO) {
  770. continue;
  771. }
  772. $onlyRefund = bcadd($onlyRefund, $refund->refundPrice, 2);
  773. }
  774. }
  775. }
  776. }
  777. }
  778. }
  779. }
  780. // 隔天售后:支付日已用 nextRefundNum;通过日再扣数量/金额;仅退款并入销售仅退款
  781. $onlyRefund = bcadd(
  782. (string)$onlyRefund,
  783. self::applyNextDayAdjustToProductIncomeRows(
  784. $arr,
  785. $tbSaleIncome,
  786. $productList,
  787. $mainId,
  788. $currentStartTime,
  789. $currentEndTime
  790. ),
  791. 2
  792. );
  793. //零售开单
  794. $lsIncome = 0;
  795. $hdWhere = ['mainId' => $mainId, 'status' => ['in', [HdOrderClass::ORDER_STATUS_UN_SEND, HdOrderClass::ORDER_STATUS_SENDING, HdOrderClass::ORDER_STATUS_COMPLETE]]];
  796. $hdWhere['payTime'] = ['between', [$currentStartTime, $currentEndTime]];
  797. $hdOrderList = HdOrderClass::getAllByCondition($hdWhere, null, '*');
  798. foreach ($hdOrderList as $hdKey => $hdOrder) {
  799. $actPrice = $hdOrder['mainPay'] ?? 0;
  800. $cash = $hdOrder['cash'] ?? 0;
  801. $lsIncome = bcadd($actPrice, $lsIncome, 2);
  802. $lsIncome = bcadd($lsIncome, $cash, 2);
  803. $orderSn = $hdOrder['orderSn'] ?? '';
  804. $tkPrice = $hdOrder['tkPrice'] ?? 0;
  805. $orderItemList = \bizHd\order\classes\OrderItemClass::getAllByCondition(['orderSn' => $orderSn], null, '*', null, true);
  806. if (!empty($orderItemList)) {
  807. foreach ($orderItemList as $orderItem) {
  808. $unitPrice = $orderItem->unitPrice ?? 0;
  809. $refundNum = $orderItem->refundNum ?? 0;
  810. $num = $orderItem->num ?? 0;
  811. $productId = $orderItem->itemId ?? 0;
  812. // 支付日净销量:隔天部分记 nextRefundNum,通过日再扣
  813. $remainNum = HdNextDayRefundClass::payDayRemainNum(
  814. $num,
  815. $refundNum,
  816. $orderItem->nextRefundNum ?? 0
  817. );
  818. $amount = bcmul($remainNum, $unitPrice, 2);
  819. $itemName = $productList[$productId]['name'] ?? '已删除';
  820. $arr[$productId]['name'] = $itemName;
  821. $arr[$productId]['id'] = $productId;
  822. if (isset($arr[$productId]['income'])) {
  823. $arr[$productId]['income'] = bcadd($amount, $arr[$productId]['income'], 2);
  824. $arr[$productId]['inNum'] = bcadd($arr[$productId]['inNum'], $remainNum, 2);
  825. } else {
  826. $arr[$productId]['expend'] = 0;
  827. $arr[$productId]['outNum'] = 0;
  828. $arr[$productId]['inNum'] = $remainNum;
  829. $arr[$productId]['income'] = $amount;
  830. }
  831. }
  832. if ($tkPrice > 0) {
  833. $refundList = HdRefundClass::getAllByCondition(['orderSn' => $orderSn, 'status' => HdRefundClass::STATUS_COMPLETE], null, '*', null, true);
  834. if (!empty($refundList)) {
  835. foreach ($refundList as $refund) {
  836. if ($refund->refundType == 2) {
  837. // 隔天仅退款记在通过日,避免与 applyNextDayAdjust 重复
  838. if (intval($refund->sameDay ?? HdRefundClass::SAME_DAY_YES) === HdRefundClass::SAME_DAY_NO) {
  839. continue;
  840. }
  841. $onlyRefund = bcadd($onlyRefund, $refund->refundPrice, 2);
  842. }
  843. }
  844. }
  845. }
  846. }
  847. }
  848. $totalIncome = 0;
  849. $totalExpend = 0;
  850. if (!empty($arr)) {
  851. foreach ($arr as $key => $val) {
  852. $income = $val['income'] ?? 0;
  853. $income = floatval($income);
  854. $totalIncome = bcadd($totalIncome, $income, 2);
  855. $expend = $val['expend'] ?? 0;
  856. $expend = floatval($expend);
  857. $totalExpend = bcadd($totalExpend, $expend, 2);
  858. $profit = bcsub($income, $expend, 2);
  859. $arr[$key]['profit'] = floatval($profit);
  860. $arr[$key]['income'] = floatval($income);
  861. $arr[$key]['expend'] = floatval($expend);
  862. }
  863. }
  864. //总成本算上物流打包等费用
  865. $totalExpend = bcadd($totalExpend, $packFreight, 2);
  866. //总收入算上采购仅退款
  867. $totalIncome = bcadd($totalIncome, $onlyRefundCg, 2);
  868. //总收入加上人工费
  869. $totalIncome = bcadd($totalIncome, $totalLabourCost, 2);
  870. //总收入减掉优惠金额
  871. $totalIncome = bcsub($totalIncome, $totalDiscountAmount, 2);
  872. //总成本算上销售仅退款
  873. $totalExpend = bcadd($totalExpend, $onlyRefund, 2);
  874. $totalProfit = bcsub($totalIncome, $totalExpend, 2);
  875. $totalIncome = floatval($totalIncome);
  876. $totalExpend = floatval($totalExpend);
  877. $totalProfit = floatval($totalProfit);
  878. $arr = array_values($arr);
  879. $arr = arrayUtil::arraySort($arr, 'profit');
  880. if ($export == 1) {
  881. $statData = [
  882. ['id' => 0, 'name' => '物流打包费', 'income' => 0, 'expend' => $packFreight, 'profit' => -$packFreight, 'inNum' => 0, 'outNum' => 0],
  883. ['id' => 0, 'name' => '采购仅退款', 'income' => $onlyRefundCg, 'expend' => 0, 'profit' => $onlyRefundCg, 'inNum' => 0, 'outNum' => 0],
  884. ['id' => 0, 'name' => '销售仅退款', 'income' => 0, 'expend' => $onlyRefund, 'profit' => -$onlyRefund, 'inNum' => 0, 'outNum' => 0],
  885. ['id' => 0, 'name' => '附加人工费', 'income' => $totalLabourCost, 'expend' => 0, 'profit' => $totalLabourCost, 'inNum' => 0, 'outNum' => 0],
  886. ['id' => 0, 'name' => '优惠金额', 'income' => -$totalDiscountAmount, 'expend' => 0, 'profit' => -$totalDiscountAmount, 'inNum' => 0, 'outNum' => 0],
  887. ['id' => 0, 'name' => '【汇总金额】', 'income' => $totalIncome, 'expend' => $totalExpend, 'profit' => $totalProfit, 'inNum' => 0, 'outNum' => 0],
  888. ];
  889. $list = array_merge($arr, $statData);
  890. self::exportIncomeOutItem($list, $mainId);
  891. }
  892. util::success(['list' => $arr, 'packFreight' => $packFreight, 'onlyRefundCg' => $onlyRefundCg, 'onlyRefund' => $onlyRefund,
  893. 'totalIncome' => $totalIncome,
  894. 'totalExpend' => $totalExpend,
  895. 'totalProfit' => $totalProfit,
  896. 'tbSaleIncome' => $tbSaleIncome,
  897. 'tbCgIncome' => $tbCgIncome,
  898. 'tbCgIncomeStaff' => $tbCgIncomeStaff,
  899. 'tbSaleIncomeStaff' => $tbSaleIncomeStaff,
  900. 'totalLabourCost' => $totalLabourCost,
  901. 'totalDiscountAmount' => $totalDiscountAmount,
  902. ]);
  903. }
  904. //按分类统计收入 ssh 20231111
  905. public static function classProfile($mainId)
  906. {
  907. $get = Yii::$app->request->get();
  908. $export = $get['export'] ?? 0;
  909. $where = [];
  910. $where['mainId'] = $mainId;
  911. $searchTime = isset($get['searchTime']) && !empty($get['searchTime']) ? $get['searchTime'] : 'today';
  912. $startTime = $get['startTime'] ?? '';
  913. $endTime = $get['endTime'] ?? '';
  914. $period = dateUtil::formatTime($searchTime, $startTime, $endTime, true);
  915. $start = $period['startTime'];
  916. $end = $period['endTime'];
  917. $where['time'] = ['between', [$start, $end]];
  918. $currentStartDate = date('Y-m-d', strtotime($start));
  919. $currentEndDate = date('Y-m-d', strtotime($end));
  920. $currentStartTime = $currentStartDate . ' 00:00:00';
  921. $currentEndTime = $currentEndDate . ' 23:59:59';
  922. $s = strtotime($currentStartTime);
  923. $e = strtotime($currentEndTime);
  924. $p = bcsub($e, $s);
  925. $xx = bcmul(86400, 90);
  926. if ($p > $xx) {
  927. util::fail('查询时间不能超过三个月,编号780');
  928. }
  929. $classList = ItemClassClass::getAllByCondition(['mainId' => $mainId], null, 'id,name', 'id');
  930. if (empty($classList)) {
  931. util::fail('没有找到花材');
  932. }
  933. $arr = [];
  934. //打包费和运费
  935. $packFreight = 0;
  936. $onlyRefundCg = 0;
  937. $onlyRefund = 0;
  938. $totalDiscountAmount = 0;
  939. $totalLabourCost = 0;
  940. //采购入库
  941. $tbCgIncome = [];
  942. $tbCgIncomeStaff = [];
  943. $cgWhere = ['mainId' => $mainId, 'status' => 4];
  944. $cgWhere['entryTime'] = ['between', [$currentStartTime, $currentEndTime]];
  945. $cgList = PurchaseOrderClass::getAllByCondition($cgWhere, null, '*');
  946. if (!empty($cgList)) {
  947. foreach ($cgList as $cgKey => $cgItem) {
  948. $packingCharge = $cgItem['packingCharge'] ?? 0;
  949. $shortCharge = $cgItem['shortCharge'] ?? 0;
  950. $longCharge = $cgItem['longCharge'] ?? 0;
  951. $pickCharge = $cgItem['pickCharge'] ?? 0;
  952. $localCharge = $cgItem['localCharge'] ?? 0;
  953. $cgStaffId = $cgItem['cgStaffId'] ?? 0;
  954. $cgStaffName = $cgItem['cgStaffName'] ?? '';
  955. $actPrice = $cgItem['actPrice'] ?? 0;
  956. if ($actPrice <= 0) {
  957. continue;
  958. }
  959. $packFreight = bcadd($packFreight, $packingCharge, 2);
  960. $packFreight = bcadd($packFreight, $shortCharge, 2);
  961. $packFreight = bcadd($packFreight, $longCharge, 2);
  962. $packFreight = bcadd($packFreight, $pickCharge, 2);
  963. $packFreight = bcadd($packFreight, $localCharge, 2);
  964. $tkPrice = $cgItem['tkPrice'] ?? 0;
  965. $orderSn = $cgItem['orderSn'] ?? '';
  966. $cgItemInfoList = PurchaseOrderItemClass::getAllByCondition(['orderSn' => $orderSn], null, '*', null, true);
  967. if (!empty($cgItemInfoList)) {
  968. foreach ($cgItemInfoList as $cgData) {
  969. $itemNum = $cgData->itemNum ?? 0;
  970. $productId = $cgData->productId ?? 0;
  971. $name = $cgData->name ?? '';
  972. $refundNum = $cgData->refundNum ?? 0;
  973. // 入库日净量:隔天退货记 nextRefundNum,通过日再扣成本
  974. $remainNum = CgPurchaseNextDayClass::payDayRemainNum(
  975. $itemNum,
  976. $refundNum,
  977. $cgData->nextRefundNum ?? 0
  978. );
  979. $bigPrice = $cgData->bigPrice ?? 0;
  980. $amount = bcmul($remainNum, $bigPrice, 2);
  981. $classId = $cgData->classId;
  982. $className = $classList[$classId]['name'] ?? '已删除';
  983. if ($className == '运费') {
  984. //noticeUtil::push("{$cgData->orderSn}运费项", '15280215347');
  985. }
  986. $arr[$classId]['className'] = $className;
  987. $arr[$classId]['classId'] = $classId;
  988. if (isset($arr[$classId]['expend'])) {
  989. $arr[$classId]['expend'] = bcadd($amount, $arr[$classId]['expend'], 2);
  990. $arr[$classId]['cgNum'] = bcadd($arr[$classId]['cgNum'], $remainNum);
  991. } else {
  992. $arr[$classId]['expend'] = $amount;
  993. $arr[$classId]['income'] = 0;
  994. $arr[$classId]['cgNum'] = $remainNum;
  995. $arr[$classId]['saleNum'] = 0;
  996. }
  997. //单独统计
  998. $belongCost = $cgData->belongCost ?? 0;
  999. if ($belongCost == 1) {
  1000. if (isset($tbCgIncome[$productId])) {
  1001. $tbCgIncome[$productId]['num'] = bcadd($tbCgIncome[$productId]['num'], $remainNum);
  1002. $tbCgIncome[$productId]['price'] = bcadd($tbCgIncome[$productId]['price'], $amount, 2);
  1003. } else {
  1004. $tbCgIncome[$productId] = ['num' => $remainNum, 'name' => $name, 'price' => $amount];
  1005. }
  1006. if (isset($tbCgIncomeStaff[$productId]) && isset($tbCgIncomeStaff[$productId][$cgStaffId])) {
  1007. $tbCgIncomeStaff[$productId][$cgStaffId]['num'] = bcadd($tbCgIncomeStaff[$productId][$cgStaffId]['num'], $remainNum);
  1008. $tbCgIncomeStaff[$productId][$cgStaffId]['price'] = bcadd($tbCgIncomeStaff[$productId][$cgStaffId]['price'], $amount, 2);
  1009. } else {
  1010. $tbCgIncomeStaff[$productId][$cgStaffId] = ['num' => $remainNum, 'name' => $name, 'price' => $amount, 'staffName' => $cgStaffName,];
  1011. }
  1012. }
  1013. }
  1014. if ($tkPrice > 0) {
  1015. $cgRefundOrderList = CgRefundClass::getAllByCondition(['relateOrderSn' => $orderSn], null, '*', null, true);
  1016. if (!empty($cgRefundOrderList)) {
  1017. foreach ($cgRefundOrderList as $cgRefundOrder) {
  1018. if ($cgRefundOrder->refundType == 2) {
  1019. // 隔天仅退款记在通过日,避免与下方汇总重复
  1020. if (intval($cgRefundOrder->sameDay ?? CgPurchaseNextDayClass::SAME_DAY_YES)
  1021. === CgPurchaseNextDayClass::SAME_DAY_NO) {
  1022. continue;
  1023. }
  1024. $onlyRefundCg = bcadd($cgRefundOrder->refundPrice, $onlyRefundCg, 2);
  1025. }
  1026. }
  1027. }
  1028. }
  1029. }
  1030. }
  1031. }
  1032. // 采购隔天售后:入库日已用 nextRefundNum;通过日再扣分类成本;仅退款进「采购仅退款」
  1033. $productClassMapForCg = ProductClass::getAllByCondition(['mainId' => $mainId], null, 'id,classId,name', 'id');
  1034. $applyCgClassDelta = function ($pidMap, $sign) use (&$arr, &$classList, $productClassMapForCg, &$tbCgIncome) {
  1035. foreach ($pidMap as $pid => $row) {
  1036. $classId = $productClassMapForCg[$pid]['classId'] ?? 0;
  1037. $deltaNum = bcmul((string)($row['num'] ?? '0'), (string)$sign, 2);
  1038. $deltaAmt = bcmul((string)($row['amount'] ?? '0'), (string)$sign, 2);
  1039. if (bccomp($deltaAmt, '0', 2) == 0 && bccomp($deltaNum, '0', 2) == 0) {
  1040. continue;
  1041. }
  1042. if (!isset($arr[$classId])) {
  1043. $arr[$classId] = [
  1044. 'className' => $classList[$classId]['name'] ?? '已删除',
  1045. 'classId' => $classId,
  1046. 'expend' => '0.00',
  1047. 'income' => 0,
  1048. 'saleNum' => 0,
  1049. 'cgNum' => '0.00',
  1050. ];
  1051. }
  1052. $arr[$classId]['expend'] = bcadd((string)($arr[$classId]['expend'] ?? 0), $deltaAmt, 2);
  1053. $arr[$classId]['cgNum'] = bcadd((string)($arr[$classId]['cgNum'] ?? 0), $deltaNum, 2);
  1054. // 采购单项明细同步扣减(无当日入库也建负行)
  1055. if (!isset($tbCgIncome[$pid])) {
  1056. $tbCgIncome[$pid] = [
  1057. 'num' => '0.00',
  1058. 'name' => $productClassMapForCg[$pid]['name'] ?? '已删除',
  1059. 'price' => '0.00',
  1060. ];
  1061. }
  1062. $tbCgIncome[$pid]['num'] = bcadd((string)($tbCgIncome[$pid]['num'] ?? 0), $deltaNum, 2);
  1063. $tbCgIncome[$pid]['price'] = bcadd((string)($tbCgIncome[$pid]['price'] ?? 0), $deltaAmt, 2);
  1064. }
  1065. };
  1066. $applyCgClassDelta(
  1067. CgPurchaseNextDayClass::sumItemByProduct($mainId, $currentStartTime, $currentEndTime),
  1068. -1
  1069. );
  1070. $onlyRefundCg = bcadd(
  1071. (string)$onlyRefundCg,
  1072. CgPurchaseNextDayClass::sumMoneyOnlyAmountByMainAndTime($mainId, $currentStartTime, $currentEndTime),
  1073. 2
  1074. );
  1075. //批发开单
  1076. $pfIncome = 0;
  1077. $tbSaleIncome = [];
  1078. $tbSaleIncomeStaff = [];
  1079. $where = ['mainId' => $mainId, 'status' => ['in', [OrderClass::ORDER_STATUS_UN_SEND, OrderClass::ORDER_STATUS_SENDING, OrderClass::ORDER_STATUS_COMPLETE]]];
  1080. $where['payTime'] = ['between', [$currentStartTime, $currentEndTime]];
  1081. $ghsOrderList = OrderClass::getAllByCondition($where, null, '*');
  1082. if (!empty($ghsOrderList)) {
  1083. foreach ($ghsOrderList as $ghsKey => $ghsOrder) {
  1084. $book = $ghsOrder['book'] ?? 0;
  1085. $status = $ghsOrder['status'] ?? 0;
  1086. if ($book == 1 && $status == 2) {
  1087. //预订单待发货的不算收入
  1088. continue;
  1089. }
  1090. if ($status == 0 || $status == 5) {
  1091. continue;
  1092. }
  1093. $shopAdminId = $ghsOrder['shopAdminId'] ?? 0;
  1094. $shopAdminName = $ghsOrder['shopAdminName'] ?? '';
  1095. $orderSn = $ghsOrder['orderSn'] ?? '';
  1096. $actPrice = $ghsOrder['actPrice'] ?? 0;
  1097. if ($actPrice <= 0) {
  1098. continue;
  1099. }
  1100. $tkPrice = $ghsOrder['tkPrice'] ?? 0;
  1101. $pfIncome = bcadd($pfIncome, $actPrice, 2);
  1102. $labourCost = $ghsOrder['labourCost'] ?? 0;
  1103. $discountAmount = $ghsOrder['discountAmount'] ?? 0;
  1104. $totalLabourCost = bcadd($labourCost, $totalLabourCost, 2);
  1105. $totalDiscountAmount = bcadd($totalDiscountAmount, $discountAmount, 2);
  1106. $orderItemList = OrderItemClass::getAllByCondition(['orderSn' => $orderSn], null, '*', null, true);
  1107. if (!empty($orderItemList)) {
  1108. foreach ($orderItemList as $orderItem) {
  1109. $refundNum = $orderItem->refundNum ?? 0;
  1110. $name = $orderItem->name ?? '';
  1111. $productId = $orderItem->productId ?? 0;
  1112. $num = $orderItem->xhNum ?? 0;
  1113. $remainNum = NextDayRefundClass::payDayRemainNum(
  1114. $num,
  1115. $refundNum,
  1116. $orderItem->nextRefundNum ?? 0
  1117. );
  1118. $classId = $orderItem->classId ?? 0;
  1119. $className = $classList[$classId]['name'] ?? '已删除';
  1120. $xhUnitPrice = $orderItem->xhUnitPrice ?? 0;
  1121. $amount = bcmul($remainNum, $xhUnitPrice, 2);
  1122. $arr[$classId]['className'] = $className;
  1123. $arr[$classId]['classId'] = $classId;
  1124. if (isset($arr[$classId]['income'])) {
  1125. $arr[$classId]['income'] = bcadd($amount, $arr[$classId]['income'], 2);
  1126. $arr[$classId]['saleNum'] = bcadd($arr[$classId]['saleNum'], $remainNum);
  1127. } else {
  1128. $arr[$classId]['expend'] = 0;
  1129. $arr[$classId]['income'] = $amount;
  1130. $arr[$classId]['saleNum'] = $remainNum;
  1131. $arr[$classId]['cgNum'] = 0;
  1132. }
  1133. //单独统计
  1134. $belongCost = $orderItem->belongCost ?? 0;
  1135. if ($belongCost == 1) {
  1136. if (isset($tbSaleIncome[$productId])) {
  1137. $tbSaleIncome[$productId]['num'] = bcadd($tbSaleIncome[$productId]['num'], $remainNum);
  1138. $tbSaleIncome[$productId]['price'] = bcadd($tbSaleIncome[$productId]['price'], $amount, 2);
  1139. } else {
  1140. $tbSaleIncome[$productId] = ['name' => $name, 'num' => $remainNum, 'price' => $amount];
  1141. }
  1142. if (isset($tbSaleIncomeStaff[$productId]) && isset($tbSaleIncomeStaff[$productId][$shopAdminId])) {
  1143. $tbSaleIncomeStaff[$productId][$shopAdminId]['num'] = bcadd($tbSaleIncomeStaff[$productId][$shopAdminId]['num'], $remainNum);
  1144. $tbSaleIncomeStaff[$productId][$shopAdminId]['price'] = bcadd($tbSaleIncomeStaff[$productId][$shopAdminId]['price'], $amount, 2);
  1145. } else {
  1146. $tbSaleIncomeStaff[$productId][$shopAdminId] = ['name' => $name, 'num' => $remainNum, 'price' => $amount, 'staffName' => $shopAdminName];
  1147. }
  1148. }
  1149. }
  1150. if ($tkPrice > 0) {
  1151. $refundList = RefundOrderClass::getAllByCondition(['relateOrderSn' => $orderSn], null, '*', null, true);
  1152. if (!empty($refundList)) {
  1153. foreach ($refundList as $refund) {
  1154. if ($refund->refundType == 1) {
  1155. //退货退款前面已经考虑,这里不再处理 OK
  1156. } else {
  1157. // 隔天仅退款记在通过日,这里跳过避免与下方 sumMoneyOnly 重复
  1158. if (intval($refund->sameDay ?? RefundOrderClass::SAME_DAY_YES) === RefundOrderClass::SAME_DAY_NO) {
  1159. continue;
  1160. }
  1161. $onlyRefund = bcadd($onlyRefund, $refund->refundPrice, 2);
  1162. }
  1163. }
  1164. }
  1165. }
  1166. }
  1167. }
  1168. }
  1169. // 隔天售后:支付日已用 nextRefundNum;通过日再按售后单扣分类收入;仅退款进「销售仅退款」
  1170. $productClassMap = ProductClass::getAllByCondition(['mainId' => $mainId], null, 'id,classId', 'id');
  1171. $applyClassDelta = function ($pidMap, $sign) use (&$arr, &$classList, $productClassMap) {
  1172. foreach ($pidMap as $pid => $row) {
  1173. $classId = $productClassMap[$pid]['classId'] ?? 0;
  1174. $deltaNum = bcmul((string)($row['num'] ?? '0'), (string)$sign, 2);
  1175. $deltaAmt = bcmul((string)($row['amount'] ?? '0'), (string)$sign, 2);
  1176. if (!isset($arr[$classId])) {
  1177. if (bccomp($deltaAmt, '0', 2) == 0 && bccomp($deltaNum, '0', 2) == 0) {
  1178. continue;
  1179. }
  1180. $arr[$classId] = [
  1181. 'className' => $classList[$classId]['name'] ?? '已删除',
  1182. 'classId' => $classId,
  1183. 'expend' => 0,
  1184. 'income' => '0.00',
  1185. 'saleNum' => '0.00',
  1186. 'cgNum' => 0,
  1187. ];
  1188. }
  1189. $arr[$classId]['income'] = bcadd((string)($arr[$classId]['income'] ?? 0), $deltaAmt, 2);
  1190. $arr[$classId]['saleNum'] = bcadd((string)($arr[$classId]['saleNum'] ?? 0), $deltaNum, 2);
  1191. }
  1192. };
  1193. $applyClassDelta(
  1194. NextDayRefundClass::sumItemByProduct($mainId, $currentStartTime, $currentEndTime),
  1195. -1
  1196. );
  1197. // hd 零售隔天退货:同样按花材归属分类扣减
  1198. $applyClassDelta(
  1199. HdNextDayRefundClass::sumItemByProduct($mainId, $currentStartTime, $currentEndTime),
  1200. -1
  1201. );
  1202. $onlyRefund = bcadd(
  1203. (string)$onlyRefund,
  1204. NextDayRefundClass::sumMoneyOnlyAmountByMainAndTime($mainId, $currentStartTime, $currentEndTime),
  1205. 2
  1206. );
  1207. $onlyRefund = bcadd(
  1208. (string)$onlyRefund,
  1209. HdNextDayRefundClass::sumMoneyOnlyAmountByMainAndTime($mainId, $currentStartTime, $currentEndTime),
  1210. 2
  1211. );
  1212. //零售开单
  1213. $lsIncome = 0;
  1214. $hdWhere = ['mainId' => $mainId, 'status' => ['in', [HdOrderClass::ORDER_STATUS_UN_SEND, HdOrderClass::ORDER_STATUS_SENDING, HdOrderClass::ORDER_STATUS_COMPLETE]]];
  1215. $hdWhere['payTime'] = ['between', [$currentStartTime, $currentEndTime]];
  1216. $hdOrderList = HdOrderClass::getAllByCondition($hdWhere, null, '*');
  1217. foreach ($hdOrderList as $hdKey => $hdOrder) {
  1218. $actPrice = $hdOrder['mainPay'] ?? 0;
  1219. $cash = $hdOrder['cash'] ?? 0;
  1220. if ($actPrice <= 0 && $cash <= 0) {
  1221. continue;
  1222. }
  1223. $lsIncome = bcadd($actPrice, $lsIncome, 2);
  1224. $lsIncome = bcadd($lsIncome, $cash, 2);
  1225. $orderSn = $hdOrder['orderSn'] ?? '';
  1226. $tkPrice = $hdOrder['tkPrice'] ?? 0;
  1227. $orderItemList = \bizHd\order\classes\OrderItemClass::getAllByCondition(['orderSn' => $orderSn], null, '*', null, true);
  1228. if (!empty($orderItemList)) {
  1229. foreach ($orderItemList as $orderItem) {
  1230. $classId = $orderItem->classId ?? 0;
  1231. $unitPrice = $orderItem->unitPrice ?? 0;
  1232. $refundNum = $orderItem->refundNum ?? 0;
  1233. $num = $orderItem->num ?? 0;
  1234. $productId = $orderItem->itemId ?? 0;
  1235. $name = $orderItem->name ?? '';
  1236. $remainNum = HdNextDayRefundClass::payDayRemainNum(
  1237. $num,
  1238. $refundNum,
  1239. $orderItem->nextRefundNum ?? 0
  1240. );
  1241. $amount = bcmul($remainNum, $unitPrice, 2);
  1242. $className = $classList[$classId]['name'] ?? '已删除';
  1243. $arr[$classId]['className'] = $className;
  1244. $arr[$classId]['classId'] = $classId;
  1245. if (isset($arr[$classId]['income'])) {
  1246. $arr[$classId]['income'] = bcadd($amount, $arr[$classId]['income'], 2);
  1247. $arr[$classId]['saleNum'] = bcadd($arr[$classId]['saleNum'], $remainNum);
  1248. } else {
  1249. $arr[$classId]['expend'] = 0;
  1250. $arr[$classId]['income'] = $amount;
  1251. $arr[$classId]['saleNum'] = $remainNum;
  1252. $arr[$classId]['cgNum'] = 0;
  1253. }
  1254. $belongCost = $orderItem->belongCost ?? 0;
  1255. if ($belongCost == 1) {
  1256. if (isset($tbSaleIncome[$productId])) {
  1257. $tbSaleIncome[$productId]['num'] = bcadd($tbSaleIncome[$productId]['num'], $remainNum);
  1258. $tbSaleIncome[$productId]['price'] = bcadd($tbSaleIncome[$productId]['price'], $amount, 2);
  1259. } else {
  1260. $tbSaleIncome[$productId] = ['name' => $name, 'num' => $remainNum, 'price' => $amount];
  1261. }
  1262. }
  1263. }
  1264. if ($tkPrice > 0) {
  1265. $refundList = HdRefundClass::getAllByCondition(['orderSn' => $orderSn, 'status' => HdRefundClass::STATUS_COMPLETE], null, '*', null, true);
  1266. if (!empty($refundList)) {
  1267. foreach ($refundList as $refund) {
  1268. if ($refund->refundType == 2) {
  1269. // 隔天仅退款记在通过日
  1270. if (intval($refund->sameDay ?? HdRefundClass::SAME_DAY_YES) === HdRefundClass::SAME_DAY_NO) {
  1271. continue;
  1272. }
  1273. $onlyRefund = bcadd($onlyRefund, $refund->refundPrice, 2);
  1274. }
  1275. }
  1276. }
  1277. }
  1278. }
  1279. }
  1280. $totalIncome = 0;
  1281. $totalExpend = 0;
  1282. if (!empty($arr)) {
  1283. foreach ($arr as $key => $val) {
  1284. $income = $val['income'] ?? 0;
  1285. $income = floatval($income);
  1286. $totalIncome = bcadd($totalIncome, $income, 2);
  1287. $expend = $val['expend'] ?? 0;
  1288. $expend = floatval($expend);
  1289. $totalExpend = bcadd($totalExpend, $expend, 2);
  1290. $profit = bcsub($income, $expend, 2);
  1291. $arr[$key]['profit'] = floatval($profit);
  1292. $arr[$key]['income'] = floatval($income);
  1293. $arr[$key]['expend'] = floatval($expend);
  1294. }
  1295. }
  1296. //总成本算上物流打包等费用
  1297. $totalExpend = bcadd($totalExpend, $packFreight, 2);
  1298. //总收入算上采购仅退款
  1299. $totalIncome = bcadd($totalIncome, $onlyRefundCg, 2);
  1300. //总收入加上人工费
  1301. $totalIncome = bcadd($totalIncome, $totalLabourCost, 2);
  1302. //总收入减掉优惠金额
  1303. $totalIncome = bcsub($totalIncome, $totalDiscountAmount, 2);
  1304. //总成本算上销售仅退款
  1305. $totalExpend = bcadd($totalExpend, $onlyRefund, 2);
  1306. $totalProfit = bcsub($totalIncome, $totalExpend, 2);
  1307. $totalIncome = floatval($totalIncome);
  1308. $totalExpend = floatval($totalExpend);
  1309. $totalProfit = floatval($totalProfit);
  1310. $arr = array_values($arr);
  1311. $arr = arrayUtil::arraySort($arr, 'profit');
  1312. if ($export == 1) {
  1313. $statData = [
  1314. ['classId' => 0, 'className' => '物流打包费', 'income' => 0, 'expend' => $packFreight, 'profit' => -$packFreight, 'saleNum' => 0, 'cgNum' => 0],
  1315. ['classId' => 0, 'className' => '采购仅退款', 'income' => $onlyRefundCg, 'expend' => 0, 'profit' => $onlyRefundCg, 'saleNum' => 0, 'cgNum' => 0],
  1316. ['classId' => 0, 'className' => '销售仅退款', 'income' => 0, 'expend' => $onlyRefund, 'profit' => -$onlyRefund, 'saleNum' => 0, 'cgNum' => 0],
  1317. ['classId' => 0, 'className' => '附加人工费', 'income' => $totalLabourCost, 'expend' => 0, 'profit' => $totalLabourCost, 'saleNum' => 0, 'cgNum' => 0],
  1318. ['classId' => 0, 'className' => '优惠金额', 'income' => -$totalDiscountAmount, 'expend' => 0, 'profit' => -$totalDiscountAmount, 'saleNum' => 0, 'cgNum' => 0],
  1319. ['classId' => 0, 'className' => '【汇总金额】', 'income' => $totalIncome, 'expend' => $totalExpend, 'profit' => $totalProfit, 'saleNum' => 0, 'cgNum' => 0],
  1320. ];
  1321. $list = array_merge($arr, $statData);
  1322. self::exportIncomeClass($list, $mainId);
  1323. }
  1324. util::success(['list' => $arr, 'packFreight' => $packFreight, 'onlyRefundCg' => $onlyRefundCg, 'onlyRefund' => $onlyRefund,
  1325. 'totalIncome' => $totalIncome,
  1326. 'totalExpend' => $totalExpend,
  1327. 'totalProfit' => $totalProfit,
  1328. 'tbSaleIncome' => $tbSaleIncome,
  1329. 'tbCgIncome' => $tbCgIncome,
  1330. 'tbCgIncomeStaff' => $tbCgIncomeStaff,
  1331. 'tbSaleIncomeStaff' => $tbSaleIncomeStaff,
  1332. 'totalLabourCost' => $totalLabourCost,
  1333. 'totalDiscountAmount' => $totalDiscountAmount,
  1334. ]);
  1335. }
  1336. public static function exportIncomeOutItem($list, $mainId)
  1337. {
  1338. $phpExcelFile = Yii::getAlias("@vendor/phpoffice/phpexcel/");
  1339. require_once($phpExcelFile . 'Classes/PHPExcel.php');
  1340. $objPHPExcel = new \PHPExcel();
  1341. $objPHPExcel->getProperties()->setCreator("Maarten Balliauw")
  1342. ->setLastModifiedBy("Maarten Balliauw")
  1343. ->setTitle("Office 2007 XLSX Document")
  1344. ->setSubject("Office 2007 XLSX Document")
  1345. ->setDescription("Test document for Office 2007 XLSX, generated using PHP classes.")
  1346. ->setKeywords("office 2007 openxml php")
  1347. ->setCategory("file");
  1348. $ghsTitle = '供货商';
  1349. $objPHPExcel->getActiveSheet()->getHeaderFooter()->setOddHeader(date('n月j日') . " " . $ghsTitle);
  1350. $objPHPExcel->getActiveSheet()->getPageMargins()->setTop(0.7);
  1351. $objPHPExcel->getActiveSheet()->getPageMargins()->setBottom(0.1);
  1352. $objPHPExcel->getActiveSheet()->getPageMargins()->setLeft(0.5);
  1353. $objPHPExcel->getActiveSheet()->getPageMargins()->setRight(0);
  1354. $objPHPExcel->getActiveSheet()->getPageMargins()->setHeader(0.1);
  1355. $objPHPExcel->getActiveSheet()->getPageMargins()->setFooter(0);
  1356. $objPHPExcel->getActiveSheet()->setCellValue('A1', '名称');
  1357. $objPHPExcel->getActiveSheet()->setCellValue('B1', '销售收入');
  1358. $objPHPExcel->getActiveSheet()->setCellValue('C1', '销售数量');
  1359. $objPHPExcel->getActiveSheet()->setCellValue('D1', '采购成本');
  1360. $objPHPExcel->getActiveSheet()->setCellValue('E1', '采购数量');
  1361. $objPHPExcel->getActiveSheet()->setCellValue('F1', '利润');
  1362. //设置宽度
  1363. $objPHPExcel->getActiveSheet()->getColumnDimension('A')->setWidth(30);
  1364. $objPHPExcel->getActiveSheet()->getColumnDimension('B')->setWidth(20);
  1365. $objPHPExcel->getActiveSheet()->getColumnDimension('C')->setWidth(20);
  1366. $objPHPExcel->getActiveSheet()->getColumnDimension('C')->setWidth(20);
  1367. //加粗
  1368. $objPHPExcel->getActiveSheet()->getStyle('A1')->getFont()->setSize(9)->setBold(true);
  1369. $objPHPExcel->getActiveSheet()->getStyle('B1')->getFont()->setSize(9)->setBold(true);
  1370. $objPHPExcel->getActiveSheet()->getStyle('C1')->getFont()->setSize(9)->setBold(true);
  1371. $objPHPExcel->getActiveSheet()->getStyle('D1')->getFont()->setSize(9)->setBold(true);
  1372. $objPHPExcel->getActiveSheet()->getStyle('A1')->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_LEFT);
  1373. $objPHPExcel->getActiveSheet()->getStyle('B1')->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_LEFT);
  1374. $objPHPExcel->getActiveSheet()->getStyle('C1')->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_LEFT);
  1375. $objPHPExcel->getActiveSheet()->getStyle('D1')->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_LEFT);
  1376. $baseRow = 2;
  1377. foreach ($list as $key => $custom) {
  1378. $i = $baseRow + $key;
  1379. $objPHPExcel->getActiveSheet()->setCellValue('A' . $i, $custom['name']);
  1380. $objPHPExcel->getActiveSheet()->setCellValue('B' . $i, $custom['income']);
  1381. $objPHPExcel->getActiveSheet()->setCellValue('C' . $i, $custom['inNum']);
  1382. $objPHPExcel->getActiveSheet()->setCellValue('D' . $i, $custom['expend']);
  1383. $objPHPExcel->getActiveSheet()->setCellValue('E' . $i, $custom['outNum']);
  1384. $objPHPExcel->getActiveSheet()->setCellValue('F' . $i, $custom['profit']);
  1385. //居左
  1386. $objPHPExcel->getActiveSheet()->getStyle('A' . $i)->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_LEFT);
  1387. $objPHPExcel->getActiveSheet()->getStyle('B' . $i)->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_LEFT);
  1388. $objPHPExcel->getActiveSheet()->getStyle('C' . $i)->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_LEFT);
  1389. $objPHPExcel->getActiveSheet()->getStyle('D' . $i)->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_LEFT);
  1390. $objPHPExcel->getActiveSheet()->getStyle('E' . $i)->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_LEFT);
  1391. $objPHPExcel->getActiveSheet()->getStyle('F' . $i)->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_LEFT);
  1392. }
  1393. $fileName = '收支花材-' . date("m-d");
  1394. $objPHPExcel->getActiveSheet()->setTitle($fileName);
  1395. $objPHPExcel->setActiveSheetIndex(0);
  1396. $dir = './priceTable/' . $mainId;
  1397. if (file_exists($dir) == false) {
  1398. mkdir($dir, 0777, true);
  1399. }
  1400. $date = $fileName;
  1401. $file = $date . '.xls';
  1402. $objWriter = \PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel5');
  1403. if (file_exists($dir . '/' . $file)) {
  1404. unlink($dir . '/' . $file);
  1405. }
  1406. $objWriter->save($dir . '/' . $file);
  1407. $fileUrl = Yii::$app->params['ghsHost'] . '/priceTable/' . $mainId . '/' . $file;
  1408. util::success(['file' => $fileUrl, 'shortFile' => $file]);
  1409. }
  1410. public static function exportIncomeClass($list, $mainId)
  1411. {
  1412. $phpExcelFile = Yii::getAlias("@vendor/phpoffice/phpexcel/");
  1413. require_once($phpExcelFile . 'Classes/PHPExcel.php');
  1414. $objPHPExcel = new \PHPExcel();
  1415. $objPHPExcel->getProperties()->setCreator("Maarten Balliauw")
  1416. ->setLastModifiedBy("Maarten Balliauw")
  1417. ->setTitle("Office 2007 XLSX Document")
  1418. ->setSubject("Office 2007 XLSX Document")
  1419. ->setDescription("Test document for Office 2007 XLSX, generated using PHP classes.")
  1420. ->setKeywords("office 2007 openxml php")
  1421. ->setCategory("file");
  1422. $ghsTitle = '供货商';
  1423. $objPHPExcel->getActiveSheet()->getHeaderFooter()->setOddHeader(date('n月j日') . " " . $ghsTitle);
  1424. $objPHPExcel->getActiveSheet()->getPageMargins()->setTop(0.7);
  1425. $objPHPExcel->getActiveSheet()->getPageMargins()->setBottom(0.1);
  1426. $objPHPExcel->getActiveSheet()->getPageMargins()->setLeft(0.5);
  1427. $objPHPExcel->getActiveSheet()->getPageMargins()->setRight(0);
  1428. $objPHPExcel->getActiveSheet()->getPageMargins()->setHeader(0.1);
  1429. $objPHPExcel->getActiveSheet()->getPageMargins()->setFooter(0);
  1430. $objPHPExcel->getActiveSheet()->setCellValue('A1', '名称');
  1431. $objPHPExcel->getActiveSheet()->setCellValue('B1', '销售数量');
  1432. $objPHPExcel->getActiveSheet()->setCellValue('C1', '销售收入');
  1433. $objPHPExcel->getActiveSheet()->setCellValue('D1', '采购数量');
  1434. $objPHPExcel->getActiveSheet()->setCellValue('E1', '采购成本');
  1435. $objPHPExcel->getActiveSheet()->setCellValue('F1', '利润');
  1436. //设置宽度
  1437. $objPHPExcel->getActiveSheet()->getColumnDimension('A')->setWidth(20);
  1438. //加粗
  1439. $objPHPExcel->getActiveSheet()->getStyle('A1')->getFont()->setSize(9)->setBold(true);
  1440. $objPHPExcel->getActiveSheet()->getStyle('B1')->getFont()->setSize(9)->setBold(true);
  1441. $objPHPExcel->getActiveSheet()->getStyle('C1')->getFont()->setSize(9)->setBold(true);
  1442. $objPHPExcel->getActiveSheet()->getStyle('D1')->getFont()->setSize(9)->setBold(true);
  1443. $objPHPExcel->getActiveSheet()->getStyle('E1')->getFont()->setSize(9)->setBold(true);
  1444. $objPHPExcel->getActiveSheet()->getStyle('F1')->getFont()->setSize(9)->setBold(true);
  1445. $objPHPExcel->getActiveSheet()->getStyle('A1')->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_CENTER);
  1446. $objPHPExcel->getActiveSheet()->getStyle('B1')->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_CENTER);
  1447. $objPHPExcel->getActiveSheet()->getStyle('C1')->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_CENTER);
  1448. $objPHPExcel->getActiveSheet()->getStyle('D1')->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_CENTER);
  1449. $objPHPExcel->getActiveSheet()->getStyle('E1')->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_CENTER);
  1450. $objPHPExcel->getActiveSheet()->getStyle('F1')->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_CENTER);
  1451. $objPHPExcel->getActiveSheet()->getStyle('A1')->getAlignment()->setVertical(\PHPExcel_Style_Alignment::VERTICAL_CENTER);
  1452. $objPHPExcel->getActiveSheet()->getStyle('B1')->getAlignment()->setVertical(\PHPExcel_Style_Alignment::VERTICAL_CENTER);
  1453. $objPHPExcel->getActiveSheet()->getStyle('C1')->getAlignment()->setVertical(\PHPExcel_Style_Alignment::VERTICAL_CENTER);
  1454. $objPHPExcel->getActiveSheet()->getStyle('D1')->getAlignment()->setVertical(\PHPExcel_Style_Alignment::VERTICAL_CENTER);
  1455. $objPHPExcel->getActiveSheet()->getStyle('E1')->getAlignment()->setVertical(\PHPExcel_Style_Alignment::VERTICAL_CENTER);
  1456. $objPHPExcel->getActiveSheet()->getStyle('F1')->getAlignment()->setVertical(\PHPExcel_Style_Alignment::VERTICAL_CENTER);
  1457. $baseRow = 2;
  1458. foreach ($list as $key => $custom) {
  1459. $i = $baseRow + $key;
  1460. $objPHPExcel->getActiveSheet()->setCellValue('A' . $i, $custom['className']);
  1461. $objPHPExcel->getActiveSheet()->setCellValue('B' . $i, $custom['saleNum']);
  1462. $objPHPExcel->getActiveSheet()->setCellValue('C' . $i, $custom['income']);
  1463. $objPHPExcel->getActiveSheet()->setCellValue('D' . $i, $custom['cgNum']);
  1464. $objPHPExcel->getActiveSheet()->setCellValue('E' . $i, $custom['expend']);
  1465. $objPHPExcel->getActiveSheet()->setCellValue('F' . $i, $custom['profit']);
  1466. //居左
  1467. $objPHPExcel->getActiveSheet()->getStyle('A' . $i)->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_CENTER);
  1468. $objPHPExcel->getActiveSheet()->getStyle('B' . $i)->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_CENTER);
  1469. $objPHPExcel->getActiveSheet()->getStyle('C' . $i)->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_CENTER);
  1470. $objPHPExcel->getActiveSheet()->getStyle('D' . $i)->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_CENTER);
  1471. $objPHPExcel->getActiveSheet()->getStyle('E' . $i)->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_CENTER);
  1472. $objPHPExcel->getActiveSheet()->getStyle('F' . $i)->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_CENTER);
  1473. }
  1474. $fileName = '收支分类-' . time();
  1475. $objPHPExcel->getActiveSheet()->setTitle($fileName);
  1476. $objPHPExcel->setActiveSheetIndex(0);
  1477. $dir = './priceTable/' . $mainId;
  1478. if (file_exists($dir) == false) {
  1479. mkdir($dir, 0777, true);
  1480. }
  1481. $date = $fileName;
  1482. $file = $date . '.xls';
  1483. $objWriter = \PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel5');
  1484. if (file_exists($dir . '/' . $file)) {
  1485. unlink($dir . '/' . $file);
  1486. }
  1487. $objWriter->save($dir . '/' . $file);
  1488. $fileUrl = Yii::$app->params['ghsHost'] . '/priceTable/' . $mainId . '/' . $file;
  1489. util::success(['file' => $fileUrl, 'shortFile' => $file]);
  1490. }
  1491. public static function exportItemSale($list, $mainId)
  1492. {
  1493. $phpExcelFile = Yii::getAlias("@vendor/phpoffice/phpexcel/");
  1494. require_once($phpExcelFile . 'Classes/PHPExcel.php');
  1495. $objPHPExcel = new \PHPExcel();
  1496. $objPHPExcel->getProperties()->setCreator("Maarten Balliauw")
  1497. ->setLastModifiedBy("Maarten Balliauw")
  1498. ->setTitle("Office 2007 XLSX Document")
  1499. ->setSubject("Office 2007 XLSX Document")
  1500. ->setDescription("Test document for Office 2007 XLSX, generated using PHP classes.")
  1501. ->setKeywords("office 2007 openxml php")
  1502. ->setCategory("file");
  1503. $ghsTitle = '供货商';
  1504. $objPHPExcel->getActiveSheet()->getHeaderFooter()->setOddHeader(date('n月j日') . " " . $ghsTitle);
  1505. $objPHPExcel->getActiveSheet()->getPageMargins()->setTop(0.7);
  1506. $objPHPExcel->getActiveSheet()->getPageMargins()->setBottom(0.1);
  1507. $objPHPExcel->getActiveSheet()->getPageMargins()->setLeft(0.5);
  1508. $objPHPExcel->getActiveSheet()->getPageMargins()->setRight(0);
  1509. $objPHPExcel->getActiveSheet()->getPageMargins()->setHeader(0.1);
  1510. $objPHPExcel->getActiveSheet()->getPageMargins()->setFooter(0);
  1511. $objPHPExcel->getActiveSheet()->setCellValue('A1', '名称');
  1512. $objPHPExcel->getActiveSheet()->setCellValue('B1', '总数量');
  1513. $objPHPExcel->getActiveSheet()->setCellValue('C1', '总金额');
  1514. $objPHPExcel->getActiveSheet()->setCellValue('D1', '毛利');
  1515. $objPHPExcel->getActiveSheet()->setCellValue('E1', '毛利率');
  1516. //设置宽度
  1517. $objPHPExcel->getActiveSheet()->getColumnDimension('A')->setWidth(30);
  1518. $objPHPExcel->getActiveSheet()->getColumnDimension('B')->setWidth(20);
  1519. $objPHPExcel->getActiveSheet()->getColumnDimension('C')->setWidth(20);
  1520. $objPHPExcel->getActiveSheet()->getColumnDimension('D')->setWidth(20);
  1521. $objPHPExcel->getActiveSheet()->getColumnDimension('E')->setWidth(20);
  1522. //加粗
  1523. $objPHPExcel->getActiveSheet()->getStyle('A1')->getFont()->setSize(9)->setBold(true);
  1524. $objPHPExcel->getActiveSheet()->getStyle('B1')->getFont()->setSize(9)->setBold(true);
  1525. $objPHPExcel->getActiveSheet()->getStyle('C1')->getFont()->setSize(9)->setBold(true);
  1526. $objPHPExcel->getActiveSheet()->getStyle('D1')->getFont()->setSize(9)->setBold(true);
  1527. $objPHPExcel->getActiveSheet()->getStyle('E1')->getFont()->setSize(9)->setBold(true);
  1528. $objPHPExcel->getActiveSheet()->getStyle('A1')->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_LEFT);
  1529. $objPHPExcel->getActiveSheet()->getStyle('B1')->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_LEFT);
  1530. $objPHPExcel->getActiveSheet()->getStyle('C1')->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_LEFT);
  1531. $objPHPExcel->getActiveSheet()->getStyle('D1')->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_LEFT);
  1532. $objPHPExcel->getActiveSheet()->getStyle('E1')->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_LEFT);
  1533. $baseRow = 2;
  1534. foreach ($list as $key => $custom) {
  1535. $i = $baseRow + $key;
  1536. $objPHPExcel->getActiveSheet()->setCellValue('A' . $i, $custom['name']);
  1537. $objPHPExcel->getActiveSheet()->setCellValue('B' . $i, $custom['num']);
  1538. $objPHPExcel->getActiveSheet()->setCellValue('C' . $i, $custom['amount']);
  1539. $objPHPExcel->getActiveSheet()->setCellValue('D' . $i, $custom['gross']);
  1540. $objPHPExcel->getActiveSheet()->setCellValue('E' . $i, $custom['mll'] . '%');
  1541. //居左
  1542. $objPHPExcel->getActiveSheet()->getStyle('A' . $i)->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_LEFT);
  1543. $objPHPExcel->getActiveSheet()->getStyle('B' . $i)->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_LEFT);
  1544. $objPHPExcel->getActiveSheet()->getStyle('C' . $i)->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_LEFT);
  1545. $objPHPExcel->getActiveSheet()->getStyle('D' . $i)->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_LEFT);
  1546. $objPHPExcel->getActiveSheet()->getStyle('E' . $i)->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_LEFT);
  1547. }
  1548. $fileName = '花材销量-' . date("m-d");
  1549. $objPHPExcel->getActiveSheet()->setTitle($fileName);
  1550. $objPHPExcel->setActiveSheetIndex(0);
  1551. $dir = './priceTable/' . $mainId;
  1552. if (file_exists($dir) == false) {
  1553. mkdir($dir, 0777, true);
  1554. }
  1555. $date = $fileName;
  1556. $file = $date . '.xls';
  1557. $objWriter = \PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel5');
  1558. if (file_exists($dir . '/' . $file)) {
  1559. unlink($dir . '/' . $file);
  1560. }
  1561. $objWriter->save($dir . '/' . $file);
  1562. $fileUrl = Yii::$app->params['ghsHost'] . '/priceTable/' . $mainId . '/' . $file;
  1563. util::success(['file' => $fileUrl, 'shortFile' => $file]);
  1564. }
  1565. public static function send($mainId)
  1566. {
  1567. $get = Yii::$app->request->get();
  1568. $where = [];
  1569. $where['mainId'] = $mainId;
  1570. $searchTime = isset($get['searchTime']) && !empty($get['searchTime']) ? $get['searchTime'] : 'today';
  1571. $startTime = $get['startTime'] ?? '';
  1572. $endTime = $get['endTime'] ?? '';
  1573. $period = dateUtil::formatTime($searchTime, $startTime, $endTime, true);
  1574. $start = $period['startTime'];
  1575. $end = $period['endTime'];
  1576. $where['time'] = ['between', [$start, $end]];
  1577. $currentStartDate = date('Y-m-d', strtotime($start));
  1578. $currentEndDate = date('Y-m-d', strtotime($end));
  1579. $currentStartTime = $currentStartDate . ' 00:00:00';
  1580. $currentEndTime = $currentEndDate . ' 23:59:59';
  1581. $s = strtotime($currentStartTime);
  1582. $e = strtotime($currentEndTime);
  1583. $p = bcsub($e, $s);
  1584. $xx = bcmul(86400, 90);
  1585. if ($p > $xx) {
  1586. util::fail('查询时间不能超过三个月,编号699');
  1587. }
  1588. $where = ['mainId' => $mainId, 'status' => ['in', [OrderClass::ORDER_STATUS_UN_SEND, OrderClass::ORDER_STATUS_SENDING, OrderClass::ORDER_STATUS_COMPLETE]]];
  1589. $where['payTime'] = ['between', [$currentStartTime, $currentEndTime]];
  1590. $ghsOrderList = OrderClass::getAllByCondition($where, null, '*');
  1591. $arr = [];
  1592. if (!empty($ghsOrderList)) {
  1593. foreach ($ghsOrderList as $ghsOrder) {
  1594. $book = $ghsOrder['book'] ?? 0;
  1595. $status = $ghsOrder['status'] ?? 0;
  1596. if ($book == 1 && $status == 2) {
  1597. //预订单待发货的不算收入
  1598. continue;
  1599. }
  1600. if ($status == 0 || $status == 5) {
  1601. continue;
  1602. }
  1603. $orderSn = $ghsOrder['orderSn'] ?? '';
  1604. $actPrice = $ghsOrder['actPrice'] ?? 0;
  1605. if ($actPrice <= 0) {
  1606. continue;
  1607. }
  1608. $sendStaffId = $ghsOrder['sendStaffId'] ?? 0;
  1609. $sendStaffName = isset($ghsOrder['sendStaffName']) && !empty($ghsOrder['sendStaffName']) ? $ghsOrder['sendStaffName'] : '未记名';
  1610. if (isset($arr[$sendStaffId])) {
  1611. $arr[$sendStaffId]['orderNum'] = bcadd($arr[$sendStaffId]['orderNum'], 1);
  1612. $arr[$sendStaffId]['amount'] = bcadd($arr[$sendStaffId]['amount'], $actPrice, 2);
  1613. } else {
  1614. $arr[$sendStaffId] = ['staffName' => $sendStaffName, 'orderNum' => 1, 'itemNum' => 0, 'amount' => $actPrice, 'staffId' => $sendStaffId];
  1615. }
  1616. $orderItemList = OrderItemClass::getAllByCondition(['orderSn' => $orderSn], null, '*', null, true);
  1617. if (!empty($orderItemList)) {
  1618. foreach ($orderItemList as $orderItem) {
  1619. $remainNum = NextDayRefundClass::payDayRemainNum(
  1620. $orderItem->xhNum ?? 0,
  1621. $orderItem->refundNum ?? 0,
  1622. $orderItem->nextRefundNum ?? 0
  1623. );
  1624. $arr[$sendStaffId]['itemNum'] = bcadd($arr[$sendStaffId]['itemNum'], $remainNum);
  1625. }
  1626. }
  1627. }
  1628. }
  1629. // 隔天售后:数量已用 nextRefundNum;通过日再扣(无当日配送也建负行)
  1630. self::applyNextDayAdjustToSendStaffRows(
  1631. $arr,
  1632. $mainId,
  1633. $currentStartTime,
  1634. $currentEndTime
  1635. );
  1636. if (!empty($arr)) {
  1637. $arr = array_values($arr);
  1638. }
  1639. return $arr;
  1640. }
  1641. /**
  1642. * 片区业绩:按片区汇总笔数/扎数/金额/成本/利润,供 ghsApp distSale、/stat-kh-cg/dist 使用。
  1643. * totalCount 为总扎数(含隔天售后扣减),与各行 count 口径一致。
  1644. */
  1645. public static function customDistStat($mainId)
  1646. {
  1647. $get = Yii::$app->request->get();
  1648. $where = [];
  1649. $where['mainId'] = $mainId;
  1650. $searchTime = !empty($get['searchTime']) ? $get['searchTime'] : 'today';
  1651. $startTime = $get['startTime'] ?? '';
  1652. $endTime = $get['endTime'] ?? '';
  1653. $period = dateUtil::formatTime($searchTime, $startTime, $endTime, true);
  1654. $start = $period['startTime'];
  1655. $end = $period['endTime'];
  1656. $where['time'] = ['between', [$start, $end]];
  1657. $currentStartDate = date('Y-m-d', strtotime($start));
  1658. $currentEndDate = date('Y-m-d', strtotime($end));
  1659. $currentStartTime = $currentStartDate . ' 00:00:00';
  1660. $currentEndTime = $currentEndDate . ' 23:59:59';
  1661. $where = ['mainId' => $mainId, 'status' => ['in', [OrderClass::ORDER_STATUS_UN_SEND, OrderClass::ORDER_STATUS_SENDING, OrderClass::ORDER_STATUS_COMPLETE]]];
  1662. $where['payTime'] = ['between', [$currentStartTime, $currentEndTime]];
  1663. $customList = CustomClass::getAllByCondition(['ownMainId' => $mainId], null, 'id,name,distId', 'id');
  1664. $distMap = DistClass::getAllByCondition(['mainId' => $mainId], null, '*', 'id');
  1665. $arr = [];
  1666. $ghsOrderList = OrderClass::getAllByCondition($where, null, '*');
  1667. $totalAmount = 0;
  1668. $totalNum = 0;
  1669. $totalCount = 0;
  1670. $totalProfit = 0;
  1671. $totalCost = 0;
  1672. if (!empty($ghsOrderList)) {
  1673. foreach ($ghsOrderList as $ghsOrder) {
  1674. $actPrice = $ghsOrder['actPrice'] ?? 0;
  1675. if ($actPrice <= 0) {
  1676. continue;
  1677. }
  1678. $cost = $ghsOrder['remainCost'] ?? 0;
  1679. $profit = bcsub($actPrice, $cost, 2);
  1680. $orderPrice = $ghsOrder['orderPrice'] ?? 0;
  1681. $tkPrice = $ghsOrder['tkPrice'] ?? 0;
  1682. $customId = $ghsOrder['customId'] ?? 0;
  1683. $status = $ghsOrder['status'] ?? 0;
  1684. $book = $ghsOrder['book'] ?? 0;
  1685. //扎数
  1686. $count = $ghsOrder['itemNum'] ?? 0;
  1687. if ($status == 1 || $status == 5) {
  1688. continue;
  1689. }
  1690. if ($book == 1 && $status == 2) {
  1691. continue;
  1692. }
  1693. $distId = $customList[$customId]['distId'] ?? 0;
  1694. $distName = isset($distMap[$distId]) ? $distMap[$distId]['name'] : '未划片区';
  1695. $totalNum++;
  1696. $totalCount = bcadd($totalCount, $count, 2);
  1697. $totalAmount = bcadd($totalAmount, $actPrice, 2);
  1698. $totalProfit = bcadd($totalProfit, $profit, 2);
  1699. $totalCost = bcadd($totalCost, $cost, 2);
  1700. if (isset($arr[$distId])) {
  1701. $arr[$distId]['num']++;
  1702. $arr[$distId]['amount'] = bcadd($arr[$distId]['amount'], $actPrice, 2);
  1703. $arr[$distId]['orderPrice'] = bcadd($arr[$distId]['orderPrice'], $orderPrice, 2);
  1704. $arr[$distId]['count'] = bcadd($arr[$distId]['count'], $count, 2);
  1705. $arr[$distId]['tkPrice'] = bcadd($arr[$distId]['tkPrice'], $tkPrice, 2);
  1706. $arr[$distId]['cost'] = bcadd($arr[$distId]['cost'], $cost, 2);
  1707. $arr[$distId]['profit'] = bcadd($arr[$distId]['profit'], $profit, 2);
  1708. } else {
  1709. $arr[$distId] = [
  1710. 'distId' => $distId,
  1711. 'distName' => $distName,
  1712. 'num' => 1,
  1713. 'count' => $count,
  1714. 'amount' => $actPrice,
  1715. 'levelName' => '',
  1716. 'orderPrice' => $orderPrice,
  1717. 'tkPrice' => $tkPrice,
  1718. 'cost' => $cost,
  1719. 'profit' => $profit,
  1720. ];
  1721. }
  1722. }
  1723. }
  1724. // 零售客户无批发片区:全部归入虚拟片区「零售片区」
  1725. $hdDistKey = 'hd_retail';
  1726. $productCostMap = ProductClass::getAllByCondition(['mainId' => $mainId], null, 'id,cost,avCost', 'id');
  1727. $hdWhere = [
  1728. 'mainId' => $mainId,
  1729. 'status' => ['in', [HdOrderClass::ORDER_STATUS_UN_SEND, HdOrderClass::ORDER_STATUS_SENDING, HdOrderClass::ORDER_STATUS_COMPLETE]],
  1730. 'payTime' => ['between', [$currentStartTime, $currentEndTime]],
  1731. ];
  1732. $hdOrderList = HdOrderClass::getAllByCondition($hdWhere, null, '*');
  1733. if (!empty($hdOrderList)) {
  1734. foreach ($hdOrderList as $hdOrder) {
  1735. if (intval($hdOrder['forward'] ?? 0) === 1) {
  1736. continue;
  1737. }
  1738. $status = intval($hdOrder['status'] ?? 0);
  1739. if ($status === HdOrderClass::ORDER_STATUS_UN_PAY || $status === HdOrderClass::ORDER_STATUS_CANCEL) {
  1740. continue;
  1741. }
  1742. $mainPay = bcadd((string)($hdOrder['mainPay'] ?? '0'), '0', 2);
  1743. $tkPrice = bcadd((string)($hdOrder['tkPrice'] ?? '0'), '0', 2);
  1744. $cash = bcadd((string)($hdOrder['cash'] ?? '0'), '0', 2);
  1745. $actPrice = bcadd(bcsub($mainPay, $tkPrice, 2), $cash, 2);
  1746. if (bccomp($actPrice, '0', 2) <= 0) {
  1747. continue;
  1748. }
  1749. $orderSn = $hdOrder['orderSn'] ?? '';
  1750. $count = '0.00';
  1751. $cost = '0.00';
  1752. $orderItemList = HdOrderItemClass::getAllByCondition(['orderSn' => $orderSn], null, '*', null, true);
  1753. if (!empty($orderItemList)) {
  1754. foreach ($orderItemList as $orderItem) {
  1755. $remainNum = HdNextDayRefundClass::payDayRemainNum(
  1756. $orderItem->num ?? 0,
  1757. $orderItem->refundNum ?? 0,
  1758. $orderItem->nextRefundNum ?? 0
  1759. );
  1760. $count = bcadd($count, $remainNum, 2);
  1761. $productId = intval($orderItem->itemId ?? 0);
  1762. $unitCost = $orderItem->cost ?? null;
  1763. if ($unitCost === null || $unitCost === '') {
  1764. $unitCost = $productCostMap[$productId]['avCost']
  1765. ?? ($productCostMap[$productId]['cost'] ?? 0);
  1766. }
  1767. $cost = bcadd($cost, bcmul($remainNum, (string)$unitCost, 2), 2);
  1768. }
  1769. }
  1770. $profit = bcsub($actPrice, $cost, 2);
  1771. $totalNum++;
  1772. $totalCount = bcadd($totalCount, $count, 2);
  1773. $totalAmount = bcadd($totalAmount, $actPrice, 2);
  1774. $totalProfit = bcadd($totalProfit, $profit, 2);
  1775. $totalCost = bcadd($totalCost, $cost, 2);
  1776. if (!isset($arr[$hdDistKey])) {
  1777. $arr[$hdDistKey] = [
  1778. 'distId' => 0,
  1779. 'distName' => '零售片区',
  1780. 'num' => 0,
  1781. 'count' => '0.00',
  1782. 'amount' => '0.00',
  1783. 'levelName' => '',
  1784. 'orderPrice' => 0,
  1785. 'tkPrice' => 0,
  1786. 'cost' => 0,
  1787. 'profit' => '0.00',
  1788. ];
  1789. }
  1790. $arr[$hdDistKey]['num']++;
  1791. $arr[$hdDistKey]['amount'] = bcadd($arr[$hdDistKey]['amount'], $actPrice, 2);
  1792. $arr[$hdDistKey]['orderPrice'] = bcadd($arr[$hdDistKey]['orderPrice'], $mainPay, 2);
  1793. $arr[$hdDistKey]['count'] = bcadd($arr[$hdDistKey]['count'], $count, 2);
  1794. $arr[$hdDistKey]['tkPrice'] = bcadd($arr[$hdDistKey]['tkPrice'], $tkPrice, 2);
  1795. $arr[$hdDistKey]['cost'] = bcadd($arr[$hdDistKey]['cost'], $cost, 2);
  1796. $arr[$hdDistKey]['profit'] = bcadd($arr[$hdDistKey]['profit'], $profit, 2);
  1797. }
  1798. }
  1799. // 隔天售后按客户归到片区扣金额/扎数(批发走客户片区;零售统一扣「零售片区」)
  1800. $fwdMap = NextDayRefundClass::sumAmountGroupByCustom($mainId, $currentStartTime, $currentEndTime);
  1801. $qtyMap = NextDayRefundClass::sumItemQtyByCustom($mainId, $currentStartTime, $currentEndTime);
  1802. $cidKeys = array_unique(array_merge(array_keys($fwdMap), array_keys($qtyMap)));
  1803. foreach ($cidKeys as $cid) {
  1804. $amt = $fwdMap[$cid] ?? '0';
  1805. $qty = $qtyMap[$cid] ?? '0';
  1806. if (bccomp((string)$amt, '0', 2) == 0 && bccomp((string)$qty, '0', 2) == 0) {
  1807. continue;
  1808. }
  1809. $distId = $customList[$cid]['distId'] ?? 0;
  1810. if (!isset($arr[$distId])) {
  1811. $arr[$distId] = [
  1812. 'distId' => $distId,
  1813. 'distName' => isset($distMap[$distId]) ? $distMap[$distId]['name'] : '未划片区',
  1814. 'num' => 0,
  1815. 'count' => '0.00',
  1816. 'amount' => '0.00',
  1817. 'levelName' => '',
  1818. 'orderPrice' => 0,
  1819. 'tkPrice' => 0,
  1820. 'cost' => 0,
  1821. 'profit' => '0.00',
  1822. ];
  1823. }
  1824. $arr[$distId]['amount'] = bcsub((string)$arr[$distId]['amount'], (string)$amt, 2);
  1825. $arr[$distId]['profit'] = bcsub((string)$arr[$distId]['profit'], (string)$amt, 2);
  1826. $arr[$distId]['count'] = bcsub((string)($arr[$distId]['count'] ?? 0), (string)$qty, 2);
  1827. $totalAmount = bcsub((string)$totalAmount, (string)$amt, 2);
  1828. $totalProfit = bcsub((string)$totalProfit, (string)$amt, 2);
  1829. $totalCount = bcsub((string)$totalCount, (string)$qty, 2);
  1830. }
  1831. $hdAmtMap = HdNextDayRefundClass::sumAmountGroupByCustom($mainId, $currentStartTime, $currentEndTime);
  1832. $hdQtyMap = HdNextDayRefundClass::sumItemQtyByCustom($mainId, $currentStartTime, $currentEndTime);
  1833. $hdAmtTotal = '0.00';
  1834. $hdQtyTotal = '0.00';
  1835. foreach ($hdAmtMap as $row) {
  1836. $hdAmtTotal = bcadd($hdAmtTotal, (string)($row['amount'] ?? '0'), 2);
  1837. }
  1838. foreach ($hdQtyMap as $row) {
  1839. $hdQtyTotal = bcadd($hdQtyTotal, (string)($row['num'] ?? '0'), 2);
  1840. }
  1841. if (bccomp($hdAmtTotal, '0', 2) > 0 || bccomp($hdQtyTotal, '0', 2) > 0) {
  1842. if (!isset($arr[$hdDistKey])) {
  1843. $arr[$hdDistKey] = [
  1844. 'distId' => 0,
  1845. 'distName' => '零售片区',
  1846. 'num' => 0,
  1847. 'count' => '0.00',
  1848. 'amount' => '0.00',
  1849. 'levelName' => '',
  1850. 'orderPrice' => 0,
  1851. 'tkPrice' => 0,
  1852. 'cost' => 0,
  1853. 'profit' => '0.00',
  1854. ];
  1855. }
  1856. $arr[$hdDistKey]['amount'] = bcsub((string)$arr[$hdDistKey]['amount'], $hdAmtTotal, 2);
  1857. $arr[$hdDistKey]['profit'] = bcsub((string)$arr[$hdDistKey]['profit'], $hdAmtTotal, 2);
  1858. $arr[$hdDistKey]['count'] = bcsub((string)($arr[$hdDistKey]['count'] ?? 0), $hdQtyTotal, 2);
  1859. $totalAmount = bcsub((string)$totalAmount, $hdAmtTotal, 2);
  1860. $totalProfit = bcsub((string)$totalProfit, $hdAmtTotal, 2);
  1861. $totalCount = bcsub((string)$totalCount, $hdQtyTotal, 2);
  1862. }
  1863. if (!empty($arr)) {
  1864. $arr = arrayUtil::arraySort($arr, 'profit');
  1865. }
  1866. $totalAmount = floatval($totalAmount);
  1867. return ['list' => $arr, 'totalAmount' => $totalAmount, 'totalNum' => $totalNum, 'totalCount' => floatval($totalCount), 'totalCost' => $totalCost, 'totalProfit' => $totalProfit];
  1868. }
  1869. /**
  1870. * 花材收支明细:支付日已用 nextRefundNum;此处按通过日扣退货数量/金额,并返回隔天仅退款合计。
  1871. *
  1872. * @param array $arr 产品收支行(引用修改 income/inNum)
  1873. * @param array $tbSaleIncome 归属成本花材销售明细(引用同步加减)
  1874. * @param array $productList 花材字典
  1875. * @param int $mainId
  1876. * @param string $startTime
  1877. * @param string $endTime
  1878. * @return string 通过日隔天「仅退款」合计,需加到 onlyRefund
  1879. */
  1880. private static function applyNextDayAdjustToProductIncomeRows(
  1881. &$arr,
  1882. &$tbSaleIncome,
  1883. $productList,
  1884. $mainId,
  1885. $startTime,
  1886. $endTime
  1887. ) {
  1888. $applyDelta = function ($pidMap, $sign) use (&$arr, &$tbSaleIncome, $productList) {
  1889. foreach ($pidMap as $pid => $row) {
  1890. $deltaNum = bcmul((string)($row['num'] ?? '0'), (string)$sign, 2);
  1891. $deltaAmt = bcmul((string)($row['amount'] ?? '0'), (string)$sign, 2);
  1892. if (bccomp($deltaAmt, '0', 2) == 0 && bccomp($deltaNum, '0', 2) == 0) {
  1893. continue;
  1894. }
  1895. // 通过日扣减:当日无该花材开单也建负行(收入/数量可为负)
  1896. if (!isset($arr[$pid])) {
  1897. $arr[$pid] = [
  1898. 'name' => $productList[$pid]['name'] ?? '已删除',
  1899. 'id' => $pid,
  1900. 'expend' => 0,
  1901. 'outNum' => 0,
  1902. 'inNum' => '0.00',
  1903. 'income' => '0.00',
  1904. ];
  1905. }
  1906. $arr[$pid]['income'] = bcadd((string)($arr[$pid]['income'] ?? 0), $deltaAmt, 2);
  1907. $arr[$pid]['inNum'] = bcadd((string)($arr[$pid]['inNum'] ?? 0), $deltaNum, 2);
  1908. if (isset($tbSaleIncome[$pid])) {
  1909. $tbSaleIncome[$pid]['price'] = bcadd((string)($tbSaleIncome[$pid]['price'] ?? 0), $deltaAmt, 2);
  1910. $tbSaleIncome[$pid]['num'] = bcadd((string)($tbSaleIncome[$pid]['num'] ?? 0), $deltaNum, 2);
  1911. }
  1912. }
  1913. };
  1914. // 通过日:扣退货数量+金额(ghs 批发 + hd 零售共用花材库存)
  1915. $applyDelta(
  1916. NextDayRefundClass::sumItemByProduct($mainId, $startTime, $endTime),
  1917. -1
  1918. );
  1919. $applyDelta(
  1920. HdNextDayRefundClass::sumItemByProduct($mainId, $startTime, $endTime),
  1921. -1
  1922. );
  1923. $onlyRefund = NextDayRefundClass::sumMoneyOnlyAmountByMainAndTime($mainId, $startTime, $endTime);
  1924. return bcadd(
  1925. (string)$onlyRefund,
  1926. HdNextDayRefundClass::sumMoneyOnlyAmountByMainAndTime($mainId, $startTime, $endTime),
  1927. 2
  1928. );
  1929. }
  1930. /**
  1931. * 配送业绩:支付日数量已用 nextRefundNum;通过日再扣数量/金额(仅退款只减金额)。
  1932. * 无当日配送单的配送员也会建负行业绩行。
  1933. *
  1934. * @param array $arr sendStaffId => 业绩行(引用修改 itemNum/amount)
  1935. * @param int $mainId
  1936. * @param string $startTime
  1937. * @param string $endTime
  1938. */
  1939. private static function applyNextDayAdjustToSendStaffRows(
  1940. &$arr,
  1941. $mainId,
  1942. $startTime,
  1943. $endTime
  1944. ) {
  1945. $applyDelta = function ($staffMap, $sign) use (&$arr) {
  1946. foreach ($staffMap as $sid => $row) {
  1947. $deltaNum = bcmul((string)($row['num'] ?? '0'), (string)$sign, 2);
  1948. $deltaAmt = bcmul((string)($row['amount'] ?? '0'), (string)$sign, 2);
  1949. if (bccomp($deltaAmt, '0', 2) == 0 && bccomp($deltaNum, '0', 2) == 0) {
  1950. continue;
  1951. }
  1952. if (!isset($arr[$sid])) {
  1953. $arr[$sid] = [
  1954. 'staffName' => '未记名',
  1955. 'orderNum' => 0,
  1956. 'itemNum' => '0.00',
  1957. 'amount' => '0.00',
  1958. 'staffId' => $sid,
  1959. ];
  1960. }
  1961. $arr[$sid]['itemNum'] = bcadd((string)($arr[$sid]['itemNum'] ?? 0), $deltaNum, 2);
  1962. $arr[$sid]['amount'] = bcadd((string)($arr[$sid]['amount'] ?? 0), $deltaAmt, 2);
  1963. }
  1964. };
  1965. $applyDelta(
  1966. NextDayRefundClass::sumItemBySendStaff($mainId, $startTime, $endTime),
  1967. -1
  1968. );
  1969. // 仅退款只减金额
  1970. $applyDelta(
  1971. NextDayRefundClass::sumMoneyOnlyAmountBySendStaff($mainId, $startTime, $endTime),
  1972. -1
  1973. );
  1974. }
  1975. }