cqp 3 hafta önce
ebeveyn
işleme
dfc5b94c3d

+ 63 - 14
app/Service/ExportFileService.php

@@ -92,14 +92,43 @@ class ExportFileService extends Service
         return [true, $return];
     }
 
-    private function fillData($data, $column, &$return){
+//    private function fillData($data, $column, &$return){
+//        foreach ($data as $value) {
+//            $tmp = [];
+//            foreach ($column as $c_v){
+//                $tmp[$c_v] = $value[$c_v] ?? "";
+//            }
+//            $return[] = $tmp;
+//        }
+//    }
+
+    private function fillData($data, $column, &$return)
+    {
+        // 预先创建包含默认值的键数组
+        $default = array_fill_keys($column, '');
+
         foreach ($data as $value) {
-            $tmp = [];
-            foreach ($column as $c_v){
-                $tmp[$c_v] = $value[$c_v] ?? "";
+            // 提取交集,并用默认值补充缺失的键
+            $return[] = array_merge($default, array_intersect_key($value, $default));
+        }
+    }
+
+    private function fillTotalData($data, $column, &$return){
+        $tmp = [];
+        foreach ($column as $value){
+            $key = $value['key'];
+            if(! empty($value['sum']) && isset($data[$value['key']])){
+                $decimals = $col['decimals'] ?? 2;
+//                $tmp[$value['key']] = $data[$value['key']];
+
+                // 用 number_format 格式化输出(保持字符串形式,避免科学计数)
+                $tmp[$key] = number_format((float)$data[$key], $decimals, '.', '');
+            }else{
+                $tmp[$value['key']] = "";
             }
-            $return[] = $tmp;
         }
+
+        $return[] = $tmp;
     }
 
     public function one($ergs,$user){
@@ -157,6 +186,7 @@ class ExportFileService extends Service
 
         // 导出数据
         $return = [];
+        $service = new StatisticsService();
         $header_default = config("header.68") ?? [];
         $column = array_column($header_default,'key');
         $header = array_column($header_default,'value');
@@ -165,19 +195,23 @@ class ExportFileService extends Service
             ->whereIn('id', $id)
             ->select(RevenueCostTotal::$field)
             ->orderBy('id','desc')
-            ->chunkById(500,function ($data) use(&$return,$column){
+            ->chunkById(500,function ($data) use(&$return, $service, $column){
                 $data = Collect($data)->map(function ($object) {
                     return (array)$object;
                 })->toArray();
                 $list['data'] = $data;
 
                 //订单数据
-                $service = new StatisticsService();
                 $list = $service->statisticsRevenueCostFillData($list);
-
+                //返回数据
                 $this->fillData($list['data'], $column, $return);
             });
 
+        //合计
+        $total = $this->countTotal($return, $header_default);
+        //填充合计
+        $this->fillTotalData($total, $header_default, $return);
+
         return $this->saveExportData($return,$header);
     }
 
@@ -186,6 +220,7 @@ class ExportFileService extends Service
 
         // 导出数据
         $return = [];
+        $service = new StatisticsService();
         $header_default = config("header.69") ?? [];
         $column = array_column($header_default,'key');
         $header = array_column($header_default,'value');
@@ -194,19 +229,23 @@ class ExportFileService extends Service
             ->whereIn('id', $id)
             ->select(RevenueCost::$field_xhd)
             ->orderBy('id','desc')
-            ->chunkById(500,function ($data) use(&$return, $column){
+            ->chunkById(500,function ($data) use(&$return, $column, $service){
                 $data = Collect($data)->map(function ($object) {
                     return (array)$object;
                 })->toArray();
                 $list['data'] = $data;
 
                 //订单数据
-                $service = new StatisticsService();
                 $list = $service->statisticsRevenueCostOneAndTwoFillData($list);
 
                 $this->fillData($list['data'], $column, $return);
             });
 
+        //合计
+        $total = $this->countTotal($return, $header_default);
+        //填充合计
+        $this->fillTotalData($total, $header_default, $return);
+
         return $this->saveExportData($return,$header);
     }
 
@@ -215,6 +254,7 @@ class ExportFileService extends Service
 
         // 导出数据
         $return = [];
+        $service = new StatisticsService();
         $header_default = config("header.70") ?? [];
         $column = array_column($header_default,'key');
         $header = array_column($header_default,'value');
@@ -223,19 +263,23 @@ class ExportFileService extends Service
             ->whereIn('id', $id)
             ->select(RevenueCost::$field_xsfp)
             ->orderBy('id','desc')
-            ->chunkById(500,function ($data) use(&$return, $column){
+            ->chunkById(500,function ($data) use(&$return, $column, $service){
                 $data = Collect($data)->map(function ($object) {
                     return (array)$object;
                 })->toArray();
                 $list['data'] = $data;
 
                 //订单数据
-                $service = new StatisticsService();
                 $list = $service->statisticsRevenueCostOneAndTwoFillData($list);
 
                 $this->fillData($list['data'], $column, $return);
             });
 
+        //合计
+        $total = $this->countTotal($return, $header_default);
+        //填充合计
+        $this->fillTotalData($total, $header_default, $return);
+
         return $this->saveExportData($return,$header);
     }
 
@@ -249,6 +293,7 @@ class ExportFileService extends Service
 
         // 导出数据
         $return = [];
+        $service = new StatisticsService();
         $header_default = config("header.71") ?? [];
         $column = array_column($header_default,'key');
         $header = array_column($header_default,'value');
@@ -259,19 +304,23 @@ class ExportFileService extends Service
             ->select($field)
             ->groupby('order_id')
             ->orderBy('id','desc')
-            ->chunkById(500,function ($data) use(&$return, $column){
+            ->chunkById(500,function ($data) use(&$return, $column, $service){
                 $data = Collect($data)->map(function ($object) {
                     return (array)$object;
                 })->toArray();
                 $list['data'] = $data;
 
                 //订单数据
-                $service = new StatisticsService();
                 $list = $service->statisticsRevenueCostThreeFillData($list);
 
                 $this->fillData($list['data'], $column, $return);
             },'order_id');
 
+        //合计
+        $total = $this->countTotal($return, $header_default);
+        //填充合计
+        $this->fillTotalData($total, $header_default, $return);
+
         return $this->saveExportData($return,$header);
     }
 

+ 106 - 0
app/Service/Service.php

@@ -729,4 +729,110 @@ class Service
 
         return [true, ''];
     }
+
+    public function countTotal($rows, $config = [])
+    {
+        if (empty($rows)) return [];
+
+        $total = [];
+
+        foreach ($config as $col) {
+            $key = $col['key'] ?? null;
+            if (!$key) continue;
+
+            $decimals = $col['decimals'] ?? 2; // 默认保留 2 位
+
+            // 1. 基础字段累加
+            if (!empty($col['sum']) && $col['sum'] == 1) {
+                $sum = '0';
+                foreach ($rows as $row) {
+                    $sum = bcadd($sum, (string)($row[$key] ?? 0), $decimals);
+                }
+                $total[$key] = $sum;
+            }
+
+            // 2. 公式字段
+            if (!empty($col['sum']) && $col['sum'] == 2 && !empty($col['formula'])) {
+                $total[$key] = $this->calcFormula($col['formula'], $total, $decimals);
+            }
+        }
+
+        return $total;
+    }
+
+
+    /**
+     * 解析并计算公式(基于 bcmath)
+     * 例: profit / income * 100
+     */
+    public function calcFormula($formula, $values, $decimals = 2)
+    {
+        $tokens = preg_split('/\s+/', trim($formula));
+
+        $stack = [];
+        foreach ($tokens as $token) {
+            if ($token === '') continue;
+
+            if (isset($values[$token])) {
+                $stack[] = $values[$token];
+            } elseif (is_numeric($token)) {
+                $stack[] = (string)$token;
+            } else {
+                $stack[] = $token;
+            }
+        }
+
+        $expr = implode(' ', $stack);
+
+        return $this->evaluateWithBCMath($expr, $decimals);
+    }
+
+
+    /**
+     * 用 bcmath 安全执行表达式 (支持 + - * /)
+     */
+    public function evaluateWithBCMath($expr, $decimals = 2)
+    {
+        preg_match_all('/(\d+(?:\.\d+)?|[+\/*-])/', $expr, $matches);
+        $tokens = $matches[0] ?? [];
+
+        if (empty($tokens)) return 0;
+
+        $output = [];
+        $ops = [];
+
+        foreach ($tokens as $token) {
+            if (is_numeric($token)) {
+                $output[] = (string)$token;
+            } elseif (in_array($token, ['+', '-', '*', '/'])) {
+                $ops[] = $token;
+            }
+        }
+
+        $result = array_shift($output);
+        foreach ($ops as $i => $op) {
+            $next = $output[$i] ?? '0';
+
+            switch ($op) {
+                case '+':
+                    $result = bcadd($result, $next, $decimals);
+                    break;
+                case '-':
+                    $result = bcsub($result, $next, $decimals);
+                    break;
+                case '*':
+                    $result = bcmul($result, $next, $decimals);
+                    break;
+                case '/':
+                    if (bccomp($next, '0', $decimals) === 0) {
+                        $result = '0'; // 避免除零
+                    } else {
+                        $result = bcdiv($result, $next, $decimals);
+                    }
+                    break;
+            }
+        }
+
+        return $result;
+    }
 }

+ 12 - 50
app/Service/StatisticsService.php

@@ -36,6 +36,8 @@ class StatisticsService extends Service
         $list = $this->limit($model,'',$data);
         $list = $this->statisticsRevenueCostFillData($list);
 
+        $header_default = config("header.68") ?? [];
+        $list['count'] = $this->countTotal($list['data'], $header_default);
         return [true, $list];
     }
 
@@ -52,56 +54,6 @@ class StatisticsService extends Service
         return $data;
     }
 
-    //无用
-    public function statisticsRevenueCostFillData1($data){
-        if(empty($data)) return $data;
-
-        $return = [];
-        foreach ($data as $key => $value){
-            $time = date("Y-m", $value['order_time']);
-            if($value['order_type'] == RevenueCost::ORDER_ONE){
-                $income = $value['price_3_total'];
-            }elseif ($value['order_type'] == RevenueCost::ORDER_TWO){
-                $income = $value['price_1_total'];
-            }else{
-                $income = $value['payment_amount'];
-            }
-            $adjust = $income < 0 ? $income : 0;
-            $business = $value['price_4_total'];
-            if(isset($return[$time][$value['order_type']])){
-                $income_total = bcadd($return[$time][$value['order_type']]['income'], $income,2);
-                $adjust_total = bcadd($return[$time][$value['order_type']]['adjust'], $adjust,2);
-                $business_total = bcadd($return[$time][$value['order_type']]['business'], $business,2);
-                $return[$time][$value['order_type']]['income'] = $income_total;
-                $return[$time][$value['order_type']]['adjust'] = $adjust_total;
-                $return[$time][$value['order_type']]['business'] = $business_total;
-            }else{
-                $return[$time][$value['order_type']] = [
-                    'income' => $income,
-                    'adjust' => $adjust,
-                    'business' => $business,
-                    'time' => $time,
-                ];
-            }
-        }
-        $final = [];
-        foreach ($return as $value){
-            foreach ($value as $key => $val){
-                $title = RevenueCost::$order_type[$key] ?? "";
-                $val['title'] = $title;
-                $profit = bcsub($val['income'], $val['business'],2);
-                $profit_rate = $val['income'] > 0 ? bcdiv($profit, $val['income'],2) : 0;
-                $profit_rate = bcmul($profit_rate, 100,2);
-                $val['profit'] = $profit;
-                $val['profit_rate'] = $profit_rate;
-                $val['order_type'] = $key;
-                $final[] = $val;
-            }
-        }
-
-        return $final;
-    }
-
     public function statisticsRevenueCostOneAndTwoCommon($data,$user, $field = []){
         if(empty($data['order_type']) || ! isset(RevenueCost::$order_type[$data['order_type']])) return [false, '单据类型不存在或错误'];
 
@@ -135,6 +87,13 @@ class StatisticsService extends Service
         $list = $this->limit($model,'',$data);
         $list = $this->statisticsRevenueCostOneAndTwoFillData($list);
 
+        if($data['order_type'] == 1){
+            $header_default = config("header.69") ?? [];
+        }else{
+            $header_default = config("header.70") ?? [];
+        }
+        $list['count'] = $this->countTotal($list['data'], $header_default);
+
         return [true, $list];
     }
 
@@ -180,6 +139,9 @@ class StatisticsService extends Service
         $list = $this->limit($model,'',$data);
         $list = $this->statisticsRevenueCostThreeFillData($list);
 
+        $header_default = config("header.71") ?? [];
+        $list['count'] = $this->countTotal($list['data'], $header_default);
+
         return [true, $list];
     }
 

+ 7 - 0
config/header/68.php

@@ -24,25 +24,32 @@ return [
         'value1' => '销货单:含税金额;
                      销售发票:开票金额;
                      回款单:回款金额;',
+        'sum' => 1,
     ],
     [
         'key' =>'adjust',
         'value' => '调整金额',
         'value1' => '金额为负数的合计',
+        'sum' => 1,
     ],
     [
         'key' =>'business',
         'value' => '业务成本金额',
         'value1' => '业务成本金额合计',
+        'sum' => 1,
     ],
     [
         'key' =>'profit',
         'value' => '毛利',
         'value1' => '收入金额 - 业务成本金额',
+        'sum' => 2,
+        'formula' => 'income - business',
     ],
     [
         'key' =>'profit_rate',
         'value' => '毛利率%',
         'value1' => '毛利 / 收入金额',
+        'sum' => 2,
+        'formula' => 'profit / income * 100',
     ],
 ];

+ 11 - 2
config/header/69.php

@@ -63,6 +63,7 @@ return [
         'key' =>'price_1_total',
         'value' => '核销总价',
         'value1' => '核销单价 * 销货数量',
+        'sum' => 1,
     ],
     [
         'key' =>'price_2',
@@ -73,10 +74,12 @@ return [
         'key' =>'price_2_total',
         'value' => '运费总价',
         'value1' => '运费单价 * 销货数量',
+        'sum' => 1,
     ],
     [
         'key' =>'quantity',
         'value' => '销货数量',
+        'sum' => 1,
     ],
     [
         'key' =>'price_3',
@@ -85,6 +88,7 @@ return [
     [
         'key' =>'price_3_total',
         'value' => '含税金额',
+        'sum' => 1,
     ],
     [
         'key' =>'price_4',
@@ -95,15 +99,20 @@ return [
         'key' =>'price_4_total',
         'value' => '业务成本金额',
         'value1' => '销货数量 * 业务成本单价',
+        'sum' => 1,
     ],
     [
         'key' =>'profit',
         'value' => '毛利',
-        'value1' => '毛利 / 业务成本金额',
+        'value1' => '销货金额(含税金额) - 业务成本金额',
+        'sum' => 2,
+        'formula' => 'price_3_total - business',
     ],
     [
         'key' =>'profit_rate',
         'value' => '毛利率%',
-        'value1' => '毛利 / 销货金额',
+        'value1' => '毛利 / 销货金额(含税金额)',
+        'sum' => 2,
+        'formula' => 'profit / price_3_total * 100',
     ],
 ];

+ 9 - 1
config/header/70.php

@@ -49,6 +49,7 @@ return [
     [
         'key' =>'quantity',
         'value' => '开票数量',
+        'sum' => 1,
     ],
     [
         'key' =>'price_1',
@@ -57,6 +58,7 @@ return [
     [
         'key' =>'price_1_total',
         'value' => '开票金额',
+        'sum' => 1,
     ],
     [
         'key' =>'price_4',
@@ -65,16 +67,22 @@ return [
     ],
     [
         'key' =>'price_4_total',
-        'value' => '开票数量 * 业务成本单价',
+        'value' => '业务成本金额',
+        'value1' => '开票数量 * 业务成本单价',
+        'sum' => 1,
     ],
     [
         'key' =>'profit',
         'value' => '毛利',
         'value1' => '开票金额 - 业务成本金额',
+        'sum' => 2,
+        'formula' => 'price_1_total - price_4_total',
     ],
     [
         'key' =>'profit_rate',
         'value' => '毛利率%',
         'value1' => '毛利 / 开票金额',
+        'sum' => 2,
+        'formula' => 'profit / price_1_total * 100',
     ],
 ];

+ 6 - 3
config/header/71.php

@@ -25,23 +25,26 @@ return [
     [
         'key' =>'payment_amount',
         'value' => '回款金额',
-        'is_total' => true,
+        'sum' => 1,
     ],
     [
         'key' =>'price_4_total',
         'value' => '业务成本金额',
         'value1' => '开票数量 * 存货业务成本单价',
-        'is_total' => true,
+        'sum' => 1,
     ],
     [
         'key' =>'profit',
         'value' => '毛利',
         'value1' => '回款金额 - 业务成本金额',
-        'is_total' => true,
+        'sum' => 2,
+        'formula' => 'payment_amount - price_4_total',
     ],
     [
         'key' =>'profit_rate',
         'value' => '毛利率%',
         'value1' => '毛利 / 回款金额',
+        'sum' => 2,
+        'formula' => 'profit / payment_amount * 100',
     ],
 ];