TPlusServerService.php 70 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384
  1. <?php
  2. namespace App\Service;
  3. use App\Jobs\ProcessDataJob;
  4. use App\Model\Depart;
  5. use App\Model\Employee;
  6. use App\Model\EmployeeDepartPermission;
  7. use App\Model\EmployeeIndex;
  8. use App\Model\Freight;
  9. use App\Model\FreightFee;
  10. use App\Model\Product;
  11. use App\Model\RevenueCost;
  12. use App\Model\RevenueCostTotal;
  13. use App\Model\SalaryEmployee;
  14. use Illuminate\Database\Schema\Blueprint;
  15. use Illuminate\Support\Facades\DB;
  16. use Illuminate\Support\Facades\Schema;
  17. class TPlusServerService extends Service
  18. {
  19. /**
  20. * @var TPlusDatabaseServerService
  21. */
  22. protected $databaseService;
  23. /**
  24. * @var string|null
  25. */
  26. protected $error;
  27. /**
  28. * TPlusServerService constructor.
  29. */
  30. public function __construct()
  31. {
  32. $service = new TPlusDatabaseServerService();
  33. $this->databaseService = $service->db;
  34. $this->error = $service->error;
  35. }
  36. /**
  37. * 获取错误信息
  38. *
  39. * @return string|null
  40. */
  41. public function getError()
  42. {
  43. return $this->error;
  44. }
  45. private $table = "tmp_revenue_cost_data";
  46. private $table_2 = "tmp_salary_employee";
  47. private $table_3 = "tmp_freight_fee";
  48. /**
  49. * 同步人员部门
  50. *
  51. * @param array $data
  52. * @param array $user
  53. * @return array
  54. */
  55. public function synPersonDepart($data, $user)
  56. {
  57. try {
  58. $this->databaseService->table('AA_Department')
  59. ->select('id','idparent as parent_id','name as title','code','disabled as is_use')
  60. ->chunkById(100, function ($data) {
  61. DB::transaction(function () use ($data) {
  62. $dataArray = Collect($data)->map(function ($object) {
  63. return (array)$object;
  64. })->toArray();
  65. $d_id = Depart::whereIn('id', array_column($dataArray,'id'))
  66. ->pluck('id')
  67. ->toArray();
  68. $insert = $update = [];
  69. foreach ($dataArray as $value){
  70. $is_use = $value['is_use'] ? 0 : 1;
  71. if(in_array($value['id'], $d_id)){
  72. $update[] = [
  73. 'id' => $value['id'],
  74. 'parent_id' => $value['parent_id'],
  75. 'title' => $value['title'],
  76. 'code' => $value['code'],
  77. 'is_use' => $is_use
  78. ];
  79. }else{
  80. $insert[] = [
  81. 'id' => $value['id'],
  82. 'parent_id' => $value['parent_id'],
  83. 'title' => $value['title'],
  84. 'code' => $value['code'],
  85. 'is_use' => $is_use
  86. ];
  87. }
  88. }
  89. if(! empty($insert)) Depart::insert($insert);
  90. if(! empty($update)) {
  91. foreach ($update as $value){
  92. Depart::where('id', $value['id'])
  93. ->update($value);
  94. }
  95. }
  96. });
  97. });
  98. $this->databaseService->table('AA_Person')
  99. ->select('id','code as number','name as emp_name','mobilePhoneNo as mobile','iddepartment as depart_id','disabled as state')
  100. ->chunkById(100, function ($data) {
  101. DB::transaction(function () use ($data) {
  102. $dataArray = Collect($data)->map(function ($object) {
  103. return (array)$object;
  104. })->toArray();
  105. $employee_id = Employee::whereIn('id', array_column($dataArray,'id'))
  106. ->pluck('id')
  107. ->toArray();
  108. $insert = $update = $depart_update = [];
  109. foreach ($dataArray as $value){
  110. $state = $value['state'] ? Employee::NOT_USE : Employee::USE;
  111. if(in_array($value['id'], $employee_id)){
  112. $update[] = [
  113. 'id' => $value['id'],
  114. 'number' => $value['number'],
  115. 'emp_name' => $value['emp_name'],
  116. 'mobile' => $value['mobile'],
  117. 'state' => $state
  118. ];
  119. }else{
  120. $insert[] = [
  121. 'id' => $value['id'],
  122. 'number' => $value['number'],
  123. 'emp_name' => $value['emp_name'],
  124. 'mobile' => $value['mobile'],
  125. 'state' => $state
  126. ];
  127. }
  128. $depart_update[] = [
  129. 'employee_id' => $value['id'],
  130. 'depart_id' => $value['depart_id']
  131. ];
  132. }
  133. if(! empty($insert)) Employee::insert($insert);
  134. if(! empty($update)) {
  135. foreach ($update as $value){
  136. Employee::where('id', $value['id'])
  137. ->update($value);
  138. }
  139. }
  140. if(! empty($depart_update)){
  141. EmployeeDepartPermission::whereIn('employee_id',array_column($depart_update,'employee_id'))->delete();
  142. EmployeeDepartPermission::insert($depart_update);
  143. }
  144. });
  145. });
  146. } catch (\Throwable $e) {
  147. return [false, $e->getMessage()];
  148. }
  149. return [true, ''];
  150. }
  151. /**
  152. * 收入成本统计同步
  153. *
  154. * @param array $data
  155. * @param array $user
  156. * @return array
  157. */
  158. public function synRevenueCost($data, $user){
  159. if(empty($data['crt_time'][0]) || empty($data['crt_time'][1])) return [false, '同步时间不能为空'];
  160. list($start_time, $end_time) = $this->changeDateToTimeStampAboutRange($data['crt_time'],false);
  161. if ($start_time === null || $end_time === null || $start_time > $end_time) return [false, "同步时间:时间区间无效"];
  162. list($bool, $bool_msg) = $this->isOverThreeMonths($start_time, $end_time);
  163. if(! $bool) return [false, $bool_msg];
  164. $data['start_timeStamp'] = $start_time;
  165. $data['end_timeStamp'] = $end_time;
  166. $start = date('Y-m-d H:i:s.000', $start_time);
  167. $end = date('Y-m-d H:i:s.000', $end_time);
  168. $data['start_time'] = $start;
  169. $data['end_time'] = $end;
  170. $data['operation_time'] = time();
  171. if(empty($data['type'])) return [false, '同步类型不能为空'];
  172. if(in_array($data['type'],[1,2,3,4])){
  173. list($status,$msg) = $this->limitingSendRequest($this->table);
  174. if(! $status) return [false, '收入成本相关信息同步正在后台运行,请稍后'];
  175. // //同步
  176. // list($status, $msg) = $this->synRevenueCostFromTPlus($data, $user);
  177. // if(! $status) {
  178. // return [false, $msg];
  179. // }
  180. //队列
  181. ProcessDataJob::dispatch($data, $user)->onQueue(RevenueCost::job);
  182. }else{
  183. return [false, '同步类型错误'];
  184. }
  185. return [true, '收入成本相关信息同步已进入后台任务'];
  186. }
  187. public function synRevenueCostFromTPlus($data, $user){
  188. //创建临时表 如果不存在
  189. $this->createTmpTable();
  190. //清理临时表 如果内容不为空
  191. $this->clearTmpTable();
  192. $type = $data['type'];
  193. //写入临时数据
  194. if($type == 1){
  195. list($status, $msg) = $this->xhdTPlus($data, $user);
  196. if(! $status) return [false, $msg];
  197. list($status, $msg) = $this->xsfpTPlus($data, $user);
  198. if(! $status) return [false, $msg];
  199. list($status, $msg) = $this->hkdTPlus($data, $user);
  200. if(! $status) return [false, $msg];
  201. }elseif ($type == 2){
  202. list($status, $msg) = $this->xhdTPlus($data, $user);
  203. if(! $status) return [false, $msg];
  204. }elseif ($type == 3){
  205. list($status, $msg) = $this->xsfpTPlus($data, $user);
  206. if(! $status) return [false, $msg];
  207. }elseif ($type == 4){
  208. list($status, $msg) = $this->hkdTPlus($data, $user);
  209. if(! $status) return [false, $msg];
  210. }
  211. //更新数据
  212. list($status,$msg) = $this->updateRevenueCost($data);
  213. if(! $status) return [false, $msg];
  214. // //更新主表数据
  215. // list($status,$msg) = $this->updateRevenueCostTotal($data);
  216. // if(! $status) return [false, $msg];
  217. //都成功后 清理临时表
  218. $this->clearTmpTable();
  219. //释放redis
  220. $this->delTableKey();
  221. return [true, '同步成功'];
  222. }
  223. private function xhdTPlus($data, $user){
  224. try {
  225. $table = $this->table;
  226. $limit = 500;
  227. $lastId = 0;
  228. do {
  229. $rows = $this->databaseService->table('SA_SaleDelivery_b as sd_b')
  230. ->join('SA_SaleDelivery as sd', 'sd_b.idSaleDeliveryDTO', '=', 'sd.ID')
  231. ->leftJoin('AA_Partner as pn', 'sd.idsettlecustomer', '=', 'pn.ID')
  232. ->leftJoin('AA_Person as ps', 'sd.idclerk', '=', 'ps.ID')
  233. ->leftJoin('AA_Person as ps2', 'pn.idsaleman', '=', 'ps2.ID')
  234. ->leftJoin('AA_Inventory as it', 'sd_b.idinventory', '=', 'it.ID')
  235. ->leftJoin('AA_Unit as ui', 'sd_b.idbaseunit', '=', 'ui.ID')
  236. ->where('sd.voucherdate', '>=', $data['start_time'])
  237. ->where('sd.voucherdate', '<=', $data['end_time'])
  238. ->where('sd_b.ID', '>', $lastId) // 用真实字段
  239. ->orderBy('sd_b.ID')
  240. ->limit($limit)
  241. ->selectRaw("
  242. COALESCE(sd.ID, 0) as order_id,
  243. COALESCE(sd.code, '') as order_number,
  244. sd.voucherdate as order_time,
  245. sd.voucherState as order_state,
  246. COALESCE(ps.name, '') as employee_id_1_title,
  247. COALESCE(sd.idclerk, 0) as employee_id_1,
  248. COALESCE(ps2.name, '') as employee_id_2_title,
  249. COALESCE(pn.idsaleman, 0) as employee_id_2,
  250. COALESCE(pn.code, '') as customer_code,
  251. COALESCE(pn.name, '') as customer_title,
  252. COALESCE(pn.priuserdefnvc14, '') as customer_profit_rate,
  253. COALESCE(sd.pubuserdefnvc11, '') as channel_finance,
  254. COALESCE(sd.pubuserdefnvc12, '') as channel_details,
  255. COALESCE(it.code, '') as product_code,
  256. COALESCE(it.name, '') as product_title,
  257. COALESCE(it.specification, '') as product_size,
  258. COALESCE(ui.name, '') as unit,
  259. COALESCE(sd_b.quantity, 0) as quantity,
  260. COALESCE(sd_b.taxPrice, 0) as price_3,
  261. COALESCE(sd_b.taxAmount, 0) as price_3_total,
  262. COALESCE(sd_b.ID, 0) as id_detail,
  263. COALESCE(sd_b.pubuserdefdecm9, 0) as is_activity
  264. ")
  265. ->get();
  266. if ($rows->isEmpty()) break;
  267. $dataArray = Collect($rows)->map(function ($object) {
  268. return (array)$object;
  269. })->toArray();
  270. //存货档案
  271. $product = Product::where('del_time', 0)
  272. ->whereIn('code', array_unique(array_column($dataArray, 'product_code')))
  273. ->select('code', 'write_off_price', 'freight_price', 'business_cost')
  274. ->get()->toArray();
  275. $product_map = array_column($product, null, 'code');
  276. //组织数据
  277. foreach ($dataArray as $key => $value) {
  278. $customer_profit_rate = rtrim($value['customer_profit_rate'],'%');
  279. if(is_numeric($customer_profit_rate)){
  280. $customer_profit_rate = bcdiv($customer_profit_rate,100,3);
  281. }else{
  282. $customer_profit_rate = 0;
  283. }
  284. $dataArray[$key]['customer_profit_rate'] = $customer_profit_rate;
  285. $p_tmp = $product_map[$value['product_code']] ?? [];
  286. $dataArray[$key]['order_type'] = RevenueCost::ORDER_ONE;
  287. $dataArray[$key]['order_time'] = strtotime($value['order_time']);
  288. $write_off_price = $p_tmp['write_off_price'] ?? 0;
  289. $dataArray[$key]['price_1'] = $write_off_price;
  290. $dataArray[$key]['price_1_total'] = bcmul($write_off_price, $value['quantity'], 2);
  291. $freight_price = $p_tmp['freight_price'] ?? 0;
  292. $dataArray[$key]['price_2'] = $freight_price;
  293. $dataArray[$key]['price_2_total'] = bcmul($freight_price, $value['quantity'], 2);
  294. $business_cost = $p_tmp['business_cost'] ?? 0;
  295. $dataArray[$key]['price_4'] = $business_cost;
  296. $price_4_total = bcmul($business_cost, $value['quantity'], 2);
  297. $dataArray[$key]['price_4_total'] = bcmul($business_cost, $value['quantity'], 2);
  298. $profit = bcsub($value['price_3_total'], $price_4_total, 2);
  299. $dataArray[$key]['profit'] = $profit;
  300. $dataArray[$key]['profit_rate'] = $value['price_3_total'] > 0 ? bcdiv($profit, $value['price_3_total'], 2) : 0;
  301. }
  302. DB::table($table)->insert($dataArray);
  303. // 更新 lastId 继续下一批
  304. $lastId = end($dataArray)['id_detail'] ?? $lastId;
  305. } while (count($rows) === $limit);
  306. }catch (\Throwable $exception){
  307. return [false, "销货单同步异常" . $exception->getMessage() . "|" . $exception->getLine() . "|" . $exception->getFile()];
  308. }
  309. return [true, ''];
  310. }
  311. private function xsfpTPlus($data, $user){
  312. try {
  313. $table = $this->table;
  314. $limit = 500;
  315. $lastId = 0;
  316. do {
  317. $rows = $this->databaseService->table('SA_SaleInvoice_b as si_b')
  318. ->join('SA_SaleInvoice as si', 'si_b.idSaleInvoiceDTO', '=', 'si.ID')
  319. ->leftJoin('SA_SaleDelivery_b as sd_b', 'si_b.sourceVoucherDetailId', '=', 'sd_b.ID')
  320. ->leftJoin('AA_Partner as pn', 'si.idsettlecustomer', '=', 'pn.ID')
  321. ->leftJoin('AA_Person as ps', 'si.idclerk', '=', 'ps.ID')
  322. ->leftJoin('AA_Person as ps2', 'pn.idsaleman', '=', 'ps2.ID')
  323. ->leftJoin('AA_Inventory as it', 'si_b.idinventory', '=', 'it.ID')
  324. ->leftJoin('AA_Unit as ui', 'si_b.idbaseunit', '=', 'ui.ID')
  325. ->where('si.voucherdate','>=',$data['start_time'])
  326. ->where('si.voucherdate','<=',$data['end_time'])
  327. ->where('si_b.ID', '>', $lastId) // 用真实字段
  328. ->orderBy('si_b.ID')
  329. ->limit($limit)
  330. ->selectRaw("
  331. COALESCE(si.ID, 0) as order_id,
  332. COALESCE(si.code, '') as order_number,
  333. si.voucherdate as order_time,
  334. si.voucherState as order_state,
  335. COALESCE(pn.code, '') as customer_code,
  336. COALESCE(pn.name, '') as customer_title,
  337. COALESCE(pn.priuserdefnvc14, '') as customer_profit_rate,
  338. COALESCE(si.idclerk, 0) as employee_id_1,
  339. COALESCE(ps.name, '') as employee_id_1_title,
  340. COALESCE(pn.idsaleman, 0) as employee_id_2,
  341. COALESCE(ps2.name, '') as employee_id_2_title,
  342. COALESCE(it.code, '') as product_code,
  343. COALESCE(it.name, '') as product_title,
  344. COALESCE(it.specification, '') as product_size,
  345. COALESCE(ui.name, '') as unit,
  346. COALESCE(si_b.quantity, 0) as quantity,
  347. COALESCE(si_b.taxPrice, 0) as price_1,
  348. COALESCE(si_b.taxAmount, 0) as price_1_total,
  349. COALESCE(si_b.ID, 0) as id_detail,
  350. COALESCE(si_b.sourceVoucherDetailId, 0) as id_detail_upstream,
  351. COALESCE(si_b.sourceVoucherCode, '') as order_number_upstream,
  352. COALESCE(sd_b.pubuserdefdecm9, 0) as is_activity
  353. ")
  354. ->get();
  355. if ($rows->isEmpty()) break;
  356. $dataArray = Collect($rows)->map(function ($object) {
  357. return (array)$object;
  358. })->toArray();
  359. //存货档案
  360. $product = Product::where('del_time',0)
  361. ->whereIn('code', array_unique(array_column($dataArray,'product_code')))
  362. ->select('code','business_cost')
  363. ->get()->toArray();
  364. $product_map = array_column($product,null,'code');
  365. //组织数据
  366. foreach ($dataArray as $key => $value){
  367. $customer_profit_rate = rtrim($value['customer_profit_rate'],'%');
  368. if(is_numeric($customer_profit_rate)){
  369. $customer_profit_rate = bcdiv($customer_profit_rate,100,3);
  370. }else{
  371. $customer_profit_rate = 0;
  372. }
  373. $dataArray[$key]['customer_profit_rate'] = $customer_profit_rate;
  374. $p_tmp = $product_map[$value['product_code']] ?? [];
  375. $dataArray[$key]['order_type'] = RevenueCost::ORDER_TWO;
  376. $dataArray[$key]['order_time'] = strtotime($value['order_time']);
  377. $business_cost = $p_tmp['business_cost'] ?? 0;
  378. $dataArray[$key]['price_4'] = $business_cost;
  379. $price_4_total = bcmul($business_cost, $value['quantity'],2);
  380. $dataArray[$key]['price_4_total'] = bcmul($business_cost, $value['quantity'],2);
  381. $profit = bcsub($value['price_1_total'], $price_4_total,2);
  382. $dataArray[$key]['profit'] = $profit;
  383. $dataArray[$key]['profit_rate'] = $value['price_1_total'] > 0 ? bcdiv($profit, $value['price_1_total'],2) : 0;
  384. }
  385. DB::table($table)->insert($dataArray);
  386. // 更新 lastId 继续下一批
  387. $lastId = end($dataArray)['id_detail'] ?? $lastId;
  388. } while (count($rows) === $limit);
  389. }catch (\Throwable $exception){
  390. return [false, "销售发票同步异常" . $exception->getMessage() . "|" . $exception->getLine() . "|" . $exception->getFile()];
  391. }
  392. return [true, ''];
  393. }
  394. private function hkdTPlus($data, $user){
  395. try{
  396. $table = $this->table;
  397. $limit = 500;
  398. $lastId = 0;
  399. do {
  400. $rows = $this->databaseService->table('ARAP_ReceivePayment_b as rp_b')
  401. ->join('ARAP_ReceivePayment as rp', 'rp_b.idArapReceivePaymentDTO', '=', 'rp.ID')
  402. // ->leftJoin('SA_SaleInvoice_b as si_b', 'rp_b.voucherDetailID', '=', 'si_b.ID')
  403. // ->leftJoin('SA_SaleInvoice as si', 'si_b.idSaleInvoiceDTO', '=', 'si.ID')
  404. // ->leftJoin('SA_SaleDelivery_b as sd_b', 'si_b.sourceVoucherDetailId', '=', 'sd_b.ID')
  405. // 发票子表关联(仅限 idvouchertype = 20)
  406. ->leftJoin('SA_SaleInvoice_b as si_b', function ($join) {
  407. $join->on('rp_b.voucherDetailID', '=', 'si_b.ID')
  408. ->where('rp_b.idvouchertype', '=', 20);
  409. })
  410. ->leftJoin('SA_SaleInvoice as si', function ($join) {
  411. $join->on('si_b.idSaleInvoiceDTO', '=', 'si.ID')
  412. ->where('rp_b.idvouchertype', '=', 20);
  413. })
  414. ->leftJoin('SA_SaleDelivery_b as sd_b', function ($join) {
  415. $join->on('si_b.sourceVoucherDetailId', '=', 'sd_b.ID')
  416. ->where('rp_b.idvouchertype', '=', 20);
  417. })
  418. ->leftJoin('AA_Partner as pn', 'si.idsettlecustomer', '=', 'pn.ID')
  419. ->leftJoin('AA_Person as ps', 'rp.idperson', '=', 'ps.ID')
  420. ->leftJoin('AA_Person as ps2', 'pn.idsaleman', '=', 'ps2.ID')
  421. ->leftJoin('AA_Inventory as it', 'si_b.idinventory', '=', 'it.ID')
  422. ->leftJoin('AA_Unit as ui', 'si_b.idbaseunit', '=', 'ui.ID')
  423. ->where('rp.voucherdate','>=',$data['start_time'])
  424. ->where('rp.voucherdate','<=',$data['end_time'])
  425. // ->where('rp_b.idvouchertype','=', 20) // 销售发票
  426. ->where('rp.isReceiveFlag','=', 1)
  427. ->where('rp_b.ID', '>', $lastId)
  428. ->orderBy('rp_b.ID')
  429. ->limit($limit)
  430. // ->selectRaw("
  431. // COALESCE(rp.ID, 0) as order_id,
  432. // COALESCE(rp.code, '') as order_number,
  433. // rp.voucherdate as order_time,
  434. // COALESCE(pn.code, '') as customer_code,
  435. // COALESCE(pn.name, '') as customer_title,
  436. // COALESCE(pn.priuserdefnvc14, '') as customer_profit_rate,
  437. // COALESCE(it.code, '') as product_code,
  438. // COALESCE(it.name, '') as product_title,
  439. // COALESCE(rp.idperson, 0) as employee_id_1,
  440. // COALESCE(ps.name, '') as employee_id_1_title,
  441. // COALESCE(pn.idsaleman, 0) as employee_id_2,
  442. // COALESCE(ps2.name, '') as employee_id_2_title,
  443. // COALESCE(it.specification, '') as product_size,
  444. // COALESCE(ui.name, '') as unit,
  445. // COALESCE(si_b.quantity, 0) as quantity,
  446. // COALESCE(si_b.taxPrice, 0) as price_1,
  447. // COALESCE(si_b.taxAmount, 0) as price_1_total,
  448. // COALESCE(rp_b.amount, 0) as payment_amount,
  449. // COALESCE(rp_b.ID, 0) as id_detail,
  450. // COALESCE(rp_b.voucherDetailID, 0) as id_detail_upstream,
  451. // COALESCE(rp_b.voucherCode, '') as order_number_upstream,
  452. // COALESCE(sd_b.pubuserdefdecm9, 0) as is_activity
  453. // ")
  454. ->selectRaw("
  455. COALESCE(rp.ID, 0) as order_id,
  456. COALESCE(rp.code, '') as order_number,
  457. rp.voucherdate as order_time,
  458. rp.voucherstate as order_state,
  459. COALESCE(rp.pubuserdefnvc11, '') as channel_finance,
  460. COALESCE(rp.pubuserdefnvc12, '') as channel_details,
  461. COALESCE(pn.code, '') as customer_code,
  462. COALESCE(pn.name, '') as customer_title,
  463. COALESCE(pn.priuserdefnvc14, '') as customer_profit_rate,
  464. CASE WHEN rp_b.idvouchertype = 20 THEN COALESCE(it.code, '') ELSE '' END as product_code,
  465. CASE WHEN rp_b.idvouchertype = 20 THEN COALESCE(it.name, '') ELSE '' END as product_title,
  466. COALESCE(rp.idperson, 0) as employee_id_1,
  467. COALESCE(ps.name, '') as employee_id_1_title,
  468. COALESCE(pn.idsaleman, 0) as employee_id_2,
  469. COALESCE(ps2.name, '') as employee_id_2_title,
  470. CASE WHEN rp_b.idvouchertype = 20 THEN COALESCE(it.specification, '') ELSE '' END as product_size,
  471. CASE WHEN rp_b.idvouchertype = 20 THEN COALESCE(ui.name, '') ELSE '' END as unit,
  472. CASE WHEN rp_b.idvouchertype = 20 THEN COALESCE(si_b.quantity, 0) ELSE 0 END as quantity,
  473. CASE WHEN rp_b.idvouchertype = 20 THEN COALESCE(si_b.taxPrice, 0) ELSE 0 END as price_1,
  474. CASE WHEN rp_b.idvouchertype = 20 THEN COALESCE(si_b.taxAmount, 0) ELSE 0 END as price_1_total,
  475. COALESCE(rp_b.origCurrentAmount, 0) as payment_amount,
  476. COALESCE(rp_b.ID, 0) as id_detail,
  477. COALESCE(rp_b.voucherDetailID, 0) as id_detail_upstream,
  478. COALESCE(rp_b.voucherCode, '') as order_number_upstream,
  479. CASE WHEN rp_b.idvouchertype = 20 THEN COALESCE(sd_b.pubuserdefdecm9, 0) ELSE 0 END as is_activity,
  480. rp_b.idvouchertype as voucher_type
  481. ")
  482. ->get();
  483. // COALESCE(rp_b.amount, 0) as payment_amount,
  484. if ($rows->isEmpty()) break;
  485. $dataArray = Collect($rows)->map(function ($object) {
  486. return (array)$object;
  487. })->toArray();
  488. //存货档案
  489. $product = Product::where('del_time',0)
  490. ->whereIn('code', array_unique(array_column($dataArray,'product_code')))
  491. ->select('code','business_cost')
  492. ->get()->toArray();
  493. $product_map = array_column($product,null,'code');
  494. //组织数据
  495. foreach ($dataArray as $key => $value){
  496. $customer_profit_rate = rtrim($value['customer_profit_rate'],'%');
  497. if(is_numeric($customer_profit_rate)){
  498. $customer_profit_rate = bcdiv($customer_profit_rate,100,3);
  499. }else{
  500. $customer_profit_rate = 0;
  501. }
  502. $dataArray[$key]['customer_profit_rate'] = $customer_profit_rate;
  503. $p_tmp = $product_map[$value['product_code']] ?? [];
  504. $dataArray[$key]['order_type'] = RevenueCost::ORDER_THREE;
  505. $dataArray[$key]['order_time'] = strtotime($value['order_time']);
  506. $business_cost = $p_tmp['business_cost'] ?? 0;
  507. $dataArray[$key]['price_4'] = $business_cost;
  508. $price_4_total = bcmul($business_cost, $value['quantity'],2);
  509. $dataArray[$key]['price_4_total'] = $price_4_total;
  510. $profit = bcsub($value['price_1_total'], $price_4_total,2);
  511. $dataArray[$key]['profit'] = $profit;
  512. $dataArray[$key]['profit_rate'] = $value['price_1_total'] > 0 ? bcdiv($profit, $value['price_1_total'],2) : 0;
  513. }
  514. DB::table($table)->insert($dataArray);
  515. // 更新 lastId 继续下一批
  516. $lastId = end($dataArray)['id_detail'] ?? $lastId;
  517. } while (count($rows) === $limit);
  518. }catch (\Throwable $exception){
  519. return [false, "回款单同步异常" . $exception->getMessage() . "|" . $exception->getLine() . "|" . $exception->getFile()];
  520. }
  521. return [true, ''];
  522. }
  523. private function updateRevenueCost($data){
  524. try {
  525. $start_timeStamp = $data['start_timeStamp'];
  526. $end_timeStamp = $data['end_timeStamp'];
  527. $tmpTable = $this->table;
  528. $time = time();
  529. $ergs = $data;
  530. DB::transaction(function () use ($start_timeStamp, $end_timeStamp, $tmpTable,$time, $ergs) {
  531. // 1. 先软删除旧数据(你已有)
  532. RevenueCost::where('del_time', 0)
  533. ->where('order_time', '>=', $start_timeStamp)
  534. ->where('order_time', '<=', $end_timeStamp)
  535. ->update(['del_time' => $time]);
  536. // 2. 分批从临时表插入新数据
  537. $batchSize = 500;
  538. $lastId = 0;
  539. do {
  540. $chunk = DB::table($tmpTable)
  541. ->where('id', '>', $lastId)
  542. ->orderBy('id')
  543. ->limit($batchSize)
  544. ->get();
  545. if ($chunk->isEmpty()) {
  546. break;
  547. }
  548. $data = $chunk->map(function ($item) use($time){
  549. return [
  550. 'order_id' => $item->order_id,
  551. 'order_number' => $item->order_number,
  552. 'order_time' => $item->order_time,
  553. 'order_state' => $item->order_state,
  554. 'employee_id_1_title' => $item->employee_id_1_title,
  555. 'employee_id_1' => $item->employee_id_1 ?? 0,
  556. 'employee_id_2' => $item->employee_id_2 ?? 0,
  557. 'employee_id_2_title' => $item->employee_id_2_title ?? "",
  558. 'customer_code' => $item->customer_code,
  559. 'customer_title' => $item->customer_title,
  560. 'channel_finance' => $item->channel_finance,
  561. 'channel_details' => $item->channel_details,
  562. 'product_code' => $item->product_code,
  563. 'product_title' => $item->product_title,
  564. 'product_size' => $item->product_size,
  565. 'unit' => $item->unit,
  566. 'quantity' => $item->quantity,
  567. 'price_1' => $item->price_1,
  568. 'price_1_total' => $item->price_1_total,
  569. 'price_2' => $item->price_2,
  570. 'price_2_total' => $item->price_2_total,
  571. 'price_3' => $item->price_3,
  572. 'price_3_total' => $item->price_3_total,
  573. 'price_4' => $item->price_4,
  574. 'price_4_total' => $item->price_4_total,
  575. 'profit' => $item->profit,
  576. 'profit_rate' => $item->profit_rate,
  577. 'id_detail' => $item->id_detail,
  578. 'order_type' => $item->order_type,
  579. 'payment_amount' => $item->payment_amount ?? 0,
  580. 'id_detail_upstream' => $item->id_detail_upstream?? 0,
  581. 'order_number_upstream' => $item->order_number_upstream ?? "",
  582. 'is_activity' => $item->is_activity ?? 0,
  583. 'customer_profit_rate' => $item->customer_profit_rate ?? '',
  584. 'voucher_type' => $item->voucher_type ?? 0,
  585. 'crt_time' => $time,
  586. ];
  587. })->toArray();
  588. // 每批单独插入(可选:加小事务)
  589. RevenueCost::insert($data);
  590. // 更新 lastId
  591. $lastId = $chunk->last()->id;
  592. } while ($chunk->count() == $batchSize);
  593. // 3. 更新主表
  594. list($status, $msg) = $this->updateRevenueCostTotal($ergs);
  595. if (! $status) {
  596. throw new \Exception($msg);
  597. }
  598. });
  599. }catch (\Throwable $exception){
  600. return [false, "单据明细同步异常" . $exception->getMessage() . "|" . $exception->getLine() . "|" . $exception->getFile()];
  601. }
  602. return [true, ''];
  603. }
  604. private function updateRevenueCostTotal($data){
  605. try {
  606. $start_timeStamp = $data['start_timeStamp'];
  607. $end_timeStamp = $data['end_timeStamp'];
  608. $time = time();
  609. //组织写入数据
  610. $return = [];
  611. $update_stamp = [];
  612. DB::table('revenue_cost')
  613. ->where('del_time',0)
  614. ->where('order_time', '>=', $start_timeStamp)
  615. ->where('order_time', '<=', $end_timeStamp)
  616. ->select(RevenueCost::$field)
  617. ->chunkById(100, function ($data) use(&$return,&$update_stamp){
  618. $dataArray = Collect($data)->map(function ($object){
  619. return (array)$object;
  620. })->toArray();
  621. foreach ($dataArray as $value){
  622. //变成每个月第一天的时间戳
  623. $time = date("Y-m-01", $value['order_time']);
  624. $stamp = strtotime($time);
  625. if(! in_array($stamp, $update_stamp)) $update_stamp[] = $stamp;
  626. if($value['order_type'] == RevenueCost::ORDER_ONE){
  627. $income = $value['price_3_total'];
  628. }elseif ($value['order_type'] == RevenueCost::ORDER_TWO){
  629. $income = $value['price_1_total'];
  630. }else{
  631. $income = $value['payment_amount'];
  632. }
  633. $adjust = $income < 0 ? $income : 0;
  634. $business = $value['price_4_total'];
  635. if(isset($return[$stamp][$value['order_type']][$value['employee_id_1']])){
  636. $income_total = bcadd($return[$stamp][$value['order_type']][$value['employee_id_1']]['income'], $income,2);
  637. $adjust_total = bcadd($return[$stamp][$value['order_type']][$value['employee_id_1']]['adjust'], $adjust,2);
  638. $business_total = bcadd($return[$stamp][$value['order_type']][$value['employee_id_1']]['business'], $business,2);
  639. $return[$stamp][$value['order_type']][$value['employee_id_1']]['income'] = $income_total;
  640. $return[$stamp][$value['order_type']][$value['employee_id_1']]['adjust'] = $adjust_total;
  641. $return[$stamp][$value['order_type']][$value['employee_id_1']]['business'] = $business_total;
  642. }else{
  643. $return[$stamp][$value['order_type']][$value['employee_id_1']] = [
  644. 'income' => $income,
  645. 'adjust' => $adjust,
  646. 'business' => $business,
  647. 'order_time' => $stamp,
  648. 'employee_id_1_title' => $value['employee_id_1_title'],
  649. ];
  650. }
  651. }
  652. });
  653. $insert = [];
  654. foreach ($return as $value){
  655. foreach ($value as $order_type => $val){
  656. foreach ($val as $employee_id => $v){
  657. $profit = bcsub($v['income'], $v['business'],2);
  658. $profit_rate = $v['income'] > 0 ? bcdiv($profit, $v['income'],2) : 0;
  659. $v['profit'] = $profit;
  660. $v['profit_rate'] = $profit_rate;
  661. $v['order_type'] = $order_type;
  662. $v['employee_id_1'] = $employee_id;
  663. $v['crt_time'] = $time;
  664. $insert[] = $v;
  665. }
  666. }
  667. }
  668. RevenueCostTotal::where('del_time', 0)
  669. ->whereIn('order_time', $update_stamp)
  670. ->update(['del_time' => $time]);
  671. RevenueCostTotal::insert($insert);
  672. }catch (\Throwable $exception){
  673. return [false, "主表同步异常" . $exception->getMessage() . "|" . $exception->getLine() . "|" . $exception->getFile()];
  674. }
  675. return [true, ''];
  676. }
  677. private function createTmpTable(){
  678. $table = $this->table;
  679. if (! Schema::hasTable($table)) {
  680. // 可以通过 migration 创建,或程序启动时检查
  681. Schema::create($table, function (Blueprint $table) {
  682. $table->bigIncrements('id'); // BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY
  683. $table->integer('order_type')->default(0);
  684. $table->bigInteger('order_id')->default(0);
  685. $table->string('order_number', 50)->default('');
  686. $table->integer('order_time')->nullable();
  687. $table->integer('order_state')->default(0);
  688. $table->string('employee_id_1_title', 100)->default('');
  689. $table->bigInteger('employee_id_1')->default(0);
  690. $table->bigInteger('employee_id_2')->default(0);
  691. $table->string('employee_id_2_title', 100)->default('');
  692. $table->string('customer_code', 50)->default('');
  693. $table->string('customer_title', 100)->default('');
  694. $table->string('channel_finance', 50)->nullable();
  695. $table->string('channel_details', 50)->nullable();
  696. $table->string('product_code', 50)->default('');
  697. $table->string('product_title', 100)->default('');
  698. $table->string('product_size', 100)->nullable();
  699. $table->string('unit', 20)->nullable();
  700. $table->decimal('quantity', 12, 2)->default(0);
  701. $table->decimal('price_1', 12, 2)->default(0); // 销项成本
  702. $table->decimal('price_1_total', 12, 2)->default(0);
  703. $table->decimal('price_2', 12, 2)->default(0); // 运费
  704. $table->decimal('price_2_total', 12, 2)->default(0);
  705. $table->decimal('price_3', 12, 2)->default(0); // 含税单价
  706. $table->decimal('price_3_total', 12, 2)->default(0); // 含税金额
  707. $table->decimal('price_4', 12, 2)->default(0); // 业务成本
  708. $table->decimal('price_4_total', 12, 2)->default(0);
  709. $table->decimal('profit', 12, 2)->default(0);
  710. $table->decimal('payment_amount', 12, 2)->default(0);
  711. $table->decimal('profit_rate', 10, 3)->default(0);
  712. $table->bigInteger('id_detail')->default(0);
  713. $table->bigInteger('id_detail_upstream')->default(0);
  714. $table->string('order_number_upstream', 100)->nullable();
  715. $table->decimal('is_activity', 2, 0)->default(0);
  716. $table->string('customer_profit_rate', 20)->default('');
  717. $table->bigInteger('voucher_type')->default(0);
  718. });
  719. }
  720. }
  721. public function clearTmpTable($type = 1){
  722. if($type == 1){
  723. if (Schema::hasTable($this->table)) DB::table($this->table)->truncate();
  724. }elseif ($type == 2){
  725. if (Schema::hasTable($this->table_2)) DB::table($this->table_2)->truncate();
  726. }elseif ($type == 3){
  727. if (Schema::hasTable($this->table_3)) DB::table($this->table_3)->truncate();
  728. }
  729. }
  730. public function delTableKey($type = 1){
  731. $key = $this->table;
  732. if($type == 2) $key = $this->table_2;
  733. if($type == 3) $key = $this->table_3;
  734. $this->dellimitingSendRequest($key);
  735. }
  736. public function synSalaryEmployee($data, $user){
  737. if(empty($data['crt_time'][0]) || empty($data['crt_time'][1])) return [false, '同步时间不能为空'];
  738. list($start_time, $end_time) = $this->changeDateToTimeStampAboutRange($data['crt_time'],false);
  739. if ($start_time === null || $end_time === null || $start_time > $end_time) return [false, "同步时间:时间区间无效"];
  740. list($bool, $bool_msg) = $this->isOverThreeMonths($start_time, $end_time);
  741. if(! $bool) return [false, $bool_msg];
  742. $data['start_timeStamp'] = $start_time;
  743. $data['end_timeStamp'] = $end_time;
  744. $data['start_time'] = strtotime(date("Y-m-01",$data['start_timeStamp']));
  745. $data['end_time'] = strtotime(date("Y-m-01"),$data['end_timeStamp']);
  746. $data['operation_time'] = time();
  747. list($status,$msg) = $this->limitingSendRequest($this->table_2);
  748. if(! $status) return [false, '业务员工资同步正在后台运行,请稍后'];
  749. //同步
  750. // list($status, $msg) = $this->synSalaryEmployeeFromMine($data, $user);
  751. // if(! $status) {
  752. // $this->dellimitingSendRequest($this->table_2);
  753. // return [false, $msg];
  754. // }
  755. //队列
  756. ProcessDataJob::dispatch($data, $user, 2)->onQueue(RevenueCost::job2);
  757. return [true, '业务员工资相关信息同步已进入后台任务'];
  758. }
  759. public function synSalaryEmployeeFromMine($data, $user){
  760. //创建临时表 如果不存在
  761. $this->createTmpTable2();
  762. //清理临时表 如果内容不为空
  763. $this->clearTmpTable(2);
  764. //写入临时表
  765. DB::table('revenue_cost')
  766. ->where('del_time', 0)
  767. ->where('order_type', RevenueCost::ORDER_THREE)
  768. ->where('order_time','>=',$data['start_timeStamp'])
  769. ->where('order_time','<=',$data['end_timeStamp'])
  770. ->select([
  771. 'employee_id_1',
  772. 'employee_id_1_title',
  773. 'order_time as time',
  774. DB::raw("DATE_FORMAT(FROM_UNIXTIME(order_time), '%Y-%m-01') as order_time"),
  775. DB::raw("SUM(payment_amount) as payment_amount"),
  776. DB::raw("SUM(CASE WHEN is_activity = 0 THEN payment_amount ELSE 0 END) as payment_amount_not_include_activity"),
  777. DB::raw("SUM(CASE WHEN is_activity = 1 THEN payment_amount ELSE 0 END) as payment_amount_activity"),
  778. DB::raw("SUM(CASE WHEN profit_rate < customer_profit_rate and is_activity = 0 THEN payment_amount ELSE 0 END) as payment_amount_lower_than_rate"),
  779. DB::raw("SUM(CASE WHEN profit_rate >= customer_profit_rate and is_activity = 0 THEN payment_amount ELSE 0 END) as payment_amount_greater_than_rate"),
  780. DB::raw("SUM(CASE WHEN profit_rate >= customer_profit_rate and is_activity = 0 THEN price_4_total ELSE 0 END) as business"),
  781. DB::raw("ROW_NUMBER() OVER (ORDER BY MIN(id)) as fake_id")
  782. ])
  783. ->groupBy('employee_id_1', DB::raw("DATE_FORMAT(FROM_UNIXTIME(order_time), '%Y-%m-01')"))
  784. ->orderBy('order_time', 'desc')
  785. ->chunkById(500, function ($data){
  786. $dataArray = Collect($data)->map(function ($object){
  787. return (array)$object;
  788. })->toArray();
  789. $indexes = $this->getEmployeeIndex($dataArray);
  790. foreach ($dataArray as $key => $value){
  791. $value['index_' . EmployeeIndex::TYPE_ONE] = 0;
  792. $value['index_' . EmployeeIndex::TYPE_SIX] = 0;
  793. $value['index_' . EmployeeIndex::TYPE_EIGHT] = 0;
  794. $this->findIndex($indexes, $value);
  795. $dataArray[$key]['order_type'] = RevenueCost::ORDER_THREE;
  796. $dataArray[$key]['order_time'] = strtotime($value['order_time']);
  797. $dataArray[$key]['sale_bonus'] = $value['index_' . EmployeeIndex::TYPE_EIGHT];
  798. $dataArray[$key]['index_' . EmployeeIndex::TYPE_ONE] = $value['index_' . EmployeeIndex::TYPE_ONE];
  799. $rate = bcdiv($value['index_' . EmployeeIndex::TYPE_ONE],100,2);
  800. $pay_in_advance = bcmul($rate, $value['payment_amount_greater_than_rate'],2);
  801. $dataArray[$key]['pay_in_advance'] = $pay_in_advance;
  802. $dataArray[$key]['basic_salary'] = $value['index_' . EmployeeIndex::TYPE_SIX];
  803. $dataArray[$key]['should_pay'] = bcadd(bcadd($value['index_' . EmployeeIndex::TYPE_EIGHT], $pay_in_advance,2),$value['index_' . EmployeeIndex::TYPE_SIX],2);
  804. unset($dataArray[$key]['fake_id']);
  805. unset($dataArray[$key]['time']);
  806. }
  807. DB::table($this->table_2)->insert($dataArray);
  808. }, 'fake_id');
  809. //更新数据
  810. list($status,$msg) = $this->updateSalaryEmployee($data);
  811. if(! $status) return [false, $msg];
  812. //清理临时表 如果内容不为空
  813. $this->clearTmpTable(2);
  814. //释放redis
  815. $this->delTableKey(2);
  816. return [true, '同步成功'];
  817. }
  818. private function getEmployeeIndex($existingData)
  819. {
  820. if (empty($existingData)) {
  821. return collect();
  822. }
  823. // 取出所有涉及的 employee_id 和时间区间
  824. $employeeIds = array_column($existingData, 'employee_id_1');
  825. $time = array_column($existingData, 'time');
  826. $minStart = ! empty($time) ? min($time) : 0;
  827. $maxEnd = ! empty($time) ? max($time) : 0;
  828. // 一次性查出这些员工在最大区间范围内的所有指标
  829. $results = EmployeeIndex::where('del_time', 0)
  830. ->whereIn('type', [EmployeeIndex::TYPE_ONE, EmployeeIndex::TYPE_EIGHT, EmployeeIndex::TYPE_SIX])
  831. ->whereIn('employee_id', $employeeIds)
  832. ->where('start_time', '<=', $maxEnd)
  833. ->where('end_time', '>=', $minStart)
  834. ->select('start_time','end_time','employee_id','index','type')
  835. ->get();
  836. return $results;
  837. }
  838. private function findIndex($indexes, &$value){
  839. // 找到所有符合条件的 index(可能多个 type)
  840. $matchedIndexes = $indexes->filter(function ($item) use ($value) {
  841. return $item['employee_id'] == $value['employee_id_1']
  842. && $item['start_time'] <= $value['time']
  843. && $item['end_time'] >= $value['time'];
  844. });
  845. // 按 type 去重,只保留第一次出现的
  846. $uniqueByType = [];
  847. foreach ($matchedIndexes as $item) {
  848. $index = "index_" . $item['type'];
  849. if (! isset($uniqueByType[$index])) {
  850. $uniqueByType[$index] = $item['index'];
  851. }
  852. }
  853. $value = array_merge($value, $uniqueByType);
  854. }
  855. private function createTmpTable2(){
  856. $table = $this->table_2;
  857. if (! Schema::hasTable($table)) {
  858. // 可以通过 migration 创建,或程序启动时检查
  859. Schema::create($table, function (Blueprint $table) {
  860. $table->bigIncrements('id'); // BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY
  861. $table->integer('order_type')->default(0);
  862. $table->integer('order_time')->default(0);
  863. $table->string('employee_id_1_title', 100)->default('');
  864. $table->bigInteger('employee_id_1')->default(0);
  865. $table->decimal('payment_amount', 12, 2)->default(0);
  866. $table->decimal('payment_amount_not_include_activity', 12, 2)->default(0);
  867. $table->decimal('payment_amount_activity', 12, 2)->default(0);
  868. $table->decimal('payment_amount_lower_than_rate', 12, 2)->default(0);
  869. $table->decimal('payment_amount_greater_than_rate', 12, 2)->default(0);
  870. $table->decimal('business', 12, 2)->default(0);
  871. $table->decimal('sale_bonus', 12, 2)->default(0);
  872. $table->decimal('index_1', 10, 2)->default(0);
  873. $table->decimal('pay_in_advance', 12, 2)->default(0);
  874. $table->decimal('basic_salary', 12, 2)->default(0);
  875. $table->decimal('should_pay', 12, 2)->default(0);
  876. });
  877. }
  878. }
  879. private function updateSalaryEmployee($data){
  880. try {
  881. $start_timeStamp = $data['start_time'];
  882. $end_timeStamp = $data['end_time'];
  883. $tmpTable = $this->table_2;
  884. $time = time();
  885. $ergs = $data;
  886. DB::transaction(function () use ($start_timeStamp, $end_timeStamp, $tmpTable,$time, $ergs) {
  887. // 1. 先软删除旧数据(你已有)
  888. SalaryEmployee::where('del_time', 0)
  889. ->where('order_time', '>=', $start_timeStamp)
  890. ->where('order_time', '<=', $end_timeStamp)
  891. ->update(['del_time' => $time]);
  892. // 2. 分批从临时表插入新数据
  893. $batchSize = 500;
  894. $lastId = 0;
  895. do {
  896. $chunk = DB::table($tmpTable)
  897. ->where('id', '>', $lastId)
  898. ->orderBy('id')
  899. ->limit($batchSize)
  900. ->get();
  901. if ($chunk->isEmpty()) {
  902. break;
  903. }
  904. $data = $chunk->map(function ($item) use($time){
  905. return [
  906. 'order_type' => $item->order_type,
  907. 'order_time' => $item->order_time,
  908. 'employee_id_1_title' => $item->employee_id_1_title,
  909. 'employee_id_1' => $item->employee_id_1 ?? 0,
  910. 'payment_amount' => $item->payment_amount,
  911. 'payment_amount_not_include_activity' => $item->payment_amount_not_include_activity,
  912. 'payment_amount_activity' => $item->payment_amount_activity,
  913. 'payment_amount_lower_than_rate' => $item->payment_amount_lower_than_rate,
  914. 'payment_amount_greater_than_rate' => $item->payment_amount_greater_than_rate,
  915. 'business' => $item->business,
  916. 'sale_bonus' => $item->sale_bonus,
  917. 'index_1' => $item->index_1,
  918. 'pay_in_advance' => $item->pay_in_advance,
  919. 'basic_salary' => $item->basic_salary,
  920. 'should_pay' => $item->should_pay,
  921. 'crt_time' => $time,
  922. ];
  923. })->toArray();
  924. // 每批单独插入(可选:加小事务)
  925. SalaryEmployee::insert($data);
  926. // 更新 lastId
  927. $lastId = $chunk->last()->id;
  928. } while ($chunk->count() == $batchSize);
  929. });
  930. }catch (\Throwable $exception){
  931. return [false, "单据明细同步异常" . $exception->getMessage() . "|" . $exception->getLine() . "|" . $exception->getFile()];
  932. }
  933. return [true, ''];
  934. }
  935. public function synFreightFee($data, $user){
  936. if(empty($data['crt_time'][0]) || empty($data['crt_time'][1])) return [false, '同步时间不能为空'];
  937. list($start_time, $end_time) = $this->changeDateToTimeStampAboutRange($data['crt_time'],false);
  938. if ($start_time === null || $end_time === null || $start_time > $end_time) return [false, "同步时间:时间区间无效"];
  939. list($bool, $bool_msg) = $this->isOverThreeMonths($start_time, $end_time);
  940. if(! $bool) return [false, $bool_msg];
  941. $data['start_timeStamp'] = $start_time;
  942. $data['end_timeStamp'] = $end_time;
  943. $start = date('Y-m-d H:i:s.000', $start_time);
  944. $end = date('Y-m-d H:i:s.000', $end_time);
  945. $data['start_time'] = $start;
  946. $data['end_time'] = $end;
  947. $data['operation_time'] = time();
  948. // list($status, $msg) = $this->synFreightFeeFromMine($data, $user);dd(333);
  949. list($status,$msg) = $this->limitingSendRequest($this->table_3);
  950. if(! $status) return [false, '运费统计同步正在后台运行,请稍后'];
  951. //同步
  952. // list($status, $msg) = $this->synFreightFeeFromMine($data, $user);
  953. // if(! $status) {
  954. // $this->dellimitingSendRequest($this->table_3);
  955. // return [false, $msg];
  956. // }
  957. //队列
  958. ProcessDataJob::dispatch($data, $user, 3)->onQueue(RevenueCost::job3);
  959. return [true, '运费统计同步已进入后台任务'];
  960. }
  961. public function synFreightFeeFromMine($data, $user){
  962. //创建临时表 如果不存在
  963. $this->createTmpTable3();
  964. //清理临时表 如果内容不为空
  965. $this->clearTmpTable(3);
  966. //生成临时数据
  967. list($status, $msg) = $this->getFreightData($data);
  968. if(! $status) return [false, $msg];
  969. //写入
  970. list($status, $msg) = $this->updateFreightFeeFromMine($data);
  971. if(! $status) return [false, $msg];
  972. //清理临时表 如果内容不为空
  973. $this->clearTmpTable(3);
  974. //释放redis
  975. $this->delTableKey(3);
  976. return [true, '同步成功'];
  977. }
  978. private function createTmpTable3(){
  979. $table = $this->table_3;
  980. if (! Schema::hasTable($table)) {
  981. // 可以通过 migration 创建,或程序启动时检查
  982. Schema::create($table, function (Blueprint $table) {
  983. // 主键
  984. $table->bigIncrements('id');
  985. // 原始字段
  986. $table->bigInteger('order_id')->default(0)->comment('订单ID');
  987. $table->string('order_number')->default('')->comment('订单编号');
  988. $table->dateTime('order_time')->nullable()->comment('订单时间');
  989. $table->integer('order_state')->default(0)->comment('订单状态');
  990. $table->string('employee_id_1_title')->default('')->comment('业务员1名称');
  991. $table->bigInteger('employee_id_1')->default(0)->comment('业务员1 ID');
  992. $table->string('employee_id_2_title')->default('')->comment('上级管理人员名称');
  993. $table->bigInteger('employee_id_2')->default(0)->comment('上级管理人员ID');
  994. $table->string('customer_code')->default('')->comment('客户编码');
  995. $table->string('customer_title')->default('')->comment('客户名称');
  996. $table->decimal('customer_store_price', 10, 2)->default(0)->comment('客户卸货费单价');
  997. $table->string('product_code')->default('')->comment('产品编码');
  998. $table->string('product_title')->default('')->comment('产品名称');
  999. $table->string('product_size')->default('')->comment('产品规格');
  1000. $table->decimal('product_box_size', 10, 2)->default(0)->comment('装箱数');
  1001. $table->decimal('product_weight', 10, 3)->default(0)->comment('单件重量(kg)');
  1002. $table->decimal('product_store_price', 10, 2)->default(0)->comment('门店卸货费单价');
  1003. $table->decimal('product_store_price2', 10, 2)->default(0)->comment('到货装卸费单价');
  1004. $table->string('product_category')->default('')->comment('货类');
  1005. $table->string('unit')->default('')->comment('单位');
  1006. $table->decimal('quantity', 12, 3)->default(0)->comment('数量');
  1007. $table->decimal('price_3', 12, 2)->default(0)->comment('含税单价');
  1008. $table->decimal('price_3_total', 14, 2)->default(0)->comment('含税金额');
  1009. $table->bigInteger('id_detail')->default(0)->comment('明细ID');
  1010. $table->boolean('is_present')->default(false)->comment('是否赠品');
  1011. $table->string('mark')->default('')->comment('订单备注');
  1012. $table->string('item_mark')->default('')->comment('明细备注');
  1013. $table->bigInteger('warehouse_id')->default(0)->comment('仓库ID');
  1014. $table->string('warehouse_name')->default('')->comment('仓库名称');
  1015. $table->bigInteger('business_type_id')->default(0)->comment('业务类型ID');
  1016. $table->string('business_type_title')->default('')->comment('业务类型名称');
  1017. $table->string('address')->default('')->comment('送货地址');
  1018. $table->string('area_hs')->default('')->comment('区域');
  1019. $table->string('delivery_mode')->default('')->comment('配送方式');
  1020. $table->decimal('sl_fee', 10, 2)->default(0)->comment('其它费用');
  1021. // 新增的计算字段
  1022. $table->integer('xs')->default(0)->comment('箱数');
  1023. $table->decimal('weight', 12, 3)->default(0)->comment('总重量(kg)');
  1024. $table->tinyInteger('area_range')->default(1)->comment('运价区间 (1: <5kg, 2: >=5kg)');
  1025. $table->decimal('freight_unit_price', 10, 2)->default(0)->comment('配送费单价');
  1026. $table->decimal('freight_amount', 12, 2)->default(0)->comment('配送费金额');
  1027. $table->decimal('js_single_amount', 12, 2)->default(0)->comment('结算金额');
  1028. $table->decimal('min_freight_amount', 12, 2)->default(0)->comment('地区最低运价');
  1029. $table->decimal('customer_store_zx_fee', 12, 2)->default(0)->comment('门店卸货费');
  1030. $table->decimal('dh_fee', 12, 2)->default(0)->comment('到货装卸费');
  1031. });
  1032. }
  1033. }
  1034. private function getFreightData($data){
  1035. //写入临时表
  1036. try {
  1037. $table = $this->table_3;
  1038. $limit = 500;
  1039. $lastId = 0;
  1040. do {
  1041. $rows = $this->databaseService->table('SA_SaleDelivery_b as sd_b')
  1042. ->join('SA_SaleDelivery as sd', 'sd_b.idSaleDeliveryDTO', '=', 'sd.ID')
  1043. ->leftJoin('AA_Partner as pn', 'sd.idsettlecustomer', '=', 'pn.ID') // 结算客户
  1044. ->leftJoin('AA_Person as ps', 'sd.idclerk', '=', 'ps.ID') // 业务员
  1045. ->leftJoin('AA_Person as ps2', 'pn.idsaleman', '=', 'ps2.ID')
  1046. ->leftJoin('AA_Inventory as it', 'sd_b.idinventory', '=', 'it.ID')
  1047. ->leftJoin('AA_Unit as ui', 'sd_b.idbaseunit', '=', 'ui.ID')
  1048. ->leftJoin('AA_Warehouse as wa', 'sd_b.idwarehouse', '=', 'wa.id')
  1049. ->leftJoin('AA_Busitype as bs', 'sd.idbusinesstype', '=', 'bs.id')
  1050. ->where('sd.voucherdate', '>=', $data['start_time'])
  1051. ->where('sd.voucherdate', '<=', $data['end_time'])
  1052. ->where('sd.voucherState', '=', 189) //181 未审核 189 已审核
  1053. ->where('sd_b.ID', '>', $lastId) // 用真实字段
  1054. ->orderBy('sd_b.ID')
  1055. ->limit($limit)
  1056. ->selectRaw("
  1057. COALESCE(sd.ID, 0) as order_id,
  1058. COALESCE(sd.code, '') as order_number,
  1059. sd.voucherdate as order_time,
  1060. sd.voucherState as order_state,
  1061. COALESCE(ps.name, '') as employee_id_1_title,
  1062. COALESCE(sd.idclerk, 0) as employee_id_1,
  1063. COALESCE(ps2.name, '') as employee_id_2_title,
  1064. COALESCE(pn.idsaleman, 0) as employee_id_2,
  1065. COALESCE(pn.code, '') as customer_code,
  1066. COALESCE(pn.name, '') as customer_title,
  1067. COALESCE(pn.priuserdefdecm3, 0) as customer_store_price,
  1068. COALESCE(it.code, '') as product_code,
  1069. COALESCE(it.name, '') as product_title,
  1070. COALESCE(it.specification, '') as product_size,
  1071. COALESCE(it.priuserdefdecm1, 0) as product_box_size,
  1072. COALESCE(it.priuserdefdecm3, 0) as product_weight,
  1073. COALESCE(it.priuserdefdecm10, 0) as product_store_price,
  1074. COALESCE(it.priuserdefdecm9, 0) as product_store_price2,
  1075. COALESCE(it.priuserdefnvc5, '') as product_category,
  1076. COALESCE(ui.name, '') as unit,
  1077. COALESCE(sd_b.quantity, 0) as quantity,
  1078. COALESCE(sd_b.taxPrice, 0) as price_3,
  1079. COALESCE(sd_b.taxAmount, 0) as price_3_total,
  1080. COALESCE(sd_b.ID, 0) as id_detail,
  1081. COALESCE(sd_b.isPresent, 0) as is_present,
  1082. COALESCE(sd_b.DetailMemo, '') as item_mark,
  1083. COALESCE(sd.memo, '') as mark,
  1084. sd_b.idwarehouse as warehouse_id,
  1085. COALESCE(wa.name, '') as warehouse_name,
  1086. sd.idbusinesstype as business_type_id,
  1087. COALESCE(bs.name , '') as business_type_title,
  1088. COALESCE(sd.address , '') as address,
  1089. COALESCE(sd.priuserdefnvc2 , '') as area_hs,
  1090. COALESCE(sd.deliveryMode , 0) as delivery_mode,
  1091. COALESCE(sd.priuserdefdecm5 , 0) as sl_fee
  1092. ")
  1093. ->get();
  1094. if ($rows->isEmpty()) break;
  1095. $dataArray = Collect($rows)->map(function ($object) {
  1096. return (array)$object;
  1097. })->toArray();
  1098. $freight = Freight::where('del_time',0)
  1099. ->whereIn('region',array_column($dataArray,'area_hs'))
  1100. ->select('region','one_and_five','greater_than_five','min_freight_fee')
  1101. ->get()->toArray();
  1102. $freight_map = array_column($freight,null,'region');
  1103. //组织数据
  1104. foreach ($dataArray as $key => $value) {
  1105. $quantity = abs($value['quantity']);
  1106. //箱数
  1107. $xs = 0;
  1108. if(! empty($value['product_box_size']) && is_numeric($value['product_box_size']) && $value['product_box_size'] > 0) $xs = ceil($quantity / $value['product_box_size']);
  1109. $dataArray[$key]['xs'] = $xs;
  1110. //总重量
  1111. $weight = 0;
  1112. if(! empty($value['product_weight']) && is_numeric($value['product_weight']) && $value['product_weight'] > 0) $weight = bcdiv(bcmul($xs, $value['product_weight']),1000,3);
  1113. $dataArray[$key]['weight'] = $weight;
  1114. //运价区间
  1115. $area_range = 1;
  1116. if($weight >= 5) $area_range = 2;
  1117. $dataArray[$key]['area_range'] = $area_range;
  1118. //配送费单价
  1119. $freight = $min_freight = 0;
  1120. if(isset($freight_map[$value['area_hs']])) {
  1121. $tmp = $freight_map[$value['area_hs']];
  1122. if($area_range == 1){
  1123. $freight = $tmp['one_and_five'];
  1124. }else{
  1125. $freight = $tmp['greater_than_five'];
  1126. }
  1127. $min_freight = $tmp['min_freight_fee'];
  1128. }
  1129. $dataArray[$key]['freight_unit_price'] = $freight;
  1130. //配送费金额
  1131. $freight_amount = bcmul($weight, $freight,2);
  1132. $dataArray[$key]['freight_amount'] = $freight_amount;
  1133. //结算金额 记录的是每一条的
  1134. $dataArray[$key]['js_single_amount'] = $freight_amount;
  1135. //地区最低运价
  1136. $dataArray[$key]['min_freight_amount'] = $min_freight;
  1137. //门店卸货费
  1138. $customer_store_price = 0;
  1139. if(! empty($value['customer_store_price']) && is_numeric($value['customer_store_price']) && $value['customer_store_price'] > 0){
  1140. $customer_store_price = bcmul($value['customer_store_price'], $xs,2);
  1141. }else{
  1142. if(! empty($value['product_store_price']) && is_numeric($value['product_store_price']) && $value['product_store_price'] > 0){
  1143. $customer_store_price = bcmul($value['product_store_price'], $xs,2);
  1144. }
  1145. }
  1146. $dataArray[$key]['customer_store_zx_fee'] = $customer_store_price;
  1147. //到货装卸费
  1148. $product_store_price2 = 0;
  1149. if(! empty($value['product_store_price2']) && is_numeric($value['product_store_price2'])) $product_store_price2 = $value['product_store_price2'];
  1150. if(! empty($value['product_category']) && $value['product_category'] == "礼盒"){
  1151. $dh_fee = bcmul($xs, $product_store_price2,2);
  1152. }else{
  1153. $dh_fee = bcmul($weight, $product_store_price2,2);
  1154. }
  1155. if($value['business_type_id'] == FreightFee::businessTypeReturn) $dh_fee = bcmul($dh_fee,2,2);
  1156. $dataArray[$key]['dh_fee'] = $dh_fee;
  1157. }
  1158. DB::table($table)->insert($dataArray);
  1159. // 更新 lastId 继续下一批
  1160. $lastId = end($dataArray)['id_detail'] ?? $lastId;
  1161. } while (count($rows) === $limit);
  1162. }catch (\Throwable $exception){
  1163. return [false, "运费获取销货单数据异常" . $exception->getMessage() . "|" . $exception->getLine() . "|" . $exception->getFile()];
  1164. }
  1165. return [true, ''];
  1166. }
  1167. private function updateFreightFeeFromMine($data){
  1168. try {
  1169. $start_timeStamp = $data['start_time'];
  1170. $end_timeStamp = $data['end_time'];
  1171. $tmpTable = $this->table_3;
  1172. $time = time();
  1173. $ergs = $data;
  1174. DB::transaction(function () use ($start_timeStamp, $end_timeStamp, $tmpTable,$time, $ergs) {
  1175. // 1. 先软删除旧数据(你已有)
  1176. FreightFee::where('del_time', 0)
  1177. ->where('order_time', '>=', $start_timeStamp)
  1178. ->where('order_time', '<=', $end_timeStamp)
  1179. ->update(['del_time' => $time]);
  1180. // 2. 分批从临时表插入新数据
  1181. $batchSize = 500;
  1182. $lastId = 0;
  1183. do {
  1184. $chunk = DB::table($tmpTable)
  1185. ->where('id', '>', $lastId)
  1186. ->orderBy('id')
  1187. ->limit($batchSize)
  1188. ->get();
  1189. if ($chunk->isEmpty()) {
  1190. break;
  1191. }
  1192. $data = $chunk->map(function ($item) use($time){
  1193. return [
  1194. // 基础字段(按你给的顺序)
  1195. 'order_id' => $item->order_id ?? 0,
  1196. 'order_number' => $item->order_number ?? '',
  1197. 'order_time' => $item->order_time ? strtotime($item->order_time) : 0,
  1198. 'order_state' => $item->order_state ?? 0,
  1199. 'employee_id_1_title' => $item->employee_id_1_title ?? '',
  1200. 'employee_id_1' => $item->employee_id_1 ?? 0,
  1201. 'employee_id_2_title' => $item->employee_id_2_title ?? '',
  1202. 'employee_id_2' => $item->employee_id_2 ?? 0,
  1203. 'customer_code' => $item->customer_code ?? '',
  1204. 'customer_title' => $item->customer_title ?? '',
  1205. 'customer_store_price' => $item->customer_store_price ?? 0,
  1206. 'product_code' => $item->product_code ?? '',
  1207. 'product_title' => $item->product_title ?? '',
  1208. 'product_size' => $item->product_size ?? '',
  1209. 'product_box_size' => $item->product_box_size ?? 0,
  1210. 'product_weight' => $item->product_weight ?? 0,
  1211. 'product_store_price' => $item->product_store_price ?? 0,
  1212. 'product_store_price2' => $item->product_store_price2 ?? 0,
  1213. 'product_category' => $item->product_category ?? '',
  1214. 'unit' => $item->unit ?? '',
  1215. 'quantity' => $item->quantity ?? 0,
  1216. 'price_3' => $item->price_3 ?? 0,
  1217. 'price_3_total' => $item->price_3_total ?? 0,
  1218. 'id_detail' => $item->id_detail ?? 0,
  1219. 'is_present' => $item->is_present ?? '',
  1220. 'mark' => $item->mark ?? '',
  1221. 'item_mark' => $item->item_mark ?? '',
  1222. 'warehouse_id' => $item->warehouse_id ?? 0,
  1223. 'warehouse_name' => $item->warehouse_name ?? '',
  1224. 'business_type_id' => $item->business_type_id ?? 0,
  1225. 'business_type_title' => $item->business_type_title ?? '',
  1226. 'address' => $item->address ?? '',
  1227. 'area_hs' => $item->area_hs ?? '',
  1228. 'delivery_mode' => $item->delivery_mode ?? 0,
  1229. 'sl_fee' => $item->sl_fee ?? 0,
  1230. 'xs' => $item->xs,
  1231. 'weight' => $item->weight ?? 0,
  1232. 'area_range' => $item->area_range ?? 0,
  1233. 'freight_unit_price' => $item->freight_unit_price ?? 0,
  1234. 'freight_amount' => $item->freight_amount ?? 0,
  1235. 'js_single_amount' => $item->js_single_amount ?? 0,
  1236. 'min_freight_amount' => $item->min_freight_amount ?? 0,
  1237. 'customer_store_zx_fee' => $item->customer_store_zx_fee ?? 0,
  1238. 'dh_fee' => $item->dh_fee ?? 0,
  1239. 'crt_time' => $time,
  1240. ];
  1241. })->toArray();
  1242. // 每批单独插入(可选:加小事务)
  1243. FreightFee::insert($data);
  1244. // 更新 lastId
  1245. $lastId = $chunk->last()->id;
  1246. } while ($chunk->count() == $batchSize);
  1247. });
  1248. }catch (\Throwable $exception){
  1249. return [false, "单据明细同步异常" . $exception->getMessage() . "|" . $exception->getLine() . "|" . $exception->getFile()];
  1250. }
  1251. return [true, ''];
  1252. }
  1253. }