Pārlūkot izejas kodu

客户管理-按单计价,满减策略

Zhouzhendong 5 gadi atpakaļ
vecāks
revīzija
22f756a36f

+ 2 - 2
app/Http/Controllers/TestController.php

@@ -48,6 +48,7 @@ use App\OrderPackageCommodities;
 use App\OrderPackageCountingRecord;
 use App\OrderTracking;
 use App\Owner;
+use App\OwnerFeeDetail;
 use App\OwnerPriceOperation;
 use App\OwnerPriceOperationItem;
 use App\Package;
@@ -131,8 +132,7 @@ class TestController extends Controller
     }
 
     public function zzd(){
-        $month = "2020-02";
-        dd("50"*"2");
+        dd(substr("2021-01-28",0,7) == date("Y-m"));
     }
 
     public function syncWeight()

+ 4 - 0
app/OwnerFeeDetail.php

@@ -49,4 +49,8 @@ class OwnerFeeDetail extends Model
     {   //作业类型
         return $this->hasOne(ProcessMethod::class,"id","process_method_id");
     }
+    public function order()
+    {   //出库单
+        return $this->belongsTo(Order::class,"outer_id","id");
+    }
 }

+ 1 - 0
app/OwnerPriceOperation.php

@@ -18,6 +18,7 @@ class OwnerPriceOperation extends Model
         "remark",           //备注
         "priority",         //优先级 值越大越高
         "discount_count",   //减免值
+        "total_price",      //按单计价
     ];
     public static $features = null;
     public static $columnMapping = null;

+ 0 - 1
app/Services/FeatureService.php

@@ -224,7 +224,6 @@ Class FeatureService
             return $features;
         });
 
-
         foreach ($result[0] as &$str) {
             if (is_numeric($str) && isset($features[$str])) {
                 $column = $features[$str]["type"];

+ 23 - 6
app/Services/OrderService.php

@@ -1007,11 +1007,16 @@ class OrderService
      */
     public function createInstantBill(Order $order):bool
     {
+        /** @var \stdClass $order */
         //检查订单对象
         if (!$order || $order->wms_status != "订单完成")return false;
         if (OwnerFeeDetail::query()->where("outer_table_name","orders")->where("outer_id",$order->id)->first())return false;
 
-        $order->loadMissing(["logistic","packages.commodities.commodity"]);
+        $key = date("Y-m")."_".$order->owner_id;
+        if (Cache::has($key))Cache::increment($key);
+        else Cache::put($key,1,2678400);
+
+        $order->loadMissing(["logistic","shop","packages.commodities.commodity"]);
 
         /** @var OwnerPriceExpressService $service */
         $service = app("OwnerPriceExpressService");
@@ -1033,7 +1038,6 @@ class OrderService
             foreach($package->commodities as &$commodity){
                 $commodity["commodity_name"] = $commodity->commodity ? $commodity->commodity->name : '';
                 $commodity["sku"] = $commodity->commodity ? $commodity->commodity->sku : '';
-                $commodity["amount"] = $commodity->amount;
                 $amount += $commodity->amount;
             }
             $commodities = array_merge($commodities,$package->commodities->toArray());
@@ -1053,13 +1057,16 @@ class OrderService
         }
         if ($logistic_fee!==null && $logistic_fee<0)$logistic_fee = null;
 
-        $object = ["commodities"=>$commodities,"logistic_name"=>($order->logistic ? $order->logistic->name : ''),"shop_name"=>($order->shop ? $order->shop->name : ''),"order_type"=>$order->order_type];
+        $object = ["commodities"=>$commodities,
+            "logistic_name"=>($order->logistic ? $order->logistic->name : ''),
+            "shop_name"=>($order->shop ? $order->shop->name : ''),
+            "order_type"=>$order->order_type,
+            "owner_id"=>$order->owner_id];
         $mapping = ["packages"=>"commodities","商品名称"=>"commodity_name","承运商"=>"logistic_name","店铺类型"=>"shop_name","订单类型"=>"order_type","订单数"=>"amount"];
 
         /** @var OwnerPriceOperationService $service */
         $service = app("OwnerPriceOperationService");
-        $work_fee = $service->matching($object,$mapping,$order->owner_id,"出库");
-        if ($work_fee < 0)$work_fee = null;
+        $result = $service->matching($object,$mapping,$order->owner_id,"出库");
 
        if (app("OwnerFeeDetailService")->create([
             "owner_id"          => $order->owner_id,
@@ -1074,7 +1081,8 @@ class OrderService
             "volume"            => $volume,
             "weight"            => $weight,
             "logistic_id"       => $order->logistic_id,
-            "work_fee"          => $work_fee,
+            "work_fee"          => is_array($result) ? ($result["money"]>0 ? $result["money"] : null) : null,
+            "owner_price_operation_id"  => is_array($result) ? $result["id"] : null,
             "logistic_fee"      => $logistic_fee,
             "created_at"        => date('Y-m-d H:i:s'),
             "outer_id"          => $order->id,
@@ -1083,4 +1091,13 @@ class OrderService
        return false;
     }
 
+    public function getMonthTotal($ownerId = null, $month = null)
+    {
+        if (!$month)$month = date("Y-m");
+        $day = date("t",strtotime($month));
+        $whereSql = $ownerId ? "and owner_id = ? " : " ";
+        $count = DB::selectOne(DB::raw("SELECT count(*) c from orders where wms_status ='订单完成' {$whereSql} and wms_edittime BETWEEN '{$month}-01 00:00:00' and '{$month}-{$day} 23:59:59';"));
+        return $count->c;
+    }
+
 }

+ 87 - 57
app/Services/OwnerPriceOperationService.php

@@ -2,12 +2,14 @@
 
 namespace App\Services;
 
+use App\OwnerFeeDetail;
 use App\OwnerPriceOperation;
 use App\OwnerPriceOperationItem;
 use App\Services\common\QueryService;
 use App\Unit;
 use Illuminate\Database\Eloquent\Builder;
 use Illuminate\Database\Eloquent\Model;
+use Illuminate\Support\Facades\Cache;
 use Illuminate\Support\Facades\DB;
 use App\Traits\ServiceAppAop;
 
@@ -86,7 +88,7 @@ Class OwnerPriceOperationService
 
     /** 参数顺序: 数量 匹配对象 列映射 货主ID 单位ID 类型 SKU .
      *  匹配顺序: 类型 货主 策略 单位 特征                    ..多对多匹配规则废弃,1对1,设单位必定为件,对应规则必然只有一项存在
-     *  单位匹配: 件,箱,单 由小到大,依次换算匹配           .
+     *  单位匹配: 件,箱 由小到大,依次换算匹配           .
      *
      *  2:没有总数量存在,都为子项内数量
      *
@@ -94,15 +96,18 @@ Class OwnerPriceOperationService
      * @param array $columnMapping       key-val
      * @param string $owner_id
      * @param string $type
-     * @return double
+     * @return double|array
      * 错误代码: -1:无匹配对象 -2:无计费模型 -3:未知单位 -4:sku为空 -5:货主未找到 -6:无箱规 -7:未匹配到计费模型
      *
      * 一. 2020-10-10 zzd
      * 二. 2021-01-08 zzd
+     * 三. 2021-01-28 zzd
+     *      增加满减策略:子策略匹配时不再考虑单,仅件箱换算,满减满足后标记模型修改历史对账单
+     *      增加按订单计价策略:主匹配模型增加字段量价,该字段存在时视为按单计价,价格为该值
      */
     public function matching($matchObject, $columnMapping, $owner_id, $type = '出库')
     {
-        $unitModels = Unit::query()->whereIn("name",["件","箱","单"])->get();
+        $unitModels = Unit::query()->whereIn("name",["件","箱"])->get();
         $units = [];
         foreach ($unitModels as $unitModel)$units[$unitModel->id] = $unitModel->name;
 
@@ -114,69 +119,80 @@ Class OwnerPriceOperationService
                 $query->where("id",$owner_id);
         })->orderByRaw("strategy desc,priority desc")->get(); //货主下的全部规则
 
-        if (!$rules)return -2;
+        if (!$rules)return -2;  //规则不存在跳出
 
-        /*if ($type == '入库'){
-            $amountColumn = $columnMapping["amount"] ?? "amount";
-            $packageColumn = $columnMapping["packages"] ?? "packages";
-            $packages = $matchObject[$packageColumn] ?? false;
-            if (!$packages)return -1;
+        $total = Cache::get(date("Y-m")."_".$matchObject["owner_id"]); //获取该货主本月单量
+        foreach ($rules as $rule){
+            if (!$rule->items)continue; //不存在子规则跳出
+            $isDiscount = false;    //是否存在满减
+            if ($type=='出库' && $rule->discount_count > 0 && $total >= $rule->discount_count)$isDiscount = true;//满足满减条件
+            //满减存在且未被标记过处理时间或处理时间不为本月,处理历史即时账单
+            if ($isDiscount && (!$rule->discount_date || substr($rule->discount_date,0,7)!=date("Y-m"))){
+                try{
+                    DB::beginTransaction();
+                    $month = date("Y-m");
+                    $day = date("t",strtotime($month));
+                    foreach (OwnerFeeDetail::query()->with(["order.logistic","order.shop","order.packages.commodities.commodity"])
+                                 ->where("owner_id",$owner_id)
+                                 ->whereBetween("worked_at",[$month."-01",$month."-".$day])->get() as $detail){
+                        $order = $detail->order;
 
-            $amount = 0;
-            foreach ($packages as $package)$amount += $package[$amountColumn] ?? 0;
-            if (!$amount)return -1;
-            foreach ($rules as $rule){
-                $sum = $amount;
-                if (!$rule->in)continue;
-                if ($rule->strategy == '特征'){
-                    $bool = app("FeatureService")->matchFeature($rule->feature,$columnMapping,$matchObject);
-                    if ($bool === true){
-                        if (!isset($units[$rule->in->unit_id])) return -3;
-                        if ($units[$rule->in->unit_id] == '箱'){ //为箱时同步商品寻找箱规
-                            $sumTemp = 0;
-                            $packageColumn = $columnMapping["packages"] ?? "packages";
-                            foreach ($matchObject[$packageColumn] as $commodity){
-                                $sumTemp += $this->changeUnit($sum,$owner_id,$commodity["sku"]);
+                        $logistic_fee = 0;
+                        $commodities = [];
+                        foreach ($order->packages as &$package){
+                            // 四维转二维
+                            foreach($package->commodities as &$commodity){
+                                $commodity["commodity_name"] = $commodity->commodity ? $commodity->commodity->name : '';
+                                $commodity["sku"] = $commodity->commodity ? $commodity->commodity->sku : '';
                             }
-                            $sum = $sumTemp;
-                            if ($sum<0)return $sum;
+                            $commodities = array_merge($commodities,$package->commodities->toArray());
                         }
-                        if ($units[$rule->in->unit_id] == '单')$sum = 1; //为单时数量设为1;
-                        return ceil($sum/$rule->in->amount)*$rule->in->unit_price;
+                        if ($logistic_fee!==null && $logistic_fee<0)$logistic_fee = null;
+
+                        $object = ["commodities"=>$commodities,
+                            "logistic_name"=>($order->logistic ? $order->logistic->name : ''),
+                            "shop_name"=>($order->shop ? $order->shop->name : ''),
+                            "order_type"=>$order->order_type,
+                            "owner_id"=>$order->owner_id];
+                        $mapping = ["packages"=>"commodities","商品名称"=>"commodity_name",
+                            "承运商"=>"logistic_name", "店铺类型"=>"shop_name",
+                            "订单类型"=>"order_type","订单数"=>"amount"];
+                        $money = $this->matchItem($rule->items,$mapping,$object,$units,$owner_id,'出库',$isDiscount);
+                        if ($money>0)$detail->update(["work_fee"=>$money]);
+                        else LogService::log(__CLASS__,"处理历史即时账单时发生匹配错误","账单主键:".$detail->id."; 错误代码".$money);
                     };
-                }else{
-                    if (!isset($units[$rule->in->unit_id])) return -3;
-                    if ($units[$rule->in->unit_id] == '箱'){ //为箱时同步商品寻找箱规
-                        $sumTemp = 0;
-                        $packageColumn = $columnMapping["packages"] ?? "packages";
-                        foreach ($matchObject[$packageColumn] as $commodity){
-                            $sumTemp += $this->changeUnit($sum,$owner_id,$commodity["sku"]);
-                        }
-                        $sum = $sumTemp;
-                        if ($sum<0)return $sum;
-                    }
-                    if ($units[$rule->in->unit_id] == '单')$sum = 1; //为单时数量设为1;
-                    return ceil($sum/$rule->in->amount)*$rule->in->unit_price;
-                };
+                    $rule->update(["discount_date"=>date("Y-m-d")]);
+                    DB::commit();
+                }catch (\Exception $e){
+                    DB::rollBack();
+                    LogService::log(__CLASS__,"处理历史即时账单时发生系统错误","计费模型主键:".$rule->id."; 错误信息".$e->getMessage());
+                }
             }
-            return -7;
-        }*/
-        foreach ($rules as $rule){
-
-            if (!$rule->items)continue;
-            if ($rule->strategy == '特征'){
+            if ($rule->strategy == '特征'){//特征策略匹配
                 $bool = app("FeatureService")->matchFeature($rule->feature,$columnMapping,$matchObject);
                 if ($bool === true){
-                    $money = $this->matchItem($rule->items,$columnMapping,$matchObject,$units,$owner_id,$type=='入库' ? true : false);
-                    if ($money>0)return $money;
+                    if ($rule->total_price)return $rule->total_price; //按单计价存在,直接返回单总价
+                    $money = $this->matchItem($rule->items,$columnMapping,$matchObject,$units,$owner_id,$type=='入库' ? true : false,$isDiscount);
+                    if ($money>0)return ["id"=>$rule->id,"money"=>$money];
                 };
-            }else{
-                $money = $this->matchItem($rule->items,$columnMapping,$matchObject,$units,$owner_id,$type=='入库' ? true : false);
-                if ($money>0)return $money;
+            }else{//默认策略匹配
+                if ($rule->total_price)return $rule->total_price; //按单计价存在,直接返回单总价
+                $money = $this->matchItem($rule->items,$columnMapping,$matchObject,$units,$owner_id,$type=='入库' ? true : false,$isDiscount);
+                if ($money>0)return ["id"=>$rule->id,"money"=>$money];
             };
         }
         return $money ?? -7;
     }
+    /**
+     * 根据货主 sku寻找箱规并将指定数量切换为箱
+     *      不满一箱视为一箱
+     *
+     * @param integer $amount
+     * @param integer $owner_id
+     * @param string $sku
+     *
+     * @return int
+     */
     private function changeUnit($amount,$owner_id,$sku)
     {
         if (!$sku)return -4;
@@ -185,7 +201,20 @@ Class OwnerPriceOperationService
         return ceil($amount/$pack);
     }
 
-    private function matchItem($rules, $columnMapping, $matchObject, $units, $owner_id, $isIn)
+    /**
+     * 匹配子策略
+     *
+     * @param array $rules 策略对象组
+     * @param array $columnMapping 映射对象
+     * @param array $matchObject 被匹配对象
+     * @param array $units 单位集
+     * @param integer $owner_id 货主ID
+     * @param bool $isIn 是否为入库单
+     * @param bool $isDiscount 是否为满减单
+     *
+     * @return double
+     */
+    private function matchItem($rules, $columnMapping, $matchObject, $units, $owner_id, $isIn, $isDiscount)
     {
         $amountColumn = $columnMapping["amount"] ?? "amount";
         $packageColumn = $columnMapping["packages"] ?? "packages";
@@ -195,6 +224,7 @@ Class OwnerPriceOperationService
 
         $unitName = "";
         foreach ($rules as $rule){
+            if ($isDiscount)$rule->unit_price = $rule->discount_price; //满足满减条件,单价调整为满减单价
             switch ($rule->strategy){
                 case "特征":
                     $inMoney = 0;
@@ -208,7 +238,7 @@ Class OwnerPriceOperationService
                                 return -3;
                         }
                         $package["price"] = $rule->unit_price;
-                        if (!isset($units[$rule->unit_id]) || $units[$rule->unit_id] == '单')return -3;
+                        if (!isset($units[$rule->unit_id]))return -3;
                         if ($units[$rule->unit_id] == '箱'){ //为箱时同步商品寻找箱规
                             $sumTemp = 0;
                             $packageColumn = $columnMapping["packages"] ?? "packages";
@@ -236,7 +266,7 @@ Class OwnerPriceOperationService
                         }
 
                         $package["price"] = $rule->unit_price;
-                        if (!isset($units[$rule->unit_id]) || $units[$rule->unit_id] == '单')return -3;
+                        if (!isset($units[$rule->unit_id]))return -3;
                         if ($units[$rule->unit_id] == '箱'){ //为箱时同步商品寻找箱规
                             $sumTemp = 0;
                             $packageColumn = $columnMapping["packages"] ?? "packages";
@@ -281,7 +311,7 @@ Class OwnerPriceOperationService
         }
         return -7;
     }
-    //设置数量
+    //递归式重新设置数量
     private function settingCount($packages,$amountColumn,$startNumber)
     {
         if (!$packages) return null;

+ 3 - 3
app/Services/StoreService.php

@@ -321,8 +321,7 @@ Class StoreService
 
         $mapping = ["packages" => "storeItems", "商品名称" => "name", "订单类型" => "stored_method", "订单数"=>"amount"];
 
-        $work_fee = $service->matching($store, $mapping, $store->owner_id, "入库");
-        if ($work_fee < 0) $work_fee = null;
+        $result = $service->matching($store, $mapping, $store->owner_id, "入库");
 
         if (app("OwnerFeeDetailService")->create([
             "owner_id" => $store->owner_id,
@@ -330,7 +329,8 @@ Class StoreService
             "type" => "收货",
             "operation_bill" => $store->asn_code,
             "commodity_amount" => array_sum(array_column($store->storeItems->toArray(), "amount")),
-            "work_fee" => $work_fee,
+            "work_fee" => is_array($result) ? ($result["money"]>0 ? $result["money"] : null) : null,
+            "owner_price_operation_id" => is_array($result) ? $result["id"] : null,
             "created_at" => date('Y-m-d H:i:s'),
             "outer_id" => $store->id,
             "outer_table_name" => "stores",

+ 44 - 0
database/migrations/2021_01_28_162431_add_column_discount_date.php

@@ -0,0 +1,44 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+class AddColumnDiscountDate extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::table('owner_price_operations', function (Blueprint $table) {
+            $table->date("discount_date")->nullable()->comment("满减标记日");
+            $table->decimal("total_price")->nullable()->comment("按单计价");
+        });
+        Schema::table('owner_fee_details', function (Blueprint $table) {
+            $table->bigInteger("owner_price_operation_id")->nullable()->comment("引用的计费模型");
+            $table->dropIndex(["worked_at"]);
+            $table->index(["worked_at","owner_price_operation_id"]);
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::table('owner_price_operations', function (Blueprint $table) {
+            $table->dropColumn("discount_date");
+            $table->dropColumn("total_price");
+        });
+        Schema::table('owner_fee_details', function (Blueprint $table) {
+            $table->dropColumn("owner_price_operation_id");
+            $table->dropIndex([["worked_at","owner_price_operation_id"]]);
+            $table->index(["worked_at"]);
+        });
+    }
+}

+ 11 - 4
resources/views/customer/project/create.blade.php

@@ -607,10 +607,17 @@
                         this.$forceUpdate();
                         return;
                     }
-                    if ((this.model.operation.operation_type === '出库' && this._verifyOperationItem(0)) || this._verifyOperationItem(1))return;
-                    if (this.model.operation.items.length>2){
-                        for (let i=2;i<this.model.operation.items.length;i++){
-                            if (this._verifyOperationItem(i))return;
+                    if (this.model.operation.isSingle && !this.model.operation.total_price){
+                        this.errors["total_price"] = ["按单价格不存在"];
+                        this.$forceUpdate();
+                        return;
+                    }
+                    if (!this.model.operation.total_price){
+                        if ((this.model.operation.operation_type === '出库' && this._verifyOperationItem(0)) || this._verifyOperationItem(1))return;
+                        if (this.model.operation.items.length>2){
+                            for (let i=2;i<this.model.operation.items.length;i++){
+                                if (this._verifyOperationItem(i))return;
+                            }
                         }
                     }
                     let url = "{{url('maintenance/priceModel/apiStoreOperation')}}";

+ 9 - 0
resources/views/customer/project/part/_operation.blade.php

@@ -42,6 +42,15 @@
     </div>
 </div>
 <div class="row mt-3">
+    <label class="col-2" for="isSingle">按单计价</label>
+    <input type="checkbox" id="isSingle" class="col-2 rounded mt-1" v-model="model.operation.isSingle">
+    <div class="col-8 row" v-if="model.operation.isSingle">
+        <label class="col-3" for="discount">价格</label>
+        <input id="discount" v-model="model.operation.total_price"
+               class="form-control form-control-sm col-6" :class="errors.total_price ? 'is-invalid' : ''" step="0.001" type="number" min="0">
+    </div>
+</div>
+<div class="row mt-3" v-if="!model.operation.isSingle">
     {{--起步--}}
     <div class="card row text-white offset-1 col-9 bg-dark" v-if="model.operation.operation_type === '出库'">
         <div class="card-header" style="max-height: 50px">