cqp 14 ساعت پیش
والد
کامیت
fdedd0ec03

+ 343 - 0
app/Exports/ExportOrder2.php

@@ -0,0 +1,343 @@
+<?php
+
+namespace App\Exports;
+
+use Illuminate\Support\Collection;
+use Maatwebsite\Excel\Concerns\FromCollection;
+use Maatwebsite\Excel\Concerns\WithCustomValueBinder;
+use Maatwebsite\Excel\Concerns\WithEvents;     // 自动注册事件监听器
+use Maatwebsite\Excel\Concerns\WithHeadings;
+use Maatwebsite\Excel\Concerns\WithStrictNullComparison;    // 导出 0 原样显示,不为 null
+use Maatwebsite\Excel\Events\AfterSheet;
+use PhpOffice\PhpSpreadsheet\Cell\Cell;
+use PhpOffice\PhpSpreadsheet\Cell\DataType;
+use PhpOffice\PhpSpreadsheet\Cell\DefaultValueBinder;
+
+class ExportOrder2 extends DefaultValueBinder implements WithCustomValueBinder , FromCollection, WithEvents, WithStrictNullComparison,withHeadings
+{
+    public $mergeStart;
+    public $mergeEnd;
+    public $mergeTitles;
+
+    /**
+     * @return \Illuminate\Support\Collection
+     */
+    public function __construct($data,$type=1,$headers)
+    {
+        $this->data = $data;
+        $this->type = $type;
+        $this->headers = $headers;
+    }
+
+    public function registerEvents(): array
+    {
+        //区分不通状态的合同导出,格式不同
+            $type = $this->type.'_set';
+            return $this->$type();
+    }
+
+    //数组转集合
+    public function collection()
+    {
+        return new Collection($this->createData());
+    }
+
+    //业务代码
+    public function createData()
+    {
+        $name = $this->type;
+        $data = $this->export();
+        return $data;
+
+    }
+
+    public function bindValue(Cell $cell, $value)
+    {
+        // 检查是否需要保留前导零
+//        if (is_string($value) && ctype_digit($value) && strpos($value, '0') === 0) {
+//            $cell->setValueExplicit($value, DataType::TYPE_STRING);
+//            return true;
+//        }
+
+        if (is_numeric($value)) {
+            $cell->setValueExplicit($value, DataType::TYPE_STRING2);
+
+            return true;
+        }
+
+        // else return default behavior
+        return parent::bindValue($cell, $value);
+    }
+
+    // 自定义表头,需实现withHeadings接口
+    public function headings(): array
+    {
+        return $this->headers;
+    }
+
+    private function export(){
+        $list = [];
+        foreach ($this->data as $v){
+            $list[] = $v;
+        }
+        return $list;
+    }
+
+
+    private function jc_set()
+    {
+        return [
+            AfterSheet::class => function (AfterSheet $event) {
+                $sheet = $event->sheet->getDelegate();
+
+                // 隐藏 C 列
+                $sheet->getColumnDimension('C')->setVisible(false);
+
+                // ==========================
+                // 固定合并
+                // ==========================
+                $sheet->mergeCells('A1:A4');  // 分类
+                $sheet->mergeCells('B1:B4');  // 项目
+                $sheet->mergeCells('D1:AA2'); // 各项目利润
+
+                // 设置标题
+                $sheet->setCellValue('A1', '分类');
+                $sheet->setCellValue('B1', '项目');
+                $sheet->setCellValue('D1', '各项目利润');
+
+                $startIndex = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::columnIndexFromString($this->mergeStart);
+                $endIndex   = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::columnIndexFromString($this->mergeEnd);
+
+                $titleIndex = 0;
+
+                for ($col = $startIndex; $col <= $endIndex; $col += 2) {
+
+                    // 当前两列坐标
+                    $col1 = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($col);
+                    $col2 = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($col + 1);
+
+                    // 合并 3-3 行
+                    $sheet->mergeCells("{$col1}3:{$col2}3");
+
+                    // 设置内容(外部传入)
+                    $title = $this->mergeTitles[$titleIndex] ?? '';
+                    $sheet->setCellValue("{$col1}3", $title);
+
+                    // ====================================
+                    // 自动填充 D4/E4、F4/G4…… 当月 / 累计
+                    // ====================================
+                    $sheet->setCellValue("{$col1}4", '当月');
+                    $sheet->setCellValue("{$col2}4", '累计');
+
+                    $titleIndex++;
+                }
+
+                // ==========================
+                // A 列合并(分类)
+                // ==========================
+                $sheet->mergeCells('A5:A7');   // 收入
+                $sheet->mergeCells('A8:A26');  // 销售费用
+
+                $sheet->setCellValue('A5', '收入');
+                $sheet->setCellValue('A8', '销售费用');
+
+                // ==========================
+                // B5 - B26 内容填充
+                // ==========================
+                $items = [
+                    "收款销售收入",
+                    "成本",
+                    "收款销售毛利",
+
+                    "结算费用",
+                    "管理费用",
+                    "物流配送",
+                    "合同费",
+                    "账扣费用",
+                    "促销员工资",
+                    "团购及其他返点",
+                    "陈列费",
+                    "客情费",
+                    "赠品费用",
+                    "广告宣传费",
+                    "快递费",
+                    "销售其他费用",
+                    "人员工资",
+                    "员工社保、福利费",
+                    "采购费用(手工)",
+                    "税金(手工)开票收入*1%",
+                    "财务手续费(手工)",
+                    "销售支出小计",
+                ];
+
+                $row = 5;
+                foreach ($items as $text) {
+                    $sheet->setCellValue("B{$row}", $text);
+                    $row++;
+                }
+
+                // =================================================================
+                // 样式
+                // =================================================================
+                $sheet->getStyle('A1:'.$this->mergeEnd.'200')
+                    ->getAlignment()
+                    ->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER)
+                    ->setVertical(\PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER)
+                    ->setWrapText(true);
+
+                foreach (range('A', 'Z') as $col) {
+                    $sheet->getColumnDimension($col)->setWidth(12);
+                }
+                foreach (range('A', 'I') as $col) {
+                    $sheet->getColumnDimension('A'.$col)->setWidth(12); // AA, AB ...
+                }
+                $sheet->getColumnDimension('B')->setWidth(25);
+
+                // D5~AC26 填充数据
+                $startRow = 5;
+                $endRow   = 26;
+                $startCol = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::columnIndexFromString($this->mergeStart);
+                $endCol   = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::columnIndexFromString($this->mergeEnd);
+
+                for ($row = $startRow; $row <= $endRow; $row++) {
+                    $dataRow = $this->data[$row - $startRow] ?? [];
+                    $colIndex = 0;
+                    for ($col = $startCol; $col <= $endCol; $col++) {
+                        $colLetter = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($col);
+                        $value = $dataRow[$colIndex] ?? '';
+                        $sheet->setCellValue($colLetter . $row, $value);
+                        $colIndex++;
+                    }
+                }
+            },
+        ];
+    }
+
+    private function jc2_set()
+    {
+        return [
+            AfterSheet::class => function (AfterSheet $event) {
+                $sheet = $event->sheet->getDelegate();
+
+                // 隐藏 C 列
+                $sheet->getColumnDimension('C')->setVisible(false);
+
+                // ==========================
+                // 固定合并
+                // ==========================
+                $sheet->mergeCells('A1:A4');  // 分类
+                $sheet->mergeCells('B1:B4');  // 项目
+                $sheet->mergeCells('D1:K2'); // 通路各业务员利润
+
+                // 设置标题
+                $sheet->setCellValue('A1', '分类');
+                $sheet->setCellValue('B1', '项目');
+                $sheet->setCellValue('D1', '通路各业务员利润');
+
+                $startIndex = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::columnIndexFromString($this->mergeStart);
+                $endIndex   = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::columnIndexFromString($this->mergeEnd);
+
+                $titleIndex = 0;
+
+                for ($col = $startIndex; $col <= $endIndex; $col += 2) {
+
+                    // 当前两列坐标
+                    $col1 = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($col);
+                    $col2 = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($col + 1);
+
+                    // 合并 3-3 行
+                    $sheet->mergeCells("{$col1}3:{$col2}3");
+
+                    // 设置内容(外部传入)
+                    $title = $this->mergeTitles[$titleIndex] ?? '';
+                    $sheet->setCellValue("{$col1}3", $title);
+
+                    // ====================================
+                    // 自动填充 D4/E4、F4/G4…… 当月 / 累计
+                    // ====================================
+                    $sheet->setCellValue("{$col1}4", '当月');
+                    $sheet->setCellValue("{$col2}4", '累计');
+
+                    $titleIndex++;
+                }
+
+                // ==========================
+                // A 列合并(分类)
+                // ==========================
+                $sheet->mergeCells('A5:A7');   // 收入
+                $sheet->mergeCells('A8:A26');  // 销售支出
+
+                $sheet->setCellValue('A5', '收入');
+                $sheet->setCellValue('A8', '销售支出');
+
+                // ==========================
+                // B5 - B26 内容填充
+                // ==========================
+                $items = [
+                    "收入",
+                    "成本",
+                    "毛利",
+                    "结算费用(560116)",
+                    "管理费用",
+                    "物流配送",
+                    "合同费(560115)",
+                    "账扣费用(560114)",
+                    "促销员工资(560113)",
+                    "团购及其他返点(560105)",
+                    "陈列费(560120)",
+                    "客情费(560112)",
+                    "赠品费用(560111)",
+                    "广告宣传费(560110)",
+                    "快递费(560109)",
+                    "销售其他费用(560101)",
+                    "员工工资支出(560115)",
+                    "员工社保、福利费(560118)",
+                    "采购费用(手工)(560107)",
+                    "税金(手工)",
+                    "财务手续费(手工)",
+                    "销售支出小计",
+                ];
+
+                $row = 5;
+                foreach ($items as $text) {
+                    $sheet->setCellValue("B{$row}", $text);
+                    $row++;
+                }
+
+                // =================================================================
+                // 样式
+                // =================================================================
+                $sheet->getStyle('A1:'.$this->mergeEnd.'200')
+                    ->getAlignment()
+                    ->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER)
+                    ->setVertical(\PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER)
+                    ->setWrapText(true);
+
+                foreach (range('A', 'Z') as $col) {
+                    $sheet->getColumnDimension($col)->setWidth(12);
+                }
+                foreach (range('A', 'I') as $col) {
+                    $sheet->getColumnDimension('A'.$col)->setWidth(12); // AA, AB ...
+                }
+                $sheet->getColumnDimension('B')->setWidth(25);
+
+                // D5~AC26 填充数据
+                $startRow = 5;
+                $endRow   = 26;
+                $startCol = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::columnIndexFromString($this->mergeStart);
+                $endCol   = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::columnIndexFromString($this->mergeEnd);
+
+                for ($row = $startRow; $row <= $endRow; $row++) {
+                    $dataRow = $this->data[$row - $startRow] ?? [];
+                    $colIndex = 0;
+                    for ($col = $startCol; $col <= $endCol; $col++) {
+                        $colLetter = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($col);
+                        $value = $dataRow[$colIndex] ?? '';
+                        $sheet->setCellValue($colLetter . $row, $value);
+                        $colIndex++;
+                    }
+                }
+            },
+        ];
+    }
+}

+ 24 - 0
app/Http/Controllers/Api/StatisticsController.php

@@ -114,4 +114,28 @@ class StatisticsController extends BaseController
             return $this->json_return(201,$data);
             return $this->json_return(201,$data);
         }
         }
     }
     }
+
+    public function statisticsItem(Request $request){
+        $service = new StatisticsService();
+        $userData = $request->userData->toArray();
+        list($status,$data) = $service->statisticsItem($request->all(),$userData);
+
+        if($status){
+            return $this->json_return(200,'',$data);
+        }else{
+            return $this->json_return(201,$data);
+        }
+    }
+
+    public function statisticsRoad(Request $request){
+        $service = new StatisticsService();
+        $userData = $request->userData->toArray();
+        list($status,$data) = $service->statisticsRoad($request->all(),$userData);
+
+        if($status){
+            return $this->json_return(200,'',$data);
+        }else{
+            return $this->json_return(201,$data);
+        }
+    }
 }
 }

+ 16 - 0
app/Http/Controllers/Api/TPlusController.php

@@ -70,4 +70,20 @@ class TPlusController extends BaseController
             return $this->json_return(201,$data);
             return $this->json_return(201,$data);
         }
         }
     }
     }
+
+    public function synItemRoad(Request $request)
+    {
+        $service = new TPlusServerService();
+        $error = $service->getError();
+        if(! empty($error)) return $this->json_return(201, $error);
+
+        $userData = $request->userData->toArray();
+        list($status,$data) = $service->synItemRoad($request->all(),$userData);
+
+        if($status){
+            return $this->json_return(200,'',$data);
+        }else{
+            return $this->json_return(201,$data);
+        }
+    }
 }
 }

+ 3 - 4
app/Jobs/ProcessDataJob.php

@@ -9,9 +9,6 @@ use Illuminate\Contracts\Queue\ShouldQueue;
 use Illuminate\Foundation\Bus\Dispatchable;
 use Illuminate\Foundation\Bus\Dispatchable;
 use Illuminate\Queue\InteractsWithQueue;
 use Illuminate\Queue\InteractsWithQueue;
 use Illuminate\Queue\SerializesModels;
 use Illuminate\Queue\SerializesModels;
-use Illuminate\Support\Facades\DB;
-use Illuminate\Support\Facades\Log;
-use Illuminate\Support\Facades\Redis;
 use MongoDB\Driver\Exception\Exception;
 use MongoDB\Driver\Exception\Exception;
 use Symfony\Component\Console\Output\ConsoleOutput;
 use Symfony\Component\Console\Output\ConsoleOutput;
 use Symfony\Component\Console\Output\OutputInterface;
 use Symfony\Component\Console\Output\OutputInterface;
@@ -46,8 +43,10 @@ class ProcessDataJob implements ShouldQueue
                 list($status, $msg) = $service->synRevenueCostFromTPlus($data, $user);
                 list($status, $msg) = $service->synRevenueCostFromTPlus($data, $user);
             }elseif($type == 2){
             }elseif($type == 2){
                 list($status, $msg) = $service->synSalaryEmployeeFromMine($data, $user);
                 list($status, $msg) = $service->synSalaryEmployeeFromMine($data, $user);
-            }else{
+            }elseif($type == 3){
                 list($status, $msg) = $service->synFreightFeeFromMine($data, $user);
                 list($status, $msg) = $service->synFreightFeeFromMine($data, $user);
+            }else{
+                list($status, $msg) = $service->synItemRoadMy($data, $user);
             }
             }
 
 
             $this->finalDo($msg, $service);
             $this->finalDo($msg, $service);

+ 16 - 0
app/Model/ItemReport.php

@@ -0,0 +1,16 @@
+<?php
+
+namespace App\Model;
+
+use Illuminate\Database\Eloquent\Model;
+
+class ItemReport extends Model
+{
+    protected $guarded = [];
+    protected $table = "item_report"; //指定表
+    const CREATED_AT = null;
+    const UPDATED_AT = null;
+    protected $dateFormat = 'U';
+
+    public static $field = ['*'];
+}

+ 25 - 0
app/Model/ItemReportRoad.php

@@ -0,0 +1,25 @@
+<?php
+
+namespace App\Model;
+
+class ItemReportRoad extends UseScopeBaseModel
+{
+    protected $guarded = [];
+    protected $table = "item_report_road"; //指定表
+    const CREATED_AT = null;
+    const UPDATED_AT = null;
+    protected $dateFormat = 'U';
+
+    const ORDER_ONE = 1;
+    const ORDER_TWO = 2;
+    const ORDER_THREE = 3;
+    const ORDER_FOUR = 4;
+
+    public static $order_type = [
+        self::ORDER_ONE => '张春勇',
+        self::ORDER_TWO => '金小勇',
+        self::ORDER_THREE => '沈强',
+        self::ORDER_FOUR => '王利英、翁春燕、叶南汝'
+    ];
+
+}

+ 1 - 0
app/Model/RevenueCost.php

@@ -24,6 +24,7 @@ class RevenueCost extends UseScopeBaseModel
     const job = 'yf_revenue_cost';
     const job = 'yf_revenue_cost';
     const job2 = 'salary_employee';
     const job2 = 'salary_employee';
     const job3 = 'freight_fee';
     const job3 = 'freight_fee';
+    const job4 = 'item_count';
 
 
     public static $field = ['id','order_time','order_type','price_1','price_1_total','price_2','price_2_total','price_3','price_3_total','price_4','price_4_total','payment_amount','profit','profit_rate','employee_id_1','employee_id_1_title'];
     public static $field = ['id','order_time','order_type','price_1','price_1_total','price_2','price_2_total','price_3','price_3_total','price_4','price_4_total','payment_amount','profit','profit_rate','employee_id_1','employee_id_1_title'];
     public static $field_xhd = ['id','order_type','order_number','order_time','employee_id_1','employee_id_1_title','employee_id_2','employee_id_2_title','customer_code','customer_title','channel_finance','channel_details','product_code','product_title','product_size','unit','price_1','price_1_total','price_2','price_2_total','quantity','price_3','price_3_total','price_4','price_4_total','profit','profit_rate'];
     public static $field_xhd = ['id','order_type','order_number','order_time','employee_id_1','employee_id_1_title','employee_id_2','employee_id_2_title','customer_code','customer_title','channel_finance','channel_details','product_code','product_title','product_size','unit','price_1','price_1_total','price_2','price_2_total','quantity','price_3','price_3_total','price_4','price_4_total','profit','profit_rate'];

+ 0 - 43
app/Model/UseScopeBaseModel.php

@@ -14,49 +14,6 @@ class UseScopeBaseModel extends Model
         parent::__construct($attributes);
         parent::__construct($attributes);
     }
     }
 
 
-    //顶级部门过滤
-    public function scopeTopClear($query, $user, $search)
-    {
-        //是否所有部门
-        $is_all_depart = $user['is_all_depart'] ?? 0;
-        //权限范围内的部门
-        $depart_range = $user['depart_range'] ?? [];
-
-        //顶级部门
-        $search_depart_id = $search['top_depart_id'] ?? 0;
-        if(empty($search_depart_id)){
-            //默认进来 自身顶级公司
-            $top_depart_id = $user['depart_top'][0] ?? [];
-            $top_depart_id = $top_depart_id['depart_id'] ?? 0;
-        }else{
-            //查询 顶级公司
-            $top_depart_id = $search_depart_id;
-        }
-
-        if($is_all_depart){
-            //所有部门
-            if(empty($search_depart_id)){
-                //全部
-                $query->whereIn('top_depart_id', $depart_range);
-            }else{
-                //查看某个分社
-                $query->where('top_depart_id', $top_depart_id);
-            }
-        }else{
-            //某个分社全部
-            $query->where('top_depart_id', $top_depart_id);
-        }
-
-        //获取当前门店下
-        if(! empty($search['get_my_top_depart_data'])){
-            $depart = ! empty($user['depart_top'][0]) ? $user['depart_top'][0]: [];
-            $depart_id = $depart['depart_id'] ?? 0;
-            $query->where('top_depart_id', $depart_id);
-        }
-
-        return $query;
-    }
-
     public function scopeClear($query, $user, $search)
     public function scopeClear($query, $user, $search)
     {
     {
         //权限范围内的部门
         //权限范围内的部门

+ 198 - 0
app/Service/ExportFileService.php

@@ -3,7 +3,9 @@
 namespace App\Service;
 namespace App\Service;
 
 
 use App\Exports\ExportOrder;
 use App\Exports\ExportOrder;
+use App\Exports\ExportOrder2;
 use App\Exports\MultiSheetExport;
 use App\Exports\MultiSheetExport;
+use App\Model\ItemReportRoad;
 use App\Model\RevenueCost;
 use App\Model\RevenueCost;
 use Maatwebsite\Excel\Facades\Excel;
 use Maatwebsite\Excel\Facades\Excel;
 
 
@@ -17,12 +19,25 @@ class ExportFileService extends Service
         1
         1
     ];
     ];
 
 
+    public $special_func = [
+        0 => 'eve',
+        1 => 'twl',
+    ];
+
     public function exportAll($data,$user){
     public function exportAll($data,$user){
         if(empty($data['menu_id'])) return [false, '菜单ID不能为空'];
         if(empty($data['menu_id'])) return [false, '菜单ID不能为空'];
         list($function, $name) = EmployeeService::fillMenu2($data['menu_id'], $user);
         list($function, $name) = EmployeeService::fillMenu2($data['menu_id'], $user);
         if (empty($function) || ! method_exists(self::class, $function)) return [false, "导出方法不存在,请联系开发"];
         if (empty($function) || ! method_exists(self::class, $function)) return [false, "导出方法不存在,请联系开发"];
         self::$filename = $name;
         self::$filename = $name;
 
 
+        //特殊的导出
+        if(in_array($function, $this->special_func)){
+            list($status, $return) = $this->$function($data,$user);
+            if(! $status) return [false, $return];
+
+            return [true, $return];
+        }
+
         $export_type = $data['export_type'] ?? 0;
         $export_type = $data['export_type'] ?? 0;
         if(! isset(self::$export_type[$export_type])) return [false,'导出文件方式错误或者不存在'];
         if(! isset(self::$export_type[$export_type])) return [false,'导出文件方式错误或者不存在'];
         if(empty($export_type)){
         if(empty($export_type)){
@@ -360,6 +375,189 @@ class ExportFileService extends Service
         return [true, $this->saveExportData2($return)];
         return [true, $this->saveExportData2($return)];
     }
     }
 
 
+    //        $data = [];
+//        for ($i = 0; $i < 22; $i++) {
+//            $row = [];
+//            foreach ($title as $value) {
+//                $row[] = rand(1000, 5000);   // 当月
+//                $row[] = rand(10000, 20000); // 累计
+//            }
+//            $data[] = $row;
+//        }dd($data);
+//
+//        dd($data, $title);
+    public function eve($ergs,$user){
+        $service = new StatisticsService();
+        $result = $service->getChannelReport($ergs);
+        if(empty($result)) return [false, '暂无数据'];
+
+        $title = array_column($result,'channel');
+        if(empty($title)) return [false, '暂无数据'];
+
+        // 2. 字段顺序(22 个)
+        $fieldOrder = [
+            'receipt_amount',
+            'cost',
+            'profit',
+            'settle_amount',
+            'gl_amount',
+            'wl_amount',
+            'ht_amount',
+            'zk_amount',
+            'cx_amount',
+            'tg_amount',
+            'cl_amount',
+            'kq_amount',
+            'zp_amount',
+            'gg_amount',
+            'kd_amount',
+            'xsqt_amount',
+            'ry_amount',
+            'sb_amount',
+            'amount1',
+            'sj_amount',
+            'amount2',
+            'total'
+        ];
+
+        // 3. 构建 channel => data 的映射,方便查找
+        $channelDataMap = [];
+        foreach ($result as $item) {
+            $channelDataMap[$item['channel']] = [
+                'monthly' => $item['monthly'],
+                'cumulative' => $item['cumulative']
+            ];
+        }
+
+        // 4. 按字段逐行构建 $data
+        $data = [];
+        foreach ($fieldOrder as $field) {
+            $row = [];
+            $m_fin = $c_fin = 0;
+            foreach ($title as $channel) {
+                $m = $channelDataMap[$channel]['monthly'][$field] ?? '0.00000';
+                $c = $channelDataMap[$channel]['cumulative'][$field] ?? '0.00000';
+                $row[] = $m;
+                $row[] = $c;
+                $m_fin = bcadd($m_fin, $m,2);
+                $c_fin = bcadd($c_fin, $c,2);
+            }
+            $row[] = $m_fin;
+            $row[] = $c_fin;
+            $data[] = $row;
+        }
+        $title[] = '合计';
+
+        $export = new ExportOrder2($data,'jc',[]);
+        $export->mergeStart = 'D';
+
+        $numChannels = count($title);
+        $totalColumns = $numChannels * 2;
+        $endColumnIndex = 3 + $totalColumns - 1; // D is index 3
+        $export->mergeEnd = $this->numberToExcelColumn($endColumnIndex);
+//        $export->mergeEnd = 'AC';
+        $export->mergeTitles = $title;
+
+        if(empty($file_name)) $file_name = self::$filename . "_". date("Y-m-d") . "_". rand(1000,9999);
+        $filename =  $file_name . '.' . 'xlsx';
+        $bool = Excel::store($export,"/public/export/{$filename}", null, 'Xlsx', []);
+
+        return [true, $filename];
+    }
+
+    public function twl($ergs,$user){
+        $service = new StatisticsService();
+        $result = $service->getChannelReportRoad($ergs);
+        if(empty($result)) return [false, '暂无数据'];
+
+        $title_type = array_column($result,'channel');
+        if(empty($title_type)) return [false, '暂无数据'];
+        $title = [];
+        foreach ($title_type as $value){
+            $title[] = ItemReportRoad::$order_type[$value];
+        }
+
+        // 2. 字段顺序(22 个)
+        $fieldOrder = [
+            'receipt_amount',
+            'cost',
+            'profit',
+            'settle_amount',
+            'gl_amount',
+            'wl_amount',
+            'ht_amount',
+            'zk_amount',
+            'cx_amount',
+            'tg_amount',
+            'cl_amount',
+            'kq_amount',
+            'zp_amount',
+            'gg_amount',
+            'kd_amount',
+            'xsqt_amount',
+            'ry_amount',
+            'sb_amount',
+            'amount1',
+            'sj_amount',
+            'amount2',
+            'total'
+        ];
+
+        // 3. 构建 channel => data 的映射,方便查找
+        $channelDataMap = [];
+        foreach ($result as $item) {
+            $channelDataMap[$item['channel']] = [
+                'monthly' => $item['monthly'],
+                'cumulative' => $item['cumulative']
+            ];
+        }
+
+        // 4. 按字段逐行构建 $data
+        $data = [];
+        foreach ($fieldOrder as $field) {
+            $row = [];
+            $m_fin = $c_fin = 0;
+            foreach ($title_type as $channel) {
+                $m = $channelDataMap[$channel]['monthly'][$field] ?? '0.00000';
+                $c = $channelDataMap[$channel]['cumulative'][$field] ?? '0.00000';
+                $row[] = $m;
+                $row[] = $c;
+                $m_fin = bcadd($m_fin, $m,2);
+                $c_fin = bcadd($c_fin, $c,2);
+            }
+            $row[] = $m_fin;
+            $row[] = $c_fin;
+            $data[] = $row;
+        }
+        $title[] = '合计';
+
+        $export = new ExportOrder2($data,'jc2',[]);
+        $export->mergeStart = 'D';
+        $numChannels = count($title);
+        $totalColumns = $numChannels * 2;
+        $endColumnIndex = 3 + $totalColumns - 1; // D is index 3
+        $export->mergeEnd = $this->numberToExcelColumn($endColumnIndex);
+        $export->mergeTitles = $title;
+
+        if(empty($file_name)) $file_name = self::$filename . "_". date("Y-m-d") . "_". rand(1000,9999);
+        $filename =  $file_name . '.' . 'xlsx';
+        $bool = Excel::store($export,"/public/export/{$filename}", null, 'Xlsx', []);
+
+        return [true, $filename];
+    }
+
+    // 辅助方法(可放在同一个类中)
+    private function numberToExcelColumn(int $num): string
+    {
+        $column = '';
+        while ($num >= 0) {
+            $remainder = $num % 26;
+            $column = chr(65 + $remainder) . $column;
+            $num = floor($num / 26) - 1;
+        }
+        return $column;
+    }
+
     public function saveExportData($data, $headers, $type = 'default',$file_name = ''){
     public function saveExportData($data, $headers, $type = 'default',$file_name = ''){
         if(empty($file_name)) $file_name = self::$filename . "_". date("Y-m-d") . "_". rand(1000,9999);
         if(empty($file_name)) $file_name = self::$filename . "_". date("Y-m-d") . "_". rand(1000,9999);
         $filename =  $file_name . '.' . 'xlsx';
         $filename =  $file_name . '.' . 'xlsx';

+ 16 - 0
app/Service/Service.php

@@ -730,6 +730,22 @@ class Service
         return [true, ''];
         return [true, ''];
     }
     }
 
 
+    function isWithinMonths($timestamp1, $timestamp2, $months = 3) {
+        $start = (new \DateTime())->setTimestamp($timestamp1);
+        $end   = (new \DateTime())->setTimestamp($timestamp2);
+
+        // 允许交换日期顺序
+        if ($start > $end) {
+            [$start, $end] = [$end, $start];
+        }
+
+        $limit = clone $start;
+        $limit->modify("+{$months} months");
+
+        return [$end <= $limit, $end <= $limit ? '' : "时间区间须在{$months}个月内"];
+    }
+
+
     public function countTotal($rows, $config = [])
     public function countTotal($rows, $config = [])
     {
     {
         if (empty($rows)) return [];
         if (empty($rows)) return [];

+ 259 - 0
app/Service/StatisticsService.php

@@ -5,6 +5,8 @@ namespace App\Service;
 use App\Model\EmployeeIndex;
 use App\Model\EmployeeIndex;
 use App\Model\FreightFee;
 use App\Model\FreightFee;
 use App\Model\GiveOut;
 use App\Model\GiveOut;
+use App\Model\ItemReport;
+use App\Model\ItemReportRoad;
 use App\Model\RevenueCost;
 use App\Model\RevenueCost;
 use App\Model\RevenueCostTotal;
 use App\Model\RevenueCostTotal;
 use App\Model\SalaryEmployee;
 use App\Model\SalaryEmployee;
@@ -843,4 +845,261 @@ class StatisticsService extends Service
 
 
         return $value;
         return $value;
     }
     }
+
+    public function statisticsItem($data, $user)
+    {
+        $model = $this->statisticsItemCommon($data, $user);
+        $list = $this->limit($model,'',$data);
+        $list = $this->statisticsItemFillData($list, $user,$data);
+
+        $list['count'] = $this->countTotal($list['data'], $user['header_default']);
+
+        return [true, $list];
+    }
+
+    public function statisticsItemCommon($data,$user, $field = []){
+        $model = ItemReport::select('*')
+            ->orderby('id', 'desc');
+
+        if(! empty($data['order_time'])){
+            $time = strtotime($data['order_time'] . "-01");
+            $model->where('time', $time);
+        }
+
+        return $model;
+    }
+
+    public function statisticsItemFillData($data, $user, $ergs){
+        if(empty($data['data'])) return $data;
+
+        foreach ($data['data'] as $key => $value) {
+            $time = $value['time'] ? date('Y-m',$value['time']) : '';
+            $data['data'][$key]['time'] = $time;
+        }
+
+        return $data;
+    }
+
+    public function statisticsRoad($data, $user)
+    {
+        $model = $this->statisticsRoadCommon($data, $user);
+        $list = $this->limit($model,'',$data);
+        $list = $this->statisticsRoadFillData($list, $user,$data);
+
+        $list['count'] = $this->countTotal($list['data'], $user['header_default']);
+
+        return [true, $list];
+    }
+
+    public function statisticsRoadCommon($data,$user, $field = []){
+        $model = ItemReportRoad::select('*')
+            ->orderby('id', 'desc');
+
+        if(! empty($data['order_time'])){
+            $time = strtotime($data['order_time'] . "-01");
+            $model->where('time', $time);
+        }
+
+        return $model;
+    }
+
+    public function statisticsRoadFillData($data, $user, $ergs){
+        if(empty($data['data'])) return $data;
+
+        foreach ($data['data'] as $key => $value) {
+            $time = $value['time'] ? date('Y-m',$value['time']) : '';
+            $data['data'][$key]['time'] = $time;
+            $data['data'][$key]['type_title'] = ItemReportRoad::$order_type[$value['type']] ?? '';
+        }
+
+        return $data;
+    }
+
+    public function getChannelReport($data)
+    {
+        if(empty($data['order_time'])) return [false, '年月不能为空'];
+        $yearMonth = $data['order_time'];
+
+        // 1. 解析输入
+        [$year, $month] = explode('-', $yearMonth);
+        $year = (int)$year;
+        $month = (int)$month;
+
+        $currentMonthTs = mktime(0, 0, 0, $month, 1, $year);
+        $yearStartTs = mktime(0, 0, 0, 1, 1, $year);
+
+        // 2. 获取所有相关数据
+        $records = DB::table('item_report')
+            ->whereBetween('time', [$yearStartTs, $currentMonthTs])
+            ->whereNotNull('channel_details') // 可选:排除空渠道
+            ->get();
+
+        // 3. 初始化结果容器
+        $channels = [];
+
+        foreach ($records as $record) {
+            $channel = $record->channel_details;
+
+            if (!isset($channels[$channel])) {
+                // 初始化当月和累计
+                $channels[$channel] = [
+                    'monthly' => array_fill_keys($this->getAmountFieldNames(), 0),
+                    'cumulative' => array_fill_keys($this->getAmountFieldNames(), 0),
+                ];
+            }
+
+            // 判断是否是当月
+            $isCurrentMonth = ($record->time == $currentMonthTs);
+
+            // 累加所有字段(包括 NULL 处理)
+            foreach ($this->getAmountFieldNames() as $field) {
+                $value = $record->$field ?? 0;
+                $channels[$channel]['cumulative'][$field] += $value;
+                if ($isCurrentMonth) {
+                    $channels[$channel]['monthly'][$field] += $value;
+                }
+            }
+        }
+
+        // 4. 插入 amount1 和 amount2,并格式化输出
+        $finalResult = [];
+        foreach ($channels as $channel => $data) {
+            $finalResult[] = [
+                'channel' => $channel,
+                'monthly' => $this->injectAmount1And2($data['monthly']),
+                'cumulative' => $this->injectAmount1And2($data['cumulative']),
+            ];
+        }
+
+        return $finalResult;
+    }
+
+    private function getAmountFieldNames()
+    {
+        return [
+            'receipt_amount',
+            'cost',
+            'profit',
+            'settle_amount',
+            'gl_amount',
+            'wl_amount',
+            'ht_amount',
+            'zk_amount',
+            'cx_amount',
+            'tg_amount',
+            'cl_amount',
+            'kq_amount',
+            'zp_amount',
+            'gg_amount',
+            'kd_amount',
+            'xsqt_amount',
+            'ry_amount',
+            'sb_amount',
+            'sj_amount', // 注意:包含 sj_amount
+        ];
+    }
+
+    private function injectAmount1And2($data)
+    {
+        $result = [];
+        $expenseFields = [
+            'settle_amount',
+            'gl_amount',
+            'wl_amount',
+            'ht_amount',
+            'zk_amount',
+            'cx_amount',
+            'tg_amount',
+            'cl_amount',
+            'kq_amount',
+            'zp_amount',
+            'gg_amount',
+            'kd_amount',
+            'xsqt_amount',
+            'ry_amount',
+            'sb_amount',
+            'sj_amount', // 注意:sj_amount 要包含
+        ];
+
+        // 第一步:按顺序构建带 amount1/2 的数组
+        foreach ($data as $key => $value) {
+            if ($key === 'sj_amount') {
+                $result['amount1'] = '0.00';
+                $result['sj_amount'] = number_format($value, 2, '.', '');
+                $result['amount2'] = '0.00';
+            } else {
+                $result[$key] = number_format($value, 2, '.', '');
+            }
+        }
+
+        // 第二步:计算 total(仅 expenseFields 中的字段,从 $data 原始值取,避免字符串运算)
+        $total = 0;
+        foreach ($expenseFields as $field) {
+            $val = $data[$field] ?? 0;
+            $total += is_numeric($val) ? (float)$val : 0;
+        }
+
+        $result['total'] = number_format($total, 2, '.', '');
+
+        return $result;
+    }
+
+    public function getChannelReportRoad($data)
+    {
+        if(empty($data['order_time'])) return [false, '年月不能为空'];
+        $yearMonth = $data['order_time'];
+
+        // 1. 解析输入
+        [$year, $month] = explode('-', $yearMonth);
+        $year = (int)$year;
+        $month = (int)$month;
+
+        $currentMonthTs = mktime(0, 0, 0, $month, 1, $year);
+        $yearStartTs = mktime(0, 0, 0, 1, 1, $year);
+
+        // 2. 获取所有相关数据
+        $records = DB::table('item_report_road')
+            ->whereBetween('time', [$yearStartTs, $currentMonthTs])
+            ->whereNotNull('employee_title_type') //
+            ->get();
+
+        // 3. 初始化结果容器
+        $channels = [];
+
+        foreach ($records as $record) {
+            $channel = $record->employee_title_type;
+
+            if (!isset($channels[$channel])) {
+                // 初始化当月和累计
+                $channels[$channel] = [
+                    'monthly' => array_fill_keys($this->getAmountFieldNames(), 0),
+                    'cumulative' => array_fill_keys($this->getAmountFieldNames(), 0),
+                ];
+            }
+
+            // 判断是否是当月
+            $isCurrentMonth = ($record->time == $currentMonthTs);
+
+            // 累加所有字段(包括 NULL 处理)
+            foreach ($this->getAmountFieldNames() as $field) {
+                $value = $record->$field ?? 0;
+                $channels[$channel]['cumulative'][$field] += $value;
+                if ($isCurrentMonth) {
+                    $channels[$channel]['monthly'][$field] += $value;
+                }
+            }
+        }
+
+        // 4. 插入 amount1 和 amount2,并格式化输出
+        $finalResult = [];
+        foreach ($channels as $channel => $data) {
+            $finalResult[] = [
+                'channel' => $channel,
+                'monthly' => $this->injectAmount1And2($data['monthly']),
+                'cumulative' => $this->injectAmount1And2($data['cumulative']),
+            ];
+        }
+
+        return $finalResult;
+    }
 }
 }

+ 1175 - 3
app/Service/TPlusServerService.php

@@ -9,6 +9,8 @@ use App\Model\EmployeeDepartPermission;
 use App\Model\EmployeeIndex;
 use App\Model\EmployeeIndex;
 use App\Model\Freight;
 use App\Model\Freight;
 use App\Model\FreightFee;
 use App\Model\FreightFee;
+use App\Model\ItemReport;
+use App\Model\ItemReportRoad;
 use App\Model\Product;
 use App\Model\Product;
 use App\Model\RevenueCost;
 use App\Model\RevenueCost;
 use App\Model\RevenueCostTotal;
 use App\Model\RevenueCostTotal;
@@ -52,6 +54,8 @@ class TPlusServerService extends Service
     private $table = "tmp_revenue_cost_data";
     private $table = "tmp_revenue_cost_data";
     private $table_2 = "tmp_salary_employee";
     private $table_2 = "tmp_salary_employee";
     private $table_3 = "tmp_freight_fee";
     private $table_3 = "tmp_freight_fee";
+    private $table_4 = "tmp_item_road";
+    private $table_5 = "tmp_item_cost";
 
 
     /**
     /**
      * 同步人员部门
      * 同步人员部门
@@ -173,7 +177,7 @@ class TPlusServerService extends Service
         if(empty($data['crt_time'][0]) || empty($data['crt_time'][1])) return [false, '同步时间不能为空'];
         if(empty($data['crt_time'][0]) || empty($data['crt_time'][1])) return [false, '同步时间不能为空'];
         list($start_time, $end_time) = $this->changeDateToTimeStampAboutRange($data['crt_time'],false);
         list($start_time, $end_time) = $this->changeDateToTimeStampAboutRange($data['crt_time'],false);
         if ($start_time === null || $end_time === null || $start_time > $end_time) return [false, "同步时间:时间区间无效"];
         if ($start_time === null || $end_time === null || $start_time > $end_time) return [false, "同步时间:时间区间无效"];
-        list($bool, $bool_msg) = $this->isOverThreeMonths($start_time, $end_time);
+        list($bool, $bool_msg) = $this->isWithinMonths($start_time, $end_time);
         if(! $bool) return [false, $bool_msg];
         if(! $bool) return [false, $bool_msg];
         $data['start_timeStamp'] = $start_time;
         $data['start_timeStamp'] = $start_time;
         $data['end_timeStamp'] = $end_time;
         $data['end_timeStamp'] = $end_time;
@@ -796,13 +800,20 @@ class TPlusServerService extends Service
             if (Schema::hasTable($this->table_2)) DB::table($this->table_2)->truncate();
             if (Schema::hasTable($this->table_2)) DB::table($this->table_2)->truncate();
         }elseif ($type == 3){
         }elseif ($type == 3){
             if (Schema::hasTable($this->table_3)) DB::table($this->table_3)->truncate();
             if (Schema::hasTable($this->table_3)) DB::table($this->table_3)->truncate();
+        }elseif ($type == 4){
+            if (Schema::hasTable($this->table_4)) DB::table($this->table_4)->truncate();
+            if (Schema::hasTable($this->table_5)) DB::table($this->table_5)->truncate();
         }
         }
+//        elseif ($type == 5){
+//            if (Schema::hasTable($this->table_5)) DB::table($this->table_5)->truncate();
+//        }
     }
     }
 
 
     public function delTableKey($type = 1){
     public function delTableKey($type = 1){
         $key = $this->table;
         $key = $this->table;
         if($type == 2) $key = $this->table_2;
         if($type == 2) $key = $this->table_2;
         if($type == 3) $key = $this->table_3;
         if($type == 3) $key = $this->table_3;
+        if($type == 4) $key = $this->table_4;
         $this->dellimitingSendRequest($key);
         $this->dellimitingSendRequest($key);
     }
     }
 
 
@@ -810,7 +821,7 @@ class TPlusServerService extends Service
         if(empty($data['crt_time'][0]) || empty($data['crt_time'][1])) return [false, '同步时间不能为空'];
         if(empty($data['crt_time'][0]) || empty($data['crt_time'][1])) return [false, '同步时间不能为空'];
         list($start_time, $end_time) = $this->changeDateToTimeStampAboutRange($data['crt_time'],false);
         list($start_time, $end_time) = $this->changeDateToTimeStampAboutRange($data['crt_time'],false);
         if ($start_time === null || $end_time === null || $start_time > $end_time) return [false, "同步时间:时间区间无效"];
         if ($start_time === null || $end_time === null || $start_time > $end_time) return [false, "同步时间:时间区间无效"];
-        list($bool, $bool_msg) = $this->isOverThreeMonths($start_time, $end_time);
+        list($bool, $bool_msg) = $this->isWithinMonths($start_time, $end_time);
         if(! $bool) return [false, $bool_msg];
         if(! $bool) return [false, $bool_msg];
         $data['start_timeStamp'] = $start_time;
         $data['start_timeStamp'] = $start_time;
         $data['end_timeStamp'] = $end_time;
         $data['end_timeStamp'] = $end_time;
@@ -1039,7 +1050,7 @@ class TPlusServerService extends Service
         if(empty($data['crt_time'][0]) || empty($data['crt_time'][1])) return [false, '同步时间不能为空'];
         if(empty($data['crt_time'][0]) || empty($data['crt_time'][1])) return [false, '同步时间不能为空'];
         list($start_time, $end_time) = $this->changeDateToTimeStampAboutRange($data['crt_time'],false);
         list($start_time, $end_time) = $this->changeDateToTimeStampAboutRange($data['crt_time'],false);
         if ($start_time === null || $end_time === null || $start_time > $end_time) return [false, "同步时间:时间区间无效"];
         if ($start_time === null || $end_time === null || $start_time > $end_time) return [false, "同步时间:时间区间无效"];
-        list($bool, $bool_msg) = $this->isOverThreeMonths($start_time, $end_time);
+        list($bool, $bool_msg) = $this->isWithinMonths($start_time, $end_time);
         if(! $bool) return [false, $bool_msg];
         if(! $bool) return [false, $bool_msg];
         $data['start_timeStamp'] = $start_time;
         $data['start_timeStamp'] = $start_time;
         $data['end_timeStamp'] = $end_time;
         $data['end_timeStamp'] = $end_time;
@@ -1396,4 +1407,1165 @@ class TPlusServerService extends Service
 
 
         return [true, ''];
         return [true, ''];
     }
     }
+
+    public function synItemRoad($data, $user){
+        if(empty($data['crt_time'][0]) || empty($data['crt_time'][1])) return [false, '同步时间不能为空'];
+        $start_time = strtotime($data['crt_time'][0] . "-01");
+        $end_time = strtotime(date('Y-m-t', strtotime($data['crt_time'][1] . '-01')) . ' 00:00:00');
+        list($bool, $bool_msg) = $this->isWithinMonths($start_time, $end_time);
+        if(! $bool) return [false, $bool_msg];
+
+        $data['start_timeStamp'] = $start_time;
+        $data['end_timeStamp'] = $end_time;
+        $start = date('Y-m-d H:i:s.000', $start_time);
+        $end = date('Y-m-d H:i:s.000', $end_time);
+        $data['start_time'] = $start;
+        $data['end_time'] = $end;
+        $data['operation_time'] = time();
+        $data['type'] = 7;
+
+//        list($status, $msg) = $this->synItemRoadMy($data, $user);dd($status, $msg);
+
+        list($status,$msg) = $this->limitingSendRequest($this->table_4);
+        if(! $status) return [false, '统计同步正在后台运行,请稍后'];
+
+        //队列
+        ProcessDataJob::dispatch($data, $user, 4)->onQueue(RevenueCost::job4);
+
+        return [true, '统计同步已进入后台任务'];
+    }
+
+    private function createTmpTable4(){
+        $table = $this->table_4;
+        if (! Schema::hasTable($table)) {
+            Schema::create($table, function (Blueprint $table) {
+                $table->bigIncrements('id');
+
+                // 订单基础信息
+                $table->bigInteger('order_id')->default(0)->comment('收款单ID(rp.ID)');
+                $table->string('order_number')->default('')->comment('收款单编号(rp.code)');
+                $table->integer('order_time')->default(0)->comment('单据时间(时间戳)');
+                $table->integer('order_state')->default(0)->comment('订单状态');
+                $table->integer('order_type')->default(0)->comment('订单分类标识');
+
+                // 渠道/分销相关
+                $table->string('channel_finance')->default('')->comment('渠道财务(rp.pubuserdefnvc11)');
+                $table->string('channel_details')->default('')->comment('渠道明细(rp.pubuserdefnvc12)');
+                $table->string('channel_details_fp')->default('')->comment('发票渠道明细(si.pubuserdefnvc12)');
+
+                // 客户信息
+                $table->string('customer_code')->default('')->comment('结算客户编码');
+                $table->string('customer_title')->default('')->comment('结算客户名称');
+
+                // 人员信息
+                $table->bigInteger('employee_id_1')->default(0)->comment('业务员ID');
+                $table->string('employee_id_1_title')->default('')->comment('业务员名称');
+                $table->bigInteger('employee_id_2')->default(0)->comment('上级管理人员ID');
+                $table->string('employee_id_2_title')->default('')->comment('上级管理人员名称');
+
+                // 产品信息 (仅限 idvouchertype = 20)
+                $table->string('product_code')->default('')->comment('产品编码');
+                $table->string('product_title')->default('')->comment('产品名称');
+                $table->decimal('quantity', 14, 3)->default(0)->comment('发票数量');
+
+                // 收入/价格相关
+                $table->decimal('price_1', 14, 4)->default(0)->comment('含税单价(taxPrice)');
+                $table->decimal('price_1_total', 14, 2)->default(0)->comment('含税金额(taxAmount)');
+                $table->decimal('payment_amount', 14, 2)->default(0)->comment('明细行的收款金额');
+
+                // 成本相关 (计算得出)
+                $table->decimal('price_4', 14, 4)->default(0)->comment('业务成本单价');
+                $table->decimal('price_4_total', 14, 2)->default(0)->comment('业务成本总额');
+
+                // 关联追溯字段
+                $table->bigInteger('id_detail')->default(0)->comment('收款明细ID(rp_b.ID)');
+                $table->bigInteger('id_detail_upstream')->default(0)->comment('上游单据明细ID(voucherDetailID)');
+                $table->string('order_number_upstream')->default('')->comment('上游单据编号(voucherCode)');
+                $table->integer('voucher_type')->default(0)->comment('单据类型(idvouchertype)');
+                $table->string('is_kh')->default('0')->comment('是否计入考核(sd.priuserdefnvc4)');
+            });
+        }
+    }
+
+    private function createTmpTable5(){
+        $table = $this->table_5;
+        if (! Schema::hasTable($table)) {
+            Schema::create($table, function (Blueprint $table) {
+                $table->bigIncrements('id');
+
+                // 基础信息
+                $table->bigInteger('order_id')->default(0)->comment('费用单ID(cs.ID)');
+                $table->string('order_number')->default('')->comment('费用单编号(cs.code)');
+                $table->integer('order_time')->default(0)->comment('单据时间(时间戳)');
+                $table->integer('order_type')->default(0)->comment('订单分类标识(1 费用单 2 其他出库单)');
+                $table->string('ck_type')->default("")->comment('出库单出库类别');
+                $table->integer('employee_id_1')->default(0)->comment('业务员id');
+
+                // 维度信息
+                $table->string('channel_details')->default('')->comment('渠道明细(cs.pubuserdefnvc12)');
+                $table->string('fy_code')->default('')->comment('费用项目编码(ae.code)');
+
+                // 金额信息
+                $table->decimal('taxamount', 14, 2)->default(0)->comment('含税金额(cs_b.taxamount)');
+
+                // 追溯信息
+                $table->bigInteger('id_detail')->default(0)->comment('费用明细行ID(cs_b.ID)');
+            });
+        }
+    }
+
+    public function synItemRoadMy($data, $user){
+        //收入
+        list($status, $msg) = $this->synReceipt($data,$user);
+        if(! $status) return [false, $msg];
+
+        //成本
+        list($status, $msg) = $this->synSaleCost($data,$user);
+        if(! $status) return [false, $msg];
+
+        //汇总项目报表
+        list($status, $msg) = $this->saveResult($data,$user);
+        if(! $status) return [false, $msg];
+
+        //清理临时表 如果内容不为空
+        $this->clearTmpTable(4);
+        //释放redis
+        $this->delTableKey(4);
+
+        return [true, ''];
+    }
+
+    private function synReceipt($data, $user){
+        //创建临时表 如果不存在
+        $this->createTmpTable4();
+        //清理临时表 如果内容不为空
+        $this->clearTmpTable(4);
+
+        try{
+            $table = $this->table_4;
+            $limit = 500;
+            $lastId = 0;
+
+            do {
+                $rows = $this->databaseService->table('ARAP_ReceivePayment_b as rp_b')
+                    ->join('ARAP_ReceivePayment as rp', 'rp_b.idArapReceivePaymentDTO', '=', 'rp.ID')
+                    // 发票子表关联(仅限 idvouchertype = 20)
+                    ->leftJoin('SA_SaleInvoice_b as si_b', function ($join) {
+                        $join->on('rp_b.voucherDetailID', '=', 'si_b.ID')
+                            ->where('rp_b.idvouchertype', '=', 20);
+                    })
+                    ->leftJoin('SA_SaleInvoice as si', function ($join) {
+                        $join->on('si_b.idSaleInvoiceDTO', '=', 'si.ID')
+                            ->where('rp_b.idvouchertype', '=', 20);
+                    })
+                    ->leftJoin('SA_SaleDelivery_b as sd_b', function ($join) {
+                        $join->on('si_b.sourceVoucherDetailId', '=', 'sd_b.ID')
+                            ->where('rp_b.idvouchertype', '=', 20);
+                    })
+                    ->leftJoin('SA_SaleDelivery as sd', function ($join) {
+                        $join->on('sd_b.idSaleDeliveryDTO', '=', 'sd.ID')
+                            ->where('rp_b.idvouchertype', '=', 20);
+                    })
+                    ->leftJoin('AA_Inventory as it', 'sd_b.idinventory', '=', 'it.ID')
+                    ->leftJoin('AA_Partner as pn', 'si.idsettlecustomer', '=', 'pn.ID') //结算客户
+                    ->leftJoin('AA_Person as ps', 'rp.idperson', '=', 'ps.ID')
+                    ->leftJoin('AA_Person as ps2', 'pn.idsaleman', '=', 'ps2.ID') // 结算客户的上级管理人员
+                    ->where('rp.voucherdate','>=',$data['start_time'])
+                    ->where('rp.voucherdate','<=',$data['end_time'])
+//                    ->where('priuserdefnvc4','=', 1) //.. 不计入考核 todo
+                    ->where('rp.isReceiveFlag','=', 1)
+                    ->where('rp_b.ID', '>', $lastId)
+                    ->orderBy('rp_b.ID')
+                    ->limit($limit)
+                    ->selectRaw("
+                        COALESCE(rp.ID, 0) as order_id,
+                        COALESCE(rp.code, '') as order_number,
+                        rp.voucherdate as order_time,
+                        rp.voucherstate as order_state,
+                        COALESCE(rp.pubuserdefnvc11, '') as channel_finance,
+                        COALESCE(rp.pubuserdefnvc12, '') as channel_details,
+                        COALESCE(si.pubuserdefnvc12, '') as channel_details_fp,
+                        COALESCE(pn.code, '') as customer_code,
+                        COALESCE(pn.name, '') as customer_title,
+                        CASE WHEN rp_b.idvouchertype = 20 THEN COALESCE(it.code, '') ELSE '' END as product_code,
+                        CASE WHEN rp_b.idvouchertype = 20 THEN COALESCE(it.name, '') ELSE '' END as product_title,
+                        COALESCE(rp.idperson, 0) as employee_id_1,
+                        COALESCE(ps.name, '') as employee_id_1_title,
+                        COALESCE(pn.idsaleman, 0) as employee_id_2,
+                        COALESCE(ps2.name, '') as employee_id_2_title,
+                        CASE WHEN rp_b.idvouchertype = 20 THEN COALESCE(si_b.quantity, 0) ELSE 0 END as quantity,
+                        CASE WHEN rp_b.idvouchertype = 20 THEN COALESCE(si_b.taxPrice, 0) ELSE 0 END as price_1,
+                        CASE WHEN rp_b.idvouchertype = 20 THEN COALESCE(si_b.taxAmount, 0) ELSE 0 END as price_1_total,
+                        COALESCE(rp_b.origCurrentAmount, 0) as payment_amount,
+                        COALESCE(rp_b.ID, 0) as id_detail,
+                        COALESCE(rp_b.voucherDetailID, 0) as id_detail_upstream,
+                        COALESCE(rp_b.voucherCode, '') as order_number_upstream,
+                        rp_b.idvouchertype as voucher_type,
+                        CASE WHEN rp_b.idvouchertype = 20 THEN COALESCE(sd.priuserdefnvc4, '') ELSE '' END as is_kh
+                    ")
+                    ->get();
+
+                if ($rows->isEmpty()) break;
+
+                $dataArray = Collect($rows)->map(function ($object) {
+                    return (array)$object;
+                })->toArray();
+
+                //存货档案业务成本
+                $product_map = Product::where('del_time',0)
+                    ->whereIn('code', array_unique(array_column($dataArray,'product_code')))
+                    ->pluck('business_cost','code')
+                    ->all();
+
+                //组织数据
+                foreach ($dataArray as $key => $value){
+                    $p_tmp = $product_map[$value['product_code']] ?? 0;
+                    $dataArray[$key]['order_type'] = RevenueCost::ORDER_THREE;
+                    $dataArray[$key]['order_time'] = strtotime($value['order_time']);
+                    //业务成本单价和金额
+                    $business_cost = $p_tmp ?? 0;
+                    $dataArray[$key]['price_4'] = $business_cost;
+                    $price_4_total = bcmul($business_cost, $value['quantity'],2);
+                    $dataArray[$key]['price_4_total'] = $price_4_total;
+                }
+
+                DB::table($table)->insert($dataArray);
+
+                // 更新 lastId 继续下一批
+                $lastId = end($dataArray)['id_detail'] ?? $lastId;
+
+            } while (count($rows) === $limit);
+
+        }catch (\Throwable $exception){
+            return [false, "收入同步异常" . $exception->getMessage() . "|" . $exception->getLine() . "|" . $exception->getFile()];
+        }
+
+        return [true, ''];
+    }
+
+    private function synSaleCost($data, $user){
+        //创建临时表 如果不存在
+        $this->createTmpTable5();
+        //清理临时表 如果内容不为空
+//        $this->clearTmpTable(5);
+
+        try{
+            $table = $this->table_5;
+            $limit = 500;
+            $lastId = 0;
+
+            do {
+                $rows = $this->databaseService->table('CS_ExpenseVoucher_b as cs_b')
+                    ->join('CS_ExpenseVoucher as cs', 'cs_b.idExpenseVoucherDTO', '=', 'cs.ID')
+                    ->leftJoin('AA_ExpenseItem as ae', 'ae.id', '=', 'cs_b.idexpenseitem')
+                    ->where('cs.voucherdate','>=',$data['start_time'])
+                    ->where('cs.voucherdate','<=',$data['end_time'])
+                    ->where('cs_b.ID', '>', $lastId)
+                    ->orderBy('cs_b.ID')
+                    ->limit($limit)
+                    ->selectRaw("
+                        1 as order_type,
+                        COALESCE(cs.ID, 0) as order_id,
+                        COALESCE(cs.code, '') as order_number,
+                        cs.voucherdate as order_time,
+                        cs.idclerk  as employee_id_1,
+                        COALESCE(cs.pubuserdefnvc12, '') as channel_details,
+                        COALESCE(ae.code, '') as fy_code,
+                        COALESCE(cs_b.taxamount, 0) as taxamount,
+                        COALESCE(cs_b.ID, 0) as id_detail
+                    ")
+                    ->get();
+
+                if ($rows->isEmpty()) break;
+
+                $dataArray = Collect($rows)->map(function ($object) {
+                    return (array)$object;
+                })->toArray();
+
+                foreach ($dataArray as $key => $value){
+                    $dataArray[$key]['order_time'] = strtotime($value['order_time']);
+                }
+
+                DB::table($table)->insert($dataArray);
+
+                // 更新 lastId 继续下一批
+                $lastId = end($dataArray)['id_detail'] ?? $lastId;
+
+            } while (count($rows) === $limit);
+
+        }catch (\Throwable $exception){
+            return [false, "费用同步异常" . $exception->getMessage() . "|" . $exception->getLine() . "|" . $exception->getFile()];
+        }
+
+        try{
+            $table = $this->table_5;
+            $limit = 500;
+            $lastId = 0;
+
+            do {
+                $rows = $this->databaseService->table('ST_RDRecord_b as st_b')
+                    ->join('ST_RDRecord as st', 'st_b.idRDRecordDTO', '=', 'st.ID')
+                    ->where('st.voucherdate','>=',$data['start_time'])
+                    ->where('st.voucherdate','<=',$data['end_time'])
+                    ->where('st.idvouchertype', 30)
+                    ->whereIn('st.idbusitype',[20048,60,20049]) //20049 客情出库 60 赠品出库  20048 员工福利
+                    ->where('st_b.ID', '>', $lastId)
+                    ->orderBy('st_b.ID')
+                    ->limit($limit)
+                    ->selectRaw("
+                        2 as order_type,
+                        COALESCE(st.ID, 0) as order_id,
+                        COALESCE(st.code, '') as order_number,
+                        st.voucherdate as order_time,
+                        st.idclerk  as employee_id_1,
+                        COALESCE(st.pubuserdefnvc12, '') as channel_details,
+                        COALESCE(st_b.amount, 0) as taxamount,
+                        COALESCE(st_b.ID, 0) as id_detail,
+                        st.idbusitype as ck_type
+                    ")->get();
+
+                if ($rows->isEmpty()) break;
+
+                $dataArray = Collect($rows)->map(function ($object) {
+                    return (array)$object;
+                })->toArray();
+
+                foreach ($dataArray as $key => $value){
+                    $dataArray[$key]['order_time'] = strtotime($value['order_time']);
+                }
+
+                DB::table($table)->insert($dataArray);
+
+                // 更新 lastId 继续下一批
+                $lastId = end($dataArray)['id_detail'] ?? $lastId;
+
+            } while (count($rows) === $limit);
+
+        }catch (\Throwable $exception){
+            return [false, "其他出库同步异常" . $exception->getMessage() . "|" . $exception->getLine() . "|" . $exception->getFile()];
+        }
+
+        return [true, ''];
+    }
+
+    private function saveResult($data, $user){
+        // 初始化汇总数组------报表一
+        $paymentSummary = [];
+        $costSummary = [];
+        $costSummary2 = [];
+
+        //报表二
+        $paymentSummary_road = [];
+        $costSummary_road = [];
+        $costSummary2_road = [];
+
+        $map = [
+            2 => [
+                'type' => 1,
+                'channel_details' => ['通路'],
+            ],
+            3 =>  [
+                'type' => 2,
+                'channel_details' => ['通路'],
+            ],
+            23 =>  [
+                'type' => 3,
+                'channel_details' => ['通路'],
+            ],
+            7 => [
+                'type' => 4,
+                'channel_details' => ['通路','大卖场'],
+            ],
+            8 => [
+                'type' => 4,
+                'channel_details' => ['通路','大卖场'],
+            ],
+            48 => [
+                'type' => 4,
+                'channel_details' => ['通路','大卖场'],
+            ],
+        ];
+
+        try{
+            $limit = 500;
+            $lastId = 0;
+            do {
+                // 分批查询:只查 is_kh 不为空的数据
+                $rows = DB::table('tmp_item_road')
+                    ->where('id', '>', $lastId)
+                    ->orderBy('id')
+                    ->limit($limit)
+                    ->select([
+                        'id',
+                        'order_time',
+                        'channel_details',
+                        'payment_amount',
+                        'price_4_total',
+                        'is_kh',
+                        'employee_id_2'
+                    ])
+                    ->get();
+
+                if ($rows->isEmpty()) {
+                    break;
+                }
+
+                // 聚合到汇总数组
+                foreach ($rows as $row) {
+                    $order_time = $row->order_time;
+                    $month = strtotime(date("Y-m-01", $order_time));
+
+                    $channel = $row->channel_details ?? '';
+
+                    $t = $map[$row->employee_id_2] ?? [];
+                    //收款销售收入
+                    if (! $row->is_kh) {
+                        if (!isset($paymentSummary[$month][$channel])) {
+                            $paymentSummary[$month][$channel] = $row->payment_amount;
+                        }else{
+                            $paymentSummary[$month][$channel] = bcadd(
+                                $paymentSummary[$month][$channel],
+                                $row->payment_amount,
+                                3
+                            );
+                        }
+
+                        if(! empty($t)){
+                            $type = $t['type'];
+                            $c_t = $t['channel_details'];
+                            if(in_array($channel, $c_t)){
+                                if (!isset($paymentSummary_road[$month][$type])) {
+                                    $paymentSummary_road[$month][$type] = $row->payment_amount;
+                                }else{
+                                    $paymentSummary_road[$month][$type] = bcadd(
+                                        $paymentSummary_road[$month][$type],
+                                        $row->payment_amount,
+                                        3
+                                    );
+                                }
+                            }
+                        }
+                    }
+
+                    //成本
+                    if (!isset($costSummary[$month][$channel])) {
+                        $costSummary[$month][$channel] = $row->price_4_total;
+                    }else{
+                        $costSummary[$month][$channel] = bcadd(
+                            $costSummary[$month][$channel],
+                            $row->price_4_total,
+                            3
+                        );
+                    }
+
+                    if(! empty($t)){
+                        $type = $t['type'];
+                        $c_t = $t['channel_details'];
+                        if(in_array($channel, $c_t)){
+                            if (!isset($costSummary_road[$month][$type])) {
+                                $costSummary_road[$month][$type] = $row->price_4_total;
+                            }else{
+                                $costSummary_road[$month][$type] = bcadd(
+                                    $costSummary_road[$month][$type],
+                                    $row->price_4_total,
+                                    3
+                                );
+                            }
+                        }
+                    }
+                }
+
+                // 更新 lastId
+                $lastId = $rows->last()->id;
+
+            } while ($rows->count() === $limit);
+
+            $limit = 500;
+            $lastId = 0;
+            do {
+                $rows = DB::table('tmp_item_cost')
+                    ->where('id', '>', $lastId)
+                    ->orderBy('id')
+                    ->limit($limit)
+                    ->select([
+                        'id',
+                        'order_time',
+                        'channel_details',
+                        'ck_type',
+                        'fy_code',
+                        'taxamount',
+                        'employee_id_1'
+                    ])
+                    ->get();
+
+                if ($rows->isEmpty()) {
+                    break;
+                }
+
+                foreach ($rows as $row) {
+                    $order_time = $row->order_time;
+                    $month = strtotime(date("Y-m-01", $order_time));
+                    $channel = $row->channel_details ?? '';
+                    $amount = $row->taxamount;
+
+                    $t = $map[$row->employee_id_1] ?? [];
+
+                    if($row->fy_code == 7004){
+                        if (! isset($costSummary2[$month][$channel]['settle_amount'])) {
+                            $costSummary2[$month][$channel]['settle_amount'] = $amount;
+                        }else{
+                            $costSummary2[$month][$channel]['settle_amount'] = bcadd(
+                                $costSummary2[$month][$channel]['settle_amount'],
+                                $amount,
+                                3
+                            );
+                        }
+
+                        if(! empty($t)){
+                            $type = $t['type'];
+                            $c_t = $t['channel_details'];
+                            if(in_array($channel, $c_t)){
+                                if (!isset($costSummary2_road[$month][$type])) {
+                                    $costSummary2_road[$month][$type]['settle_amount'] = $amount;
+                                }else{
+                                    $costSummary2_road[$month][$type]['settle_amount'] = bcadd(
+                                        $costSummary2_road[$month][$type]['settle_amount'],
+                                        $amount,
+                                        3
+                                    );
+                                }
+                            }
+                        }
+                    }
+
+                    $gl_amount = bcmul($amount, 0.05,3);
+                    if (! isset($costSummary2[$month][$channel]['gl_amount'])) {
+                        $costSummary2[$month][$channel]['gl_amount'] = $gl_amount;
+                    }else{
+                        $costSummary2[$month][$channel]['gl_amount'] = bcadd(
+                            $costSummary2[$month][$channel]['gl_amount'],
+                            $gl_amount,
+                            3
+                        );
+                    }
+
+                    if(! empty($t)){
+                        $type = $t['type'];
+                        $c_t = $t['channel_details'];
+                        if(in_array($channel, $c_t)){
+                            if (!isset($costSummary2_road[$month][$type]['gl_amount'])) {
+                                $costSummary2_road[$month][$type]['gl_amount'] = $amount;
+                            }else{
+                                $costSummary2_road[$month][$type]['gl_amount'] = bcadd(
+                                    $costSummary2_road[$month][$type]['gl_amount'],
+                                    $amount,
+                                    3
+                                );
+                            }
+                        }
+                    }
+
+                    $wl_amount = bcmul($amount, 2.3,3);
+                    if (! isset($costSummary2[$month][$channel]['wl_amount'])) {
+                        $costSummary2[$month][$channel]['wl_amount'] = $wl_amount;
+                    }else{
+                        $costSummary2[$month][$channel]['wl_amount'] = bcadd(
+                            $costSummary2[$month][$channel]['wl_amount'],
+                            $wl_amount,
+                            3
+                        );
+                    }
+
+                    if(! empty($t)){
+                        $type = $t['type'];
+                        $c_t = $t['channel_details'];
+                        if(in_array($channel, $c_t)){
+                            if (!isset($costSummary2_road[$month][$type]['wl_amount'])) {
+                                $costSummary2_road[$month][$type]['wl_amount'] = $amount;
+                            }else{
+                                $costSummary2_road[$month][$type]['wl_amount'] = bcadd(
+                                    $costSummary2_road[$month][$type]['wl_amount'],
+                                    $amount,
+                                    3
+                                );
+                            }
+                        }
+                    }
+
+                    if($row->fy_code == 7005){
+                        if (! isset($costSummary2[$month][$channel]['ht_amount'])) {
+                            $costSummary2[$month][$channel]['ht_amount'] = $amount;
+                        }else{
+                            $costSummary2[$month][$channel]['ht_amount'] = bcadd(
+                                $costSummary2[$month][$channel]['ht_amount'],
+                                $amount,
+                                3
+                            );
+                        }
+
+                        if(! empty($t)){
+                            $type = $t['type'];
+                            $c_t = $t['channel_details'];
+                            if(in_array($channel, $c_t)){
+                                if (!isset($costSummary2_road[$month][$type]['ht_amount'])) {
+                                    $costSummary2_road[$month][$type]['ht_amount'] = $amount;
+                                }else{
+                                    $costSummary2_road[$month][$type]['ht_amount'] = bcadd(
+                                        $costSummary2_road[$month][$type]['ht_amount'],
+                                        $amount,
+                                        3
+                                    );
+                                }
+                            }
+                        }
+                    }
+
+                    if($row->fy_code == 7006){
+                        if (! isset($costSummary2[$month][$channel]['zk_amount'])) {
+                            $costSummary2[$month][$channel]['zk_amount'] = $amount;
+                        }else{
+                            $costSummary2[$month][$channel]['zk_amount'] = bcadd(
+                                $costSummary2[$month][$channel]['zk_amount'],
+                                $amount,
+                                3
+                            );
+                        }
+
+                        if(! empty($t)){
+                            $type = $t['type'];
+                            $c_t = $t['channel_details'];
+                            if(in_array($channel, $c_t)){
+                                if (!isset($costSummary2_road[$month][$type]['zk_amount'])) {
+                                    $costSummary2_road[$month][$type]['zk_amount'] = $amount;
+                                }else{
+                                    $costSummary2_road[$month][$type]['zk_amount'] = bcadd(
+                                        $costSummary2_road[$month][$type]['zk_amount'],
+                                        $amount,
+                                        3
+                                    );
+                                }
+                            }
+                        }
+                    }
+
+                    if($row->fy_code == 7007){
+                        if (! isset($costSummary2[$month][$channel]['cx_amount'])) {
+                            $costSummary2[$month][$channel]['cx_amount'] = $amount;
+                        }else{
+                            $costSummary2[$month][$channel]['cx_amount'] = bcadd(
+                                $costSummary2[$month][$channel]['cx_amount'],
+                                $amount,
+                                3
+                            );
+                        }
+
+                        if(! empty($t)){
+                            $type = $t['type'];
+                            $c_t = $t['channel_details'];
+                            if(in_array($channel, $c_t)){
+                                if (!isset($costSummary2_road[$month][$type]['cx_amount'])) {
+                                    $costSummary2_road[$month][$type]['cx_amount'] = $amount;
+                                }else{
+                                    $costSummary2_road[$month][$type]['cx_amount'] = bcadd(
+                                        $costSummary2_road[$month][$type]['cx_amount'],
+                                        $amount,
+                                        3
+                                    );
+                                }
+                            }
+                        }
+                    }
+
+                    if($row->fy_code == 7013){
+                        if (! isset($costSummary2[$month][$channel]['tg_amount'])) {
+                            $costSummary2[$month][$channel]['tg_amount'] = $amount;
+                        }else{
+                            $costSummary2[$month][$channel]['tg_amount'] = bcadd(
+                                $costSummary2[$month][$channel]['tg_amount'],
+                                $amount,
+                                3
+                            );
+                        }
+
+                        if(! empty($t)){
+                            $type = $t['type'];
+                            $c_t = $t['channel_details'];
+                            if(in_array($channel, $c_t)){
+                                if (!isset($costSummary2_road[$month][$type]['tg_amount'])) {
+                                    $costSummary2_road[$month][$type]['tg_amount'] = $amount;
+                                }else{
+                                    $costSummary2_road[$month][$type]['tg_amount'] = bcadd(
+                                        $costSummary2_road[$month][$type]['tg_amount'],
+                                        $amount,
+                                        3
+                                    );
+                                }
+                            }
+                        }
+                    }
+
+                    if($row->fy_code == 7012){
+                        if (! isset($costSummary2[$month][$channel]['cl_amount'])) {
+                            $costSummary2[$month][$channel]['cl_amount'] = $amount;
+                        }else{
+                            $costSummary2[$month][$channel]['cl_amount'] = bcadd(
+                                $costSummary2[$month][$channel]['cl_amount'],
+                                $amount,
+                                3
+                            );
+                        }
+
+                        if(! empty($t)){
+                            $type = $t['type'];
+                            $c_t = $t['channel_details'];
+                            if(in_array($channel, $c_t)){
+                                if (!isset($costSummary2_road[$month][$type]['cl_amount'])) {
+                                    $costSummary2_road[$month][$type]['cl_amount'] = $amount;
+                                }else{
+                                    $costSummary2_road[$month][$type]['cl_amount'] = bcadd(
+                                        $costSummary2_road[$month][$type]['cl_amount'],
+                                        $amount,
+                                        3
+                                    );
+                                }
+                            }
+                        }
+                    }
+
+                    //20049 客情出库 60 赠品出库  20048 员工福利
+                    if($row->fy_code == 7008 || $row->ck_type == 20049){
+                        if (! isset($costSummary2[$month][$channel]['kq_amount'])) {
+                            $costSummary2[$month][$channel]['kq_amount'] = $amount;
+                        }else{
+                            $costSummary2[$month][$channel]['kq_amount'] = bcadd(
+                                $costSummary2[$month][$channel]['kq_amount'],
+                                $amount,
+                                3
+                            );
+                        }
+
+                        if(! empty($t)){
+                            $type = $t['type'];
+                            $c_t = $t['channel_details'];
+                            if(in_array($channel, $c_t)){
+                                if (!isset($costSummary2_road[$month][$type]['kq_amount'])) {
+                                    $costSummary2_road[$month][$type]['kq_amount'] = $amount;
+                                }else{
+                                    $costSummary2_road[$month][$type]['kq_amount'] = bcadd(
+                                        $costSummary2_road[$month][$type]['kq_amount'],
+                                        $amount,
+                                        3
+                                    );
+                                }
+                            }
+                        }
+                    }
+
+                    if($row->fy_code == 7009 || $row->ck_type == 60){
+                        if (! isset($costSummary2[$month][$channel]['zp_amount'])) {
+                            $costSummary2[$month][$channel]['zp_amount'] = $amount;
+                        }else{
+                            $costSummary2[$month][$channel]['zp_amount'] = bcadd(
+                                $costSummary2[$month][$channel]['zp_amount'],
+                                $amount,
+                                3
+                            );
+                        }
+
+                        if(! empty($t)){
+                            $type = $t['type'];
+                            $c_t = $t['channel_details'];
+                            if(in_array($channel, $c_t)){
+                                if (!isset($costSummary2_road[$month][$type]['zp_amount'])) {
+                                    $costSummary2_road[$month][$type]['zp_amount'] = $amount;
+                                }else{
+                                    $costSummary2_road[$month][$type]['zp_amount'] = bcadd(
+                                        $costSummary2_road[$month][$type]['zp_amount'],
+                                        $amount,
+                                        3
+                                    );
+                                }
+                            }
+                        }
+                    }
+
+                    if($row->fy_code == 7010){
+                        if (! isset($costSummary2[$month][$channel]['gg_amount'])) {
+                            $costSummary2[$month][$channel]['gg_amount'] = $amount;
+                        }else{
+                            $costSummary2[$month][$channel]['gg_amount'] = bcadd(
+                                $costSummary2[$month][$channel]['gg_amount'],
+                                $amount,
+                                3
+                            );
+                        }
+
+                        if(! empty($t)){
+                            $type = $t['type'];
+                            $c_t = $t['channel_details'];
+                            if(in_array($channel, $c_t)){
+                                if (!isset($costSummary2_road[$month][$type]['gg_amount'])) {
+                                    $costSummary2_road[$month][$type]['gg_amount'] = $amount;
+                                }else{
+                                    $costSummary2_road[$month][$type]['gg_amount'] = bcadd(
+                                        $costSummary2_road[$month][$type]['gg_amount'],
+                                        $amount,
+                                        3
+                                    );
+                                }
+                            }
+                        }
+                    }
+
+                    if($row->fy_code == 7011){
+                        if (! isset($costSummary2[$month][$channel]['kd_amount'])) {
+                            $costSummary2[$month][$channel]['kd_amount'] = $amount;
+                        }else{
+                            $costSummary2[$month][$channel]['kd_amount'] = bcadd(
+                                $costSummary2[$month][$channel]['kd_amount'],
+                                $amount,
+                                3
+                            );
+                        }
+
+                        if(! empty($t)){
+                            $type = $t['type'];
+                            $c_t = $t['channel_details'];
+                            if(in_array($channel, $c_t)){
+                                if (!isset($costSummary2_road[$month][$type]['kd_amount'])) {
+                                    $costSummary2_road[$month][$type]['kd_amount'] = $amount;
+                                }else{
+                                    $costSummary2_road[$month][$type]['kd_amount'] = bcadd(
+                                        $costSummary2_road[$month][$type]['kd_amount'],
+                                        $amount,
+                                        3
+                                    );
+                                }
+                            }
+                        }
+                    }
+
+                    if($row->fy_code == 7001){
+                        if (! isset($costSummary2[$month][$channel]['xsqt_amount'])) {
+                            $costSummary2[$month][$channel]['xsqt_amount'] = $amount;
+                        }else{
+                            $costSummary2[$month][$channel]['xsqt_amount'] = bcadd(
+                                $costSummary2[$month][$channel]['xsqt_amount'],
+                                $amount,
+                                3
+                            );
+                        }
+
+                        if(! empty($t)){
+                            $type = $t['type'];
+                            $c_t = $t['channel_details'];
+                            if(in_array($channel, $c_t)){
+                                if (!isset($costSummary2_road[$month][$type]['xsqt_amount'])) {
+                                    $costSummary2_road[$month][$type]['xsqt_amount'] = $amount;
+                                }else{
+                                    $costSummary2_road[$month][$type]['xsqt_amount'] = bcadd(
+                                        $costSummary2_road[$month][$type]['xsqt_amount'],
+                                        $amount,
+                                        3
+                                    );
+                                }
+                            }
+                        }
+                    }
+
+                    if($row->fy_code == 7003){
+                        if (! isset($costSummary2[$month][$channel]['ry_amount'])) {
+                            $costSummary2[$month][$channel]['ry_amount'] = $amount;
+                        }else{
+                            $costSummary2[$month][$channel]['ry_amount'] = bcadd(
+                                $costSummary2[$month][$channel]['ry_amount'],
+                                $amount,
+                                3
+                            );
+                        }
+
+                        if(! empty($t)){
+                            $type = $t['type'];
+                            $c_t = $t['channel_details'];
+                            if(in_array($channel, $c_t)){
+                                if (!isset($costSummary2_road[$month][$type]['ry_amount'])) {
+                                    $costSummary2_road[$month][$type]['ry_amount'] = $amount;
+                                }else{
+                                    $costSummary2_road[$month][$type]['ry_amount'] = bcadd(
+                                        $costSummary2_road[$month][$type]['ry_amount'],
+                                        $amount,
+                                        3
+                                    );
+                                }
+                            }
+                        }
+                    }
+
+                    if($row->fy_code == 7019 || $row->ck_type == 20048){
+                        if (! isset($costSummary2[$month][$channel]['sb_amount'])) {
+                            $costSummary2[$month][$channel]['sb_amount'] = $amount;
+                        }else{
+                            $costSummary2[$month][$channel]['sb_amount'] = bcadd(
+                                $costSummary2[$month][$channel]['sb_amount'],
+                                $amount,
+                                3
+                            );
+                        }
+
+                        if(! empty($t)){
+                            $type = $t['type'];
+                            $c_t = $t['channel_details'];
+                            if(in_array($channel, $c_t)){
+                                if (!isset($costSummary2_road[$month][$type]['sb_amount'])) {
+                                    $costSummary2_road[$month][$type]['sb_amount'] = $amount;
+                                }else{
+                                    $costSummary2_road[$month][$type]['sb_amount'] = bcadd(
+                                        $costSummary2_road[$month][$type]['sb_amount'],
+                                        $amount,
+                                        3
+                                    );
+                                }
+                            }
+                        }
+                    }
+
+                    $sj_amount = bcmul($amount,0.01,3);
+                    if (! isset($costSummary2[$month][$channel]['sj_amount'])) {
+                        $costSummary2[$month][$channel]['sj_amount'] = $sj_amount;
+                    }else{
+                        $costSummary2[$month][$channel]['sj_amount'] = bcadd(
+                            $costSummary2[$month][$channel]['sj_amount'],
+                            $sj_amount,
+                            3
+                        );
+                    }
+
+                    if(! empty($t)){
+                        $type = $t['type'];
+                        $c_t = $t['channel_details'];
+                        if(in_array($channel, $c_t)){
+                            if (!isset($costSummary2_road[$month][$type]['sj_amount'])) {
+                                $costSummary2_road[$month][$type]['sj_amount'] = $amount;
+                            }else{
+                                $costSummary2_road[$month][$type]['sj_amount'] = bcadd(
+                                    $costSummary2_road[$month][$type]['sj_amount'],
+                                    $amount,
+                                    3
+                                );
+                            }
+                        }
+                    }
+                }
+
+                // 更新 lastId
+                $lastId = $rows->last()->id;
+
+            } while ($rows->count() === $limit);
+        }catch (\Throwable $exception){
+            return [false, "初步汇总异常" . $exception->getMessage() . "|" . $exception->getLine() . "|" . $exception->getFile()];
+        }
+
+        try {
+            $start_timeStamp = $data['start_timeStamp'];
+            $end_timeStamp = $data['end_timeStamp'];
+
+            $result = $this->sumRes($paymentSummary,$costSummary,$costSummary2);
+            $result2 = $this->sumRes2($paymentSummary_road, $costSummary_road, $costSummary2_road);
+
+            DB::transaction(function () use ($start_timeStamp, $end_timeStamp, $result, $result2) {
+                // 1. 先删除旧数据
+                ItemReport::where('time', '>=', $start_timeStamp)
+                    ->where('time', '<=', $end_timeStamp)
+                    ->delete();
+                ItemReportRoad::where('time', '>=', $start_timeStamp)
+                    ->where('time', '<=', $end_timeStamp)
+                    ->delete();
+
+                // 2. 插入新数据
+                ItemReport::insert($result);
+                ItemReportRoad::insert($result2);
+
+            });
+        }catch (\Throwable $exception){
+            return [false, "结果异常" . $exception->getMessage() . "|" . $exception->getLine() . "|" . $exception->getFile()];
+        }
+
+        return [true, ''];
+    }
+
+    private function sumRes($paymentSummary, $costSummary, $costSummary2){
+        // 合并三个汇总数组为最终结果
+        $result = [];
+
+        // 获取所有唯一的时间+渠道组合
+        $keys = [];
+        foreach (array_keys($paymentSummary) as $month) {
+            foreach (array_keys($paymentSummary[$month]) as $channel) {
+                $keys["$month|$channel"] = ['month' => $month, 'channel' => $channel];
+            }
+        }
+        foreach (array_keys($costSummary) as $month) {
+            foreach (array_keys($costSummary[$month]) as $channel) {
+                $keys["$month|$channel"] = ['month' => $month, 'channel' => $channel];
+            }
+        }
+        foreach (array_keys($costSummary2) as $month) {
+            foreach (array_keys($costSummary2[$month]) as $channel) {
+                $keys["$month|$channel"] = ['month' => $month, 'channel' => $channel];
+            }
+        }
+
+        // 遍历所有组合,构建最终数据
+        foreach ($keys as $key => $info) {
+            $month = $info['month'];
+            $channel = $info['channel'];
+
+            // 收款销售收入(注意:原逻辑中 is_kh == false 才计入)
+            $receipt_amount = isset($paymentSummary[$month][$channel])
+                ? $paymentSummary[$month][$channel]
+                : '0.000';
+
+            // 业务成本(来自 tmp_item_road 的 price_4_total)
+            $cost = isset($costSummary[$month][$channel])
+                ? $costSummary[$month][$channel]
+                : '0.000';
+
+            // 利润 = 收入 - 成本
+            $profit = bcsub($receipt_amount, $cost, 3);
+
+            // 从 costSummary2 提取各项费用(默认为 0.000)
+            $cs2 = $costSummary2[$month][$channel] ?? [];
+
+            $settle_amount = $cs2['settle_amount'] ?? '0.000';
+            $gl_amount     = $cs2['gl_amount']     ?? '0.000';
+            $wl_amount     = $cs2['wl_amount']     ?? '0.000';
+            $ht_amount     = $cs2['ht_amount']     ?? '0.000';
+            $zk_amount     = $cs2['zk_amount']     ?? '0.000';
+            $cx_amount     = $cs2['cx_amount']     ?? '0.000';
+            $tg_amount     = $cs2['tg_amount']     ?? '0.000';
+            $cl_amount     = $cs2['cl_amount']     ?? '0.000';
+            $kq_amount     = $cs2['kq_amount']     ?? '0.000';
+            $zp_amount     = $cs2['zp_amount']     ?? '0.000';
+            $gg_amount     = $cs2['gg_amount']     ?? '0.000';
+            $kd_amount     = $cs2['kd_amount']     ?? '0.000';
+            $xsqt_amount   = $cs2['xsqt_amount']   ?? '0.000';
+            $ry_amount     = $cs2['ry_amount']     ?? '0.000';
+            $sb_amount     = $cs2['sb_amount']     ?? '0.000';
+            $sj_amount     = $cs2['sj_amount']     ?? '0.000';
+
+            $result[] = [
+                'time'           => $month,
+                'channel_details'=> $channel,
+                'receipt_amount' => $receipt_amount,
+                'cost'           => $cost,
+                'profit'         => $profit,
+                'settle_amount'  => $settle_amount,
+                'gl_amount'      => $gl_amount,
+                'wl_amount'      => $wl_amount,
+                'ht_amount'      => $ht_amount,
+                'zk_amount'      => $zk_amount,
+                'cx_amount'      => $cx_amount,
+                'tg_amount'      => $tg_amount,
+                'cl_amount'      => $cl_amount,
+                'kq_amount'      => $kq_amount,
+                'zp_amount'      => $zp_amount,
+                'gg_amount'      => $gg_amount,
+                'kd_amount'      => $kd_amount,
+                'xsqt_amount'    => $xsqt_amount,
+                'ry_amount'      => $ry_amount,
+                'sb_amount'      => $sb_amount,
+                'sj_amount'      => $sj_amount,
+            ];
+        }
+
+        return $result;
+    }
+
+    private function sumRes2($paymentSummary, $costSummary, $costSummary2){
+        // 合并三个汇总数组为最终结果
+        $result = [];
+
+        // 获取所有唯一的时间+渠道组合
+        $keys = [];
+        foreach (array_keys($paymentSummary) as $month) {
+            foreach (array_keys($paymentSummary[$month]) as $channel) {
+                $keys["$month|$channel"] = ['month' => $month, 'channel' => $channel];
+            }
+        }
+        foreach (array_keys($costSummary) as $month) {
+            foreach (array_keys($costSummary[$month]) as $channel) {
+                $keys["$month|$channel"] = ['month' => $month, 'channel' => $channel];
+            }
+        }
+        foreach (array_keys($costSummary2) as $month) {
+            foreach (array_keys($costSummary2[$month]) as $channel) {
+                $keys["$month|$channel"] = ['month' => $month, 'channel' => $channel];
+            }
+        }
+
+        // 遍历所有组合,构建最终数据
+        foreach ($keys as $key => $info) {
+            $month = $info['month'];
+            $channel = $info['channel'];
+
+            // 收款销售收入
+            $receipt_amount = isset($paymentSummary[$month][$channel])
+                ? $paymentSummary[$month][$channel]
+                : '0.000';
+
+            // 业务成本(来自 tmp_item_road 的 price_4_total)
+            $cost = isset($costSummary[$month][$channel])
+                ? $costSummary[$month][$channel]
+                : '0.000';
+
+            // 利润 = 收入 - 成本
+            $profit = bcsub($receipt_amount, $cost, 3);
+
+            // 从 costSummary2 提取各项费用(默认为 0.000)
+            $cs2 = $costSummary2[$month][$channel] ?? [];
+
+            $settle_amount = $cs2['settle_amount'] ?? '0.000';
+            $gl_amount     = $cs2['gl_amount']     ?? '0.000';
+            $wl_amount     = $cs2['wl_amount']     ?? '0.000';
+            $ht_amount     = $cs2['ht_amount']     ?? '0.000';
+            $zk_amount     = $cs2['zk_amount']     ?? '0.000';
+            $cx_amount     = $cs2['cx_amount']     ?? '0.000';
+            $tg_amount     = $cs2['tg_amount']     ?? '0.000';
+            $cl_amount     = $cs2['cl_amount']     ?? '0.000';
+            $kq_amount     = $cs2['kq_amount']     ?? '0.000';
+            $zp_amount     = $cs2['zp_amount']     ?? '0.000';
+            $gg_amount     = $cs2['gg_amount']     ?? '0.000';
+            $kd_amount     = $cs2['kd_amount']     ?? '0.000';
+            $xsqt_amount   = $cs2['xsqt_amount']   ?? '0.000';
+            $ry_amount     = $cs2['ry_amount']     ?? '0.000';
+            $sb_amount     = $cs2['sb_amount']     ?? '0.000';
+            $sj_amount     = $cs2['sj_amount']     ?? '0.000';
+
+            $result[] = [
+                'time'           => $month,
+                'receipt_amount' => $receipt_amount,
+                'cost'           => $cost,
+                'profit'         => $profit,
+                'settle_amount'  => $settle_amount,
+                'gl_amount'      => $gl_amount,
+                'wl_amount'      => $wl_amount,
+                'ht_amount'      => $ht_amount,
+                'zk_amount'      => $zk_amount,
+                'cx_amount'      => $cx_amount,
+                'tg_amount'      => $tg_amount,
+                'cl_amount'      => $cl_amount,
+                'kq_amount'      => $kq_amount,
+                'zp_amount'      => $zp_amount,
+                'gg_amount'      => $gg_amount,
+                'kd_amount'      => $kd_amount,
+                'xsqt_amount'    => $xsqt_amount,
+                'ry_amount'      => $ry_amount,
+                'sb_amount'      => $sb_amount,
+                'sj_amount'      => $sj_amount,
+                'employee_title_type'  => $channel
+            ];
+        }
+
+        return $result;
+    }
 }
 }

+ 112 - 0
config/header/78.php

@@ -0,0 +1,112 @@
+<?php
+/**
+ * '菜单ID' => [
+ *     '字段英文名' =》 '字段中文名'
+ * ]
+ */
+
+return [
+    [
+        'key' => 'time',
+        'value' => '年月'
+    ],
+    [
+        'key' => 'channel_details',
+        'value' => '渠道明细'
+    ],
+    [
+        'key' =>'receipt_amount',
+        'value' => '收款销售收入',
+        'sum' => 1,
+    ],
+    [
+        'key' =>'cost',
+        'value' => '成本',
+        'sum' => 1,
+    ],
+    [
+        'key' =>'profit',
+        'value' => '收款销售毛利',
+        'sum' => 1,
+    ],
+    [
+        'key' =>'settle_amount',
+        'value' => '结算费用',
+        'sum' => 1,
+    ],
+    [
+        'key' =>'gl_amount',
+        'value' => '管理费用',
+        'sum' => 1,
+    ],
+    [
+        'key' =>'wl_amount',
+        'value' => '物流配送',
+        'sum' => 1,
+    ],
+    [
+        'key' =>'ht_amount',
+        'value' => '合同费',
+        'sum' => 1,
+    ],
+    [
+        'key' =>'zk_amount',
+        'value' => '账扣费',
+        'sum' => 1,
+    ],
+    [
+        'key' =>'cx_amount',
+        'value' => '促销员工资',
+        'sum' => 1,
+    ],
+    [
+        'key' =>'tg_amount',
+        'value' => '团购及其他返点',
+        'sum' => 1,
+    ],
+    [
+        'key' =>'cl_amount',
+        'value' => '陈列费',
+        'sum' => 1,
+    ],
+    [
+        'key' =>'kq_amount',
+        'value' => '客情费',
+        'sum' => 1,
+    ],
+    [
+        'key' =>'zp_amount',
+        'value' => '赠品费用',
+        'sum' => 1,
+    ],
+    [
+        'key' =>'gg_amount',
+        'value' => '广告宣传费',
+        'sum' => 1,
+    ],
+    [
+        'key' =>'kd_amount',
+        'value' => '快递费',
+        'sum' => 1,
+    ],
+    [
+        'key' =>'xsqt_amount',
+        'value' => '销售其他费用',
+        'sum' => 1,
+    ],
+    [
+        'key' =>'ry_amount',
+        'value' => '人员工资',
+        'sum' => 1,
+    ],
+    [
+        'key' =>'sb_amount',
+        'value' => '员工社保、福利费',
+        'sum' => 1,
+    ],
+    [
+        'key' =>'sj_amount',
+        'value' => '税金(手工)',
+        'sum' => 1,
+    ],
+];

+ 112 - 0
config/header/79.php

@@ -0,0 +1,112 @@
+<?php
+/**
+ * '菜单ID' => [
+ *     '字段英文名' =》 '字段中文名'
+ * ]
+ */
+
+return [
+    [
+        'key' => 'time',
+        'value' => '年月'
+    ],
+    [
+        'key' => 'type_title',
+        'value' => '渠道明细'
+    ],
+    [
+        'key' =>'receipt_amount',
+        'value' => '收款销售收入',
+        'sum' => 1,
+    ],
+    [
+        'key' =>'cost',
+        'value' => '成本',
+        'sum' => 1,
+    ],
+    [
+        'key' =>'profit',
+        'value' => '收款销售毛利',
+        'sum' => 1,
+    ],
+    [
+        'key' =>'settle_amount',
+        'value' => '结算费用',
+        'sum' => 1,
+    ],
+    [
+        'key' =>'gl_amount',
+        'value' => '管理费用',
+        'sum' => 1,
+    ],
+    [
+        'key' =>'wl_amount',
+        'value' => '物流配送',
+        'sum' => 1,
+    ],
+    [
+        'key' =>'ht_amount',
+        'value' => '合同费',
+        'sum' => 1,
+    ],
+    [
+        'key' =>'zk_amount',
+        'value' => '账扣费',
+        'sum' => 1,
+    ],
+    [
+        'key' =>'cx_amount',
+        'value' => '促销员工资',
+        'sum' => 1,
+    ],
+    [
+        'key' =>'tg_amount',
+        'value' => '团购及其他返点',
+        'sum' => 1,
+    ],
+    [
+        'key' =>'cl_amount',
+        'value' => '陈列费',
+        'sum' => 1,
+    ],
+    [
+        'key' =>'kq_amount',
+        'value' => '客情费',
+        'sum' => 1,
+    ],
+    [
+        'key' =>'zp_amount',
+        'value' => '赠品费用',
+        'sum' => 1,
+    ],
+    [
+        'key' =>'gg_amount',
+        'value' => '广告宣传费',
+        'sum' => 1,
+    ],
+    [
+        'key' =>'kd_amount',
+        'value' => '快递费',
+        'sum' => 1,
+    ],
+    [
+        'key' =>'xsqt_amount',
+        'value' => '销售其他费用',
+        'sum' => 1,
+    ],
+    [
+        'key' =>'ry_amount',
+        'value' => '人员工资',
+        'sum' => 1,
+    ],
+    [
+        'key' =>'sb_amount',
+        'value' => '员工社保、福利费',
+        'sum' => 1,
+    ],
+    [
+        'key' =>'sj_amount',
+        'value' => '税金(手工)',
+        'sum' => 1,
+    ],
+];

+ 2 - 0
config/header/common.php

@@ -19,4 +19,6 @@ return [
     73 => ['has_detail' => true, 'is_jump' => true], // 业务员工资
     73 => ['has_detail' => true, 'is_jump' => true], // 业务员工资
     74 => ['has_detail' => true, 'is_jump' => true], // 利润分配
     74 => ['has_detail' => true, 'is_jump' => true], // 利润分配
     75 => ['has_detail' => true, 'is_jump' => false], // 运费统计
     75 => ['has_detail' => true, 'is_jump' => false], // 运费统计
+    78 => ['has_detail' => false, 'is_jump' => false], // 项目报表
+    79 => ['has_detail' => false, 'is_jump' => false], // 通路报表
 ];
 ];

+ 3 - 0
routes/api.php

@@ -136,6 +136,7 @@ Route::group(['middleware'=> ['checkLogin']],function ($route){
     $route->any('synRevenueCost', 'Api\TPlusController@synRevenueCost');
     $route->any('synRevenueCost', 'Api\TPlusController@synRevenueCost');
     $route->any('synSalaryEmployee', 'Api\TPlusController@synSalaryEmployee');
     $route->any('synSalaryEmployee', 'Api\TPlusController@synSalaryEmployee');
     $route->any('synFreightFee', 'Api\TPlusController@synFreightFee');
     $route->any('synFreightFee', 'Api\TPlusController@synFreightFee');
+    $route->any('synItemRoad', 'Api\TPlusController@synItemRoad');
 
 
     //收入成本
     //收入成本
     $route->any('statisticsRevenueCost', 'Api\StatisticsController@statisticsRevenueCost');
     $route->any('statisticsRevenueCost', 'Api\StatisticsController@statisticsRevenueCost');
@@ -150,4 +151,6 @@ Route::group(['middleware'=> ['checkLogin']],function ($route){
     //运费
     //运费
     $route->any('statisticsFreightFee', 'Api\StatisticsController@statisticsFreightFee');
     $route->any('statisticsFreightFee', 'Api\StatisticsController@statisticsFreightFee');
     $route->any('statisticsFreightFeeDetail', 'Api\StatisticsController@statisticsFreightFeeDetail');
     $route->any('statisticsFreightFeeDetail', 'Api\StatisticsController@statisticsFreightFeeDetail');
+    $route->any('statisticsItem', 'Api\StatisticsController@statisticsItem');
+    $route->any('statisticsRoad', 'Api\StatisticsController@statisticsRoad');
 });
 });