소스 검색

客户管理-计费模型

Zhouzhendong 5 년 전
부모
커밋
cf9a3001e3

+ 1 - 1
app/Http/Controllers/CitiesController.php

@@ -82,6 +82,6 @@ class CitiesController extends Controller
     }
 
     public function get(){
-        return City::query()->select("id","name")->get();
+        return City::query()->select("id","name","province_id")->get();
     }
 }

+ 51 - 2
app/Http/Controllers/PriceModelController.php

@@ -3,12 +3,14 @@
 namespace App\Http\Controllers;
 
 use App\Imports\ExpressImport;
+use App\Imports\OwnerPriceLogisticDetailImport;
 use App\Owner;
 use App\OwnerOutStorageRule;
 use App\OwnerPriceExpress;
 use App\OwnerPriceExpressProvince;
 use App\OwnerPriceLogistic;
 use App\OwnerPriceOperation;
+use App\Services\common\ExportService;
 use App\Services\OwnerOutStorageRuleService;
 use App\Services\OwnerPriceOperationService;
 use Illuminate\Database\Eloquent\Builder;
@@ -589,6 +591,47 @@ class PriceModelController extends Controller
 
     public function expressExport($id)
     {
+        if(!Gate::allows('计费模型-物流-查询')){ return ["success"=>false,"data"=>"无权操作"];  }
+        $model = app("OwnerPriceExpressService")->find($id,[
+            "owners","logistics","initialWeightUnit","additionalWeightUnit","details"=>function($query){
+                /** @var Builder $query */
+                $query->with("province");
+            }]);
+        $row = ["客户","首重单位","续重单位","承运商"];
+        $list = [[
+            implode(",",array_column($model->owners->toArray(),"name")),
+            $model->initialWeightUnit ? $model->initialWeightUnit->name : '',
+            $model->additionalWeightUnit ? $model->additionalWeightUnit->name : '',
+            implode(",",array_column($model->logistics->toArray(),"name")),
+        ],[
+            "价格名称","省","首重价格","续重价格",
+        ]];
+        foreach ($model->details as $detail){
+            $list[] = [
+                $model->name,
+                $detail->province ? $detail->province->name : '',
+                $detail->initial_weight_price,
+                $detail->additional_weight_price,
+            ];
+        }
+        return app(ExportService::class)->json($row,$list,"快递计费模型");
+    }
+
+    public function logisticImport(Request $request)
+    {
+        if(!Gate::allows('计费模型-物流-录入')){ return ["success"=>false,"data"=>"无权操作"];  }
+        $fileSuffix=$request->file('file')->getClientOriginalExtension();
+        if ($fileSuffix != 'xlsx' && $fileSuffix != 'xls' && $fileSuffix != 'csv')
+            return ['success'=>false,'data'=>'不支持该文件类型'];
+        ini_set('max_execution_time',2500);
+        ini_set('memory_limit','1526M');
+        $fileSuffix = ucwords($fileSuffix);
+        /** @var OwnerPriceLogistic $model */
+        $model = app('OwnerPriceLogisticService')->find($request->input("id"),["unit","otherUnit","details"]);
+        Excel::import(new OwnerPriceLogisticDetailImport($model),$request->file('file')->path(),null,$fileSuffix);
+        if (Cache::has('logistic'))return Cache::pull('logistic');
+
+        return ["success"=>false,"data"=>"导入发生错误,数据无响应"];
     }
 
     private function logisticValidator(array $params,$id = null)
@@ -599,9 +642,15 @@ class PriceModelController extends Controller
             'fuel_price'=>['nullable','numeric','min:0'],
             'service_price'=>['nullable','numeric','min:0'],
             'unit_id'=>['required'],
-            'unit_range'=>['required'],
+            'unit_range'=>['required',function ($attribute, $value, $fail) {
+                $bool = app("OwnerPriceLogisticService")->checkRange($value);
+                if (!$bool)$fail("格式错误,值必须为连续的且最后一个值不允许封闭");
+            }],
             'other_unit_id'=>['required'],
-            'other_unit_range'=>['required'],
+            'other_unit_range'=>['required',function ($attribute, $value, $fail) {
+                $bool = app("OwnerPriceLogisticService")->checkRange($value);
+                if (!$bool)$fail("格式错误,值必须为连续的且最后一个值不允许封闭");
+            }],
         ],[
             'required'=>':attribute 为必填项',
             'unique' => ':attribute 已存在',

+ 11 - 6
app/Http/Controllers/TestController.php

@@ -8,6 +8,7 @@ use App\Batch;
 use App\City;
 use App\Commodity;
 use App\CommodityBarcode;
+use App\Customer;
 use App\Events\CancelOrder;
 use App\InventoryAccountMission;
 use App\InventoryCompare;
@@ -97,11 +98,17 @@ class TestController extends Controller
     }
 
     public function test2(){
-        $a = Unit::query()->first();
+        $b = Logistic::query()->first();
+        $a = OrderPackage::query()->with("order")->first();
+        $a->bulk = 521;
+        $a->save();
+        if (!$a->order) $a->order = new Order();
+        dd($a);
+        if (!$a->order->logistic)$a->order->logistic = $b;
+        dd($a->order->logistic);
+        dd($a);
+        $a->save();
         dd($a);
-        /** @var Process $process */
-        $process = Process::query()->first();
-        event(new ResetProcessStatisticStartDateEvent($process));
     }
 
     function packageFromLog(Request $request)
@@ -129,8 +136,6 @@ class TestController extends Controller
                 $uploaded += 1;
         });
         dd($uploaded . '/' . $count);
-
-
     }
 
     function wmsSql()

+ 1 - 1
app/Http/Controllers/api/thirdPart/weight/PackageController.php

@@ -260,6 +260,7 @@ class PackageController extends Controller
             $package->fetchAllFromOracle();
             $package->fetchPaperBox();
             try{
+                $package->save();
                 $package->load(['order'=>function($query){
                     $query->with('owner','logistic');
                 },'paperBox','measuringMachine']);
@@ -270,7 +271,6 @@ class PackageController extends Controller
                 if (!$package->order->logistic){
                     $package->order->logistic = $logisticNumberController->getLogisticByFeatures($package->logistic_number);
                 }
-                $package->save();
             }catch (\Exception $e){
                 $response=["msg"=>$e->getMessage(),"code"=>500,"data"=>$e->getTraceAsString()];
                 app('LogService')->log(__METHOD__,'weightApi(ERROR)'.__FUNCTION__,json_encode($request).'||'.json_encode($response).'||'.$e->getTraceAsString(),null);

+ 186 - 0
app/Imports/OwnerPriceLogisticDetailImport.php

@@ -0,0 +1,186 @@
+<?php
+
+namespace App\Imports;
+
+use App\City;
+use App\OwnerPriceLogistic;
+use App\OwnerPriceLogisticDetail;
+use App\Province;
+use App\Services\common\BatchUpdateService;
+use App\Services\LogService;
+use Illuminate\Support\Collection;
+use Illuminate\Support\Facades\Cache;
+use Maatwebsite\Excel\Concerns\ToCollection;
+use Maatwebsite\Excel\Concerns\WithHeadingRow;
+use Maatwebsite\Excel\Imports\HeadingRowFormatter;
+
+
+HeadingRowFormatter::default('none');
+class OwnerPriceLogisticDetailImport implements ToCollection,WithHeadingRow
+{
+    protected $logistic;
+    public function __construct(OwnerPriceLogistic $logistic = null)
+    {
+        $this->logistic = $logistic;
+    }
+
+    /**
+    * @param Collection $collection
+    * @return bool
+    */
+    public function collection(Collection $collection)
+    {
+
+        if (!$this->logistic){
+            Cache::put("logistic",["success"=>false, "data"=>"不存在父级"],86400);
+            return false;
+        }
+        $row = $collection->first();
+        $header = [
+            "计数单位","计数区间","省份","市","单价","送货费","起始计费","起始计数","费率"
+        ];
+        foreach ($header as $str){
+            if (!isset($row[$str])){
+                Cache::put("logistic",["success"=>false, "data"=>"表头不存在“".$str."”"],86400);
+                return false;
+            }
+        }
+
+        //省份map
+        $map = [];
+        $provinces = Province::query()->get();
+        foreach ($provinces as $province){
+            $map[$province->name] = $province->id;
+        }
+
+        //市map
+        $cityMap = [];
+        $cityMappingProvince = [];
+        $cities = City::query()->get();
+        foreach ($cities as $city){
+            $cityMap[$city->name] = $city->id;
+            $cityMappingProvince[$city->id] = $city->province_id;
+        }
+
+        //对比单位
+        $unit = $this->logistic->unit ? strtoupper($this->logistic->unit->name) : '';
+        $otherUnit = $this->logistic->otherUnit ? strtoupper($this->logistic->otherUnit->name) : '';
+
+        //对比区间
+        $range = [
+            $unit => explode(",",$this->logistic->unit_range),
+            $otherUnit => explode(",",$this->logistic->other_unit_range),
+        ];
+
+        //已存在的计费
+        $existDetails = [];
+        foreach ($this->logistic->details as $detail){
+            $existDetails[$detail->unit_id.'_'.$detail->range."_".$detail->province_id."_".$detail->city_id] = $detail->id;
+        }
+
+        //生成列表内的重复条目
+        $existInsert = [];
+
+        //导入的数据整理,存在更新
+        $id = $this->logistic->id;
+        $errors = [];
+        $insert = [];
+        $update = [["id","unit_price","delivery_fee","initial_fee","initial_amount","rate","updated_at"]];
+        $date = date('Y-m-d H:i:s');
+        foreach ($collection as $index => $item){
+            /* 数据校验 */
+            if (!$item["省份"]){
+                $errors[] = "第“".($index+2)."”行省份为空";
+                continue;
+            }else{
+                if ((!isset($map[$item["省份"]]) && !isset($map[mb_substr($item["省份"], 0,-1)]))){
+                    $errors[] = "第“".($index+2)."”行未知省份";
+                    continue;
+                }
+                if (isset($map[mb_substr($item["省份"], 0,-1)]))$item["省份"] = mb_substr($item["省份"], 0,-1);
+            }
+            if (!$item["计数单位"] || (strtoupper($item["计数单位"]) != $unit && strtoupper($item["计数单位"]) != $otherUnit)){
+                //$errors[] = "第“".($index+2)."”行单位不符合";
+                //continue;
+                Cache::put("logistic",["success"=>false, "data"=>"第“".($index+2)."”行单位不符合"],86400);
+                return false;
+            }
+            if (!$item["计数区间"] || array_search($item["计数区间"],$range[strtoupper($item["计数单位"])]) === false){
+                //$errors[] = "第“".($index+2)."”行非法首重价格";
+                //continue;
+                Cache::put("logistic",["success"=>false, "data"=>"第“".($index+2)."”行区间不符合"],86400);
+                return false;
+            }
+            if (!isset($cityMap[$item["市"]])){
+                $errors[] = "第“".($index+2)."”行未知城市";
+                continue;
+            }
+            if (!$item["单价"] || !is_numeric($item["单价"]) || $item["单价"] <= 0){
+                $errors[] = "第“".($index+2)."”行非法单价";
+                continue;
+            }
+            $numeric = ["送货费","起始计费","起始计数","费率"];
+            $sign = false;
+            foreach ($numeric as $column){
+                if ($item[$column] && (!is_numeric($item[$column]) || $item[$column] <= 0)){
+                    $errors[] = "第“".($index+2)."”行非法".$column;
+                    $sign = true;
+                    break;
+                }
+            }
+            if ($sign)continue;
+
+            /* 数据转换及存在校验 */
+            if ($cityMappingProvince[$cityMap[$item["市"]]] != $map[$item["省份"]]){
+                $errors[] = "第“".($index+2)."”行城市不属于该省份";
+                continue;
+            }
+            $item["省份"] = $map[$item["省份"]];
+            $item["计数单位"] = strtoupper($item["计数单位"]) == $unit ? $this->logistic->unit_id : $this->logistic->other_unit_id;
+            $item["市"] = $cityMap[$item["市"]];
+            $key = $item["计数单位"]."_".$item["计数区间"]."_".$item["省份"]."_".$item["市"];
+            if (isset($existInsert[$key])){
+                $errors[] = "第“".($index+2)."”行重复条目";
+                continue;
+            }
+
+            if (isset($existDetails[$key])){
+                $update[] = [
+                    "id" => $existDetails[$key],
+                    "unit_price" => $item["单价"],
+                    "delivery_fee" => $item["送货费"],
+                    "initial_fee" => $item["起始计费"],
+                    "initial_amount" => $item["起始计数"],
+                    "rate" => $item["费率"],
+                    "updated_at" => $date,
+                ];
+                continue;
+            }
+            $insert[] = [
+                "owner_price_logistic_id" => $id,
+                "unit_id" => $item["计数单位"],
+                "range" => $item["计数区间"],
+                "province_id" => $item["省份"],
+                "city_id" => $item["市"],
+                "unit_price" => $item["单价"],
+                "delivery_fee" => $item["送货费"],
+                "initial_fee" => $item["起始计费"],
+                "initial_amount" => $item["起始计数"],
+                "rate" => $item["费率"],
+                "created_at" => $date,
+            ];
+        }
+        if (count($update) > 1){
+            app(BatchUpdateService::class)->batchUpdate("owner_price_logistic_details",$update);
+            LogService::log(__METHOD__,"物流计费导入修改",json_encode($update));
+        }
+        if (count($insert) > 0){
+            OwnerPriceLogisticDetail::query()->insert($insert);
+            LogService::log(__METHOD__,"物流计费导入录入",json_encode($insert));
+        }
+
+        $this->logistic->load(["details"=>function($query){$query->with(["province","unit","city"]);}]);
+        Cache::put("logistic",["success"=>true,"data"=>$this->logistic->details,"errors"=>$errors],86400);
+        return true;
+    }
+}

+ 26 - 0
app/Services/OwnerPriceLogisticService.php

@@ -29,4 +29,30 @@ Class OwnerPriceLogisticService
         }
         return $query->update($values);
     }
+
+    public function checkRange($range):bool
+    {
+        $arrRanges = explode(",",$range);
+        $len = count($arrRanges);
+        if ($len==0)return false;   //范围为空 false
+        $previous = []; //慢指针数组 不记录第一个值
+        foreach ($arrRanges as $index => $value){
+            if ($index == $len-1)$preg = "/\d*\-/"; //最后一个范围不允许封闭
+            else $preg = "/\d*\-\d*/";
+            preg_match($preg, $value, $match);
+            if (!$match || $match[0]!=$value)return false;//匹配结果不等于值 false
+            $arr = explode("-",$value); //二次切割判断是否连续
+            if ($index != 0){
+                if ($index == $len-1){
+                    if ((int)$arr[0] != (int)$previous[$index-1][1])return false; //最后一个范围只需要判断起始值
+                }else{
+                    if ((int)$arr[0] != (int)$previous[$index-1][1] || (int)$arr[1] <= (int)$arr[0])return false; //普通范围起始值必须为上一个结束值 当前结束值必须大于起始值
+                }
+            }else{
+                if ((int)$arr[1] <= (int)$arr[0])return false; //第一个范围只需判断当前结束值大于起始值
+            }
+            $previous[] = $arr;
+        }
+        return true;
+    }
 }

+ 15 - 12
resources/views/maintenance/priceModel/logistic/_detailModal.blade.php

@@ -1,12 +1,12 @@
-<div class="modal fade" tabindex="-1" role="dialog" id="detailModal">
-    <div class="modal-dialog modal-lg modal-dialog-centered modal-dialog-scrollable">
+<div class="modal fade" tabindex="-1" role="dialog" id="detailModal" v-if="models[index]">
+    <div class="modal-dialog modal-xl modal-dialog-centered modal-dialog-scrollable">
         <div class="modal-content">
             <div class="modal-header">
                 <div class="row w-100">
                     <input id="file" type="file" style="display:none" accept=".csv, .xlsx, .xls" @change="importDetail($event)"/>
                     @can("计费模型-物流-录入")<button type="button" class="btn btn-sm btn-outline-info col-1" @click="selectFile()">导入</button>@endcan
                     <button type="button" class="btn btn-sm btn-outline-dark col-1">导出</button>
-                    <div class="font-weight-bolder offset-3">“<label class="text-primary">@{{ name }}</label>”物流收费标准</div>
+                    <div class="font-weight-bolder offset-3">“<label class="text-primary">@{{ models[index].name }}</label>”物流收费标准</div>
                 </div>
             </div>
             <div class="modal-body">
@@ -19,21 +19,24 @@
                         </div>
                     </div>
                 </div>
+                <div class="w-100 text-primary text-center mt-0" v-else>
+                    <div class="small"><b class="text-dark font-weight-bold">导入导出表头示例:</b>计数单位,计数区间,省份,市,单价,送货费,起始计费,起始计数,费率</div>
+                </div>
                 <table class="table table-sm table-bordered">
                     <tr class="text-success">
                         <th>操作</th>
                         <th>计价单位</th>
                         <th>计价区间</th>
-                        <th>省份</th>
-                        <th>市</th>
+                        <th style="min-width: 100px">省份</th>
+                        <th style="min-width: 100px">市</th>
                         <th>单价</th>
                         <th>送货费</th>
                         <th>起始计费</th>
                         <th>起始计数</th>
-                        <th>费率</th>
+                        <th>费率(%)</th>
                         <th>操作</th>
                     </tr>
-                    <tr v-for="(detail,i) in details[id]">
+                    <tr v-for="(detail,i) in details[models[index].id]">
                         <td>
                             <div v-if="detail.edit">
                                 <button type="button" class="btn btn-sm btn-success" @click="submitDetail(detail)">确定</button>
@@ -46,8 +49,8 @@
                         </td>
                         <td>
                             <label v-if="detail.edit && !detail.id">
-                                <select id="unit_id" class="form-control form-control-sm col-6" v-model="detail.unit_id">
-                                    <option v-for="unit in units" :value="unit.id">@{{ unit.name }}</option>
+                                <select id="unit_id" class="form-control form-control-sm col-6" style="min-width: 80px" v-model="detail.unit_id">
+                                    <option v-for="unit in units" :value="unit.id" v-if="unit.id == models[index].unitId || unit.id == models[index].otherUnitId">@{{ unit.name }}</option>
                                 </select>
                             </label>
                             <label v-else>@{{ detail.unit ? detail.unit.name : '' }}</label>
@@ -59,7 +62,7 @@
                         </td>
                         <td>
                             <label v-if="detail.edit && !detail.id">
-                                <select class="form-control form-control-sm" v-model="detail.province_id">
+                                <select class="form-control form-control-sm" v-model="detail.province_id" @change="$forceUpdate()">
                                     <option v-for="province in provinces" :value="province.id">@{{ province.name }}</option>
                                 </select>
                             </label>
@@ -67,8 +70,8 @@
                         </td>
                         <td>
                             <label v-if="detail.edit && !detail.id">
-                                <select class="form-control form-control-sm" v-model="detail.city_id">
-                                    <option v-for="city in cities" :value="city.id">@{{ city.name }}</option>
+                                <select class="form-control form-control-sm" v-model="detail.city_id" v-if="detail.province_id">
+                                    <option v-for="city in cities[detail.province_id]" :value="city.id">@{{ city.name }}</option>
                                 </select>
                             </label>
                             <label v-else>@{{ detail.city ? detail.city.name : '' }}</label>

+ 77 - 8
resources/views/maintenance/priceModel/logistic/index.blade.php

@@ -31,7 +31,7 @@
             <tr v-for="(model,i) in models">
                 <td>@{{ i+1 }}</td>
                 <td>
-                    <button class="btn btn-sm btn-info" @click="showDetailModal(model)">维护详情</button>
+                    <button class="btn btn-sm btn-info" @click="showDetailModal(model,i)">维护详情</button>
                 </td>
                 <td>@{{ model.name }}</td>
                 <td>
@@ -70,6 +70,8 @@
                     @foreach($models as $model)
                     {   id:"{{$model->id}}",
                         name:"{{$model->name}}",
+                        unitId:"{{$model->unit_id}}",
+                        otherUnitId:"{{$model->other_unit_id}}",
                         unitRange:{!! $model->unit_range_json !!},
                         unitName:"{{$model->unit ? $model->unit->name : ''}}",
                         otherUnitRange:{!! $model->other_unit_range_json !!},
@@ -83,17 +85,18 @@
                     },
                     @endforeach
                 ],
-                id:'',
-                name:"",
+                index:"",
                 details : [],
                 units : null,
                 provinces : null,
                 cities : null,
-                errors:{!! $errors !!}
+                errors:{!! $errors !!},
+                isShowError : false,
             },
             methods:{
                 _dataReady(){
                     if (this.units === null){
+                        this.units = [];
                         window.axios.post("{{url('maintenance/unit/getUnits')}}")
                             .then(res=>{
                                 if (res.data.success){
@@ -108,22 +111,33 @@
                         });
                     }
                     if (this.provinces === null){
+                        this.provinces = [];
                         window.axios.post('{{url('maintenance/province/get')}}')
                             .then(res=>{
                                 this.provinces = res.data;
                             });
                     }
                     if (this.cities === null){
+                        this.cities = [];
                         window.axios.post('{{url('maintenance/city/get')}}')
                             .then(res=>{
-                                this.cities = res.data;
+                                let cities = [];
+                                res.data.forEach(function (city) {
+                                    if (city.province_id){
+                                        if (cities[city.province_id]){
+                                            cities[city.province_id].push(city)
+                                        }else{
+                                            cities[city.province_id] = [city];
+                                        }
+                                    }
+                                });
+                                this.cities = cities;
                             });
                     }
                 },
-                showDetailModal(model){
+                showDetailModal(model,index){
                     this._dataReady();
-                    this.id = model.id;
-                    this.name = model.name;
+                    this.index = index;
                     if (this.details[model.id]){
                         $("#detailModal").modal("show");
                         return;
@@ -164,6 +178,61 @@
                 edit(id){
                     window.location.href = "{{url('maintenance/priceModel/logistic')}}/"+id+"/edit";
                 },
+                selectFile(){
+                    $("#file").click();
+                },
+                importDetail(e){
+                    let file=e.target.files[0];
+                    if (!file){
+                        tempTip.setDuration(3000);
+                        tempTip.setIndex(1099);
+                        tempTip.show("未选择文件");
+                        return;
+                    }
+                    let formData = new FormData();
+                    formData.append("file",file);
+                    formData.append("id",this.id);
+                    window.tempTip.setIndex(1099);
+                    window.tempTip.setDuration(9999);
+                    window.tempTip.waitingTip("执行中,请耐心等候......");
+                    axios.post('{{url('maintenance/priceModel/logistic/import')}}',formData,{
+                        'Content-Type':'multipart/form-data'
+                    })
+                        .then(res=>{
+                            if (res.data.success) {
+                                this.details[this.id] = res.data.data;
+                                this.errors = res.data.errors;
+                                tempTip.cancelWaitingTip();
+                                tempTip.setDuration(2000);
+                                tempTip.showSuccess("导入成功!");
+                                return;
+                            }
+                            tempTip.cancelWaitingTip();
+                            tempTip.setDuration(3000);
+                            tempTip.show(res.data.data);
+                        }).catch(err=> {
+                        tempTip.cancelWaitingTip();
+                        tempTip.setDuration(3000);
+                        tempTip.show("网络错误:"+err);
+                    })
+                },
+                addDetail(){
+                    this._dataReady();
+                    this.details[this.models[this.index].id].unshift({
+                        "id" : "",
+                        "unit_id" : "",
+                        "range" : "",
+                        "province_id" : "",
+                        "city_id" : "",
+                        "unit_price" : "",
+                        "delivery_fee" : "",
+                        "initial_fee" : "",
+                        "initial_amount" : "",
+                        "rate" : "",
+                        "edit" : true,
+                    });
+                    this.$forceUpdate();
+                },
             },
         });
     </script>

+ 1 - 0
routes/web.php

@@ -125,6 +125,7 @@ Route::group(['prefix'=>'maintenance'],function(){
             Route::get('{id}/edit','PriceModelController@logisticEdit');
             Route::post('{id}/edit','PriceModelController@logisticUpdate');
             Route::post('getDetail','PriceModelController@logisticGetDetail');
+            Route::post('import','PriceModelController@logisticImport');
         });
         Route::get('logistic','PriceModelController@logisticIndex');
         Route::post('logistic','PriceModelController@logisticStore');