Преглед на файлове

Merge branch 'zzd-temp'

# Conflicts:
#	app/Http/Controllers/TestController.php
zhouzhendong преди 4 години
родител
ревизия
7c1b597598
променени са 28 файла, в които са добавени 1004 реда и са изтрити 491 реда
  1. 22 18
      app/Console/Commands/CreateOwnerBillReport.php
  2. 11 14
      app/Jobs/ResetInstantBill.php
  3. 19 0
      app/OwnerFeeExpress.php
  4. 38 0
      app/OwnerFeeLogistic.php
  5. 30 0
      app/OwnerFeeOperation.php
  6. 21 0
      app/OwnerFeeOperationDetail.php
  7. 18 0
      app/OwnerFeeStorage.php
  8. 1 1
      app/OwnerPriceOperation.php
  9. 16 0
      app/Services/CityService.php
  10. 172 120
      app/Services/OrderService.php
  11. 21 25
      app/Services/OwnerPriceExpressService.php
  12. 28 24
      app/Services/OwnerPriceLogisticService.php
  13. 210 111
      app/Services/OwnerPriceOperationService.php
  14. 29 0
      app/Services/OwnerService.php
  15. 15 12
      app/Services/OwnerStoragePriceModelService.php
  16. 17 0
      app/Services/ProvinceService.php
  17. 15 1
      app/Services/RejectedBillService.php
  18. 56 138
      app/Services/StoreService.php
  19. 0 17
      app/Services/TestService.php
  20. 26 2
      app/Services/UnitService.php
  21. 31 4
      app/Services/WaybillService.php
  22. 35 0
      database/migrations/2021_08_11_173608_create_owner_fee_storages_table.php
  23. 43 0
      database/migrations/2021_08_12_140948_create_owner_fee_expresses_table.php
  24. 52 0
      database/migrations/2021_08_12_141004_create_owner_fee_logistics_table.php
  25. 39 0
      database/migrations/2021_08_13_093747_create_owner_fee_operations.php
  26. 35 0
      database/migrations/2021_08_13_093836_create_owner_fee_operation_details.php
  27. 1 1
      resources/views/customer/project/index.blade.php
  28. 3 3
      resources/views/customer/project/part/_operation.blade.php

+ 22 - 18
app/Console/Commands/CreateOwnerBillReport.php

@@ -3,6 +3,7 @@
 namespace App\Console\Commands;
 
 use App\OwnerAreaReport;
+use App\OwnerFeeStorage;
 use App\OwnerPriceSystem;
 use App\Services\LogService;
 use Carbon\Carbon;
@@ -51,22 +52,30 @@ class CreateOwnerBillReport extends Command
         $sql = "SELECT owner_id,SUM(IFNULL(work_fee,0)) AS work_fee,SUM(IFNULL(logistic_fee,0)) AS logistic_fee FROM owner_fee_details WHERE worked_at LIKE ? AND ((type = '发货' AND logistic_fee IS NOT NULL AND work_fee IS NOT NULL) OR (type <> '发货' AND work_fee IS NOT NULL))  GROUP BY owner_id";
         $billDetails = DB::select(DB::raw($sql),[$year."-".$lastMonth."%"]);
 
-        $areas = OwnerAreaReport::query()->with(["ownerStoragePriceModel.timeUnit","ownerStoragePriceModel.taxRate"])->where("counting_month","like",$year."-".$lastMonth."%")->get();
+        $areas = OwnerAreaReport::query()->with(["ownerStoragePriceModel.timeUnit","ownerStoragePriceModel.taxRate","ownerStoragePriceModel.unit"])
+            ->where("counting_month","like",$year."-".$lastMonth."%")->get();
         $map = [];
         $mapTax = [];
         foreach($areas as $area){
+            if (!$area->ownerStoragePriceModel)continue;
+            //信息提取模板
+            $GLOBALS["FEE_INFO"] = [
+                "area_id"           =>$area->id,
+                "counting_type"     =>$area->ownerStoragePriceModel->counting_type,
+                "using_type"        =>$area->ownerStoragePriceModel->using_type,
+                "fee_description"   =>"",
+                "total_fee"         =>0,
+                "tax_rate"          =>0,
+            ];
             $key = $area->owner_id."_".$area->counting_month;
-            if (isset($map[$key])){
-                if (!$area->ownerStoragePriceModel)continue;
-                list($money,$taxFee) = app('OwnerStoragePriceModelService')
-                    ->calculationAmount($area->ownerStoragePriceModel,$area->accounting_area,$area->owner_id,$area->counting_month);
-                $map[$key] += $money;
-                $mapTax[$key] += $taxFee;
-            }else{
-                if (!$area->ownerStoragePriceModel)continue;
-                list($map[$key],$mapTax[$key]) = app('OwnerStoragePriceModelService')
-                    ->calculationAmount($area->ownerStoragePriceModel,$area->accounting_area,$area->owner_id,$area->counting_month);
-            }
+            if (!isset($map[$key]))$map[$key] = $mapTax[$key] = 0;
+            list($money,$taxFee) = app('OwnerStoragePriceModelService')
+                ->calculationAmount($area->ownerStoragePriceModel,$area->accounting_area,$area->owner_id,$area->counting_month);
+            $map[$key] += $money;
+            $mapTax[$key] += $taxFee;
+
+            $GLOBALS["FEE_INFO"]["total_fee"] = $money;
+            OwnerFeeStorage::query()->create($GLOBALS["FEE_INFO"]);
         }
         foreach (OwnerPriceSystem::query()->with(["timeUnit","taxRate"])->select("owner_id","usage_fee")->whereNull("operation")->orWhere("operation","")->get() as $system){
             list($systemFee[$system->owner_id],$systemTaxFee[$system->owner_id]) = $this->systemFee($system,$year."-".$lastMonth);
@@ -120,12 +129,7 @@ class CreateOwnerBillReport extends Command
                     $money = $system->usage_fee;
             }
         }
-        if ($system->taxRate)$taxFee = $money * ($system->taxRate->value/100);
-        else{
-            $system->load("owner.taxRate");
-            if ($system->owner && $system->owner->taxRate)$taxFee = $money * ($system->owner->taxRate->value/100);
-            else $taxFee = null;
-        }
+        $taxFee = app("OwnerService")->getTaxRateFee($system, $system->owner_id, $money);
         return array($money,$taxFee);
     }
 }

+ 11 - 14
app/Jobs/ResetInstantBill.php

@@ -49,14 +49,12 @@ class ResetInstantBill implements ShouldQueue
      */
     public function handle()
     {
-        switch ($this->detail->outer_table_name){
+        /*switch ($this->detail->outer_table_name){
             case "orders":
-                /** @var \stdClass $order */
                 //检查订单对象
                 $order = Order::query()->find($this->detail->outer_id);
                 if (!$order || $order->wms_status != "订单完成")break;
                 $order->loadMissing(["logistic","shop","packages.commodities.commodity","batch"]);
-                /** @var OwnerPriceExpressService $service */
                 $service = app("OwnerPriceExpressService");
                 $logistic_fee = 0;
                 $amount = 0;
@@ -109,10 +107,9 @@ class ResetInstantBill implements ShouldQueue
                     }
                     $logisticTaxFee += $tax;
                 }
-                /* 为字母单 且 重量与省份完整 且 重量大于0 且 省份存在 进入子母单计算 */
                 if ($isBunched && !$weightExceptionMark && $weight>0 && $provinceId)list($logistic_fee,$logisticTaxFee) = $service->matching($weight, $order->owner_id, $order->logistic_id, $provinceId);
                 if ($logistic_fee!==null && $logistic_fee<0)$logistic_fee = null;
-                /** @var OwnerPriceOperationService $service */
+
                 $service = app("OwnerPriceOperationService");
                 list($id,$money,$workTaxFee) = $service->matching($order,Feature::MAPPING["order"],$order->owner_id,"出库");
 
@@ -140,14 +137,14 @@ class ResetInstantBill implements ShouldQueue
                 app("OrderService")->setOrderQuantity($order->owner_id,$order->logistic_id);
                 break;
             case "processes":
-                /** @var \stdClass $process */
+
                 $process = Process::query()->with("processStatistic")->find($this->detail->outer_id);
                 $this->detail->update([
                     "work_fee"  => $process->processStatistic ? $process->processStatistic->revenue : null,
                 ]);
                 break;
             case "waybills":
-                /** @var \stdClass $waybill */
+
                 $waybill = Waybill::query()->find($this->detail->outer_id);
                 $waybill->loadMissing(["destinationCity","order.owner"]);
                 if (!$waybill->destinationCity && !$waybill->order)break;
@@ -159,13 +156,13 @@ class ResetInstantBill implements ShouldQueue
                 if ($detail && $detail->logistic_fee !== null)break;
 
                 if ($waybill->type == "专线"){
-                    /** @var OwnerPriceLogisticService $service */
+
                     $service = app("OwnerPriceLogisticService");
                     list($fee,$taxFee) = $service->matching($waybill->carrier_weight_other,$owner_id,$waybill->logistic_id,
                         $waybill->carrier_weight_unit_id_other,$waybill->order ? app("RegionService")->getProvince($waybill->order->province) : $waybill->destinationCity->province_id,
                         $waybill->destination_city_id);
                 }else{
-                    /** @var OwnerPriceDirectLogisticService $service */
+
                     $service = app("OwnerPriceDirectLogisticService");
                     list($fee,$taxFee) = $service->matching($waybill->mileage,$owner_id,$waybill->carType_id);
                 }
@@ -175,12 +172,12 @@ class ResetInstantBill implements ShouldQueue
                 ]);
                 break;
             case "stores":
-                /** @var \stdClass $store */
+
                 $store = Store::query()->find($this->detail->outer_id);
                 if (!$store || $store->status != "已入库") break;
                 $store->loadMissing(["storeItems.commodity","warehouse"]);
 
-                /** @var OwnerPriceOperationService $service */
+
                 $service = app("OwnerPriceOperationService");
                 list($id,$money,$taxFee) = $service->matching($store, Feature::MAPPING["store"], $store->owner_id, "入库");
                 $this->detail->update([
@@ -195,11 +192,11 @@ class ResetInstantBill implements ShouldQueue
                 break;
             case "rejected_bills":
                 $rejectedBill = RejectedBill::query()->find($this->detail->outer_id);
-                /** @var \stdClass $rejectedBill */
+
                 $number = array_column(StoreRejected::query()->where("logistic_number_return",$rejectedBill->logistic_number_return)->get()->toArray(),"store_id");
 
                 foreach (Store::query()->with("storeItems")->whereIn("id",$number)->get() as $store){
-                    /** @var OwnerPriceOperationService $service */
+
                     $service = app("OwnerPriceOperationService");
                     list($id,$money,$taxFee) = $service->matching($store, Feature::MAPPING["store"], $store->owner_id, "入库",0);
                     $bill = OwnerFeeDetail::query()->where("outer_id",$store->id)->where("outer_table_name","stores")->first();
@@ -222,6 +219,6 @@ class ResetInstantBill implements ShouldQueue
                         "work_tax_fee" => $taxFee,
                     ]);
                 }
-        }
+        }*/
     }
 }

+ 19 - 0
app/OwnerFeeExpress.php

@@ -0,0 +1,19 @@
+<?php
+
+namespace App;
+
+use Illuminate\Database\Eloquent\Model;
+
+use App\Traits\ModelLogChanging;
+
+class OwnerFeeExpress extends Model
+{
+    use ModelLogChanging;
+
+    public $timestamps = false;
+    protected $fillable = [
+        "province_id", "logistic_id", "logistic_number",
+        "weight", "initial_weight","initial_weight_price","additional_weight","additional_weight_amount", "additional_weight_price",
+        "total_fee","tax_rate", "created_at","owner_id"
+    ];
+}

+ 38 - 0
app/OwnerFeeLogistic.php

@@ -0,0 +1,38 @@
+<?php
+
+namespace App;
+
+use Illuminate\Database\Eloquent\Model;
+
+use App\Traits\ModelLogChanging;
+
+class OwnerFeeLogistic extends Model
+{
+    use ModelLogChanging;
+
+    public $timestamps=false;
+    protected $fillable = [
+        "province_id",
+        "owner_id",
+        "city_id",
+        "logistic_id",
+        "order_number",
+        "recipient_name",
+        "recipient_phone",
+        "quantity",
+        "unit_id",
+        "interval",
+        "price",
+        "delivery_fee",
+        "pick_fee",
+        "fuel_fee",
+        "info_fee",
+        "other_fee",
+        "initial_fee",
+        "initial_amount",
+        "total_fee",
+        "tax_rate",
+        "remark",
+        "created_at",
+    ];
+}

+ 30 - 0
app/OwnerFeeOperation.php

@@ -0,0 +1,30 @@
+<?php
+
+namespace App;
+
+use Illuminate\Database\Eloquent\Model;
+
+use App\Traits\ModelLogChanging;
+
+class OwnerFeeOperation extends Model
+{
+    use ModelLogChanging;
+
+    public $timestamps = false;
+    protected $fillable = [
+        "worked_at",
+        "model_id",
+        "source_number",
+        "doc_number",
+        "commodity_id",
+        "total_fee",
+        "tax_rate",
+        "fee_description",
+        "owner_id",
+    ];
+
+    public function details()
+    {
+        return $this->hasMany(OwnerFeeOperationDetail::class);
+    }
+}

+ 21 - 0
app/OwnerFeeOperationDetail.php

@@ -0,0 +1,21 @@
+<?php
+
+namespace App;
+
+use Illuminate\Database\Eloquent\Model;
+
+use App\Traits\ModelLogChanging;
+
+class OwnerFeeOperationDetail extends Model
+{
+    use ModelLogChanging;
+
+    public $timestamps = false;
+    protected $fillable = [
+        "owner_fee_operation_id",
+        "name",
+        "amount",
+        "price",
+        "unit_id",
+    ];
+}

+ 18 - 0
app/OwnerFeeStorage.php

@@ -0,0 +1,18 @@
+<?php
+
+namespace App;
+
+use Illuminate\Database\Eloquent\Model;
+
+use App\Traits\ModelLogChanging;
+
+class OwnerFeeStorage extends Model
+{
+    use ModelLogChanging;
+
+    protected $primaryKey = 'area_id';
+    public $timestamps = false;
+    protected $fillable = [
+        "area_id", "counting_type", "using_type", "fee_description", "total_fee", "tax_rate"
+    ];
+}

+ 1 - 1
app/OwnerPriceOperation.php

@@ -22,7 +22,7 @@ class OwnerPriceOperation extends Model
         "total_discount_price",//按单计价减免 array
         "operation",        //操作
         "target_id",        //目标ID
-        "type_mark",        //类型标记
+        "type_mark",        //类型标记 用于某些特殊项的匹配限制 0:退货 例:退货模块的单据进入模型匹配时仅匹配为0的项
         "surcharge",        //附加费
         "surcharge_unit_id",//附加费单位
         "max_fee",          //封顶费

+ 16 - 0
app/Services/CityService.php

@@ -3,6 +3,7 @@
 namespace App\Services;
 
 use App\City;
+use Illuminate\Database\Eloquent\Model;
 use Illuminate\Support\Str;
 use App\Traits\ServiceAppAop;
 
@@ -49,5 +50,20 @@ class CityService
         return null;
     }
 
+    /**
+     * 根据名称获取城市
+     *
+     * @param string|null $name
+     *
+     * @return Model|null
+     */
+    public function getCity(?string $name):?Model
+    {
+        if (!$name)return null;
+        $cityName = mb_substr($name,0,3);
+        return app(CacheService::class)->getOrExecute("city_".$cityName,function ()use($cityName){
+            return City::query()->where("name","like",$cityName."%")->first();
+        },86400);
+    }
 
 }

+ 172 - 120
app/Services/OrderService.php

@@ -17,6 +17,7 @@ use App\OrderIssue;
 use App\Owner;
 use App\OwnerFeeDetail;
 use App\OwnerFeeDetailLogistic;
+use App\OwnerFeeExpress;
 use App\OwnerReport;
 use App\Province;
 use App\RejectedBill;
@@ -1089,7 +1090,7 @@ sql
      * @param int $owner
      * @param int $logistic
      */
-    public function setOrderQuantity($owner, $logistic)
+    public function setOrderQuantity(int $owner, int $logistic)
     {
         $logistics = app("OwnerPriceExpressService")->getBuildLogistic($owner);
         $date = date("Y-m");
@@ -1118,7 +1119,7 @@ sql
      *
      * @return int
      */
-    public function getOrderQuantity($owner, $isToB = false)
+    public function getOrderQuantity(int $owner, bool $isToB = false):int
     {
         $date = date("Y-m");
         if ($isToB)$type = "|B|";
@@ -1128,60 +1129,80 @@ sql
     }
 
     /**
-     * 生成即时账单
+     * 获取物流费用信息
      *
-     * @param Order $order
-     * @return bool
+     * @param Order|\stdClass $order
+     *
+     * @return array
      */
-    public function createInstantBill(Order $order):bool
+    public function getLogisticFeeInfo(Order $order):array
     {
-        /** @var \stdClass $order */
-        //检查订单对象
-        if (!$order || $order->wms_status != "订单完成")return true;
-        if (Cache::has("owner_fee_details:orders_".$order->id))return true;
-
-        $order->loadMissing(["logistic","shop","packages.commodities.commodity","batch"]);
-
-        /** @var OwnerPriceExpressService $service */
-        $service = app("OwnerPriceExpressService");
-        $logistic_fee = 0;
-        $amount = 0;
-        $volume = 0;
-        $weight = 0;
-        $logistic_bill = "";
-
-        $isBunched = false;
-        if (!$order->logistic || $order->logistic->type == "物流")$logistic_fee = null;
-        else $isBunched = $order->logistic->is_bunched=='Y';
-        $items = [];
-        $weightExceptionMark = false;
-        $provinceId = null;
-        $logisticTaxFee = 0;
-        foreach ($order->packages as &$package){
-            $tax = 0;
-
-            $logistic_bill .= $package->logistic_number.",";
-            $volume += $package->bulk;
-            $weight += $package->weight;
-            if (!$weightExceptionMark && (!$package->weight || $package->weight<0))$weightExceptionMark = true;
+        //预定义返回参数
+        $default = function (){
+            return [
+                "amount" => 0, "logisticBill" => null, "volume" => 0,
+                "weight" => 0, "logisticFee" => null, "logisticTaxFee" => null,
+                "items"  => [],"fee_info" => [],
+            ];
+        };
+        $result = $default();
+        if (!$order->logistic)return $result;
+        //21-8-12 询问业务方(周旭)确定物流单都会在运输管理推单 所以此处直接排除物流单
+        if ($order->logistic->type=='物流')return $result;
+        //获取城市
+        $province = app("ProvinceService")->getProvince($order->province);
+        $unit = app("UnitService")->getUnit();
+        $city = app("CityService")->getCity($order->city);
+        $isBunched = $order->logistic->is_bunched=='Y';
+        $exe = function ($type,$ownerId,$logisticId,$amount,$weight)use($province, $unit, $city){
+            switch ($type){
+                case "快递":
+                    /** @var OwnerPriceExpressService $service */
+                    $service = app("OwnerPriceExpressService");
+                    return $service->matching($weight,$ownerId,$logisticId,$province->id);
+                default:
+                    /** @var OwnerPriceLogisticService $service */
+                    $service = app("OwnerPriceLogisticService");
+                    return $service->matching($amount,$ownerId,$logisticId,$unit->id,$province->id,$city->id);
+            }
+        };
+        //默认的提取信息模板
+        $defaultInfo = function ($number, $weight)use($province,$order){
+            return [
+                "province_id" => $province->id,
+                "owner_id"=>$order->owner_id,
+                "logistic_id" => $order->logistic->id,
+                "logistic_number" => $number,
+                "weight" => $weight,
+                "initial_weight" => 0,
+                "initial_weight_price" => 0,
+                "additional_weight" => 0,
+                "additional_weight_amount" => 0,
+                "additional_weight_price" => 0,
+                "total_fee" => 0,
+                "tax_rate" => 0,
+                "created_at" => $order->wms_edittime ?: $order->updated_at,
+            ];
+        };
+        foreach ($order->packages as $package){
+            if (!$package->weight)return $default(); //任一包裹不存在重量 放弃所有计算 直接跳出
+            $tax = $fee = null;
+            $result["logisticBill"] .= $package->logistic_number.",";
+            $result["volume"] += $package->bulk;
+            $result["weight"] += $package->weight;
             $partAmount = 0;
-            foreach($package->commodities as $commodity){
-                $partAmount += $commodity->amount;
+            foreach($package->commodities as $commodity)$partAmount += $commodity->amount;
+            $result["amount"] += $partAmount;
+
+            if (!$isBunched){
+                $GLOBALS["FEE_INFO"] = $defaultInfo($package->logistic_number,$package->weight);
+                list($fee,$tax) = $exe($order->logistic->type,$order->owner_id, $order->logistic_id, $partAmount, $package->weight);
+                $result["logisticFee"] += $fee;
+                $result["logisticTaxFee"] += $tax;
+                $GLOBALS["FEE_INFO"]["total_fee"] = $fee;
+                if ($order->logistic->type=='快递')$result["fee_info"][] = $GLOBALS["FEE_INFO"];
             }
-            $amount += $partAmount;
-
-            $provinceName = mb_substr($order->province,0,2);
-            $province = app(CacheService::class)->getOrExecute("province_".$provinceName,function ()use($provinceName){
-                return Province::query()->where("name","like",$provinceName."%")->first();
-            },86400);
-            $fee = null;
-            if ($province){
-                if (!$provinceId)$provinceId = $province->id;
-                else if ($provinceId!=$province->id)$weightExceptionMark = true;
-                if (!$isBunched)list($fee,$tax) = $service->matching($package->weight, $order->owner_id, $order->logistic_id, $province->id);
-            }else $logistic_fee = null;
-
-            $items[] = [
+            $result["items"][] = [
                 "amount" => $partAmount,
                 "logistic_bill" => $package->logistic_number,
                 "volume"=>$package->bulk,
@@ -1189,50 +1210,90 @@ sql
                 "logistic_fee" => $fee,
                 "tax_fee" => $tax,
             ];
-            if ($logistic_fee!==null){
-                if (!$fee || $fee<0)$logistic_fee = null;
-                else $logistic_fee += $fee;
-            }
-            $logisticTaxFee += $tax;
         }
-        /* 为字母单 且 重量与省份完整 且 重量大于0 且 省份存在 进入子母单计算 */
-        if ($isBunched && !$weightExceptionMark && $weight>0 && $provinceId)list($logistic_fee,$logisticTaxFee) = $service->matching($weight, $order->owner_id, $order->logistic_id, $provinceId);
-        if ($logistic_fee!==null && $logistic_fee<0)$logistic_fee = null;
+        /* 子母单计算 */
+        if ($isBunched){
+            $GLOBALS["FEE_INFO"] = $defaultInfo($result["logisticBill"],$result["weight"]);
+
+            list($result["logisticFee"],$result["logisticTaxFee"]) =
+                $exe($order->logistic->type,$order->owner_id, $order->logistic_id, $result["amount"], $result["weight"]);
+            $GLOBALS["FEE_INFO"]["total_fee"] = $result["logisticFee"];
+            if ($order->logistic->type=='快递')$result["fee_info"][] = $GLOBALS["FEE_INFO"];
+        }
+        if ($result["logisticFee"]<0)$result["logisticFee"] = null;
+        if ($result["logisticTaxFee"]<0)$result["logisticTaxFee"] = null;
+        $result["logisticBill"] = rtrim($result["logisticBill"],",");
+        return $result;
+    }
+    /**
+     * 生成即时账单
+     *
+     * @param Order $order
+     * @return bool
+     */
+    public function createInstantBill(Order $order):bool
+    {
+        /** @var \stdClass $order */
+        //检查订单对象
+        if (!$order || $order->wms_status != "订单完成")return true;
+        //防重推标记
+        if (Cache::has("owner_fee_details:orders_".$order->id))return true;
+        //加载所需数据
+        $order->loadMissing(["logistic","shop","packages.commodities.commodity","batch"]);
+        //获取运输费
+        $logisticInfo = $this->getLogisticFeeInfo($order);
+
+        //信息仍然不完整
+        if (!$logisticInfo["fee_info"])return false;
+        //向指定表插入标记
+        OwnerFeeExpress::query()->insert($logisticInfo["fee_info"]);
+        //获取作业费
         /** @var OwnerPriceOperationService $service */
         $service = app("OwnerPriceOperationService");
-        list($id,$money,$workTaxFee) = $service->matching($order,Feature::MAPPING["order"],$order->owner_id,"出库");
+        $GLOBALS["FEE_INFO"] = [];
+        list($id,$money,$workTaxFee) = $service->matching($order,Feature::MAPPING["order"],$order->owner_id);
+        if ($money>0)app("StoreService")->constructFeeInfo([
+            "worked_at" => $order->wms_edittime ?: $order->updated_at,
+            "owner_id" => $order->owner_id,
+            "model_id"  => $id,
+            "source_number"=> $order->client_code,
+            "doc_number"   => $order->code,
+            "commodity_id" => 0,
+            "total_fee"    =>0,
+            "tax_rate"     =>0,
+            "fee_description"=>'',
+        ]);
 
-        $detail = app("OwnerFeeDetailService")->create([
+        //数据组装
+        $detail = OwnerFeeDetail::query()->create([
             "owner_id"          => $order->owner_id,
-            "worked_at"         => $order->wms_edittime ?? $order->updated_at,
+            "worked_at"         => $order->wms_edittime ?: $order->updated_at,
             "type"              => "发货",
             "shop_id"           => $order->shop_id,
             "operation_bill"    => $order->code,
             "province"          => $order->province,
             "consignee_name"    => $order->consignee_name,
             "consignee_phone"   => $order->consignee_phone,
-            "commodity_amount"  => $amount,
-            "logistic_bill"     => rtrim($logistic_bill,","),
-            "volume"            => $volume,
-            "weight"            => $weight,
+            "commodity_amount"  => $logisticInfo["amount"],
+            "logistic_bill"     => $logisticInfo["logisticBill"],
+            "volume"            => $logisticInfo["volume"],
+            "weight"            => $logisticInfo["weight"],
             "logistic_id"       => $order->logistic_id,
             "work_fee"          => $money,
             "owner_price_operation_id"  => $id,
-            "logistic_fee"      => $logistic_fee,
+            "logistic_fee"      => $logisticInfo["logisticFee"],
             "created_at"        => date('Y-m-d H:i:s'),
             "outer_id"          => $order->id,
             "outer_table_name"  => "orders",
             "work_tax_fee"      => $workTaxFee,
-            "logistic_tax_fee"  => $logistic_fee ? $logisticTaxFee : null,
+            "logistic_tax_fee"  => $logisticInfo["logisticTaxFee"],
         ]);
-        if ($detail){
-            foreach ($items as &$item)$item["owner_fee_detail_id"] = $detail->id;
-            if (count($items)>1)OwnerFeeDetailLogistic::query()->insert($items);
-            $this->setOrderQuantity($order->owner_id,$order->logistic_id);
-            Cache::put("owner_fee_details:orders_".$order->id,1,86400);
-            return true;
-        }
-        return false;
+        //后续处理
+        foreach ($logisticInfo["items"] as &$item)$item["owner_fee_detail_id"] = $detail->id;
+        if (count($logisticInfo["items"])>1)OwnerFeeDetailLogistic::query()->insert($logisticInfo["items"]);
+        $this->setOrderQuantity($order->owner_id,$order->logistic_id);
+        Cache::put("owner_fee_details:orders_".$order->id,1,86400);
+        return true;
     }
 
     public function getMonthTotal($ownerId = null, $month = null)
@@ -1323,52 +1384,43 @@ sql
             ->where("outer_id",$order->id)->whereNull("logistic_fee")->first();
         if (!$feeBill)return false; //是否已有即时账单,没有跳出
         $order->loadMissing("packages","owner");//加载包裹
-        if (!$order->packages || !$order->owner)return false;
-        $order->owner->loadCount("ownerPriceExpresses");
-        //if ($order->owner->owner_price_expresses_count < 1)return false; //不存在计费模型 跳出
-        foreach ($order->packages as $package)if (!$package->weight)return false; //包裹存在且全部存在数量
-
-        $logistic_fee = 0;
-        $volume = 0;
-        $weight = 0;
-        $isBunched = false;
-        if (!$order->logistic || $order->logistic->type != "快递")$logistic_fee = null;
-        else $isBunched = $order->logistic->is_bunched=='Y';
-        $weightExceptionMark = false;
-        $provinceId = null;
-        $taxFee = 0;
-        foreach ($order->packages as $package){
-            $tax = 0;
-
-            $volume += $package->bulk;
-            $weight += $package->weight;
-            if (!$weightExceptionMark && (!$package->weight || $package->weight<0))$weightExceptionMark = true;
-            $fee = null;
-
-            $provinceName = mb_substr($order->province,0,2);
-            $province = app(CacheService::class)->getOrExecute("province_".$provinceName,function ()use($provinceName){
-                return Province::query()->where("name","like",$provinceName."%")->first();
-            },86400);
-            $fee = null;
-            if ($province){
-                if (!$provinceId)$provinceId = $province->id;
-                else if ($provinceId!=$province->id)$weightExceptionMark = true;
-                if (!$isBunched)list($fee,$tax) = app("OwnerPriceExpressService")->matching($package->weight, $order->owner_id, $order->logistic_id, $province->id);
-            }else $logistic_fee = null;
-
-            OwnerFeeDetailLogistic::query()->where("owner_fee_detail_id",$feeBill->id)->where("logistic_bill",$package->logistic_number)->update([
-                "volume"=>$package->bulk,
-                "weight"=>$package->weight,
-                "logistic_fee" => $fee,
+        if (!$order->packages->count() || !$order->owner)return false;
+        //获取运输费
+        $logisticInfo = $this->getLogisticFeeInfo($order);
+        //向指定表插入标记
+        if ($logisticInfo["fee_info"])OwnerFeeExpress::query()->insert($logisticInfo["fee_info"]);
+        foreach ($logisticInfo["items"] as &$item)$item["owner_fee_detail_id"] = $feeBill->id;
+        if (count($logisticInfo["items"])>1)OwnerFeeDetailLogistic::query()->insert($logisticInfo["items"]);
+
+        //获取作业费
+        $GLOBALS["FEE_INFO"] = [];
+        list($id,$money,$workTaxFee) = app("OwnerPriceOperationService")->matching($order,Feature::MAPPING["order"],$order->owner_id);
+        if ($money>0){
+            app("StoreService")->clearFeeInfo($order->code);
+            app("StoreService")->constructFeeInfo([
+                "worked_at" => $order->wms_edittime ?: $order->updated_at,
+                "owner_id" => $order->owner_id,
+                "model_id"  => $id,
+                "source_number"=> $order->client_code,
+                "doc_number"   => $order->code,
+                "commodity_id" => 0,
+                "total_fee"    =>0,
+                "tax_rate"     =>0,
+                "fee_description"=>'',
             ]);
-            if ($logistic_fee!==null){
-                if ($fee<0)$logistic_fee = null;
-                else $logistic_fee += $fee;
-            }
-            $taxFee += $tax;
         }
-        if ($isBunched && !$weightExceptionMark && $weight>0 && $provinceId)list($logistic_fee,$taxFee) = app("OwnerPriceExpressService")->matching($weight, $order->owner_id, $order->logistic_id, $provinceId);
-        $feeBill->update(["logistic_fee"=>$logistic_fee,"volume"=>$volume,"weight"=>$weight,"logistic_tax_fee"=>$taxFee]);
+        //数据组装
+        $feeBill->update([
+            "commodity_amount"  => $logisticInfo["amount"],
+            "logistic_bill"     => $logisticInfo["logisticBill"],
+            "volume"            => $logisticInfo["volume"],
+            "weight"            => $logisticInfo["weight"],
+            "logistic_fee"      => $logisticInfo["logisticFee"],
+            "logistic_tax_fee"  => $logisticInfo["logisticTaxFee"],
+            "work_fee"          => $money,
+            "owner_price_operation_id"  => $id,
+            "work_tax_fee"      => $workTaxFee,
+        ]);
         return true;
     }
 

+ 21 - 25
app/Services/OwnerPriceExpressService.php

@@ -256,49 +256,45 @@ sql
     /**
      *
      * @param double $weight
-     * @param integer $owner_id
-     * @param integer $logistic_id
-     * @param integer $province_id
+     * @param integer $ownerId
+     * @param integer $logisticId
+     * @param integer $provinceId
+     *
      * @return array
      */
-    public function matching($weight, $owner_id, $logistic_id, $province_id)
+    public function matching(float $weight, int $ownerId, int $logisticId, int $provinceId):array
     {
         if (!$weight)return array(null,null);
 
-        $model = $this->getOwnerPriceExpress($owner_id,$logistic_id,$province_id);
+        $model = $this->getOwnerPriceExpress($ownerId,$logisticId,$provinceId);
         if (!$model || count($model->details)<1)return array(null,null);
 
+        $to1 = $to2 = 0;
         if ($model->amount_interval){
-            $total = app("OrderService")->getOrderQuantity($owner_id)+1;//获取该货主本月C端单量
-            for ($i=count($model->amount_interval);$i<0;$i--){
-                if ($total>=$model->amount_interval[$i]){$to1 = $i;break;}
-            }
+            $total = app("OrderService")->getOrderQuantity($ownerId)+1;//获取该货主本月C端单量
+            for ($i=count($model->amount_interval);$i<0;$i--)if ($total>=$model->amount_interval[$i]){$to1 = $i;break;}
             if (isset($to1) && isset($model->weight_interval[$to1])){
                 for ($i=count($model->weight_interval[$to1]);$i<0;$i--){
                     if ($weight>=$model->weight_interval[$to1][$i]){$to2 = $i;break;}
                 }
             }
         }
-        if (!isset($to1))$to1 = 0;
-        if (!isset($to2))$to2 = 0;
 
         $initPrice = $model->details[0]->initial_weight_price[$to1][$to2];
         $additionalPrice = $model->details[0]->additional_weight_price[$to1][$to2];
-        if ($weight <= $model->initial_weight)$money = $initPrice;
-        else{
+        if ($weight>$model->initial_weight){
             $weight -= $model->initial_weight;
-            $money = (ceil($weight/$model->additional_weight)*$additionalPrice)+$initPrice;
-        }
-        if ($model->tax_rate_id && $model->taxRate){
-            $taxFee = $money*($model->taxRate->value/100);
-        }else{
-            /** @var Owner|\stdClass $owner */
-            $owner = Owner::query()->with("taxRate")
-                ->whereHas("taxRate")->find($owner_id);
-            if ($owner)$taxFee = $money*($owner->taxRate->value/100);
-            else $taxFee = null;
-        }
-
+            $amount = ceil($weight/$model->additional_weight);
+            $GLOBALS["FEE_INFO"]["additional_weight_amount"] = $amount; //续重量向上取整 例:不足1为1
+            $money = ($amount*$additionalPrice)+$initPrice;
+        }else $money = $initPrice;
+        $GLOBALS["FEE_INFO"]["initial_weight"] = $model->initial_weight;
+        $GLOBALS["FEE_INFO"]["additional_weight"] = $model->additional_weight;
+        $GLOBALS["FEE_INFO"]["initial_weight_price"] = $model->initial_weight_price;
+        $GLOBALS["FEE_INFO"]["additional_weight_price"] = $model->additional_weight_price;
+        $taxRate = app("OwnerService")->getTaxRateFee($model, $ownerId);//获取税率
+        $taxFee = $money*($taxRate/100);
+        $GLOBALS["FEE_INFO"]["tax_rate"] = $taxRate;
         return array($money,$taxFee);
     }
 }

+ 28 - 24
app/Services/OwnerPriceLogisticService.php

@@ -188,35 +188,39 @@ class OwnerPriceLogisticService
     /**
      *
      * @param double $amount
-     * @param integer $owner_id
-     * @param integer $logistic_id
-     * @param integer $unit_id
-     * @param integer $province_id
-     * @param integer $city_id
+     * @param integer $ownerId
+     * @param integer $logisticId
+     * @param integer $unitId
+     * @param integer $provinceId
+     * @param integer $cityId
      * @return array
      */
-    public function matching($amount, $owner_id, $logistic_id, $unit_id, $province_id, $city_id)
+    public function matching(float $amount, int $ownerId, int $logisticId, int $unitId, int $provinceId, int $cityId):array
     {
-        $model = OwnerPriceLogistic::query()->with(["details"=>function($query)use($province_id,$city_id){
+        $model = OwnerPriceLogistic::query()->with(["details"=>function($query)use($provinceId,$cityId){
             /** @var Builder $query */
-            $query->where("province_id",$province_id)->where("city_id",$city_id);
-        }])->where(function ($query)use($unit_id){
+            $query->where("province_id",$provinceId)->where("city_id",$cityId);
+        }])->where(function ($query)use($unitId){
             /** @var Builder $query */
-            $query->where("unit_id",$unit_id)->orWhere("other_unit_id",$unit_id);
-        })->whereHas("owners",function ($query)use($owner_id){
+            $query->where("unit_id",$unitId)->orWhere("other_unit_id",$unitId);
+        })->whereHas("owners",function ($query)use($ownerId){
             /** @var Builder $query */
-            $query->where("id",$owner_id);
-        })->whereHas("logistics",function ($query)use($logistic_id){
+            $query->where("id",$ownerId);
+        })->whereHas("logistics",function ($query)use($logisticId){
             /** @var Builder $query */
-            $query->where("id",$logistic_id);
+            $query->where("id",$logisticId);
         })->where(function(Builder $query){
             $query->whereNull("operation")->orWhere("operation","");
         })->first();
         if (!$model || !$model->details)return array(null,null);
         $money = null;
+        $GLOBALS["FEE_INFO"]["pick_fee"] = $model->pick_up_price;
+        $GLOBALS["FEE_INFO"]["fuel_fee"] = $model->fuel_price;
+        $GLOBALS["FEE_INFO"]["info_fee"] = $model->service_price;
+        $GLOBALS["FEE_INFO"]["other_fee"] = 0;
         $fee = $model->pick_up_price + $model->fuel_price + $model->service_price; //服务费
         foreach ($model->details as $detail){
-            if ($unit_id != $detail->unit_id)continue;
+            if ($unitId != $detail->unit_id)continue;
             $arr = explode("-",$detail->range);
             if (count($arr) < 2 || !$arr[1]){
                 if ($amount >= $arr[0])$money = $this->calculation($amount,$detail,$fee);
@@ -225,20 +229,20 @@ class OwnerPriceLogisticService
             }
             if ($money)break;
         }
-        if ($model->tax_rate_id && $model->taxRate){
-            $taxFee = $money*($model->taxRate->value/100);
-        }else{
-            /** @var Owner|\stdClass $owner */
-            $owner = Owner::query()->with("taxRate")
-                ->whereHas("taxRate")->find($owner_id);
-            if ($owner)$taxFee = $money*($owner->taxRate->value/100);
-            else $taxFee = null;
-        }
+        $taxRate = app("OwnerService")->getTaxRateFee($model, $ownerId);
+        $taxFee = $money*($taxRate/100);
+        $GLOBALS["FEE_INFO"]["tax_rate"] = $taxRate;
+        $GLOBALS["FEE_INFO"]["total_fee"] = $money;
         return array($money,$taxFee);
     }
 
     private function calculation($amount, $detail, $fee)
     {
+        $GLOBALS["FEE_INFO"]["interval"] = $detail->range;
+        $GLOBALS["FEE_INFO"]["price"] = $detail->unit_price;
+        $GLOBALS["FEE_INFO"]["delivery_fee"] = $detail->delivery_fee;
+        $GLOBALS["FEE_INFO"]["initial_fee"] = $detail->initial_fee;
+        $GLOBALS["FEE_INFO"]["initial_amount"] = $detail->initial_amount;
         if ($amount < $detail->initial_amount)$amount = $detail->initial_amount; //小于起始数以起始数为准
         $money = $amount * $detail->unit_price;
         if ($money < $detail->initial_fee)$money = $detail->initial_fee; //小于起始计费以起始计费为准

+ 210 - 111
app/Services/OwnerPriceOperationService.php

@@ -198,7 +198,7 @@ class OwnerPriceOperationService
      * @return array|Collection
      *
      */
-    public function getOwnerPriceOperation($owner, $type, $typeMark)
+    public function getOwnerPriceOperation(int $owner, string $type, ?int $typeMark)
     {
         return Cache::tags("operationFeeOwner:".$owner)->remember("operationFee:".$owner.$type.$typeMark,config("cache.expirations.rarelyChange"),
             function ()use($owner,$type,$typeMark){
@@ -227,12 +227,11 @@ class OwnerPriceOperationService
      *
      * @return bool|array
      */
-    public function getDiscount($discount, $total)
+    public function getDiscount(?string $discount, int $total)
     {
-        if ($discount){
-            foreach (array_reverse(explode(",",$discount),true) as $index=>$discount){
-                if ($total >= $discount)return [$index=>$discount];
-            }
+        if (!$discount)return false;
+        foreach (array_reverse(explode(",",$discount),true) as $index=>$discount){
+            if ($total >= $discount)return [$index=>$discount];
         }
         return false;
     }
@@ -240,12 +239,13 @@ class OwnerPriceOperationService
     /**
      * 处理折扣单
      *
-     * @param object $rule
+     * @param Model|\stdClass $rule
      * @param integer $owner
      * @param bool|array $result
      */
-    public function handleDiscount($rule, $owner, $result)
+    public function handleDiscount(Model $rule, int $owner, $result)
     {
+        if (!$result)return;
         $sign = false;
         //入口仅在此处存在 缓存1000s
         $key = "owner_price_operation_owner_".$rule->id."_".$owner;
@@ -287,7 +287,7 @@ class OwnerPriceOperationService
      *
      * @param array|object|Model $matchObject  key-val
      * @param array $columnMapping       key-val
-     * @param string $ownerId
+     * @param integer $ownerId
      * @param string $type
      * @param int|null $typeMark
      *
@@ -312,16 +312,11 @@ class OwnerPriceOperationService
      * 九. 2021-04-21 zzd
      *      排除掉order不存在包裹情况,预设输出值为null防止返回0,历史账单处理推进队列防止超时
      */
-    public function matching($matchObject, $columnMapping, $ownerId, $type = '出库', $typeMark = null)
+    public function matching($matchObject, array $columnMapping, int $ownerId, string $type = '出库', ?int $typeMark = null):array
     {
-        $units = app("UnitService")->getUnitMapping(["件","单","箱","m³","T","kg"]); //获取单位映射集
+        $units = app("UnitService")->getUnitMapping(["件","单","箱","m³","T","kg"],true); //获取单位映射集
         $rules = $this->getOwnerPriceOperation($ownerId,$type,$typeMark);//货主下的全部规则
-        if (!$rules)return -2;  //规则不存在跳出
-
-        //建立一组返回变量
-        $id = null;
-        $money = null;
-        $taxFee = null;
+        if (!$rules)return array(null,null,null);  //规则不存在跳出
 
         if ($type == '出库'){
             $total = app("OrderService")->getOrderQuantity($ownerId)+1;//获取该货主本月C端单量
@@ -342,44 +337,90 @@ class OwnerPriceOperationService
         }
         foreach ($rules as $rule){
             if (!$rule->items)continue; //不存在子规则跳出
-
+            //第一级匹配 特征类别不符合要求跳出
+            if ($rule->strategy == '特征' && !app("FeatureService")->matchFeature($rule->feature,$columnMapping,$matchObject))continue;
+            //获取满减信息并处理满减
             $result = $this->getDiscount($rule->discount_count,$total); //满减信息
-            if ($result)$this->handleDiscount($rule,$ownerId,$result);//满减存在
-
-            if ($rule->strategy == '特征'){
-                if (app("FeatureService")->matchFeature($rule->feature,$columnMapping,$matchObject)){
-                    if (!$rule->total_price)$money = $this->matchItem($rule,$columnMapping,$matchObject,$units,$ownerId,$result);
-                    $id = $rule->id;
-                };
-            }else{
-                if (!$rule->total_price)$money = $this->matchItem($rule,$columnMapping,$matchObject,$units,$ownerId,$result);
-                $id = $rule->id;
-            };
-            if ($id){
-                if ($rule->total_price)$money = $result ? explode(",",$rule->total_discount_price)[key($result)] : $rule->total_price;//按单计价存在,直接返回单总价或减免总价
-                $money = $rule->max_fee&&$money>$rule->max_fee ? $rule->max_fee : $money;//封顶费
-                if ($money<=0)$money=null;//计算失误
-                if ($rule->tax_rate_id && $rule->taxRate)$taxFee = $money*($rule->taxRate->value/100);
-                else{
-                    /** @var Owner|\stdClass $owner */
-                    $owner = Owner::query()->with("taxRate")
-                        ->whereHas("taxRate")->find($ownerId);
-                    if ($owner)$taxFee = $money*($owner->taxRate->value/100);
+            $this->handleDiscount($rule,$ownerId,$result);
+
+            $taxRate = app("OwnerService")->getTaxRateFee($rule, $ownerId);//获取税率
+
+            if (!$rule->total_price){
+                $money = $this->matchItem($rule,$columnMapping,$matchObject,$units,$ownerId,$result);//匹配子项获取详细报价
+                if ($rule->max_fee&&$money>$rule->max_fee){
+                    $money = $rule->max_fee;//封顶费
+                    foreach ($GLOBALS["FEE_INFO"] as $index=>$info){
+                        foreach ($GLOBALS["FEE_INFO"][$index]["details"] as &$item)$item["price"] = 0;
+                        $GLOBALS["FEE_INFO"][$index]["tax_rate"] = $taxRate;
+                        if ($index==0){
+                            $GLOBALS["FEE_INFO"][$index]["total_fee"] = $rule->max_fee;
+                            $GLOBALS["FEE_INFO"][$index]["fee_description"] = "订单费用超出封顶费,封顶费:".$rule->max_fee
+                                ."(详:".$GLOBALS["FEE_INFO"][$index]["fee_description"].")";
+                            $GLOBALS["FEE_INFO"][$index]["details"][] = [
+                                "name"      => "封顶费",
+                                "amount"    => 1,
+                                "price"     => $rule->max_fee,
+                                "unit_id"   => array_flip($units)["单"],
+                            ];
+                        }else{
+                            $GLOBALS["FEE_INFO"][$index]["total_fee"] = 0;
+                            $GLOBALS["FEE_INFO"][$index]["fee_description"] = "封顶费平摊"
+                                ."(详:".$GLOBALS["FEE_INFO"][$index]["fee_description"].")";
+                        }
+                    }
                 }
-                break;
+            } else{
+                $money = $result ? explode(",",$rule->total_discount_price)[key($result)] : $rule->total_price;//按单计价存在,直接返回单总价或减免总价
+                $arr = $this->resetChildNodeMapping($matchObject->toArray(),$columnMapping);
+                if ($arr)$this->extractInfo($arr,$money,$result,array_flip($units),$taxRate);
             }
+
+            if ($money<=0)$money=null;//计算失误
+            $taxFee = $money*($taxRate/100);
+            return array($rule->id,$money,$taxFee);
+        }
+        return array(null,null,null);
+    }
+
+    /**
+     * 提取信息插入超全局变量中
+     */
+    private function extractInfo(array $arr, $money, $result, $flip, $taxRate)
+    {
+        foreach ($arr as $index => $item){
+            $details = [
+                "name"      => "单价",
+                "amount"    => $item["amount"] ?? 0,
+                "price"     => 0,
+                "unit_id"   => $flip["件"],
+            ];
+            if ($index==0){
+                $feeDescription = "按单计价".($result ? '('.value($result).')' : '').':'.$money.'元';
+                $details[] = [
+                    "name"      => "按单计费",
+                    "amount"    => 1,
+                    "price"     => $money,
+                    "unit_id"   => $flip["单"],
+                ];
+            }else $feeDescription = '该单已被按单计费,续费0元';
+            $GLOBALS["FEE_INFO"][] = [
+                "commodity_id"  => $item["commodity_id"] ?? 0,
+                "total_fee"     => $money,
+                "tax_rate"      => $taxRate,
+                "fee_description"  => $feeDescription,
+                "details" => $details
+            ];
         }
-        return array($id,$money,$taxFee);
     }
     /**
      * 根据货主 sku寻找箱规并将指定数量切换为箱 返回箱规
      *
      * @param integer $ownerId
-     * @param null|object $commodity
+     * @param null|object|array $commodity
      *
      * @return int
      */
-    private function changeUnit($ownerId,$commodity)
+    private function changeUnit(int $ownerId,$commodity):int
     {
         if (!$commodity)return -4;
         if ($commodity["pack_spec"])return $commodity["pack_spec"];
@@ -394,9 +435,9 @@ class OwnerPriceOperationService
      * @param object|array $matchObject
      * @param array $columnMapping
      *
-     * @return array
+     * @return array|null
      */
-    private function resetChildNodeMapping($matchObject,&$columnMapping)
+    private function resetChildNodeMapping($matchObject, array &$columnMapping):?array
     {
         $need = "";
         foreach (Feature::TYPE_NODE as $index){
@@ -421,128 +462,186 @@ class OwnerPriceOperationService
      *
      * @param Model|\stdClass $obj 策略对象
      * @param array $columnMapping 映射对象
-     * @param Model $matchObject 被匹配对象
+     * @param Model|\stdClass $matchObject 被匹配对象
      * @param array $units 单位集
      * @param integer $ownerId 货主ID
      * @param bool|array $result 满减信息
      *
      * @return double
      */
-    public function matchItem($obj, $columnMapping, $matchObject, $units, $ownerId, $result)
+    public function matchItem($obj, array $columnMapping, $matchObject, array $units, int $ownerId, $result)
     {
         $matchObject = $this->resetChildNodeMapping($matchObject->toArray(),$columnMapping);
         if (!$matchObject)return -1;
-        $total = 0; //商品总数
+        $total = $surcharge = $oddMoney = $surchargeAmount = 0; //商品总数 附加费 零头附加费
+
         foreach ($matchObject as $commodity)$total += $commodity[$columnMapping[8]]; //取对象内商品数量总数将其当作子属性插入原对象
-        $unitName = "";
-        $surcharge = 0;
         if ($obj->surcharge_unit_id && $obj->surcharge){
-            if ($units[$obj->surcharge_unit_id] == '件')$surcharge += $obj->surcharge*$total;
-            else $surcharge += $obj->surcharge;
+            $surchargeAmount = $units[$obj->surcharge_unit_id] == '件' ? $total : 1;
+            $surcharge += $obj->surcharge*$surchargeAmount;
         }//耗材附加费
-
+        $flip = array_flip($units);
+        $exe = function ($package,$index,$partMoney,$startNumber,$rule)use($columnMapping,$flip,$surcharge,$surchargeAmount,$obj,$units){
+            $details = $package["fee_info"] ?? []; //获取在匹配过程中记录的费用,例:零头费
+            $fee_description = $details ? "箱装包含零头附加费" : '';
+            $details[] = [
+                "name"      => "单价",
+                "amount"    => $package[$columnMapping[8]],
+                "price"     => $package["price"],
+                "unit_id"   => $package["unitId"] ?? $flip["件"],
+            ];
+            if ($index==0){
+                if ($surcharge){
+                    $details[] = [
+                        "name"      => "耗材附加费",
+                        "amount"    => $surchargeAmount,
+                        "price"     => $obj->surcharge,
+                        "unit_id"   => $obj->surcharge_unit_id,
+                    ];
+                    $fee_description .= ",包含耗材附加费".$surcharge."元";
+                    $partMoney += $surcharge;
+                }
+                if ($startNumber){
+                    $details[] = [
+                        "name"      => "起步费",
+                        "amount"    => $startNumber,
+                        "price"     => $rule->unit_price,
+                        "unit_id"   => $rule->unit_id,
+                    ];
+                    $fee_description .= ",包含起步费({$startNumber}/{$units[$rule->unit_id]})".$rule->unit_price."元";
+                    $partMoney += $rule->unit_price;
+                }
+            }else if ($startNumber) $fee_description .= "数量被起步数平摊";
+            $GLOBALS["FEE_INFO"][] = [
+                "commodity_id"  => $package["commodity_id"] ?? 0,
+                "total_fee"     => $partMoney,
+                "tax_rate"      => 0,
+                "fee_description"  => ltrim($fee_description,","),
+                "details" => $details
+            ];
+        };
         foreach ($obj->items as $rule){
-
             if ($result)$rule->unit_price = explode(",",$rule->discount_price)[key($result)]; //满足满减条件,单价调整为满减单价
 
             if ($rule->strategy=='起步'){
                 $startNumber = $rule->amount;
                 $money = 0;
-                if ($unitName && $startNumber && $rule->unit_id && $unitName != $units[$rule->unit_id])return -3; //校验单位是否一致
-
-                if ($startNumber){
+                if ($startNumber){ //起步数存在 重设数量
                     $money = $rule->unit_price;
-                    $matchObject=$this->settingCount($matchObject,$columnMapping[8],$startNumber);
+                    $matchObject=$this->settingCount($matchObject,$columnMapping[8],$startNumber,$rule->unit_id);
+                }
+                //费用计算
+                if ($matchObject)foreach ($matchObject as $index=>$package){
+                    if (!isset($package["price"]))$package["price"] = 0;
+                    $partMoney = $package[$columnMapping[8]] * $package["price"];
+                    $money += $partMoney;
+                    $exe($package,$index,$partMoney,$startNumber,$rule); //向信息提取器输入信息
                 }
-                if ($matchObject)foreach ($matchObject as $package)if ($package["price"] ?? false)$money += $package[$columnMapping[8]] * $package["price"];
-                if (!$startNumber && $money<$rule->unit_price)$money = $rule->unit_price;
-                return $money+$surcharge;
+                //起步费存在 验证重设
+                if (!$startNumber && $money<$rule->unit_price){
+                    $diffMoney = $rule->unit_price-$money;
+                    if ($GLOBALS["FEE_INFO"]){
+                        $GLOBALS["FEE_INFO"][0]["fee_description"] .= ",低于起步费".$rule->unit_price."元,加收差费".$diffMoney;
+                        $GLOBALS["FEE_INFO"][0]["details"][] = [
+                            "name"      => "起步费补差",
+                            "amount"    => 1,
+                            "price"     => $diffMoney,
+                            "unit_id"   => $flip["单"],
+                        ];
+                    }
+                    $money += $diffMoney;
+                }
+                return $money+$surcharge+$oddMoney;
             }
             foreach ($matchObject as &$package){
-                if ($package["price"] ?? false)continue;
-                if (!isset($units[$rule->unit_id]))return -3;
-                if (!$unitName)$unitName = $units[$rule->unit_id];
-                else if ($unitName != $units[$rule->unit_id]) return -3;
+                if (isset($package["price"]))continue; //单价存在 跳过
+                if (!isset($units[$rule->unit_id]))return -3;//单位未知 跳出
                 if ($rule->strategy=='特征'){
                     $package[$columnMapping[10]] = $total; //设置一个不存在的总数进入原对象
                     if (!app("FeatureService")->matchFeature($rule->feature,$columnMapping,$package)) continue;
                 }
                 $package["price"] = $rule->unit_price;
-            }
-            if ($units[$rule->unit_id] != '件'){ //非件时改变商品数量 此时数量可能为小数
-                foreach ($matchObject as &$commodity){
-                    switch ($units[$rule->unit_id]){
-                        case "箱"://为箱时同步商品寻找箱规并改变商品数量
-                            $pack = $this->changeUnit($ownerId,$commodity[$columnMapping[9]]);
-                            if ($rule->odd_price){
-                                $amount = floor($commodity[$columnMapping[8]]/$pack);
-                                $surcharge += $rule->odd_price * ($amount%$pack); //零头附加费
-                            }else$amount = ceil($commodity[$columnMapping[8]]/$pack);
-                            if ($amount<0)return $amount;
-                            $commodity[$columnMapping[8]] = $amount;
-                            break;
-                        case "m³":
-                            $commodity[$columnMapping[8]] = $commodity["bulk"] ?? 0;
-                            break;
-                        case "kg":
-                            $commodity[$columnMapping[8]] = $commodity["weight"] ?? 0;
-                            break;
-                        case "T":
-                            $commodity[$columnMapping[8]] = ($commodity["weight"]/1000) ?? 0;
-                            break;
-                    }
+                $package["unitId"] = $rule->unit_id;
+                $package["fee_info"] = [];
+                switch ($units[$rule->unit_id]){
+                    case "箱"://为箱时同步商品寻找箱规并改变商品数量
+                        $pack = $this->changeUnit($ownerId,$package[$columnMapping[9]]);
+                        if ($pack<=0)return $pack;
+                        if ($rule->odd_price){
+                            $amount = $package[$columnMapping[8]];
+                            $odd = $amount%$pack;
+                            $amount = floor($amount/$pack);
+                            $oddMoney += $rule->odd_price * $odd; //零头附加费
+                            $package["fee_info"][] = [
+                                "name"      => "零头附加费",
+                                "amount"    => $odd,
+                                "price"     => $rule->odd_price,
+                                "unit_id"   => $flip["件"],
+                            ];
+                        }else $amount = ceil($package[$columnMapping[8]]/$pack);
+                        if ($amount<0)return $amount;
+                        $package[$columnMapping[8]] = $amount;
+                        break;
+                    case "m³":
+                        $package[$columnMapping[8]] = $package["bulk"] ?? 0;
+                        break;
+                    case "kg":
+                        $package[$columnMapping[8]] = $package["weight"] ?? 0;
+                        break;
+                    case "T":
+                        $package[$columnMapping[8]] = ($package["weight"]/1000) ?? 0;
+                        break;
                 }
             }
         }
-        $money = $surcharge;
-        foreach ($matchObject as $mo)if ($mo["price"] ?? false)$money += $mo[$columnMapping[8]] * $mo["price"];
-        return $money ?? -7;
+        $money = $surcharge+$oddMoney;
+        foreach ($matchObject as $index=>$mo){
+            if (!isset($mo["price"]))$mo["price"] = 0;
+            $partMoney = $mo[$columnMapping[8]] * $mo["price"];
+            $money += $partMoney;
+            $exe($mo,$index,$partMoney,0,null);
+        }
+        return $money ?: -7;
     }
-    //递归式重新设置数量
-    private function settingCount($packages,$amountColumn,$startNumber)
+    //递归式重新设置数量 只设置单位匹配对象
+    private function settingCount($packages,$amountColumn,$startNumber,$unitId)
     {
         if (!$packages) return null;
         $maxPrice = 0;
-        $index = null;
         foreach ($packages as $i => $package){
-            if ($package[$amountColumn] <= 0){
-                unset($packages[$i]);continue;
-            }
-            if (!($package["price"] ?? false)){
-                $package["price"] = 0;
-                $packages[$i]["price"] = 0;
-            }
-            if ($package["price"] > $maxPrice || ($package["price"]==0 && $maxPrice==0)){
-                $maxPrice = $package["price"];
-                $index = $i;
-            }
+            if ($package[$amountColumn] <= 0){$package["price"] = 0;$packages[$i]["price"] = 0;continue;}
+            if (!isset($package["price"])){$package["price"] = 0;$packages[$i]["price"] = 0;}
+            if (isset($package["unitId"]) && $package["unitId"]!=$unitId)continue;//单位不一致 跳过
+            if ($package["price"] > $maxPrice || ($package["price"]==0 && $maxPrice==0)){$maxPrice = $package["price"];$index = $i;}
         }
+        if (!isset($index))return $packages;
         if ($packages[$index][$amountColumn] >= $startNumber){
             $packages[$index][$amountColumn] -= $startNumber;
             return $packages;
         }else{
             $startNumber -= $packages[$index][$amountColumn];
-            unset($packages[$index]);
-            $this->settingCount($packages,$amountColumn,$startNumber);
+            $packages[$index]["price"] = 0;
+            $this->settingCount($packages,$amountColumn,$startNumber,$unitId);
         }
+        return $packages;
     }
 
     /**
      * 处理历史账单
      *
-     * @param object $rule
+     * @param Model|\stdClass $rule
      * @param int $owner
      * @param int $discountIndex
-     * @param object $pivot
+     * @param \stdClass|object $pivot
      */
-    public function handlePastBill($rule, $owner, $discountIndex, $pivot)
+    public function handlePastBill($rule, int $owner, int $discountIndex, $pivot)
     {
         DB::beginTransaction();
         try{
             $month = date("Y-m");
             $day = date("t",strtotime($month));
             $query = OwnerFeeDetail::query()->where("owner_id",$owner)->whereBetween("worked_at",[$month."-01",$month."-".$day]);
-            $units = app("UnitService")->getUnitMapping(["件","单","箱","m³","T","kg"]); //获取单位映射集
+            $units = app("UnitService")->getUnitMapping(["件","单","箱","m³","T","kg"],true); //获取单位映射集
             $exe = function ($mapping,$object,$detail)use($rule,$units,$owner,$discountIndex){
                 $money = $this->matchItem($rule,$mapping,$object,$units,$owner,[$discountIndex=>true]);
                 $rate = $rule->taxRate ?: (Owner::query()->with("taxRate")->find($owner)->taxRate ?? null);

+ 29 - 0
app/Services/OwnerService.php

@@ -15,6 +15,7 @@ use App\Services\common\BatchUpdateService;
 use App\User;
 use Carbon\Carbon;
 use Illuminate\Database\Eloquent\Builder;
+use Illuminate\Database\Eloquent\Model;
 use Illuminate\Support\Collection;
 use Illuminate\Support\Facades\Auth;
 use Illuminate\Support\Facades\Cache;
@@ -497,4 +498,32 @@ sql;
         OwnerPriceSystem::query()->where("owner_id",$owner->id)
             ->update(["tax_rate_id"=>null]);
     }
+
+    /**
+     * 获取税率 或 税费
+     *
+     * @param Model|\stdClass $model
+     * @param int $ownerId
+     * @param float|null $money
+     *
+     * @return float|null
+     */
+    public function getTaxRateFee(Model $model, int $ownerId, ?float $money = null):?float
+    {
+        $taxRate = null;
+        if ($model->tax_rate_id){
+            $model->loadMissing("taxRate");
+            $taxRate = $model->taxRate;
+        }
+        if (!$taxRate){
+            /** @var Model|\stdClass $owner */
+            $owner = new Owner();
+            $owner->id = $ownerId;
+            $owner->load("taxRate");
+            $taxRate = $owner->taxRate;
+        }
+        if (!$taxRate)return null;
+        if ($money===null)return $taxRate->value;
+        return $money*($taxRate->value/100);
+    }
 }

+ 15 - 12
app/Services/OwnerStoragePriceModelService.php

@@ -105,15 +105,20 @@ class OwnerStoragePriceModelService
     }
 
     //暂时不考虑单位换算问题:后期可能存在多单位换算,此处仅视为单位为 m²,m³
-    public function calculationAmount(OwnerStoragePriceModel $model, $area, $owner_id = null, $month = null)
+    public function calculationAmount(OwnerStoragePriceModel $model, $area, $owner_id = null, $month = null):array
     {
         /** @var \stdClass $model */
         if (!$model || !$area || $model->operation) return array(0,null);
         if ($area < $model->minimum_area) $area = $model->minimum_area;
+
+        $GLOBALS["FEE_INFO"]["fee_description"] .= "起租面积:".$model->minimum_area;
+
         switch ($model->discount_type){
             case "按单减免":
+                $GLOBALS["FEE_INFO"]["fee_description"] .= ",按单减免";
                 if ($owner_id && $month){
                     if ($model->timeUnit->name == '月'){
+                        $GLOBALS["FEE_INFO"]["fee_description"] .= ":每".$model->discount_value."单/月 减免1".$model->unit->name ?? 'm²';
                         $report = OwnerReport::query()->select("id",DB::raw("(to_business_quantity+to_customer_quantity) AS total"))
                             ->where("owner_id",$owner_id)
                             ->where("counting_month","like",$month."%")->first();
@@ -125,6 +130,7 @@ class OwnerStoragePriceModelService
                         $logisticSql = "(''";
                         foreach ($logistics as $logistic)$logisticSql.=",".$logistic;
                         $logisticSql .= ")";
+                        $GLOBALS["FEE_INFO"]["fee_description"] .= ":每".$model->discount_value."单/".$model->timeUnit->name." 减免1".$model->unit->name ?? 'm²';
                         for($i=1;$i<=$days;$i++){
                             $d = $i<10 ? "0".$i : $i;
                             $query = DB::raw("SELECT COUNT(1) c FROM orders WHERE logistic_id IN {$logisticSql} AND wms_status = ? AND wms_edittime BETWEEN ? AND ?");
@@ -135,6 +141,7 @@ class OwnerStoragePriceModelService
                 }
                 break;
             case "固定减免":
+                $GLOBALS["FEE_INFO"]["fee_description"] .= ",固定减免:每".$model->timeUnit->name."减免".$model->discount_value.($model->unit->name ?? 'm²');
                 if ($model->timeUnit->name == '月'){
                     $area -= $model->discount_value;
                 }else{
@@ -147,20 +154,16 @@ class OwnerStoragePriceModelService
         $index = 0;
         if ($model->amount_interval){
             $total = app("OrderService")->getOrderQuantity($owner_id);
-            for ($i=count($model->amount_interval);$i<0;$i--){
-                if ($total>=$model->amount_interval[$i])$model->price = $index;
+            for ($i=count($model->amount_interval)-1;$i>=0;$i--){
+                if ($total>=$model->amount_interval[$i])$index = $i;
             }
         }
+        $GLOBALS["FEE_INFO"]["fee_description"] .= ",单价: ".$model->price[$index]."/".($model->unit->name ?? 'm²')."/".$model->timeUnit->name;
         $money = $area>0 ? $area*$model->price[$index] : 0;
-        if ($model->taxRate)$taxFee = $money * ($model->taxRate->value/100);
-        else{
-            /** @var Owner|\stdClass $owner */
-            $owner = new Owner();
-            $owner->id = $owner_id;
-            $owner->load("taxRate");
-            if ($owner->taxRate)$taxFee = $money * ($owner->taxRate->value/100);
-            else $taxFee = null;
-        }
+        $taxRate = app("OwnerService")->getTaxRateFee($model, $owner_id);
+        $taxFee = $money*($taxRate/100);
+        $GLOBALS["FEE_INFO"]["fee_description"] .= ",税费: ".$taxFee;
+        $GLOBALS["FEE_INFO"]["tax_rate"] = $taxRate;
         return array($money,$taxFee);
     }
 }

+ 17 - 0
app/Services/ProvinceService.php

@@ -3,6 +3,7 @@
 namespace App\Services;
 
 use App\Province;
+use Illuminate\Database\Eloquent\Model;
 use Illuminate\Support\Str;
 use App\Traits\ServiceAppAop;
 
@@ -44,4 +45,20 @@ class ProvinceService
         }
         return $query->first();
     }
+
+    /**
+     * 根据名称获取省份
+     *
+     * @param string|null $name
+     *
+     * @return Model|\stdClass
+     */
+    public function getProvince(?string $name):?Model
+    {
+        if (!$name)return null;
+        $provinceName = mb_substr($name,0,2);
+        return app(CacheService::class)->getOrExecute("province_".$provinceName,function ()use($provinceName){
+            return Province::query()->where("name","like",$provinceName."%")->first();
+        },86400);
+    }
 }

+ 15 - 1
app/Services/RejectedBillService.php

@@ -353,16 +353,19 @@ class RejectedBillService
         foreach (Store::query()->with("storeItems")->whereIn("id",$number)->get() as $store){
             /** @var OwnerPriceOperationService $service */
             $service = app("OwnerPriceOperationService");
+            $GLOBALS["FEE_INFO"] = [];
             list($id,$money,$taxFee) = $service->matching($store, Feature::MAPPING["store"], $store->owner_id, "入库",0);
             $bill = OwnerFeeDetail::query()->where("outer_id",$store->id)->where("outer_table_name","stores")->first();
+            if ($bill)app("StoreService")->clearFeeInfo($store->asn_code);
             if ($bill) $bill->update([
                     "work_fee" => $money,
+                    "worked_at" => $rejectedBill->updated_at,
                     "owner_price_operation_id" => $id,
                     "outer_id" => $rejectedBill->id,
                     "outer_table_name" => "rejected_bills",
             ]); else app("OwnerFeeDetailService")->create([
                 "owner_id" => $store->owner_id,
-                "worked_at" => $store->created_at,
+                "worked_at" => $rejectedBill->updated_at,
                 "type" => "收货",
                 "operation_bill" => $store->asn_code,
                 "commodity_amount" => array_sum(array_column($store->storeItems->toArray(), "amount")),
@@ -373,6 +376,17 @@ class RejectedBillService
                 "outer_table_name" => "rejected_bills",
                 "work_tax_fee" => $taxFee,
             ]);
+            app("StoreService")->constructFeeInfo([
+                "worked_at" => $rejectedBill->updated_at,
+                "owner_id" => $rejectedBill->id_owner,
+                "model_id"  => $id,
+                "source_number"=> null,
+                "doc_number"   => $store->asn_code,
+                "commodity_id" => 0,
+                "total_fee"    =>0,
+                "tax_rate"     =>0,
+                "fee_description"=>'',
+            ]);
         }
     }
 }

+ 56 - 138
app/Services/StoreService.php

@@ -6,6 +6,8 @@ use App\Feature;
 use App\Jobs\StoreCreateInstantBill;
 use App\Order;
 use App\OwnerFeeDetail;
+use App\OwnerFeeOperation;
+use App\OwnerFeeOperationDetail;
 use App\Services\common\BatchUpdateService;
 use App\Services\common\DataHandlerService;
 use App\Services\common\QueryService;
@@ -325,9 +327,21 @@ class StoreService
         /** @var OwnerPriceOperationService $service */
         $service = app("OwnerPriceOperationService");
 
+        $GLOBALS["FEE_INFO"] = [];
         list($id,$money,$taxFee) = $service->matching($store, Feature::MAPPING["store"], $store->owner_id, "入库");
+        if ($money>0)$this->constructFeeInfo([
+                "worked_at" => $store->updated_at,
+                "owner_id" => $store->owner_id,
+                "model_id"  => $id,
+                "source_number"=> null,
+                "doc_number"   => $store->asn_code,
+                "commodity_id" => 0,
+                "total_fee"    =>0,
+                "tax_rate"     =>0,
+                "fee_description"=>'',
+            ]);
 
-        if (app("OwnerFeeDetailService")->create([
+        if (!app("OwnerFeeDetailService")->create([
             "owner_id" => $store->owner_id,
             "worked_at" => $store->updated_at,
             "type" => "收货",
@@ -339,15 +353,48 @@ class StoreService
             "outer_id" => $store->id,
             "outer_table_name" => "stores",
             "work_tax_fee" => $taxFee,
-        ])){
-            $amount = 0;
-            if ($store->storeItems)foreach ($store->storeItems as $item)$amount += $item->amount;
-            $this->setStoreAmount($store->owner_id,$amount);
-            Cache::put("owner_fee_details:stores_".$store->id,1,86400);
-            return true;
+        ])) return false;
+        $amount = 0;
+        if ($store->storeItems)foreach ($store->storeItems as $item)$amount += $item->amount;
+        $this->setStoreAmount($store->owner_id,$amount);
+        Cache::put("owner_fee_details:stores_".$store->id,1,86400);
+        return true;
+    }
+
+    /**
+     * 构建费用信息
+     *
+     * @param array $defaultInfo
+     *
+     * @return void
+     */
+    public function constructFeeInfo(array $defaultInfo)
+    {
+        foreach ($GLOBALS["FEE_INFO"] as $info){
+            $operation = $defaultInfo;
+            foreach ($operation as $key=>$val)if (isset($info[$key]))$operation[$key] = $info[$key];
+            $model = OwnerFeeOperation::query()->create($operation);
+            foreach ($info['details'] as &$detail)$detail["owner_fee_operation_id"] = $model->id;
+            OwnerFeeOperationDetail::query()->insert($info['details']);
         }
-        return false;
     }
+    /**
+     * 清除费用信息
+     *
+     * @param string $docNumber
+     */
+    public function clearFeeInfo(string $docNumber)
+    {
+        $models = OwnerFeeOperation::query()->where("doc_number",$docNumber)->get();
+        if ($models->count()==0)return;
+        OwnerFeeOperationDetail::query()->whereIn("owner_fee_operation_id",array_column($models->toArray(),"id"))->delete();
+        OwnerFeeOperation::query()->where("doc_number",$docNumber)->delete();
+    }
+
+    /**
+     * @param $asnHerders
+     * @return null
+     */
     public function createStoreRejected($asnHerders){
         if (!$asnHerders) return null;
         $stores = $this->getByWms($asnHerders);
@@ -388,7 +435,7 @@ SELECT sum(amount) total FROM `store_items` LEFT JOIN stores ON store_items.stor
 sql
         );
         $statistics = DB::selectOne($query,[$owner,date("Y-m")."%"]);
-        Cache::put(date("Y-m")."|A|".$owner,$statistics->total ? $statistics->total : 0,2764800);
+        Cache::put(date("Y-m")."|A|".$owner,$statistics->total ?: 0,2764800);
     }
 
     /**
@@ -417,133 +464,4 @@ sql
         if (!Cache::has($date."|A|".$owner))$this->storeAmountCompensationLogic($owner);
         return Cache::get($date."|A|".$owner);
     }
-
-    public function warehousing(array $params)
-    {
-        $conn = oci_connect(config('database.connections.oracle.username'),
-            config('database.connections.oracle.password'),
-            config('database.connections.oracle.host'). '/' . config('database.connections.oracle.service_name'),"utf8");
-        $sp = "begin SPASN_Receiving_Process(:IN_Warehouse, :In_Process_Action, :In_ASNNo_C, :In_ASNLineNo_C, :In_FMTraceID_C, :In_New_TraceID_C, :In_ProductStatus," .
-            ":In_ProductStatus_Descr, :In_HoldRejectCode_C, :In_HoldRejectReason_C, :In_PONo_C, :In_CustomerID, :In_SKU, :In_ReceivedQty, :In_RejectedQty,:In_UOM, :In_PackID," .
-            " :In_ContainerID, :In_LotAtt01_C, :In_LotAtt02_C, :In_LotAtt03_C, :In_LotAtt04_C, :In_LotAtt05_C, :In_LotAtt06_C," .
-            ":In_LotAtt07_C, :In_LotAtt08_C, :In_LotAtt09_C, :In_LotAtt10_C, :In_LotAtt11_C, :In_LotAtt12_C," .
-            ":In_TotalCubic, :In_TotalGrossWeight, :In_TotalNetWeight, :In_TotalPrice, :In_UserDefine1, :In_UserDefine2,:In_UserDefine3, :In_UserDefine4, :In_UserDefine5, :In_FMLocation," .
-            ":In_TOLocation_C,:In_QC_Type_C, :In_PlanToLoc_C,:In_ReceivingTime, :In_LPN, :In_Operator, :IN_RCVModule, :IN_RCVStation, :In_Language, :In_UserID, :OUT_Return_Code); end;";
-        $inParams = array(
-            "IN_Warehouse"=>"",
-            "In_Process_Action"=>"",
-            "In_ASNNo_C"=>"",
-            "In_ASNLineNo_C"=>"",
-            "In_FMTraceID_C"=>"",
-            "In_New_TraceID_C"=>"",
-            "In_ProductStatus"=>"00",
-            "In_ProductStatus_Descr"=>"正常",
-            "In_HoldRejectCode_C"=>"OK",
-            "In_HoldRejectReason_C"=>"正常",
-            "In_PONo_C"=>"",
-            "In_CustomerID"=>"",
-            "In_SKU"=>"",
-            "In_ReceivedQty"=>"",
-            "In_RejectedQty"=>"",
-            "In_UOM"=>"EA",
-            "In_PackID"=>"",
-            "In_ContainerID"=>"",
-            "In_LotAtt01_C"=>"",
-            "In_LotAtt02_C"=>"",
-            "In_LotAtt03_C"=>"",
-            "In_LotAtt04_C"=>"",
-            "In_LotAtt05_C"=>"",
-            "In_LotAtt06_C"=>"",
-            "In_LotAtt07_C"=>"",
-            "In_LotAtt08_C"=>"",
-            "In_LotAtt09_C"=>"",
-            "In_LotAtt10_C"=>"",
-            "In_LotAtt11_C"=>"",
-            "In_LotAtt12_C"=>"",
-            "In_TotalCubic"=>"0.00",
-            "In_TotalGrossWeight"=>"0.00",
-            "In_TotalNetWeight"=>"0.00",
-            "In_TotalPrice"=>"0.00",
-            "In_UserDefine1"=>"",
-            "In_UserDefine2"=>"",
-            "In_UserDefine3"=>"",
-            "In_UserDefine4"=>"",
-            "In_UserDefine5"=>"",
-            "In_FMLocation"=>"",
-            "In_TOLocation_C"=>"",
-            "In_QC_Type_C"=>"OK",
-            "In_PlanToLoc_C"=>"",
-            "In_ReceivingTime"=>"",
-            "In_LPN"=>"*",
-            "In_Operator"=>"WCS",
-            "IN_RCVModule"=>"",
-            "IN_RCVStation"=>"",
-            "In_Language"=>"cn",
-            "In_UserID"=>"WCS",
-            "result"=>""
-        );
-        foreach ($params as $key=>$val)$inParams[$key] = $val;
-        list($IN_Warehouse,$In_Process_Action,$In_ASNNo_C,$In_ASNLineNo_C,$In_FMTraceID_C,$In_New_TraceID_C,$In_ProductStatus,
-            $In_ProductStatus_Descr,$In_HoldRejectCode_C,$In_HoldRejectReason_C,$In_PONo_C,$In_CustomerID,$In_SKU,$In_ReceivedQty,
-            $In_RejectedQty,$In_UOM,$In_PackID,$In_ContainerID,$In_LotAtt01_C,$In_LotAtt02_C,$In_LotAtt03_C,$In_LotAtt04_C,$In_LotAtt05_C,
-            $In_LotAtt06_C,$In_LotAtt07_C,$In_LotAtt08_C,$In_LotAtt09_C,$In_LotAtt10_C,$In_LotAtt11_C,$In_LotAtt12_C,$In_TotalCubic,
-            $In_TotalGrossWeight,$In_TotalNetWeight,$In_TotalPrice,$In_UserDefine1,$In_UserDefine2,$In_UserDefine3,$In_UserDefine4,
-            $In_UserDefine5,$In_FMLocation,$In_TOLocation_C,$In_QC_Type_C,$In_PlanToLoc_C,$In_ReceivingTime,$In_LPN,$In_Operator,
-            $IN_RCVModule,$IN_RCVStation,$In_Language,$In_UserID,$result) = array_values($inParams);
-        $stmt = oci_parse($conn, $sp);
-        oci_bind_by_name($stmt, ':IN_Warehouse', $IN_Warehouse);
-        oci_bind_by_name($stmt, ':In_Process_Action', $In_Process_Action);
-        oci_bind_by_name($stmt, ':In_ASNNo_C', $In_ASNNo_C);
-        oci_bind_by_name($stmt, ':In_ASNLineNo_C', $In_ASNLineNo_C);
-        oci_bind_by_name($stmt, ':In_FMTraceID_C', $In_FMTraceID_C);
-        oci_bind_by_name($stmt, ':In_New_TraceID_C', $In_New_TraceID_C);
-        oci_bind_by_name($stmt, ':In_ProductStatus', $In_ProductStatus);
-        oci_bind_by_name($stmt, ':In_ProductStatus_Descr', $In_ProductStatus_Descr);
-        oci_bind_by_name($stmt, ':In_HoldRejectCode_C', $In_HoldRejectCode_C);
-        oci_bind_by_name($stmt, ':In_HoldRejectReason_C', $In_HoldRejectReason_C);
-        oci_bind_by_name($stmt, ':In_PONo_C', $In_PONo_C);
-        oci_bind_by_name($stmt, ':In_CustomerID', $In_CustomerID);
-        oci_bind_by_name($stmt, ':In_SKU', $In_SKU);
-        oci_bind_by_name($stmt, ':In_ReceivedQty', $In_ReceivedQty);
-        oci_bind_by_name($stmt, ':In_RejectedQty', $In_RejectedQty);
-        oci_bind_by_name($stmt, ':In_UOM', $In_UOM);
-        oci_bind_by_name($stmt, ':In_PackID', $In_PackID);
-        oci_bind_by_name($stmt, ':In_ContainerID', $In_ContainerID);
-        oci_bind_by_name($stmt, ':In_LotAtt01_C', $In_LotAtt01_C);
-        oci_bind_by_name($stmt, ':In_LotAtt02_C', $In_LotAtt02_C);
-        oci_bind_by_name($stmt, ':In_LotAtt03_C', $In_LotAtt03_C);
-        oci_bind_by_name($stmt, ':In_LotAtt04_C', $In_LotAtt04_C);
-        oci_bind_by_name($stmt, ':In_LotAtt05_C', $In_LotAtt05_C);
-        oci_bind_by_name($stmt, ':In_LotAtt06_C', $In_LotAtt06_C);
-        oci_bind_by_name($stmt, ':In_LotAtt07_C', $In_LotAtt07_C);
-        oci_bind_by_name($stmt, ':In_LotAtt08_C', $In_LotAtt08_C);
-        oci_bind_by_name($stmt, ':In_LotAtt09_C', $In_LotAtt09_C);
-        oci_bind_by_name($stmt, ':In_LotAtt10_C', $In_LotAtt10_C);
-        oci_bind_by_name($stmt, ':In_LotAtt11_C', $In_LotAtt11_C);
-        oci_bind_by_name($stmt, ':In_LotAtt12_C', $In_LotAtt12_C);
-        oci_bind_by_name($stmt, ':In_TotalCubic', $In_TotalCubic);
-        oci_bind_by_name($stmt, ':In_TotalGrossWeight', $In_TotalGrossWeight);
-        oci_bind_by_name($stmt, ':In_TotalNetWeight', $In_TotalNetWeight);
-        oci_bind_by_name($stmt, ':In_TotalPrice', $In_TotalPrice);
-        oci_bind_by_name($stmt, ':In_UserDefine1', $In_UserDefine1);
-        oci_bind_by_name($stmt, ':In_UserDefine2', $In_UserDefine2);
-        oci_bind_by_name($stmt, ':In_UserDefine3', $In_UserDefine3);
-        oci_bind_by_name($stmt, ':In_UserDefine4', $In_UserDefine4);
-        oci_bind_by_name($stmt, ':In_UserDefine5', $In_UserDefine5);
-        oci_bind_by_name($stmt, ':In_FMLocation', $In_FMLocation);
-        oci_bind_by_name($stmt, ':In_TOLocation_C', $In_TOLocation_C);
-        oci_bind_by_name($stmt, ':In_QC_Type_C', $In_QC_Type_C);
-        oci_bind_by_name($stmt, ':In_PlanToLoc_C', $In_PlanToLoc_C);
-        oci_bind_by_name($stmt, ':In_ReceivingTime', $In_ReceivingTime);
-        oci_bind_by_name($stmt, ':In_LPN', $In_LPN);
-        oci_bind_by_name($stmt, ':In_Operator', $In_Operator);
-        oci_bind_by_name($stmt, ':IN_RCVModule', $IN_RCVModule);
-        oci_bind_by_name($stmt, ':IN_RCVStation', $IN_RCVStation);
-        oci_bind_by_name($stmt, ':In_Language', $In_Language);
-        oci_bind_by_name($stmt, ':In_UserID', $In_UserID);
-        oci_bind_by_name($stmt, ':OUT_Return_Code', $result,300);
-        oci_execute($stmt);
-        oci_close($conn);
-        return $result;
-    }
 }

+ 0 - 17
app/Services/TestService.php

@@ -1,17 +0,0 @@
-<?php
-
-
-namespace App\Services;
-
-use App\Traits\ServiceAppAop;
-
-
-class TestService
-{
-    use ServiceAppAop;
-    protected $modelClass=Test::class;
-    public function __construct()
-    {
-        dd(323333);
-    }
-}

+ 26 - 2
app/Services/UnitService.php

@@ -4,6 +4,7 @@ namespace App\Services;
 
 use App\Unit;
 use App\Traits\ServiceAppAop;
+use Illuminate\Database\Eloquent\Model;
 use Illuminate\Support\Facades\Cache;
 
 
@@ -49,12 +50,14 @@ class UnitService
      * 获取单位集的映射
      *
      * @param null|array $targets
+     * @param bool $notExistToCreate
      *
      * @return array
      */
-    public function getUnitMapping($targets = null)
+    public function getUnitMapping(?array $targets = null, bool $notExistToCreate = false):array
     {
-        return Cache::remember("unitMapping:".json_encode($targets,JSON_UNESCAPED_UNICODE),config("cache.expirations.rarelyChange"),function ()use($targets){
+        $key = "unitMapping:".json_encode($targets,JSON_UNESCAPED_UNICODE);
+        $units = Cache::remember($key,config("cache.expirations.rarelyChange"),function ()use($targets){
             $result = [];
             $units = $this->getUnitCache();
             if (!$units)return [];
@@ -64,6 +67,27 @@ class UnitService
             }
             return $result;
         });
+        if ($notExistToCreate && $targets && count($units)!=count($targets)){
+            $flip = array_flip($targets);
+            foreach ($units as $key=>$val)unset($flip[$val]);
+            foreach ($flip as $key=>$val)Unit::query()->firstOrCreate(["name"=>$key]);
+            Cache::pull($key);
+            return $this->getUnitMapping($targets);
+        }
+        return $units;
+    }
+
+    /**
+     * 根据名称获取单位
+     *
+     * @param string|null $name
+     *
+     * @return Model|\stdClass|null
+     */
+    public function getUnit(?string $name):?Unit
+    {
+        if (!$name)return Unit::query()->firstOrCreate(["name"=>"件"]);
+        return Unit::query()->where("name",$name)->first();
     }
 
 }

+ 31 - 4
app/Services/WaybillService.php

@@ -5,6 +5,7 @@ namespace App\Services;
 use App\Http\Controllers\api\thirdPart\flux\WaybillController;
 use App\Order;
 use App\OwnerFeeDetail;
+use App\OwnerFeeLogistic;
 use App\Services\common\BatchUpdateService;
 use App\Services\common\QueryService;
 use App\Traits\ModelSearchWay;
@@ -44,7 +45,7 @@ class WaybillService
                 /** @var Builder $query */
                 $query->whereIn("waybills.owner_id",$ownerIds)->orWhereHas("order",function ($query)use($ownerIds){
                     /** @var Builder $query */
-                    $query->whereIn("owner",$ownerIds);
+                    $query->whereIn("owner_id",$ownerIds);
                 });
             });
             unset($param["owner"]);
@@ -212,7 +213,7 @@ class WaybillService
      *
      * @param Waybill|\stdClass $waybill
      * @param array $param
-     * @param $id
+     *
      * @return Model
      */
     public function update(Waybill $waybill,array $param)
@@ -252,11 +253,28 @@ class WaybillService
         if ($detail && $detail->logistic_fee !== null)return false;
 
         if ($waybill->type == "专线"){
+            $provinceId = $waybill->order ? app("ProvinceService")->getProvince($waybill->order->province) : $waybill->destinationCity->province_id;
+            $cityId = $waybill->destination_city_id;
+            $consigneeName = $waybill->order ? $waybill->order->consignee_name : $waybill->recipient;
+            $consigneePhone = $waybill->order ? $waybill->order->consignee_phone : $waybill->recipient_mobile;
+            $GLOBALS["FEE_INFO"] = [
+                "province_id" => $provinceId,
+                "owner_id"  => $waybill->owner_id,
+                "city_id"   => $cityId,
+                "logistic_id" => $waybill->logistic_id,
+                "order_number" => $waybill->wms_bill_number,
+                "recipient_name"=>$consigneeName,
+                "recipient_phone"=>$consigneePhone,
+                "quantity"=>$waybill->carrier_weight_other,
+                "unit_id"=>$waybill->carrier_weight_unit_id_other,
+                "remark"=>$waybill->ordering_remark,
+                "created_at"=>$waybill->updated_at,
+            ];
             /** @var OwnerPriceLogisticService $service */
             $service = app("OwnerPriceLogisticService");
             list($fee,$taxFee) = $service->matching($waybill->carrier_weight_other,$owner_id,$waybill->logistic_id,
-                $waybill->carrier_weight_unit_id_other,$waybill->order ? app("RegionService")->getProvince($waybill->order->province) : $waybill->destinationCity->province_id,
-                $waybill->destination_city_id);
+                $waybill->carrier_weight_unit_id_other,$provinceId, $cityId);
+            $this->buildWaybillFeeInfo();
         }else{
             /** @var OwnerPriceDirectLogisticService $service */
             $service = app("OwnerPriceDirectLogisticService");
@@ -286,6 +304,15 @@ class WaybillService
         return true;
     }
 
+    /**
+     * 构建运输费用信息
+     */
+    public function buildWaybillFeeInfo()
+    {
+        if (!isset($GLOBALS["FEE_INFO"]))return;
+        OwnerFeeLogistic::query()->create($GLOBALS["FEE_INFO"]);
+    }
+
     /**
      * 生成德邦单据
      *

+ 35 - 0
database/migrations/2021_08_11_173608_create_owner_fee_storages_table.php

@@ -0,0 +1,35 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+class CreateOwnerFeeStoragesTable extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::create('owner_fee_storages', function (Blueprint $table) {
+            $table->bigInteger("area_id")->primary()->comment("面积ID");
+            $table->string("counting_type")->comment("计费类型");
+            $table->string("using_type")->comment("用仓类型");
+            $table->text("fee_description")->comment("费用描述");
+            $table->decimal("total_fee",10,3)->comment("总金额");
+            $table->decimal("tax_rate",10,3)->nullable()->comment("税率");
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::dropIfExists('owner_fee_storages');
+    }
+}

+ 43 - 0
database/migrations/2021_08_12_140948_create_owner_fee_expresses_table.php

@@ -0,0 +1,43 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+class CreateOwnerFeeExpressesTable extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::create('owner_fee_expresses', function (Blueprint $table) {
+            $table->id();
+            $table->bigInteger("province_id")->index()->comment("外联省份");
+            $table->bigInteger("logistic_id")->index()->comment("快递公司");
+            $table->bigInteger("owner_id")->index()->comment("货主ID");
+            $table->string("logistic_number")->comment("快递单号");
+            $table->decimal("weight",10,3)->comment("重量");
+            $table->decimal("initial_weight",10,3)->comment("首重重量");
+            $table->decimal("initial_weight_price",10,3)->comment("首重价格");
+            $table->decimal("additional_weight",10,3)->comment("续重重量");
+            $table->decimal("additional_weight_amount",10,3)->comment("续重量");
+            $table->decimal("additional_weight_price",10,3)->comment("续重价格");
+            $table->decimal("total_fee",10,3)->comment("快递费");
+            $table->decimal("tax_rate",10,3)->nullable()->comment("税率");
+            $table->timestamp("created_at")->comment("建立时间");
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::dropIfExists('owner_fee_expresses');
+    }
+}

+ 52 - 0
database/migrations/2021_08_12_141004_create_owner_fee_logistics_table.php

@@ -0,0 +1,52 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+class CreateOwnerFeeLogisticsTable extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::create('owner_fee_logistics', function (Blueprint $table) {
+            $table->id();
+            $table->bigInteger("province_id")->index()->comment("外联省份");
+            $table->bigInteger("owner_id")->index()->comment("货主ID");
+            $table->bigInteger("city_id")->index()->comment("外联城市");
+            $table->bigInteger("logistic_id")->index()->comment("快递公司");
+            $table->string("order_number")->nullable()->comment("订单号");
+            $table->string("recipient_name")->nullable()->comment("收件人姓名");
+            $table->string("recipient_phone")->nullable()->comment("收件人电话");
+            $table->decimal("quantity",10,3)->comment("计量");
+            $table->bigInteger("unit_id")->comment("单位");
+            $table->string("interval")->comment("区间");
+            $table->decimal("price",10,3)->comment("单价");
+            $table->decimal("delivery_fee",10,3)->nullable()->comment("送货费");
+            $table->decimal("pick_fee",10,3)->nullable()->comment("提货费");
+            $table->decimal("fuel_fee",10,3)->nullable()->comment("燃油附加费");
+            $table->decimal("info_fee",10,3)->nullable()->comment("信息费");
+            $table->decimal("other_fee",10,3)->nullable()->comment("其他费用");
+            $table->decimal("initial_fee",10,3)->default(0)->comment("起始计费");
+            $table->decimal("initial_amount",10,3)->default(0)->comment("起始计数");
+            $table->decimal("total_fee",10,3)->comment("总费用");
+            $table->decimal("tax_rate",10,3)->nullable()->comment("税率");
+            $table->text("remark")->nullable()->comment("备注");
+            $table->timestamp("created_at")->comment("建立时间");
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::dropIfExists('owner_fee_logistics');
+    }
+}

+ 39 - 0
database/migrations/2021_08_13_093747_create_owner_fee_operations.php

@@ -0,0 +1,39 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+class CreateOwnerFeeOperations extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::create('owner_fee_operations', function (Blueprint $table) {
+            $table->id();
+            $table->bigInteger("owner_id")->index()->comment("货主ID");
+            $table->date("worked_at")->comment("作业时间");
+            $table->bigInteger("model_id")->comment("外联计费模型");
+            $table->string("source_number")->nullable()->comment("上游单号");
+            $table->string("doc_number")->comment("单据单号");
+            $table->bigInteger("commodity_id")->comment("外键商品");
+            $table->decimal("total_fee",10,3)->comment("总价");
+            $table->decimal("tax_rate",10,3)->nullable()->comment("税率");
+            $table->text("fee_description")->nullable()->comment("描述");
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::dropIfExists('owner_fee_operations');
+    }
+}

+ 35 - 0
database/migrations/2021_08_13_093836_create_owner_fee_operation_details.php

@@ -0,0 +1,35 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+class CreateOwnerFeeOperationDetails extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::create('owner_fee_operation_details', function (Blueprint $table) {
+            $table->id();
+            $table->bigInteger("owner_fee_operation_id")->comment("外键父级");
+            $table->string("name")->comment("名称");
+            $table->decimal("amount",10,3)->comment("数量");
+            $table->decimal("price",10,3)->comment("单价");
+            $table->bigInteger("unit_id")->comment("外键单位");
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::dropIfExists('owner_fee_operation_details');
+    }
+}

+ 1 - 1
resources/views/customer/project/index.blade.php

@@ -74,7 +74,7 @@
                         <button class="btn btn-sm btn-info text-white" @click="showModal(owner)">各项计价</button>
                     </td>
                     <td>
-                        <a :href="'{{url('customer/project')}}/'+owner.id+'/edit'"><button class="btn btn-sm btn0sm btn-outline-info">编辑</button></a>
+                        @can("项目管理-项目-编辑")<a :href="'{{url('customer/project')}}/'+owner.id+'/edit'"><button class="btn btn-sm btn0sm btn-outline-info">编辑</button></a>@endcan
                         @can('客户管理-项目-停用')
                             <button class="btn btn-sm btn-outline-danger" @click="destroy(owner)">停用</button>
                         @endcan

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

@@ -79,7 +79,7 @@
         <label class="col-4 text-secondary">@{{ value ? (model.operation.discount_count[i+1] ? value+'-'+(model.operation.discount_count[i+1]-1)+' '+(model.operation.operation_type=='入库' ? '件' : '单')+'/月' : value+'+ '+(model.operation.operation_type=='入库' ? '件' : '单')+'/月') : '' }}</label>
     </div>
 </div>
-<div class="row mt-3">
+<div class="row mt-3" v-if="!model.operation.isSingle">
     <label class="col-2" for="max_fee">封顶费</label>
     <input type="number" id="max_fee" step="0.01" min="0" class="form-control col-6" v-model="model.operation.max_fee">
 </div>
@@ -170,7 +170,7 @@
         </div>
     </div>
 </div>
-<div class="row mt-3" v-if="model.operation.operation_type=='出库'">
+<div class="row mt-3" v-if="model.operation.operation_type=='出库' && !model.operation.isSingle">
     <label for="surcharge" class="col-2">耗材附加费</label>
     <input id="surcharge" type="number" step="0.01" min="0" class="form-control col-3" :class="errors.surcharge ? 'is-invalid' : ''" v-model="model.operation.surcharge">
     <label for="surcharge_unit_id" class="col-2 text-right">单位</label>
@@ -189,4 +189,4 @@
         <option> </option>
         <option v-for="tax in pool.taxRates" :value="tax.id">@{{ tax.value }}%</option>
     </select>
-</div>
+</div>