瀏覽代碼

Merge branch 'master' into zzd

dream 5 年之前
父節點
當前提交
ac2ced4be5
共有 98 個文件被更改,包括 4553 次插入549 次删除
  1. 8 3
      .gitlab-ci.yml
  2. 5 5
      app/Console/Commands/SyncBatchTask.php
  3. 196 0
      app/Filters/OrderTrackFilters.php
  4. 95 0
      app/Http/Controllers/CacheShelfController.php
  5. 13 1
      app/Http/Controllers/OrderIssueController.php
  6. 85 0
      app/Http/Controllers/OrderPackageCommoditySerialNumberController.php
  7. 25 24
      app/Http/Controllers/OrderTrackingController.php
  8. 7 7
      app/Http/Controllers/RejectedBillController.php
  9. 85 0
      app/Http/Controllers/StationCacheShelfGridController.php
  10. 1 0
      app/Http/Controllers/StationController.php
  11. 77 18
      app/Http/Controllers/TestController.php
  12. 1 0
      app/Http/Controllers/UserDutyCheckController.php
  13. 10 2
      app/Http/Controllers/api/thirdPart/haiq/LightController.php
  14. 12 8
      app/Http/Controllers/api/thirdPart/haiq/StorageController.php
  15. 12 1
      app/OracleActAllocationDetails.php
  16. 28 0
      app/OracleDocOrderSerialNo.php
  17. 13 3
      app/OrderPackageCommodities.php
  18. 23 0
      app/OrderPackageCommoditySerialNumber.php
  19. 21 3
      app/OrderTracking.php
  20. 6 1
      app/Providers/AppServiceProvider.php
  21. 42 0
      app/Providers/HorizonServiceProvider.php
  22. 25 10
      app/Services/BatchService.php
  23. 108 0
      app/Services/CacheShelfService.php
  24. 1 0
      app/Services/CityService.php
  25. 98 32
      app/Services/ForeignHaiRoboticsService.php
  26. 19 0
      app/Services/OracleDOCOrderHeaderService.php
  27. 1 0
      app/Services/OrderIssueService.php
  28. 153 0
      app/Services/OrderPackageCommoditySerialNumberService.php
  29. 6 3
      app/Services/OrderService.php
  30. 2 130
      app/Services/OrderTrackingService.php
  31. 2 0
      app/Services/ProvinceService.php
  32. 106 0
      app/Services/StationCacheShelfGridService.php
  33. 6 3
      app/Services/StationRuleBatchService.php
  34. 16 3
      app/Services/StationTaskBatchService.php
  35. 18 14
      app/Services/StationTaskCommodityService.php
  36. 84 23
      app/Services/StationTaskMaterialBoxService.php
  37. 17 1
      app/Services/StationTaskService.php
  38. 3 3
      app/Services/StoreService.php
  39. 8 1
      app/Station.php
  40. 61 0
      app/StationCacheShelfGrid.php
  41. 1 1
      app/StationTask.php
  42. 11 0
      bootstrap/cache/packages.php
  43. 29 25
      bootstrap/cache/services.php
  44. 25 0
      ci/executeCommand.sh
  45. 35 0
      ci/judgePush.sh
  46. 6 1
      composer.json
  47. 312 164
      composer.lock
  48. 1 0
      config/app.php
  49. 5 0
      config/haiRou.php
  50. 192 0
      config/horizon.php
  51. 304 0
      config/jwt.php
  52. 7 0
      config/sync,php.php
  53. 1 0
      database/factories/OracleActAllocationDetailsFactory.php
  54. 14 0
      database/factories/OracleDocOrderSerialNoFactory.php
  55. 12 0
      database/factories/OrderPackageCommoditySerialNumberFactory.php
  56. 15 0
      database/factories/StationCacheShelfGridFactory.php
  57. 2 1
      database/factories/StationFactory.php
  58. 69 0
      database/migrations/2021_04_13_140632_add_station_type_and_add_station.php
  59. 35 0
      database/migrations/2021_04_15_132002_create_station_cache_shelf_grids_table.php
  60. 33 0
      database/migrations/2021_04_23_174700_create_order_package_commodity_serial_numbers_table.php
  61. 32 0
      database/migrations/2021_04_25_185620_change_index_of_station_task_batches_field_batch_id.php
  62. 16 0
      database/seeds/OrderPackageCommoditySerialNumberSeeder.php
  63. 16 0
      database/seeds/StationCacheShelfGridSeeder.php
  64. 7 0
      public/vendor/horizon/app-dark.css
  65. 7 0
      public/vendor/horizon/app.css
  66. 1 0
      public/vendor/horizon/app.js
  67. 二進制
      public/vendor/horizon/img/favicon.png
  68. 1 0
      public/vendor/horizon/img/horizon.svg
  69. 806 0
      public/vendor/horizon/img/sprite.svg
  70. 5 0
      public/vendor/horizon/mix-manifest.json
  71. 1 1
      resources/js/utilities/barcode.js
  72. 34 0
      resources/scripts/git-hooks/pre-push
  73. 1 1
      resources/views/demand/_create.blade.php
  74. 1 1
      resources/views/demand/_createjs.blade.php
  75. 78 34
      resources/views/order/tracking/index.blade.php
  76. 4 4
      resources/views/procurement/procurement/index.blade.php
  77. 5 4
      resources/views/rejected/importRejectedNumber.blade.php
  78. 2 2
      resources/views/rejected/search/general.blade.php
  79. 42 0
      resources/views/station/cachingShelf/list/_fillBox.blade.php
  80. 61 0
      resources/views/station/cachingShelf/list/_table.blade.php
  81. 190 0
      resources/views/station/cachingShelf/list/index.blade.php
  82. 9 0
      resources/views/station/cachingShelf/menu.blade.php
  83. 1 1
      resources/views/station/menu.blade.php
  84. 5 9
      resources/views/transport/waybill/index.blade.php
  85. 9 0
      routes/apiLocal.php
  86. 3 0
      routes/web.php
  87. 1 1
      syncProject.sh
  88. 49 0
      tests/Services/CacheShelfService/GetTasksTest.php
  89. 70 0
      tests/Services/CacheShelfService/LightOffTaskTest.php
  90. 88 0
      tests/Services/CacheShelfService/PutBinToStoreTest.php
  91. 62 0
      tests/Services/ForeignHaiRoboticsService/PutBinToStoreFromCacheShelfTest.php
  92. 94 0
      tests/Services/OrderPackageCommoditySerialNumberService/GetCreateModelBy.php
  93. 38 0
      tests/Services/StationCacheShelfGrid/GetGridByCodeTest.php
  94. 39 0
      tests/Services/StationCacheShelfGrid/GetLocationTest.php
  95. 39 0
      tests/Services/StationCacheShelfGridService/CancelTaskTest.php
  96. 47 0
      tests/Services/StationCacheShelfGridService/LightOffTest.php
  97. 42 0
      tests/Services/StationCacheShelfGridService/ProcessGridTest.php
  98. 39 0
      tests/webApi/cacheShelf.http

+ 8 - 3
.gitlab-ci.yml

@@ -17,9 +17,7 @@ build:
   tags:
     - testwas
   script:
-#    - echo "build ....."
-    - path=`pwd`
-    - echo "$path"
+    - echo "build ....."
 
 test:
   stage: test
@@ -27,6 +25,8 @@ test:
     - testwas
   script:
     - echo "run test....."
+#    - path=`pwd`
+#    - sudo bash ci/judgePush.sh $path
 #    - vendor/bin/phpunit --testsuite=Unit
 
 deploy:
@@ -35,3 +35,8 @@ deploy:
     - testwas
   script:
     - echo "deploy ...."
+#    - cd /var/www/bswas_test && sudo git config --global credential.helper store
+#    - sudo git pull
+#    - sudo bash ci/executeCommand.sh
+#  only:
+#    - master

+ 5 - 5
app/Console/Commands/SyncBatchTask.php

@@ -228,7 +228,7 @@ sql;
         }
         $updateOrder = [["code","batch_id"]];
         $existOrder = [];
-        $updateBatch = [];
+        $updatingBatches = [];
         foreach (Order::query()->select("code","batch_id")->whereNotNull("batch_id")->whereIn("code",$ods)->get() as $item){
             $existOrder[$item->code] = $item->batch_id;
         }
@@ -243,15 +243,15 @@ sql;
                     $mark = true;
                 }
             }
-            if ($mark)$updateBatch[] = $batch;
+            if ($mark)$updatingBatches[] = $batch;
             unset($map[$batch->code]);
         }
         if (count($updateOrder)>1){
             app("OrderService")->batchUpdate($updateOrder);//反向修改订单
             LogService::log(__METHOD__,"波次同步-修改订单波次号",json_encode($updateOrder));
-            LogService::log(__METHOD__,"修改过的波次_",json_encode($updateBatch));
-            app("BatchService")->checkBatchOrderInfo($updateBatch);
-            BatchTaskJob::dispatch($updateBatch);    //在这里为波次注册队列任务!
+            app("BatchService")->checkBatchOrderInfo($updatingBatches);
+            LogService::log(__METHOD__,"修改过的波次_",json_encode($updatingBatches));
+            BatchTaskJob::dispatch($updatingBatches);    //在这里为波次注册队列任务!
         }
 
         if ($map){

+ 196 - 0
app/Filters/OrderTrackFilters.php

@@ -0,0 +1,196 @@
+<?php
+
+
+namespace App\Filters;
+
+
+use App\Commodity;
+use App\Order;
+use App\OrderPackage;
+use App\OrderPackageCommodities;
+use App\Services\UserService;
+use App\Traits\ModelSearchWay;
+use App\User;
+use Illuminate\Database\Eloquent\Builder;
+use Illuminate\Http\Request;
+use Illuminate\Support\Carbon;
+use Illuminate\Support\Facades\Auth;
+
+class OrderTrackFilters
+{
+    use ModelSearchWay;
+
+    protected $request;
+    protected $queryBuilder;
+    protected $params = [];
+    protected $filters = ['client_code', 'order_client_code_wms',
+        'logistic_id', 'client', 'web_order_number', 'sku', 'logistic_number','remark','ids',
+        'start_at', 'end_at', 'start_planning_sent_at', 'end_planning_sent_at'];
+    protected $array_filter;
+
+    /** @var Builder $orderPackageCommodityQuery */
+    protected $commodityQuery;
+    /** @var Builder $orderPackageCommodityQuery */
+    protected $orderPackageCommodityQuery;
+    /** @var Builder $orderPackageQuery */
+    protected $orderPackageQuery;
+    /** @var Builder $orderQuery */
+    protected $orderQuery;
+
+    public function __construct(Request $request)
+    {
+        $this->request = $request;
+        $this->params = $request->all();
+        $this->array_filter = array_filter($this->request->only($this->filters));
+    }
+
+    public function apply($builder)
+    {
+        $this->queryBuilder = $builder;
+        $this->afterApply();
+        foreach ($this->array_filter as $filter => $value) {
+            if (method_exists($this, $filter)) {
+                $this->$filter($value, $this->queryBuilder);
+            }
+        }
+        $this->beforeApply();
+        return $this->queryBuilder;
+    }
+
+    public function afterApply()
+    {
+        if(isset($this->params['data']))
+            $this->id(explode(',',$this->params['data']));
+
+        // 货主
+        $user = Auth::user();
+        $owner_ids = $user ? app(UserService::class)->getPermittingOwnerIds($user) : [];
+        if (isset($this->params['owner_id'])) {
+            $ownerIds = explode(",", $this->params['owner_id']);
+            $owner_ids = array_intersect($owner_ids, $ownerIds);
+        }
+        $this->ownerId($owner_ids);
+    }
+
+    public function getCommodityQuery(): Builder
+    {
+        if(!$this->commodityQuery)
+            $this->commodityQuery = Commodity::query()->selectRaw('id');
+        return $this->commodityQuery;
+    }
+
+    public function getOrderPackageCommodityQuery(): Builder
+    {
+        if(!$this->orderPackageCommodityQuery)
+            $this->orderPackageCommodityQuery = OrderPackageCommodities::query()->selectRaw('id');
+        return $this->orderPackageCommodityQuery;
+    }
+
+    public function getOrderPackageQuery(): Builder
+    {
+        if(!$this->orderPackageQuery)
+            $this->orderPackageQuery = OrderPackage::query()->selectRaw('id');
+        return $this->orderPackageQuery;
+    }
+
+    public function getOrderQuery(): Builder
+    {
+        if(!$this->orderQuery)
+            $this->orderQuery = Order::query()->selectRaw('id');
+        return $this->orderQuery;
+    }
+
+    public function client_code($client_code)
+    {
+        $this->searchWay($this->queryBuilder,$client_code,'order_trackings.order_client_code');
+    }
+
+    public function order_client_code_wms($order_client_code_wms)
+    {
+        $this->searchWay($this->queryBuilder,$order_client_code_wms,'order_trackings.order_client_code_wms');
+    }
+
+    public function logistic_id($logistic_id)
+    {
+        $this->queryBuilder->where('order_trackings.logistic_id',$logistic_id);
+    }
+
+    public function client($client)
+    {
+        $this->queryBuilder->where('order_trackings.client','like',$client.'%');
+    }
+
+    public function web_order_number($web_order_number)
+    {
+        $this->searchWay($this->queryBuilder,$web_order_number,'order_trackings.web_order_number');
+    }
+
+    public function sku($sku)
+    {
+        $this->searchWay($this->getCommodityQuery(),$sku,'sku');
+        $this->getOrderPackageCommodityQuery()->whereIn('commodity_id',$this->getCommodityQuery());
+    }
+
+    public function logistic_number($logistic_number)
+    {
+        $this->searchWay($this->getOrderPackageQuery(),$logistic_number,'logistic_number');
+    }
+
+    public function remark($remark)
+    {
+        $this->queryBuilder->where('order_trackings.remark','like','%'.$remark.'%');
+        if(isset($this->params['remark_addtime'])){
+            $this->queryBuilder->where('order_trackings.created_at','>',Carbon::now()->subDays($this->params['remark_addtime']));
+        }else{
+            $this->queryBuilder->where('order_trackings.created_at','>=',Carbon::now()->subDays(15));
+        }
+    }
+
+    public function id($id)
+    {
+        if(is_array($id))$this->queryBuilder->whereIn('order_trackings.id',$id);
+        else $this->queryBuilder->where('order_trackings.id',$id);
+    }
+
+    public function ownerId($ids)
+    {
+        $this->getOrderQuery()->whereIn('orders.owner_id',$ids);
+    }
+
+
+    public function start_at($start_at)
+    {
+        $this->queryBuilder->where('order_trackings.pick_up_at','>=',$start_at.' 00:00:00');
+    }
+
+    public function end_at($end_at)
+    {
+        $this->queryBuilder->where('order_trackings.pick_up_at','<=',$end_at.' 23:59:59');
+    }
+
+    public function start_planning_sent_at($start_planning_sent_at)
+    {
+        $this->queryBuilder->where('order_trackings.planning_sent_at','>=',$start_planning_sent_at.' 00:00:00');
+    }
+
+    public function end_planning_sent_at($end_planning_sent_at)
+    {
+        $this->queryBuilder->where('order_trackings.planning_sent_at','<=',$end_planning_sent_at.' 23:59:59');
+    }
+
+    public function beforeApply()
+    {
+        if($this->orderQuery)
+            $this->getOrderPackageQuery()->whereIn('order_packages.order_id',$this->orderQuery);
+
+        if($this->orderPackageQuery)
+            $this->getOrderPackageCommodityQuery()->whereIn('order_package_commodities.order_package_id',$this->getOrderPackageQuery());
+
+        if($this->commodityQuery)
+            $this->getOrderPackageCommodityQuery()->whereIn('order_package_commodities.commodity_id',$this->commodityQuery);
+
+        if($this->orderPackageCommodityQuery)
+            $this->queryBuilder->whereIn('order_trackings.order_package_commodity_id',$this->getOrderPackageCommodityQuery());
+
+    }
+}

+ 95 - 0
app/Http/Controllers/CacheShelfController.php

@@ -0,0 +1,95 @@
+<?php
+
+namespace App\Http\Controllers;
+
+use App\Components\AsyncResponse;
+use App\Exceptions\ErrorException;
+use App\MaterialBox;
+use App\Services\CacheShelfService;
+use App\Services\StationCacheShelfGridService;
+use App\Station;
+use App\StationCacheShelfGrid;
+use Illuminate\Contracts\Foundation\Application;
+use Illuminate\Contracts\View\Factory;
+use Illuminate\Database\Eloquent\Builder;
+use Illuminate\Http\Request;
+use Illuminate\View\View;
+
+class CacheShelfController extends Controller
+{
+    use AsyncResponse;
+
+    /**
+     * 缓存货架
+     * @return Application|Factory|View
+     */
+    public function index()
+    {
+        $stations = Station::query()->with('stationType:name', 'parent:name')->whereHas('stationType', function ($query) {
+            /** @var Builder $query */
+            $query->where('name', '缓存架');
+        })->paginate(100);
+
+        return view('station.cachingShelf.list.index', compact('stations'));
+    }
+
+    /**
+     * 获取缓存货架上的任务列表
+     * @param Request $request
+     * @param string $id
+     * @param CacheShelfService $service
+     */
+    public function getTasksApi(Request $request,string $id,CacheShelfService $service)
+    {
+        /** @var Station $station */
+        $station = Station::query()->where('id',$id)->first();
+
+        $service->getTasks($station);
+        $this->success($station['grids']);
+    }
+
+    /**
+     * 缓存架亮灯
+     * @param Request $request
+     * @param StationCacheShelfGridService $gridService
+     */
+    public function lightOnApi(Request $request,StationCacheShelfGridService $gridService)
+    {
+        $grid_id = $request['index'];
+        /** @var Station $station */
+        $station = Station::query()->where('id',$request['id'])->first();
+
+        $grid = StationCacheShelfGrid::query()->firstOrCreate(['station_id'=>$station['id'],'grid_id'=>$grid_id]);
+        $materialBox = MaterialBox::query()->firstOrCreate(['code'=>$request['code']]);
+
+        $grid->update(['material_box_id' => $materialBox['id'],'status' => 1]);
+
+        return $gridService->lightOn($station,$request['x'],$request['y']);
+    }
+
+//    /**
+//     * 拍灯推送任务
+//     * @param Request $request
+//     * @param CacheShelfService $service
+//     */
+//    public function pushTaskApi(Request $request,CacheShelfService $service)
+//    {
+//        /**
+//         * @var Station $station
+//         * @var MaterialBox $materialBox
+//         * @var StationCacheShelfGrid $grid
+//         */
+//        $station = Station::query()->where('id',$request['id'])->first();
+//        $materialBox = MaterialBox::query()->where('code',$request['code'])->first();
+//        $grid = StationCacheShelfGrid::query()->where(['station_id'=>$station['id'],'grid_id'=>$request['index']])->first();
+//
+//        try {
+//            $bool = $service->putBinToStore($station, $materialBox, $grid);
+//            if($bool)$this->success();
+//            else $this->error('推送任务异常');
+//        } catch (ErrorException $e) {
+//            $this->error($e->getMessage());
+//        }
+//    }
+
+}

+ 13 - 1
app/Http/Controllers/OrderIssueController.php

@@ -338,11 +338,16 @@ class OrderIssueController extends Controller
         }
     }
 
-    public function updateColumnApi(Request $request)
+    public function updateColumnApi(Request $request,OrderIssueService $service): array
     {
         if (!Gate::allows('订单管理-问题件-编辑')) {
             return ['success'=>false,'fail_info' => '没有对应权限'];
         }
+        if($request['column']=='final_status'  && $request['value'] == '已归档'){
+            $orderIssues = OrderIssue::query()->where('id',$request['id'])->get();
+            $service->archive($orderIssues);
+            return ['success' =>true];
+        }
         try {
             OrderIssue::query()
                 ->where('id', $request->input('id'))
@@ -737,6 +742,13 @@ class OrderIssueController extends Controller
         }
     }
 
+
+    /**
+     * 批量归档
+     * @param Request $request
+     * @param OrderIssueService $service
+     * @return array|bool[]
+     */
     public function archiveOrderIssueApi(Request $request,OrderIssueService $service): array
     {
         if (!Gate::allows('订单管理-问题件-编辑')) return ['success'=>false,'error'=>'没有对应权限'];

+ 85 - 0
app/Http/Controllers/OrderPackageCommoditySerialNumberController.php

@@ -0,0 +1,85 @@
+<?php
+
+namespace App\Http\Controllers;
+
+use App\OrderPackageCommoditySerialNumber;
+use Illuminate\Http\Request;
+
+class OrderPackageCommoditySerialNumberController extends Controller
+{
+    /**
+     * Display a listing of the resource.
+     *
+     * @return \Illuminate\Http\Response
+     */
+    public function index()
+    {
+        //
+    }
+
+    /**
+     * Show the form for creating a new resource.
+     *
+     * @return \Illuminate\Http\Response
+     */
+    public function create()
+    {
+        //
+    }
+
+    /**
+     * Store a newly created resource in storage.
+     *
+     * @param  \Illuminate\Http\Request  $request
+     * @return \Illuminate\Http\Response
+     */
+    public function store(Request $request)
+    {
+        //
+    }
+
+    /**
+     * Display the specified resource.
+     *
+     * @param  \App\OrderPackageCommoditySerialNumber  $orderPackageCommoditySerialNumber
+     * @return \Illuminate\Http\Response
+     */
+    public function show(OrderPackageCommoditySerialNumber $orderPackageCommoditySerialNumber)
+    {
+        //
+    }
+
+    /**
+     * Show the form for editing the specified resource.
+     *
+     * @param  \App\OrderPackageCommoditySerialNumber  $orderPackageCommoditySerialNumber
+     * @return \Illuminate\Http\Response
+     */
+    public function edit(OrderPackageCommoditySerialNumber $orderPackageCommoditySerialNumber)
+    {
+        //
+    }
+
+    /**
+     * Update the specified resource in storage.
+     *
+     * @param  \Illuminate\Http\Request  $request
+     * @param  \App\OrderPackageCommoditySerialNumber  $orderPackageCommoditySerialNumber
+     * @return \Illuminate\Http\Response
+     */
+    public function update(Request $request, OrderPackageCommoditySerialNumber $orderPackageCommoditySerialNumber)
+    {
+        //
+    }
+
+    /**
+     * Remove the specified resource from storage.
+     *
+     * @param  \App\OrderPackageCommoditySerialNumber  $orderPackageCommoditySerialNumber
+     * @return \Illuminate\Http\Response
+     */
+    public function destroy(OrderPackageCommoditySerialNumber $orderPackageCommoditySerialNumber)
+    {
+        //
+    }
+}

+ 25 - 24
app/Http/Controllers/OrderTrackingController.php

@@ -2,8 +2,8 @@
 
 namespace App\Http\Controllers;
 
+use App\Filters\OrderTrackFilters;
 use App\Logistic;
-use App\Order;
 use App\OrderTracking;
 use App\Services\common\ExportService;
 use App\Services\OrderTrackingService;
@@ -18,25 +18,25 @@ use Ramsey\Uuid\Uuid;
 class OrderTrackingController extends Controller
 {
 
-    public function index(Request $request)
+    public function index(Request $request,OrderTrackFilters $filters)
     {
-        /**
-         * @var OrderTrackingService $service
-         */
-        if (!Gate::allows('订单管理-跟踪'))
-            return redirect(url('/'));
-        $owners =  app(OwnerService::class)->getAuthorizedOwners();
-        $service = app('OrderTrackingService');
+        if (!Gate::allows('订单管理-跟踪')) return redirect(url('/'));
+
+        /** @var OrderTrackingService $service */
+        $service = app(OrderTrackingService::class);
         $service->fillInOrderTracking();
-        $trackOrders = $service->paginate($request->input());
+
+        $trackOrders = OrderTracking::query()->filter($filters)->defaultQuery()->defaultWith()->paginate($request['paginate'] ?? 50);
+
         $trackOrders->map(function($trackOrder){
             if($trackOrder->uploadFile){
-                if(!str_starts_with($trackOrder->uploadFile->url,'http')){
+                if(!str_starts_with($trackOrder->uploadFile->url,'http'))
                     $trackOrder->uploadFile->url = asset('/storage'.$trackOrder->uploadFile->url);
-                }
             }
         });
+
         $logistics = Logistic::all();
+        $owners =  app(OwnerService::class)->getAuthorizedOwners();
         return view('order.tracking.index', compact('owners', 'trackOrders','logistics'));
     }
 
@@ -140,20 +140,14 @@ class OrderTrackingController extends Controller
         return ['success'=>false,'error'=>'图片上传失败'];
     }
 
-    public function export(Request $request)
+    public function export(Request $request,OrderTrackFilters  $filters)
     {
-        if (!Gate::allows('订单管理-跟踪'))
-            return redirect(url('/'));
-        /** @var OrderTrackingService $service */
-        $service = app(OrderTrackingService::class);
-        $orderTrackings = $service->getOrderTrackings($request->input());
-//        $column = ['owners_name','order_client_code','web_order_number','pick_up_at','sale','client','sku','sku_name','amount','order_remark',
-//            'gross_weight','bulk','pallet_total','logistic_name','logistic_number','city','planning_sent_at','is_on_duty_shift','is_arrival','signed_at','receive_bill_status','remark'];
-        $rule = ["pick_up_at"=> "mysqlDate",'planning_sent_at'=>"mysqlDate"];
+        if (!Gate::allows('订单管理-跟踪')) return redirect(url('/'));
+
+        $orderTrackings = OrderTracking::query()->filter($filters)->defaultQuery()->defaultWith()->get();
 
         $row = ['公司','订单号','WEB+订单号','提货日期','销售','客户','SKU','物料描述','数量','订单备注','重量','体积',
-            '托盘合计','运输方式','运输单号','到达城市','应送达时间','是否赶上卡班','到货情况','签收日期','签收单情况','备注'];
-        $mergeColumn = ['A','B','C','D','E','F','J','N','P','P','Q','R','S','T','U','V','M'];
+            '托盘合计','运输方式','运输单号','到达城市','序列号','应送达时间','是否赶上卡班','到货情况','签收日期','签收单情况','备注'];
         $mergeRow = [];
         $list=[];
         $start_row = 0;
@@ -183,6 +177,12 @@ class OrderTrackingController extends Controller
                 $row_count=1;
                 $order_client_code_temp = $orderTracking->order_client_code;
             }
+            $serialNumberStr = '';
+            if($orderTracking['commodities']['serialNumbers']){
+                foreach ($orderTracking['commodities']['serialNumbers'] as $serialNumber){
+                    $serialNumberStr.=$serialNumber['serial_number'].",\r\n";;
+                }
+            }
 
             $order_client_code = $orderTracking->web_order_number?'':$orderTracking->order_client_code;
             $web_order_number = $orderTracking->web_order_number??'';
@@ -213,6 +213,7 @@ class OrderTrackingController extends Controller
                 $logistic,
                 $logistic_number,
                 $orderTracking->commodities->package->order->city ?? '',
+                rtrim($serialNumberStr,",\r\n"),              //原始运单号
                 $planning_sent_at=='0000-00-00'?'':$planning_sent_at,
                 $orderTracking->is_on_duty_shift,
                 $orderTracking->is_arrival,
@@ -221,7 +222,7 @@ class OrderTrackingController extends Controller
                 $orderTracking->remark,
             ];
         }
-        return app(ExportService::class)->json($row,$list,'订单跟踪件','merge',$mergeColumn,$mergeRow);
+        return app(ExportService::class)->json($row,$list,'订单跟踪件');
     }
 
     public function destroyImg(Request $request)

+ 7 - 7
app/Http/Controllers/RejectedBillController.php

@@ -310,7 +310,7 @@ class RejectedBillController extends Controller
          */
         $service = app('RejectedBillService');
         $rejectedBillItemService = app('RejectedBillItemService');
-        $params = [['logistic_number_return','logistic_number','fee_collected']];
+        $params = [['logistic_number_return','order_number','fee_collected']];
         $logistic_numbers = [];             // 原单单号
         $logistic_number_map = [];
         $logistic_number_return = [];       // 退回单号
@@ -341,8 +341,8 @@ class RejectedBillController extends Controller
         $rejectedBill_collect = collect();
 
         // 对已用的进行修改
-        $update_fee_params = [['id','logistic_number','fee_collected']];                //对原有的进行修改 有到付费用
-        $update_params = [['id','logistic_number']];                                    // 无到付费用
+        $update_fee_params = [['id','order_number','fee_collected']];                //对原有的进行修改 有到付费用
+        $update_params = [['id','order_number']];                                    // 无到付费用
         $exist_picktotraceid = [];                                                      // 已有退回单号
         $rejectedBills = RejectedBill::query()->whereIn('logistic_number_return',$logistic_number_return)->get();
         $rejectedBills->each(function($rejectedBill)use(&$update_fee_params,&$update_params,$logistic_number_return_map,&$exist_picktotraceid,&$rejectedBill_collect){
@@ -351,9 +351,9 @@ class RejectedBillController extends Controller
             $params = $logistic_number_return_map[$logistic_number_return];
             $rejectedBill->logistic_number ? $exist_picktotraceid[$rejectedBill->logistic_number] = $rejectedBill->logistic_number : null;    // 记录已有原单单号
             if(isset($params[2])){
-                $update_fee_params[] = ['id'=>$rejectedBill->id,'logistic_number'=>$params[1],'fee_collected'=>$params[2]];
+                $update_fee_params[] = ['id'=>$rejectedBill->id,'order_number'=>$params[1],'fee_collected'=>$params[2]];
             }else if(isset($params[1])){
-                $update_params[] = ['id'=>$rejectedBill->id,'logistic_number'=>$params[1]];
+                $update_params[] = ['id'=>$rejectedBill->id,'order_number'=>$params[1]];
             }
         });
         // 对不存在的进行创建
@@ -380,9 +380,9 @@ class RejectedBillController extends Controller
                     'logistic_number_return' => $logistic_number_map[$key][0] ?? '',
                     'id_logistic_return' =>$logistic->id ?? '',
                     'fee_collected' => $logistic_number_map[$key][2] ?? '',
-                    'order_number' =>$order_header->soreference1,
+//                    'order_number' =>$order_header->soreference1,
                     'mobile_sender' => empty($order_header->c_tel2)?$order_header->c_tel1:$order_header->c_tel2,
-                    'logistic_number' => $logistic_number_map[$key][1] ?? '',
+                    'order_number' => $logistic_number_map[$key][1] ?? '',
                     'remark' => $order_header->notes,
                     'is_loaded' =>0,
                     "id_operator" => Auth::id(),

+ 85 - 0
app/Http/Controllers/StationCacheShelfGridController.php

@@ -0,0 +1,85 @@
+<?php
+
+namespace App\Http\Controllers;
+
+use App\StationCacheShelfGrid;
+use Illuminate\Http\Request;
+
+class StationCacheShelfGridController extends Controller
+{
+    /**
+     * Display a listing of the resource.
+     *
+     * @return \Illuminate\Http\Response
+     */
+    public function index()
+    {
+        //
+    }
+
+    /**
+     * Show the form for creating a new resource.
+     *
+     * @return \Illuminate\Http\Response
+     */
+    public function create()
+    {
+        //
+    }
+
+    /**
+     * Store a newly created resource in storage.
+     *
+     * @param  \Illuminate\Http\Request  $request
+     * @return \Illuminate\Http\Response
+     */
+    public function store(Request $request)
+    {
+        //
+    }
+
+    /**
+     * Display the specified resource.
+     *
+     * @param  \App\StationCacheShelfGrid  $stationCacheShelfGrid
+     * @return \Illuminate\Http\Response
+     */
+    public function show(StationCacheShelfGrid $stationCacheShelfGrid)
+    {
+        //
+    }
+
+    /**
+     * Show the form for editing the specified resource.
+     *
+     * @param  \App\StationCacheShelfGrid  $stationCacheShelfGrid
+     * @return \Illuminate\Http\Response
+     */
+    public function edit(StationCacheShelfGrid $stationCacheShelfGrid)
+    {
+        //
+    }
+
+    /**
+     * Update the specified resource in storage.
+     *
+     * @param  \Illuminate\Http\Request  $request
+     * @param  \App\StationCacheShelfGrid  $stationCacheShelfGrid
+     * @return \Illuminate\Http\Response
+     */
+    public function update(Request $request, StationCacheShelfGrid $stationCacheShelfGrid)
+    {
+        //
+    }
+
+    /**
+     * Remove the specified resource from storage.
+     *
+     * @param  \App\StationCacheShelfGrid  $stationCacheShelfGrid
+     * @return \Illuminate\Http\Response
+     */
+    public function destroy(StationCacheShelfGrid $stationCacheShelfGrid)
+    {
+        //
+    }
+}

+ 1 - 0
app/Http/Controllers/StationController.php

@@ -107,4 +107,5 @@ class StationController extends Controller
     {
         //
     }
+
 }

+ 77 - 18
app/Http/Controllers/TestController.php

@@ -36,8 +36,8 @@ use App\Jobs\BatchTaskJob;
 use App\Jobs\DeleteRepetitionSkuItem;
 use App\Jobs\OrderCreateInstantBill;
 use App\Jobs\OrderFreeze;
-use App\Jobs\ResetInstantBill;
 use App\Jobs\StoreCreateInstantBill;
+use App\Jobs\ResetInstantBill;
 use App\LaborReport;
 use App\LaborReportStatus;
 use App\Log;
@@ -50,6 +50,11 @@ use App\OracleBasSKU;
 use App\OracleDOCASNHeader;
 use App\OracleDOCOrderDetail;
 use App\OracleDOCOrderHeader;
+use App\OracleDocOrderPackingSummary;
+use App\OracleDocOrderSerialNo;
+use App\OracleDOCWaveDetails;
+use App\OracleDOCWaveHeader;
+use App\OracleInvLotLocId;
 use App\Order;
 use App\OrderBin;
 use App\OrderCommodity;
@@ -109,15 +114,18 @@ use App\Services\ShopService;
 use App\Services\StoreService;
 use App\Services\SupplierService;
 use App\Services\WarehouseService;
+use App\Station;
 use App\StationRuleBatch;
 use App\StationTask;
 use App\StationTaskMaterialBox;
+use App\StationType;
 use App\Store;
 use App\StationTaskBatch;
 use App\StoreCheckingReceiveItem;
 use App\StoreItem;
 use App\StoreItems;
 use App\Supplier;
+use App\Traits\ModelLogChanging;
 use App\Unit;
 use App\User;
 use App\UserDetail;
@@ -132,7 +140,6 @@ use ChangeColumnOrderIdToOrderIssues;
 use Doctrine\DBAL\Exception\DatabaseObjectExistsException;
 use Doctrine\DBAL\Query\QueryBuilder;
 use Illuminate\Database\Connection;
-use Illuminate\Database\Eloquent\Builder;
 use Illuminate\Database\Eloquent\Model;
 use Illuminate\Support\Facades\Auth;
 use Illuminate\Support\Facades\Cache;
@@ -397,16 +404,18 @@ class TestController extends Controller
     public function tj2(Request $request)
     {
 
-        /** @var OrderCommodityService $orderCommodityService */
-        $orderCommodityService=app('OrderCommodityService');
-        $orderCommodities=$orderCommodityService->get(
-            ['location'=>
-                ['IDE0005676','IDE0005714']
-            ]);
-        $orderCommodityService->correctLocation_fromWMS($orderCommodities);
-
-
-
+        $batches=Batch::query()->whereIn('id',[1220])->get();
+        $a=StationTaskMaterialBox::query()
+            ->where('status','<>','完成')
+            ->whereHas('materialBox',function ($query)use($batches){
+                $locations=OrderCommodity::query()
+                    ->whereHas('order',function ($queryO)use($batches){
+                        $queryO->whereIn('batch_id',data_get($batches,'*.id')??[]);
+                    })->get('location');
+                $query->whereIn('code',data_get($locations,'*.location')??[]);
+            })
+            ->get('id');
+        dd(data_get($a,'*.id')??[]);
     }
 
     public function mergerPackageData()
@@ -1079,7 +1088,7 @@ where purch.islower=1');
                 'receipt_amount'=>$param->receipt_amount,
             ];
         }
-       ProcurementDeliverie::query()->insert($insert);
+        ProcurementDeliverie::query()->insert($insert);
     }
 
     public function addProcurement()
@@ -1371,13 +1380,63 @@ where purch.islower=1 and deliver.id>'.$id);
         }
     }
 
-    public function testOrderTracking()
+    public function firstAndCreated()
     {
-        $orderNos =  ['SO210416004929'];
-        $service = app(OrderTrackingService::class);
-        $orderHeadService= app(OracleDOCOrderHeaderService::class);
-        $orderHeaders = $orderHeadService->getQuery()->whereIn('DOC_Order_Header.OrderNo',$orderNos)->get();
+        $arr = ['name' => '缓存货架3'];
+        $created = ['name'=>'缓存货架3','station_type_id' => (function(){
+            $type =StationType::query()->where('name','缓存架')->first();
+            return $type['id'];
+        })()];
+        $station = Station::query()->firstOrCreate($arr,$created);
+        dd($station);
+    }
+
+
+    public function syncOrderTracking()
+    {
+        $orderNos = ['OSSO21040013','OSSO21040012'];
+        $service = new OrderTrackingService();
+        $orderHeaderService = new OracleDOCOrderHeaderService();
+        $orderHeaders = $orderHeaderService->getQuery()->whereIn('DOC_Order_Header.SoReference1',$orderNos)->get();
+        $service->createByWmsOrderHeader($orderHeaders);
         $service->updateByWmsOrderHeaders($orderHeaders);
+    }
 
+    public function testArr()
+    {
+        $serialNos = OracleDocOrderSerialNo::query()->selectRaw('OrderNo')->get();
+        $orderNos  = data_get($serialNos,'*.orderno');
+        $service = app(OrderTrackingService::class);
+        $orderHeadService = app(OracleDOCOrderHeaderService::class);
+        $orderHeaders = $orderHeadService->getOrderTracking()->whereIn('DOC_Order_Header.OrderNo',$orderNos)->get();
+        app(OrderService::class)->syncOrderByWMSOrderHeaders($orderHeaders);
+        app("OrderCommodityService")->syncOrderCommodity($orderHeaders);
+        app('OrderPackageService')->syncOrderPackage($orderHeaders);
+        app("OrderPackageCommoditiesService")->syncOrderPackageCommodities($orderHeaders);
+        app('OrderPackageCommoditySerialNumberService')->syncOrderPackageCommoditySerialNumbers($orderHeaders);
+        $service->createByWmsOrderHeader($orderHeaders);
+    }
+    public function testArray1()
+    {
+        $arr = (array)'update_at';
+        dd($arr);
+    }
+    public function addHaiBox(Request $request)
+    {
+        echo <<<TEXT
+    <form>
+    <textarea name="boxes">
+
+</textarea>
+<input type="submit"/>
+</form>
+TEXT;
+        $input = $request->input('boxes');
+        if($input){
+            $input=preg_replace('/[\n\r]/',',',trim($input));
+            $boxes=explode(',',$input);
+            dd($boxes);
+        }
     }
 }
+

+ 1 - 0
app/Http/Controllers/UserDutyCheckController.php

@@ -346,6 +346,7 @@ class UserDutyCheckController extends Controller
         $userWorkgroupID = $request->input('userWorkgroupID');
         $mobile_phone = $request->input('mobile_phone');
         $userDetail = UserDetail::query()->where('mobile_phone', $mobile_phone)->first();
+        if (!$userDetail) return "<h1 style='color: red;text-align:center'>系统未识别当前手机号,请验证输入是否正确!</h1>";
         $userLaborToken=$userDetail->getDutyCheckToken(config('users.token_check_in_expire_minutes'));
         $dateNow = Carbon::now()->format('Y-m-d');
         $userDutyCheck = UserDutyCheck::where('user_id', $userDetail->user_id)->where('checked_at', 'like', $dateNow. '%')->orderBy('id', 'desc')->first();

+ 10 - 2
app/Http/Controllers/api/thirdPart/haiq/LightController.php

@@ -6,6 +6,7 @@ namespace App\Http\Controllers\api\thirdPart\haiq;
 
 
 
+use App\Services\CacheShelfService;
 use App\Services\ForeignHaiRoboticsService;
 use Illuminate\Http\Request;
 
@@ -19,7 +20,7 @@ class LightController
         $this->service=app('ForeignHaiRoboticsService');
     }
 
-    public function lightOn(Request $request){
+    public function lightOn($post){
     }
     public function lightOff(Request $request){
     }
@@ -28,7 +29,14 @@ class LightController
      * @param Request $request {"areaCode":"1004","locCode":"HAIB2-02-03","displayInfo":null,"PTLAction":0,"PTLSettings":null}
      * @return array
      */
-    public function update(Request $request){
+    public function update(Request $request){// 拍灯以后的消息发至此处
+
+//        if($request['PTLAction'] == 0){  // 拍灯 推送任务
+//            /** @var CacheShelfService $cacheShelfService */
+//            $cacheShelfService = app(CacheShelfService::class);
+//            $result =   $cacheShelfService->lightOffTask($request['locCode'],$request['PTLAction']);
+//            return ['location'=>$result['success'] ? 200 :0,'errMsg'=>$result['errMsg'] ?? null,'data'=>$request->all()];
+//        }
         $success = $request->input('success');
         $location = $success?200:0;
         app('LogService')->log(__METHOD__,__FUNCTION__,json_encode($request->all()));

+ 12 - 8
app/Http/Controllers/api/thirdPart/haiq/StorageController.php

@@ -41,10 +41,11 @@ class StorageController
         ]];*///IDE0005714,IDE0004970,IDE0005676,IDE0005665
         $this->post = [[
             "taskMode"      => 1,
-            "bins"=>[[
-                "taskCode"  =>'t2'.microtime(true),
-                "binCode"   => "IDE0005714",
-                "fromLocCode" => "BIN-IN1",
+            "bins"=>[
+                [
+                    "binCode"   => "IDE0000159",
+                    "taskCode"  =>'t1'.microtime(true),
+                    "fromLocCode" => "BIN-IN1",
                 "toLocCode" => "",
             ],
                 ],
@@ -54,12 +55,15 @@ class StorageController
         ]];
         $this->post2 = [[
             "taskMode"      => 2,
-            "bins"=>[[
-                "taskCode"  =>'t'.microtime(true),
-                "binCode"   => "IDE0005714",
+            "bins"=>[
+
+                [
+                "taskCode"  =>'t1'.microtime(true),
+                "binCode"   => "IDE0004952",
                 "fromLocCode" => "",
                 "toLocCode" => "BIN-OUT1",
-            ],],
+            ],
+                ],
             "groupCode"     => 'g'.microtime(true),
             "priority"      => 20,
             "sequenceFlag"  => 1,

+ 12 - 1
app/OracleActAllocationDetails.php

@@ -2,13 +2,17 @@
 
 namespace App;
 
+use App\Traits\ModelTimeFormat;
 use Illuminate\Database\Eloquent\Model;
 
 use App\Traits\ModelLogChanging;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
+use Illuminate\Database\Eloquent\Relations\HasMany;
 
 class OracleActAllocationDetails extends Model
 {
     use ModelLogChanging;
+    use ModelTimeFormat;
 
     protected $connection="oracle";
     protected $primaryKey="ALLOCATIONDETAILSID";
@@ -16,7 +20,14 @@ class OracleActAllocationDetails extends Model
     public $timestamps=false;
     public function getIncrementing(){ return false;}
 
-    public function oracleDocOrderHeader(){
+    public function oracleDocOrderHeader(): BelongsTo
+    {
         return $this->belongsTo('App\OracleDOCOrderHeader','orderno','orderno');
     }
+
+    public function oracleDocOrderSerialNos(): HasMany
+    {
+        return $this->hasMany(OracleDocOrderSerialNo::class,'allocationdetailsid','allocationdetailsid');
+    }
+
 }

+ 28 - 0
app/OracleDocOrderSerialNo.php

@@ -0,0 +1,28 @@
+<?php
+
+namespace App;
+
+use App\Traits\ModelTimeFormat;
+use Illuminate\Database\Eloquent\Model;
+use Illuminate\Database\Eloquent\Relations;
+use App\Traits\ModelLogChanging;
+
+class OracleDocOrderSerialNo extends Model
+{
+    use ModelLogChanging;
+    use ModelLogChanging;
+    use ModelTimeFormat;
+
+    protected $connection="oracle";
+    protected $table="Doc_Order_SerialNo";
+    protected $primaryKey="SerialNo";
+    public $timestamps=false;
+
+    public function getIncrementing(){ return false;}
+
+    public function actAllocationDetail(): Relations\HasOne
+    {
+        return $this->hasOne(OracleActAllocationDetails::class,'allocationdetailsid','allocationdetailsid');
+    }
+    //
+}

+ 13 - 3
app/OrderPackageCommodities.php

@@ -6,6 +6,9 @@ use App\Traits\ModelTimeFormat;
 use Illuminate\Database\Eloquent\Model;
 
 use App\Traits\ModelLogChanging;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
+use Illuminate\Database\Eloquent\Relations\BelongsToMany;
+use Illuminate\Database\Eloquent\Relations\HasMany;
 
 class OrderPackageCommodities extends Model
 {
@@ -22,16 +25,23 @@ class OrderPackageCommodities extends Model
     ];
 
 
-    public function package(){
+    public function package(): BelongsTo
+    {
         return $this->belongsTo(OrderPackage::class,'order_package_id','id');
     }
 
-    public function commodity(){
+    public function commodity(): BelongsTo
+    {
         return $this->belongsTo(Commodity::class,'commodity_id','id');
     }
 
-    public function orderTracking(){
+    public function orderTracking(): BelongsTo
+    {
         return $this->belongsTo(OrderTracking::class,'id','order_package_commodity_id');
     }
 
+    public function serialNumbers(): HasMany
+    {
+        return $this->hasMany(OrderPackageCommoditySerialNumber::class,'order_package_commodity_id','id');
+    }
 }

+ 23 - 0
app/OrderPackageCommoditySerialNumber.php

@@ -0,0 +1,23 @@
+<?php
+
+namespace App;
+
+use App\Traits\ModelTimeFormat;
+use App\Traits\ModelLogChanging;
+
+use Illuminate\Database\Eloquent\Model;
+use Illuminate\Database\Eloquent\Relations\HasOne;
+
+class OrderPackageCommoditySerialNumber extends Model
+{
+    use ModelLogChanging;
+    use ModelTimeFormat;
+
+    protected $fillable = ['order_package_commodity_id','serial_number'];
+    //
+
+    public function orderPackageCommodity(): HasOne
+    {
+        return $this->hasOne(OrderPackageCommodities::class);
+    }
+}

+ 21 - 3
app/OrderTracking.php

@@ -4,15 +4,14 @@ namespace App;
 
 use App\Traits\ModelTimeFormat;
 use Illuminate\Database\Eloquent\Model;
-
+use Illuminate\Database\Eloquent\Builder;
 use App\Traits\ModelLogChanging;
 
 class OrderTracking extends Model
 {
     use ModelLogChanging;
-
-
     use ModelTimeFormat;
+
     protected $fillable = [
         'order_package_commodity_id','owner_id','logistic_id',
         'web_order_number',
@@ -85,4 +84,23 @@ class OrderTracking extends Model
     public function getOrderClientNumberAttribute(){
         return $this->commodities->package->order['client_code'] ?? '';
     }
+
+    public function scopeFilter($query, $filters)
+    {
+        return $filters->apply($query);
+    }
+
+    public function scopeDefaultQuery($query): Builder
+    {
+        return $query->orderByDesc('order_trackings.pick_up_at')
+            ->orderBy('order_trackings.order_client_code');
+    }
+
+    public function scopeDefaultWith($query)
+    {
+        return $query->with(['commodities'=>function($query){
+            $query->with(['commodity.barcodes','serialNumbers','package.order']);
+        },'owner','logistic','uploadFile']);
+    }
+
 }

+ 6 - 1
app/Providers/AppServiceProvider.php

@@ -6,6 +6,7 @@ use App\Http\Controllers\Controller;
 use App\Services\AuthorityService;
 use App\Services\BatchService;
 use App\Services\CacheService;
+use App\Services\CacheShelfService;
 use App\Services\CommodityService;
 use App\Services\common\BatchUpdateService;
 use App\Services\CommodityBarcodeService;
@@ -18,7 +19,6 @@ use App\Services\DepositoryService;
 use App\Services\FacilitatorService;
 use App\Services\DemandService;
 use App\Services\DemandProcessService;
-use App\Services\DischargeTaskDossierService;
 use App\Services\FeatureService;
 use App\Services\ForeignHaiRoboticsService;
 use App\Services\InventoryAccountMissionService;
@@ -101,6 +101,7 @@ use App\Services\RegionService;
 use App\Services\UserWorkgroupService;
 use App\Services\DischargeTaskService;
 use App\Services\DeliveryAppointmentService;
+use App\Services\StationCacheShelfGridService;
 use Illuminate\Queue\Events\JobFailed;
 use Illuminate\Support\Facades\Queue;
 use Illuminate\Support\Facades\Schema;
@@ -109,6 +110,7 @@ use Illuminate\Support\ServiceProvider;
 use Ramsey\Uuid\Uuid;
 use Illuminate\Support\Facades\Validator;
 use App\Services\SupplierService;
+use App\Services\OrderPackageCommoditySerialNumberService;
 
 class AppServiceProvider extends ServiceProvider
 {
@@ -157,6 +159,7 @@ class AppServiceProvider extends ServiceProvider
         app()->singleton('BatchService',BatchService::class);
         app()->singleton('BatchUpdateService', BatchUpdateService::class);
         app()->singleton('CacheService',CacheService::class);
+        app()->singleton('CacheShelfService',CacheShelfService::class);
         app()->singleton('CheckActiveMenuService',CheckActiveMenuService::class);
         app()->singleton('CommodityBarcodeService',CommodityBarcodeService::class);
         app()->singleton('CommodityService', CommodityService::class);
@@ -197,6 +200,7 @@ class AppServiceProvider extends ServiceProvider
         app()->singleton('OrderIssueService',OrderIssueService::class);
         app()->singleton('OrderIssueWorkLoadService',OrderIssueWorkLoadService::class);
         app()->singleton('OrderPackageCommoditiesService',OrderPackageCommoditiesService::class);
+        app()->singleton('OrderPackageCommoditySerialNumberService',OrderPackageCommoditySerialNumberService::class);
         app()->singleton('OrderPackageReceivedSyncService',OrderPackageReceivedSyncService::class);
         app()->singleton('OrderPackageService',OrderPackageService::class);
         app()->singleton('OrderService',OrderService::class);
@@ -230,6 +234,7 @@ class AppServiceProvider extends ServiceProvider
         app()->singleton('RejectedBillService',RejectedBillService::class);
         app()->singleton('RejectedService',RejectedService::class);
         app()->singleton('ShopService',ShopService::class);
+        app()->singleton('StationCacheShelfGridService',StationCacheShelfGridService::class);
         app()->singleton('StationRuleBatchService',StationRuleBatchService::class);
         app()->singleton('StationService',StationService::class);
         app()->singleton('StationTaskBatchService',StationTaskBatchService::class);

+ 42 - 0
app/Providers/HorizonServiceProvider.php

@@ -0,0 +1,42 @@
+<?php
+
+namespace App\Providers;
+
+use Illuminate\Support\Facades\Gate;
+use Laravel\Horizon\Horizon;
+use Laravel\Horizon\HorizonApplicationServiceProvider;
+
+class HorizonServiceProvider extends HorizonApplicationServiceProvider
+{
+    /**
+     * Bootstrap any application services.
+     *
+     * @return void
+     */
+    public function boot()
+    {
+        parent::boot();
+
+        // Horizon::routeSmsNotificationsTo('15556667777');
+        // Horizon::routeMailNotificationsTo('example@example.com');
+        // Horizon::routeSlackNotificationsTo('slack-webhook-url', '#channel');
+
+        // Horizon::night();
+    }
+
+    /**
+     * Register the Horizon gate.
+     *
+     * This gate determines who can access Horizon in non-local environments.
+     *
+     * @return void
+     */
+    protected function gate()
+    {
+        Gate::define('viewHorizon', function ($user) {
+            return in_array($user->name,
+            config('users.superAdmin')??[]
+            );
+        });
+    }
+}

+ 25 - 10
app/Services/BatchService.php

@@ -4,12 +4,15 @@ namespace App\Services;
 
 use App\Batch;
 use App\Exceptions\ErrorException;
+use App\Jobs\BatchTaskJob;
 use App\OracleActAllocationDetails;
 use App\Order;
 use App\OrderCommodity;
 use App\Owner;
 use Exception;
 use Illuminate\Support\Collection;
+use Illuminate\Support\Facades\Cache;
+use Illuminate\Support\Facades\DB;
 use Illuminate\Support\Facades\Http;
 use App\Traits\ServiceAppAop;
 
@@ -70,23 +73,35 @@ class BatchService
             $this->instant($this->stationTaskCommodityService,'StationTaskCommodityService');
             $this->instant($this->stationTaskMaterialBoxService,'StationTaskMaterialBoxService');
 
+            $stationTaskBatches=null;
             $batches_shouldProcess = $this->stationRuleBatchService->getBatches_shouldProcess($batches); //按规则过滤需要的波次
             if($batches_shouldProcess->isEmpty()) return;
             LogService::log(__METHOD__,'assignTasks','波次任务分配2:'.json_encode($batches));
-
-            $stationTasks =  $this->stationTaskService->create($batches_shouldProcess->count()); //生成总任务
-            LogService::log(__METHOD__,'assignTasks','波次任务分配3:'.json_encode($batches));
-            $stationTaskBatches=$this->stationTaskBatchService->createByBatches($batches_shouldProcess,$stationTasks); //注册波次任务
-            LogService::log(__METHOD__,'assignTasks','波次任务分配4:'.json_encode($batches));
-            $stationTaskMaterialBoxes=$this->stationTaskMaterialBoxService->createByBatches($batches_shouldProcess,$stationTasks); //注册料箱任务
-            LogService::log(__METHOD__,'assignTasks','波次任务分配5:'.json_encode($stationTaskMaterialBoxes).json_encode($batches));
-            $stationTaskCommodities=$this->stationTaskCommodityService->createByBatches($batches_shouldProcess,$stationTasks); //注册商品任务
-            LogService::log(__METHOD__,'assignTasks','波次任务分配6:'.json_encode($batches));
+            $stationTaskMaterialBoxes_occupied = $this->stationTaskMaterialBoxService->getOccupied_byBatches($batches_shouldProcess); //按规则过滤需要的波次
+            LogService::log(__METHOD__,'assignTasks','波次任务分配2b:'.json_encode($batches));
+            if($stationTaskMaterialBoxes_occupied->isNotEmpty()) {
+                BatchTaskJob::dispatch($batches_shouldProcess)
+                    ->delay(now()->addMinutes(1));    //因为料箱被占用了,所以将任务推迟1分钟后尝试
+                return;
+            }
+            DB::transaction(function ()use($batches,&$stationTaskBatches,&$batches_shouldProcess){
+                $stationTasks =  $this->stationTaskService->create($batches_shouldProcess->count()); //生成总任务
+                LogService::log(__METHOD__,'assignTasks','波次任务分配3:'.json_encode($batches));
+                $stationTaskBatches=$this->stationTaskBatchService->createByBatches($batches_shouldProcess,$stationTasks); //注册波次任务
+                LogService::log(__METHOD__,'assignTasks','波次任务分配4:'.json_encode($batches));
+                $stationTaskMaterialBoxes=$this->stationTaskMaterialBoxService->createByBatches($batches_shouldProcess,$stationTasks); //注册料箱任务
+                LogService::log(__METHOD__,'assignTasks','波次任务分配5:'.json_encode($stationTaskMaterialBoxes).json_encode($batches));
+                $stationTaskCommodities=$this->stationTaskCommodityService->createByBatches($batches_shouldProcess,$stationTasks); //注册商品任务
+                LogService::log(__METHOD__,'assignTasks','波次任务分配6:'.json_encode($batches));
+            });
 
             $ran=$this->stationTaskBatchService->runMany($stationTaskBatches);//执行波次任务
             LogService::log(__METHOD__,'assignTasks','波次任务分配7:'.json_encode($batches));
         }catch(Exception $e){
-            throw new ErrorException('注册任务失败: '.json_encode($batches). $e->getMessage());
+            foreach ($batches as $batch){
+                Cache::tags(['波次防重叠'.$batch['id']])->flush();
+            }
+            throw new ErrorException('注册任务失败: '.json_encode($batches). $e->getMessage().$e->getTrace());
         }
     }
 

+ 108 - 0
app/Services/CacheShelfService.php

@@ -0,0 +1,108 @@
+<?php
+
+namespace App\Services;
+
+use App\Exceptions\ErrorException;
+use App\MaterialBox;
+use App\Station;
+use App\StationCacheShelfGrid;
+use App\StationTaskMaterialBox;
+use App\Traits\ServiceAppAop;
+use Illuminate\Support\Facades\Http;
+
+class CacheShelfService
+{
+    use ServiceAppAop;
+
+    protected $modelClass = Station::class;
+    /** @var StationTaskMaterialBoxService $stationTaskMaterialBoxService */
+    private $stationTaskMaterialBoxService;
+    /** @var StationCacheShelfGridService $stationCacheShelfGridService */
+    private $stationCacheShelfGridService;
+    /** @var ForeignHaiRoboticsService $foreignHaiRoboticsService */
+    private $foreignHaiRoboticsService;
+
+    /**
+     * 获取现有的缓存架任务
+     * @param Station $station
+     */
+    public function getTasks(Station $station)
+    {
+        $grids = StationCacheShelfGrid::query()->with('materialBox')->where('station_id', $station['id'])->where('status', 1)->orderBy('grid_id')->get();
+        $station->setRelation('grids', $grids);
+    }
+
+    public function lightOffTask($locCode, $PTLAction): array
+    {
+        $this->instant($this->stationCacheShelfGridService, 'StationCacheShelfGridService');
+        list($stationCode, $gridId, $row, $col) = StationCacheShelfGrid::getGridByCode($locCode);
+        $station = Station::query()->where('code', $stationCode)->first();
+        $grid = StationCacheShelfGrid::query()->where('station_id', $station['id'])->where('grid_id', $gridId)->first();
+        $materialBox = MaterialBox::query()->where('id', $grid['material_box_id'])->first();
+        try {
+            $bool = $this->putBinToStore($station, $materialBox, $grid);                    // 推送任务
+            if($bool)$this->stationCacheShelfGridService->lightOff($locCode, $PTLAction);    // 灭灯广播
+            return ['success' => $bool];
+        } catch (ErrorException $e) {
+            LogService::log(__FUNCTION__,'缓存架推送任务失败',json_encode($e->getMessage()));
+            return ['success' => false,'errMsg' => $e->getMessage()];
+        }
+    }
+
+    /**
+     * 推任务
+     * @param  $station
+     * @param  $materialBox
+     * @param  $grid
+     * @return bool
+     * @throws ErrorException
+     */
+    public function putBinToStore($station, $materialBox, $grid): bool
+    {
+        $this->instant($this->stationTaskMaterialBoxService, 'StationTaskMaterialBoxService');
+        $this->instant($this->stationCacheShelfGridService, 'StationCacheShelfGridService');
+        $this->instant($this->foreignHaiRoboticsService, 'ForeignHaiRoboticsService');
+
+        /** @var StationTaskMaterialBox $stationTaskMaterialBox */
+        $stationTaskMaterialBox = $this->stationTaskMaterialBoxService->createByStationAndMaterialBox($station, $materialBox);
+        $this->stationCacheShelfGridService->processGrid($grid, $station, $materialBox);
+
+        $station->setRelation('grids', $grid);
+        $stationTaskMaterialBox->setRelation('station', $station);
+        $stationTaskMaterialBox->setRelation('materialBox', $materialBox);
+
+        return $this->foreignHaiRoboticsService->putBinToStore_fromCacheShelf($stationTaskMaterialBox);
+    }
+
+//    /**
+//     * 入库任务完成
+//     * @param $params
+//     */
+//    public function putBinToStoreFinish($params)
+//    {
+//        $this->instant($this->stationTaskMaterialBoxService,'StationTaskMaterialBoxService');
+//
+//        $locCode = $params['locCode'];
+//
+//        list($stationCode, $gridId) = StationCacheShelfGrid::getGridByCode($locCode);
+//        $station = Station::query()->where('code', $stationCode)->first();
+//
+//        $stationCacheShelfGrid = StationCacheShelfGrid::query()->with('materialBox')->where('station_id', $station)->where('grid_id', $gridId)->first();
+//        $stationCacheShelfGrid->update(['status' => '0', 'material_box_id' => null]);
+//
+//        $StationTaskMaterialBox = StationTaskMaterialBox::query()->where('station_id', $station['id'])->where('material_box_id', $stationCacheShelfGrid['$stationCacheShelfGrid'])->first();
+//        $this->stationTaskMaterialBoxService->set($StationTaskMaterialBox, ['status' => '已完成']);
+//    }
+
+//    /**
+//     * 取消格口任务
+//     * @param Station $station
+//     * @param array $girds
+//     */
+//    public function cancelTask(Station $station, array $girds = [])
+//    {
+//        $gridQuery = StationCacheShelfGrid::query()->where('station_id', $station['id']);
+//        if (count($girds) > 0) $gridQuery->whereIn('grid_id', $girds);
+//        $this->stationCacheShelfGridService->cancelTask($gridQuery->get());
+//    }
+}

+ 1 - 0
app/Services/CityService.php

@@ -32,6 +32,7 @@ class CityService
     {
         $city = City::query()->where('name',$name)->first();
         if(isset($city))return $city;
+        if(!$name)return null;
         foreach (CityService::$cities as $city) {
             if(strstr($city,$name) != null){
                return City::query()->where('name','like',$city.'%')->first();

+ 98 - 32
app/Services/ForeignHaiRoboticsService.php

@@ -6,8 +6,10 @@ namespace App\Services;
 
 use App\Exceptions\ErrorException;
 use App\MaterialBox;
+use App\StationCacheShelfGrid;
 use App\StationTaskMaterialBox;
 use Illuminate\Support\Collection;
+use Illuminate\Support\Facades\DB;
 use Illuminate\Support\Facades\Http;
 use App\Traits\ServiceAppAop;
 
@@ -152,42 +154,66 @@ class ForeignHaiRoboticsService
             LogService::log('海柔请求','markBinProcessed1.6',
                 json_encode($stationTaskMaterialBox));
             if(!$stationTaskMaterialBox){
-                throw new ErrorException('该料箱没有安排在处理队列中');
+                throw new ErrorException($binCode.'该料箱没有安排在处理队列中.');
             }
-            $this->putBinToStore($stationTaskMaterialBox)
-                ?true
-                :(function(){throw new ErrorException('呼叫机器人回收U型线料箱失败');})();
             LogService::log('海柔请求','markBinProcessed1.7',
                 json_encode($stationTaskMaterialBox));
-            $stationTaskMaterialBox_next=
-                $this->stationTaskMaterialBoxService
-                    ->processNextQueued($stationTaskMaterialBox); //找到队列中下一个料箱,并标记为处理中
-            $this->stationTaskCommoditiesService
-                ->markProcessed($stationTaskMaterialBox['stationTaskCommodities']);
-            LogService::log('海柔请求','markBinProcessed1.8',
-                json_encode($stationTaskMaterialBox));
-            if($stationTaskMaterialBox_next)
+
+            DB::transaction(function ()use($stationTaskMaterialBox){
+                $stationTaskMaterialBox_next=
+                    $this->stationTaskMaterialBoxService
+                        ->processNextQueued($stationTaskMaterialBox); //找到队列中下一个料箱,并标记为处理中
                 $this->stationTaskCommoditiesService
-                    ->markProcessing($stationTaskMaterialBox_next['stationTaskCommodities']);//因为上边商品任务被标记完成了,所以这里要将队列中找出正在处理的料箱对应的标记为“处理中”
-            if(!$stationTaskMaterialBox_next){
-                $this->instant($this->stationTaskService,'StationTaskService');
-                LogService::log('海柔请求','markBinProcessed1.81',
-                    json_encode($stationTaskMaterialBox['stationTaskBatch']));
-                $stationTaskMaterialBox->loadMissing('stationTaskBatch');
-                $this->stationTaskBatchService->markProcessed($stationTaskMaterialBox['stationTaskBatch']);
-                LogService::log('海柔请求','markBinProcessed1.82',
-                    json_encode($stationTaskMaterialBox['stationTaskBatch']));
-                $this->stationTaskService->markProcessed($stationTaskMaterialBox['stationTask']);
-            }
-            LogService::log('海柔请求','markBinProcessed1.9',
-                json_encode($stationTaskMaterialBox));
-            $this->stationService->broadcastBinMonitor($stationTaskMaterialBox['station_id'],$stationTaskMaterialBox['stationTask']);
-            $this->stationTaskMaterialBoxService
-                ->markProcessed($stationTaskMaterialBox);
-            LogService::log('海柔请求','markBinProcessed1.99',
-                json_encode($stationTaskMaterialBox));
+                    ->markProcessed($stationTaskMaterialBox['stationTaskCommodities']);
+                LogService::log('海柔请求','markBinProcessed1.8',
+                    json_encode($stationTaskMaterialBox));
+                if($stationTaskMaterialBox_next)
+                    $this->stationTaskCommoditiesService
+                        ->markProcessing($stationTaskMaterialBox_next['stationTaskCommodities']);//因为上边商品任务被标记完成了,所以这里要将队列中找出正在处理的料箱对应的标记为“处理中”
+
+                $this->stationTaskMaterialBoxService
+                    ->markProcessed($stationTaskMaterialBox);
+                $notProcessedBoxTasks = $this->stationTaskMaterialBoxService->getNotProcessedSiblings($stationTaskMaterialBox);
+                if($notProcessedBoxTasks->isEmpty()){
+                    $this->instant($this->stationTaskService,'StationTaskService');
+                    LogService::log('海柔请求','markBinProcessed1.81',
+                        json_encode($stationTaskMaterialBox['stationTaskBatch']));
+                    $stationTaskMaterialBox->loadMissing('stationTaskBatch');
+                    $this->stationTaskBatchService->markProcessed($stationTaskMaterialBox['stationTaskBatch']);
+                    LogService::log('海柔请求','markBinProcessed1.82',
+                        json_encode($stationTaskMaterialBox['stationTaskBatch']));
+                    $this->stationTaskService->markProcessed($stationTaskMaterialBox['stationTask']);
+                }
+                $this->storeBox($stationTaskMaterialBox)
+                    ?true
+                    :(function(){throw new ErrorException('呼叫机器人回收U型线料箱失败');})();
+                LogService::log('海柔请求','markBinProcessed1.9',
+                    json_encode($stationTaskMaterialBox));
+                $this->stationService->broadcastBinMonitor($stationTaskMaterialBox['station_id'],$stationTaskMaterialBox['stationTask']);
+                LogService::log('海柔请求','markBinProcessed1.99',
+                    json_encode($stationTaskMaterialBox));
+            });
+
             return true;
         }catch (\Exception $e){
+
+            $this->instant($this->stationTaskMaterialBoxService,'StationTaskMaterialBoxService');
+            $stationTaskMaterialBox_toStore=
+                $this->stationTaskMaterialBoxService->create([
+                    'station_id' => $this->stationService->getStation_byType('立库')['id'],
+                    'material_box_id' => $stationTaskMaterialBox['materialBox']['id'],
+                    'status' => '处理中'
+                ] );
+
+            $dataToPost=$this->makeJson_move(
+                collect([$stationTaskMaterialBox_toStore]),
+                '输送线入立架',
+                'BIN-IN1',//TODO:这里应该是动态取得,参考出立架getULineExit()方法,不然不能从站获得对应的出口,而且要改Station的child为children
+                '',
+                $stationTaskMaterialBox['stationTaskBatch']['id']
+            );
+            $this->controlHaiRobot($dataToPost);
+
             $stationTaskMaterialBox = $stationTaskMaterialBox??$materialBox??null;
             if($stationTaskMaterialBox && get_class($stationTaskMaterialBox)==MaterialBox::class){
                 $stationTaskMaterialBox = StationTaskMaterialBox::query()
@@ -203,15 +229,22 @@ class ForeignHaiRoboticsService
         }
     }
 
-    public function putBinToStore(?StationTaskMaterialBox $stationTaskMaterialBox): bool
+    public function storeBox(?StationTaskMaterialBox $stationTaskMaterialBox): bool
     {
         LogService::log('海柔请求','putBinToStore1',
             '');
         LogService::log('海柔请求','putBinToStore2',
             json_encode($stationTaskMaterialBox));
         $this->instant($this->stationService,'StationService');
+        $this->instant($this->stationTaskMaterialBoxService,'StationTaskMaterialBoxService');
+        $stationTaskMaterialBox_toStore=
+            $this->stationTaskMaterialBoxService->create([
+                'station_id' => $this->stationService->getStation_byType('立库')['id'],
+                'material_box_id' => $stationTaskMaterialBox['materialBox']['id'],
+                'status' => '处理中'
+            ] );
         $dataToPost=$this->makeJson_move(
-            collect([$stationTaskMaterialBox]),
+            collect([$stationTaskMaterialBox_toStore]),
             '输送线入立架',
             'BIN-IN1',//TODO:这里应该是动态取得,参考出立架getULineExit()方法,不然不能从站获得对应的出口,而且要改Station的child为children
             '',
@@ -220,11 +253,44 @@ class ForeignHaiRoboticsService
         LogService::log('海柔请求','putBinToStore3',
             json_encode($dataToPost));
         $controlSuccess = $this->controlHaiRobot($dataToPost);
+        if($controlSuccess){
+        }
+        return $controlSuccess;
+    }
+
+    /** 缓存架入立架 料箱 任务
+     * @param StationTaskMaterialBox|null $stationTaskMaterialBox
+     * @return bool
+     * @throws ErrorException
+     */
+    public function putBinToStore_fromCacheShelf(?StationTaskMaterialBox $stationTaskMaterialBox): bool
+    {
+        LogService::log('海柔请求','putBinToStore_fromCacheShelf1', '');
+        LogService::log('海柔请求','putBinToStore_fromCacheShelf2', json_encode($stationTaskMaterialBox));
+
+        $this->instant($this->stationService,'StationService');
+
+        // 缓存架格口
+        $formLocation = StationCacheShelfGrid::getLocation($stationTaskMaterialBox['station'],$stationTaskMaterialBox['station']['grids']->first());
+
+        /** 创建料箱 从缓存架 到 立架任务 */
+        $dataToPost=$this->makeJson_move(
+            collect([$stationTaskMaterialBox]),
+            '缓存架入立架',
+            $formLocation,              //TODO:通过计算StationCacheShelfGrid 的 Grid_id 获取格口的编号
+            ''
+        );
+
+        LogService::log('海柔请求','putBinToStore_fromCacheShelf3', json_encode($dataToPost));
+
+        /** 控制海柔机器人执行任务 */
+        $controlSuccess = $this->controlHaiRobot($dataToPost);
         if($controlSuccess){
             $this->instant($this->stationTaskMaterialBoxService,'StationTaskMaterialBoxService');
             $this->stationTaskMaterialBoxService->set($stationTaskMaterialBox,[
                 'id' => $stationTaskMaterialBox['id'],
                 'status' => $stationTaskMaterialBox['status']='处理中',
+                'station_id' => $stationTaskMaterialBox['station']['id']?? '',
             ]);
         }
         return $controlSuccess;

+ 19 - 0
app/Services/OracleDOCOrderHeaderService.php

@@ -44,6 +44,25 @@ class OracleDOCOrderHeaderService
             }]);
     }
 
+    public function getOrderTracking()
+    {
+        return OracleDOCOrderHeader::query()->selectRaw(implode(',',self::$columns))
+            ->with(['oracleBASCustomer'=>function($query){
+                $query->selectRaw('BAS_CUSTOMER.CustomerID,BAS_CUSTOMER.Customer_Type,BAS_CUSTOMER.Descr_C,BAS_CUSTOMER.Active_Flag');
+            },'oracleDOCOrderDetails'=>function($query){
+                $query->selectRaw('doc_order_details.orderNo,doc_order_details.customerid,doc_order_details.sku,doc_order_details.QtyOrdered,doc_order_details.Location,doc_order_details.OrderLineNo,doc_order_details.d_edi_03');
+            }, 'actAllocationDetails'=>function($query){
+                $query->with(['oracleDocOrderSerialNos'=>function($query){
+                    $query->selectRaw('SerialNo,OrderNo,Sku,AllocationDetailsId');
+                }]);
+                $query->selectRaw('ACT_Allocation_Details.AllocationDetailsID,ACT_Allocation_Details.OrderNo,ACT_Allocation_Details.Qty_Each,ACT_Allocation_Details.PickToTraceID,ACT_Allocation_Details.CustomerID,ACT_Allocation_Details.Sku,ACT_Allocation_Details.Location,ACT_Allocation_Details.SkuLineNo,ACT_Allocation_Details.OrderLineno,ACT_Allocation_Details.CheckTime');
+            },'oracleBASCode'=>function($query){
+                $query->selectRaw('BAS_Codes.CodeID,BAS_Codes.CodeName_C,BAS_Codes.Code');
+            },'orderType'=>function($query){
+                $query->selectRaw('BAS_Codes.CodeID,BAS_Codes.CodeName_C,BAS_Codes.Code');
+            }]);
+    }
+
     function first(array $params){
         $order = OracleDOCOrderHeader::query();
         foreach ($params as $column => $value){

+ 1 - 0
app/Services/OrderIssueService.php

@@ -382,6 +382,7 @@ class OrderIssueService
     }
 
     /**
+     * 归档
      * @param $orderIssues
      */
     public function archive($orderIssues)

+ 153 - 0
app/Services/OrderPackageCommoditySerialNumberService.php

@@ -0,0 +1,153 @@
+<?php
+
+namespace App\Services;
+
+use App\Order;
+use App\OrderPackage;
+use App\Services\common\BatchUpdateService;
+use App\Services\common\DataHandlerService;
+use App\Traits\ServiceAppAop;
+use App\OrderPackageCommoditySerialNumber;
+use Illuminate\Support\Carbon;
+
+class OrderPackageCommoditySerialNumberService
+{
+    use ServiceAppAop;
+
+    protected $modelClass = OrderPackageCommoditySerialNumber::class;
+
+    public function batchUpdate(array $params)
+    {
+        return app(BatchUpdateService::class)->batchUpdate('order_packages_commodity_serial_numbers',$params);
+    }
+
+    public function syncOrderPackageCommoditySerialNumbers($orderHeaders)
+    {
+        $orderQuery = Order::query()->selectRaw('id')->whereIn('code', data_get($orderHeaders, '*.orderno'));
+        $orderPackages = OrderPackage::query()->with(['commodities' => function ($query) {
+            $query->with('commodity', 'serialNumbers');
+        }])->whereIn('order_id', $orderQuery)->get();
+
+        $createdModels = $this->getCreateModelBy($orderHeaders, $orderPackages);
+
+        $regroupParams = $this->regroupOrderPackage($orderPackages);
+
+        $this->deletedModelBy($createdModels,$regroupParams);
+        $this->createdModelBy($createdModels,$regroupParams);
+    }
+
+    public function regroupOrderPackage($orderPackages): array
+    {
+        $regroupArr = [];
+        if (!$orderPackages) return $regroupArr;
+        foreach ($orderPackages as $orderPackage) {
+            $logistic_number = $orderPackage['logistic_number'];
+            $regroupArr[$logistic_number] = [];
+            foreach ($orderPackage['commodities'] as $commodity) {
+                $sku = $commodity['commodity']['sku'] ?? null;
+                $regroupArr[$logistic_number][$sku] = [];
+                foreach ($commodity['serialNumbers'] as $serialNumber) {
+                    $serial_number = $serialNumber['serial_number'];
+                    $id = $serialNumber['id'];
+                    $regroupArr[$logistic_number][$sku][$serial_number]
+                        = ['id'=>$id,'order_package_commodity_id' => $commodity['id'], 'serial_number' => $serial_number];
+                }
+            }
+        }
+        return $regroupArr;
+    }
+
+    public function createdModelBy($createdModels, $regroupParams)
+    {
+        $innerParams = [];
+        foreach ($createdModels as $key => $createdParams) {
+            foreach ($createdParams as $sku => $params) {
+                foreach ($params as $serialNumber =>$param) {
+                    if(!isset($regroupParams[$key][$sku][$serialNumber])){
+                        $innerParams[] = $param;
+                    }
+                }
+            }
+        }
+        OrderPackageCommoditySerialNumber::query()->insert($innerParams);
+    }
+
+//    public function updatedModelBy($createdModels, $regroupParams)
+//    {
+//        $updateParams = [['id','serial_number']];
+//        if (count($updateParams) == 0) return;
+//        $this->batchUpdate($updateParams);
+//    }
+
+    public function deletedModelBy($createdModels, $regroupParams)
+    {
+        $deleteParams = [];
+        foreach ($regroupParams as $key => $params) {
+            if (!isset($regroupParams[$key])) continue;
+            foreach ($params as $sku => $items) {
+                foreach ($items as $serialNumber=>$item) {
+                    if (!isset($createdModels[$key][$sku][$serialNumber])){
+                        $deleteParams[] = $item;
+                    }
+                }
+            }
+        }
+        if (count($deleteParams)==0) return;
+        $delete = OrderPackageCommoditySerialNumber::query()->whereIn('id',data_get($deleteParams,'*.id'))->delete();
+        if($delete>0)LogService::log(__CLASS__,'Delete OrderPackageCommoditySerialNumber',json_encode($deleteParams));
+    }
+
+    public function getCreateModelBy($orderHeaders, $orderPackages): array
+    {
+        /** @var DataHandlerService $dataHandlerService */
+        $dataHandlerService = app(DataHandlerService::class);
+
+        $orderPackageMaps = $dataHandlerService->dataHeader(['logistic_number'], $orderPackages);
+
+        $orderPackageSerialNumbers = [];
+
+        foreach ($orderHeaders as $orderHeader) {
+            if(!isset($orderHeader['actAllocationDetails']))continue;
+            foreach ($orderHeader['actAllocationDetails'] as $actAllocationDetail) {
+                $logisticNumber = $actAllocationDetail['picktotraceid'];
+
+                if (!$logisticNumber) continue;
+                $orderPackage = $dataHandlerService->getKeyValue(['logistic_number' => $logisticNumber], $orderPackageMaps) ??
+                    $dataHandlerService->getKeyValue(['logistic_number' => $orderHeader['soreference5']], $orderPackageMaps);
+
+                if (!$orderPackage) continue;
+                $orderPackageCommodityMap = [];
+                if(!isset($orderPackage['commodities']))continue;
+                foreach ($orderPackage['commodities'] as $orderPackageCommodity) {
+                    $sku = $orderPackageCommodity['commodity']['sku'] ?? '';
+                    if($sku === '')continue;
+                    $orderPackageCommodityMap[$sku] = $orderPackageCommodity;
+                }
+                foreach ($actAllocationDetail['oracleDocOrderSerialNos'] as $oracleDocOrderSerialNo) {
+                    $sku = $oracleDocOrderSerialNo['sku'];
+                    $createParams = $this->getCreateModel($oracleDocOrderSerialNo, $orderPackageCommodityMap);
+                    if (count($createParams) == 0) continue;
+                    if (!isset($orderPackageSerialNumbers[$logisticNumber])) $orderPackageSerialNumbers[$logisticNumber] = [];
+                    if (!isset($orderPackageSerialNumbers[$logisticNumber][$sku])) $orderPackageSerialNumbers[$logisticNumber][$sku] = [];
+                    $orderPackageSerialNumbers[$logisticNumber][$sku][$createParams['serial_number']] = $createParams;
+                }
+            }
+        }
+        return $orderPackageSerialNumbers;
+    }
+
+    public function getCreateModel($oracleDocOrderSerialNo, $orderPackageCommodityMap)
+    {
+        $orderPackageCommodity = $orderPackageCommodityMap[$oracleDocOrderSerialNo['sku']] ?? null;
+        if (!$orderPackageCommodity) return [];
+        $created_At = Carbon::now()->format(Carbon::DEFAULT_TO_STRING_FORMAT);
+        return [
+            'order_package_commodity_id' => $orderPackageCommodity['id'],
+            'serial_number' => $oracleDocOrderSerialNo['serialno'],
+            'created_at' => $created_At,
+            'updated_at' => $created_At
+        ];
+    }
+
+
+}

+ 6 - 3
app/Services/OrderService.php

@@ -98,13 +98,16 @@ class OrderService
             $sql .= " AND (c_tel2 like '".$c_tel2."' OR c_tel1 like '".$c_tel2."')";
         }
         if ($c_province){
-            $sql .= " AND c_province like '".$c_province."'";
+            if ($c_province=='空')$sql .= " AND c_province is null";
+            else$sql .= " AND c_province like '".$c_province."'";
         }
         if ($c_city){
-            $sql .= " AND c_city like '".$c_city."'";
+            if ($c_city=='空')$sql .= " AND c_city is null";
+            else$sql .= " AND c_city like '".$c_city."'";
         }
         if ($c_district){
-            $sql .= " AND c_district like '".$c_district."'";
+            if ($c_district=='空')$sql .= " AND c_district is null";
+            else$sql .= " AND c_district like '".$c_district."'";
         }
         if ($releasestatus){
             $sql .= " AND releasestatus = '".$releasestatus."'";

+ 2 - 130
app/Services/OrderTrackingService.php

@@ -28,136 +28,6 @@ class OrderTrackingService
         return app(BatchUpdateService::class)->batchUpdate('order_trackings',$params);
     }
 
-    public function getQuery($params)
-    {
-        $user = Auth::user();
-        $owner_ids = $user ? app('UserService')->getPermittingOwnerIds($user) : [];
-        if ($params['owner_id'] ?? false) {
-            $ownerids = explode(",", $params['owner_id']);
-            $owner_ids = array_intersect($owner_ids, $ownerids);
-        }
-        $query = OrderTracking::query()->with(['logistic','owner','uploadFile', 'commodities' => function ($query) {
-            $query->with(['commodity'=>function($query){
-                $query->with('barcodes');
-            }, 'package.order']);
-        },])->whereHas('owner', function ($query) use ($owner_ids) {
-            $query->whereIn('id', $owner_ids); //货主
-        });
-
-        $queryParam = [
-            'start_at' => ['alias' => 'pick_up_at', 'startDate' => ' 00:00:00'],
-            'end_at' => ['alias' => 'pick_up_at', 'endDate' => ' 23:59:59'],
-            'start_planning_sent_at' => ['alias' => 'planning_sent_at', 'startDate' => ' 00:00:00'],
-            'end_planning_sent_at' => ['alias' => 'planning_sent_at', 'endDate' => ' 23:59:59'],
-        ];
-
-        $param = [
-            'start_at' => $params['start_at'] ?? '',
-            'end_at' => $params['end_at'] ?? '',
-            'start_planning_sent_at' => $params['start_planning_sent_at'] ?? '',
-            'end_planning_sent_at' => $params['end_planning_sent_at'] ?? '',
-        ];
-
-        $query = app(QueryService::class)->query($param, $query, $queryParam);
-        return $query;
-    }
-
-    public function getConditionQuery($params)
-    {
-        $query = $this->getQuery($params);
-
-        if (isset($params['client_code'])){     // client_code logistic
-            $query->where('order_client_code','like',$params['client_code']);
-//            $query->whereHas('commodities.package.order', function ($query) use ($params) {
-//                if($params['client_code'] ?? false){
-//                    $query->where('client_code','like', $params['client_code']);
-//                }
-//            });
-        }
-
-        if(isset($params['order_client_code_wms'])){
-            $query->where('order_client_code_wms','like',$params['order_client_code_wms']);
-        }
-
-        if(isset($params['logistic_id']))$query->where('logistic_id',$params['logistic_id']);   // 快递
-
-        if ($params['client'] ?? false) $query->where('client', 'like', $params['client']);  // 客户
-
-        if(!empty($params['web_order_number'])) $query->where('web_order_number', 'like', $params['web_order_number'].'%'); // web+订单好
-
-        if ($params['sku'] ?? false) {
-            $query->whereHas('commodities.commodity', function ($query) use ($params) {
-                $query->where('sku', $params['sku']);
-            });
-        }  // sku
-
-        if ($params['logistic_number'] ?? false) {
-            $query->whereHas('commodities.package', function ($query) use ($params) {
-                $query->where('logistic_number', $params['logistic_number']);
-            });
-        }// 运输单号(快递单号)
-
-        if(isset($params['remark'])){
-            $query->where('remark','like','%'.$params['remark'].'%');
-            if(isset($params['remark_addtime'])){
-                $query->where('created_at','>',Carbon::now()->subDays($params['remark_addtime']));
-            }else{
-                $query->where('created_at','>=',Carbon::now()->subDays(15));
-            }
-        }
-
-        if ($params['ids'] ?? false) {
-            $ids = explode(',', $params['ids']);
-            $query->whereIn('order_trackings.id', $ids);
-        }
-        return $query;
-    }
-
-    public function paginate($params)
-    {
-        return $this->getConditionQuery($params)
-            ->orderByDesc('order_trackings.pick_up_at')
-            ->orderBy('order_trackings.order_client_code')
-            ->paginate($params['paginate'] ?? 50);
-    }
-
-    public function getSql($params)
-    {
-        if($params['data'] ?? false){
-            $params['ids'] = $params['data'];
-        }
-        $order_tracking_colnums = "case order_trackings.order_client_code when  order_trackings.web_order_number then '' when order_trackings.order_client_code then  order_trackings.order_client_code end order_client_code,";
-        $order_tracking_colnums .= 'order_trackings.web_order_number,order_trackings.pick_up_at,order_trackings.sale,order_trackings.client,order_trackings.order_remark,order_trackings.gross_weight,order_trackings.bulk,';
-        $order_tracking_colnums .= "case order_trackings.planning_sent_at when '0000-00-00 00:00:00' then null else order_trackings.planning_sent_at end planning_sent_at,";
-        $order_tracking_colnums .= 'order_trackings.pallet_total,order_trackings.is_on_duty_shift,order_trackings.is_arrival,order_trackings.signed_at,order_trackings.receive_bill_status,order_trackings.remark';
-        return  $this->getConditionQuery($params)
-            ->orderBy('order_trackings.order_client_code')
-            ->orderByDesc('order_trackings.pick_up_at')
-            ->selectRaw($order_tracking_colnums)
-            ->leftJoin('order_package_commodities','order_trackings.order_package_commodity_id','order_package_commodities.id')
-                ->selectRaw('order_package_commodities.amount amount')
-            ->leftJoin('order_packages','order_package_commodities.order_package_id','order_packages.id')
-                ->selectRaw('order_packages.logistic_number logistic_number')
-            ->leftJoin('orders','order_packages.order_id','orders.id')
-                ->selectRaw('orders.city city')
-            ->leftJoin('logistics','orders.logistic_id','logistics.id')
-                ->selectRaw("case logistics.code when 'ZT' then '新杰物流' else  logistics.name end  logistic_name")
-            ->leftJoin('commodities','order_package_commodities.commodity_id','commodities.id')
-                ->selectRaw('commodities.sku sku,commodities.name sku_name')
-            ->leftJoin('owners','orders.owner_id','owners.id')
-               ->selectRaw('owners.name owners_name')
-            ->sql();
-    }
-
-    public function getOrderTrackings($params)
-    {
-        if($params['data'] ?? false){
-            $params['ids'] = $params['data'];
-        }
-        return  $this->getConditionQuery($params)
-            ->orderByDesc('order_trackings.pick_up_at')->orderBy('order_trackings.order_client_code')->get();
-    }
-
     /**
      * @param $startData
      */
@@ -172,6 +42,7 @@ class OrderTrackingService
         app("OrderCommodityService")->syncOrderCommodity($orderHeaders);
         app('OrderPackageService')->syncOrderPackage($orderHeaders);
         app("OrderPackageCommoditiesService")->syncOrderPackageCommodities($orderHeaders);
+        app('OrderPackageCommoditySerialNumberService')->syncOrderPackageCommoditySerialNumbers($orderHeaders);
         $this->createByWmsOrderHeader($orderHeaders);
     }
 
@@ -182,6 +53,7 @@ class OrderTrackingService
         app("OrderCommodityService")->syncOrderCommodity($orderHeaders);
         app('OrderPackageService')->syncOrderPackage($orderHeaders);
         app("OrderPackageCommoditiesService")->syncOrderPackageCommodities($orderHeaders);
+        app('OrderPackageCommoditySerialNumberService')->syncOrderPackageCommoditySerialNumbers($orderHeaders);
         $this->updateByWmsOrderHeaders($orderHeaders);
     }
 

+ 2 - 0
app/Services/ProvinceService.php

@@ -16,8 +16,10 @@ class ProvinceService
 
     public function findByName($name)
     {
+        if(!$name)return null;
         $province = Province::query()->where('name',$name)->first();
         if(isset($province))return $province;
+
         foreach (ProvinceService::$provinces as $province) {
             if(strstr($province,$name) != null){
                 return Province::query()->where('name',$province)->first();

+ 106 - 0
app/Services/StationCacheShelfGridService.php

@@ -0,0 +1,106 @@
+<?php
+
+namespace App\Services;
+
+use App\Events\BroadcastToStation;
+use App\Station;
+use App\Traits\ServiceAppAop;
+use App\StationCacheShelfGrid;
+use Illuminate\Support\Facades\Http;
+
+class StationCacheShelfGridService
+{
+    use ServiceAppAop;
+
+    protected $modelClass = StationCacheShelfGrid::class;
+
+    /**
+     * processGrid 格口上放入料箱
+     * lightOff 解析HaiQ格口灭灯请求  完成格口上的任务 进行灭灯完成后的广播
+     * cancelTask 完成格口上的任务
+     * lightOn 发送给HaiQ的格口亮灯
+     */
+
+    /**
+     * 修改格口的状态
+     * @param $stationCacheShelfGrid
+     * @param $station
+     * @param $materialBox
+     */
+    public function processGrid($stationCacheShelfGrid, $station, $materialBox)
+    {
+        $stationCacheShelfGrid->update(['station_id' => $station['id'], 'material_box_id' => $materialBox['id'], 'status' => 1]);
+    }
+
+    /**
+     * 清空任务
+     * @param $grids
+     * @return bool
+     */
+    public function cancelTask($grids): bool
+    {
+
+        return StationCacheShelfGrid::query()->whereIn('id',data_get($grids,'*.id'))->update(['material_box_id' => null, 'status' => 0]);
+    }
+
+    /**
+     * 格口灭灯完成后的广播
+     * @param $locCode
+     * @param $PTLAction
+     */
+    public function lightOff($locCode, $PTLAction)
+    {
+        if ($PTLAction == 0) {
+            list($stationCode, $gridId, $x, $y) = StationCacheShelfGrid::getGridByCode($locCode);
+            $station = Station::query()->where('code', $stationCode)->first();
+            $gird = StationCacheShelfGrid::query()->where('station_id',$station['id'])->where('grid_id',$gridId)->get();
+            $this->cancelTask($gird);
+            $json = json_encode([
+                'code' => $stationCode,
+                'x' => $x,
+                'y' => $y,
+                'id' => $station['id'] ?? '',
+                'grid_id' => $gridId,
+            ]);
+            broadcast(new BroadcastToStation($station['id'] ?? '', $json));
+            StationCacheShelfGrid::query()->where('station_id',$station['id'])->where('grid_id', $gridId)->update(['material_box_id' => null, 'status' => 0]);
+        }
+    }
+
+    /**
+     * 通知HaiQ亮灯
+     * @param Station $station
+     * @param $pointX
+     * @param $pointY
+     * @return string
+     */
+    public function lightOn(Station $station, $pointX, $pointY)
+    {
+        $locCode = 'HAI' . $station['code'] . '-0' . $pointX . '-0' . $pointY;
+        $params = [
+            "areaCode" => "1004",
+            "PTLAction" => 1, //1是开,0是关
+            "PTLSettings" => [
+                "color" => 1,
+                "frequency" => 1
+            ],
+            "displayInfo" => [
+                "detail01" => "detail01",
+                "detail02" => "detail02",
+                "detail03" => "detail03",
+                "qrCode" => "qrCode",
+                "qty00" => "11",
+                "qty01" => 1,
+                "qty02" => 2,
+                "title" => "title",
+                "uomDesc01" => "uo",
+                "uomDesc02" => "uo"
+            ],
+            "locCode" => $locCode//灯条口,B1\B2=设备编号,中间号码代表从右往左数的列,右边号码时从下往上数
+        ];
+//        LogService::log(__METHOD__,'海柔亮灯请求',json_encode($params));
+        $response = Http::post(config('api.haiq.storage.light'), $params);
+//        LogService::log(__METHOD__,'海柔亮灯请求返回',json_encode($params));
+        return $response->body();
+    }
+}

+ 6 - 3
app/Services/StationRuleBatchService.php

@@ -65,10 +65,13 @@ class StationRuleBatchService
         $batches=$batches->whereNotIn('id',data_get($batches_inTask,'*.id')??[]);
         foreach ($batches as $batch){
             $stationRuleBatch=$this->getByBatch($batch);
-            if($stationRuleBatch)
-                $batches_toProcess->push($batch);
+            if(!$stationRuleBatch)continue;
+            if(Cache::tags(['波次防重叠'.$batch['id']])->get($batch['id']))
+                continue;
+            $batches_toProcess->push($batch);
+            Cache::tags([ '波次防重叠'.$batch['id']])->put($batch['id'],true,config('haiRou.波次防重叠时间_秒'));
         }
-        LogService::log(__METHOD__,'shouldProcess','波次任务分配1.3:'.json_encode($batches_inTask));
+        LogService::log(__METHOD__,'shouldProcess','波次任务分配1.3:'.json_encode($batches_toProcess));
         return $batches_toProcess;
     }
 }

+ 16 - 3
app/Services/StationTaskBatchService.php

@@ -126,10 +126,11 @@ class StationTaskBatchService
     }
 
     /**
-     * @param Collection $stationTaskBatches
+     * @param Collection|null $stationTaskBatches
      * @return Collection|\Tightenco\Collect\Support\Collection|null 返回执行失败的记录
+     * @throws ErrorException
      */
-    function runMany(Collection $stationTaskBatches):?Collection
+    function runMany(?Collection $stationTaskBatches):?Collection
     {
         LogService::log(__METHOD__,'runMany','波次任务分配6.1:'.json_encode($stationTaskBatches));
         $stationTaskBatches_failed = null;
@@ -219,10 +220,22 @@ class StationTaskBatchService
     {
         if(!$ids)$ids=[];
         if(!is_array($ids))$ids=[$ids];
+
+
+        $hasProcessing=StationTaskBatch::query()
+            ->whereNotIn('id', $ids)
+            ->where(['status'=>'处理中'])
+            ->get('id')->isNotEmpty();
+
+        $status = '处理中';
+        if($hasProcessing)
+            $status = '处理队列';
+
         StationTaskBatch::query()
             ->whereIn('id', $ids)
-            ->update(['status'=>'处理中']);
+            ->update(['status'=>$status]);
     }
+
     function markProcessed($stationTaskBatch_orCollection)
     {
         if (get_class($stationTaskBatch_orCollection)==StationTaskBatch::class){

+ 18 - 14
app/Services/StationTaskCommodityService.php

@@ -146,17 +146,21 @@ class StationTaskCommodityService
 
         $stationTaskCommodities_grouped->each(function(&$groupByBatch){
             ($标记本波次内是处理中或处理队列=function()use(&$groupByBatch){
-                $taskBatchId=$groupByBatch[0]['station_task_batch_id'];
-                $processing=$this->getProcessing_byTaskBatch($taskBatchId);
-                if(!$processing
-                    || $processing['material_box_id'] == $taskBatchId['material_box_id']){
-                    $status='处理中';
-                }else{
-                    $status='处理队列';
-                }
-                $groupByBatch->each(function(&$stationTaskCommodity)use($status){
-                    $stationTaskCommodity['status']=$status;
+                $stationId=$groupByBatch[0]['station_id'];
+                $processingIds=data_get($this->getProcessings_byStationId($stationId),'*.id')??[];
+                $groupByBatch->each(function(&$stationTaskCommodity)use($processingIds){
+                    if(in_array($stationTaskCommodity['id'],$processingIds))
+                        $stationTaskCommodity['status']='处理中';
+                    else
+                        $stationTaskCommodity['status']='处理队列';
                 });
+                if(count($processingIds)==0){
+                    foreach($groupByBatch as &$stationTaskCommodity){
+                        if($stationTaskCommodity['material_box_id']==$groupByBatch[0]['material_box_id']){
+                            $stationTaskCommodity['status']='处理中';
+                        }
+                    }
+                }
             })();
         });
         ($持久化处理队列的记录=function()use(&$stationTaskCommodities_grouped){
@@ -173,13 +177,13 @@ class StationTaskCommodityService
 
     }
 
-    function getProcessing_byTaskBatch($stationTaskBatch_id)
+    function getProcessings_byStationId($stationTaskBatch_id)
     {
-        //这里不用缓存,因为更新会非常快
+        //这里不用缓存,因为更新会非常快
         return StationTaskCommodity::query()
-            ->where('station_task_batch_id',$stationTaskBatch_id)
+            ->where('station_id',$stationTaskBatch_id)
             ->where('status','处理中')
-            ->first();
+            ->get();
     }
 
 

+ 84 - 23
app/Services/StationTaskMaterialBoxService.php

@@ -5,16 +5,19 @@ namespace App\Services;
 
 
 use App\Batch;
+use App\Events\BroadcastToStation;
 use App\Exceptions\ErrorException;
 use App\Exceptions\Exception;
 use App\MaterialBox;
 use App\OrderCommodity;
+use App\StationCacheShelfGrid;
 use App\StationTask;
 use App\StationTaskBatch;
 use App\StationTaskMaterialBox;
 use Illuminate\Support\Collection;
 use Illuminate\Support\Facades\Cache;
 use App\Traits\ServiceAppAop;
+use Illuminate\Support\Facades\DB;
 
 
 class StationTaskMaterialBoxService
@@ -35,6 +38,8 @@ class StationTaskMaterialBoxService
     private $orderCommodityService;
     /** @var MaterialBoxService $materialBoxService */
     private $materialBoxService;
+    /** @var StationCacheShelfGridService $stationCacheShelfGridService */
+    private $stationCacheShelfGridService;
     public function __construct(){
         $this->stationService=null;
         $this->stationTypeService=null;
@@ -44,6 +49,32 @@ class StationTaskMaterialBoxService
         $this->stationTaskCommodityService=null;
     }
 
+    function create($kvPairs)
+    {
+        return StationTaskMaterialBox::query()->create($kvPairs);
+    }
+    function createByStationAndMaterialBox($station, $materialBox)
+    {
+        return StationTaskMaterialBox::query()->create([
+            'station_id' => $station['id'],
+            'material_box_id' => $materialBox['id'],
+            'status' => '待处理'
+        ]);
+    }
+    function getOccupied_byBatches(?Collection $batches): ?Collection
+    {
+        return StationTaskMaterialBox::query()
+            ->where('status','<>','完成')
+            ->whereHas('materialBox',function ($query)use($batches){
+                $locations=OrderCommodity::query()
+                    ->whereHas('order',function ($queryO)use($batches){
+                        $queryO->whereIn('batch_id',data_get($batches,'*.id')??[]);
+                    })->get('location');
+                $query->whereIn('code',data_get($locations,'*.location')??[]);
+            })
+            ->get();
+    }
+
     function createByBatches(Collection $batches,Collection $stationTasks_toAttach): Collection
     {
         $this->instant($this->stationTaskService,'StationTaskService');
@@ -51,14 +82,14 @@ class StationTaskMaterialBoxService
         $stationTaskMaterialBoxes_byBatch = (function () use ($batches) {
             $stationTaskMaterialBoxes_listByBatch = new Collection();
             foreach ($batches as $batch) {
-                    $stationTaskMaterialBoxes_listByBatch->push(
+                $stationTaskMaterialBoxes_listByBatch->push(
                     $this->createByBatch($batch)
                 );
             }
             return $stationTaskMaterialBoxes_listByBatch;
         })();
         LogService::log(__METHOD__,'assignTasks','波次任务分配4.c2:'.json_encode($batches));
-         $this->stationTaskService
+        $this->stationTaskService
             ->registerSubTasks(
                 $stationTasks_toAttach,
                 $stationTaskMaterialBoxes_byBatch);
@@ -79,15 +110,18 @@ class StationTaskMaterialBoxService
         if($orderCommodities->isEmpty())return $stationMaterialBoxes_toCreate;
         $stationType=$this->stationTypeService->getForMaterialBox_onBatchProcess();
         $stationTaskBatch=$this->stationTaskBatchService->get(['batch_id'=>$batch['id']])->first();
+        $materialBoxIds_used=[];
         foreach ($orderCommodities as $orderCommodity){
             $station=$this->stationService->getStation_byType($stationType['name']);
             $materialBox=$this->materialBoxService->firstOrCreate(['code' => $orderCommodity['location']]);
+            if(in_array($materialBox['id'],$materialBoxIds_used))continue;
             $stationMaterialBoxes_toCreate->push([
                 'station_id'=>$station['id'],
                 'material_box_id'=>$materialBox['id'],
                 'station_task_batch_id'=>$stationTaskBatch['id'],
                 'status'=>'待处理'
             ]);
+            $materialBoxIds_used[]=$materialBox['id'];
         }
         return $this->insert($stationMaterialBoxes_toCreate->toArray(),true);
     }
@@ -124,7 +158,9 @@ class StationTaskMaterialBoxService
                     $this->stationTaskBatchService->markProcessing_byIds($stationTaskMaterialBox['station_task_batch_id']);
                     $this->stationTaskCommodityService->markProcessing($stationTaskMaterialBox['stationTaskCommodities']);
                     $this->stationTaskService->markProcessing_byIds(data_get($stationTaskMaterialBox['stationTaskCommodities'],'*.station_task_id'));
-                    $this->stationService->broadcastBinMonitor($stationTaskMaterialBox['station_id'],$stationTaskMaterialBox['stationTask']);
+                    /** @var StationTask $stationTask */
+                    $stationTask = $this->stationTaskService->getProcessing();
+                    $this->stationService->broadcastBinMonitor($stationTaskMaterialBox['station_id'], $stationTask);
                     break;
                 case '入立库':
                     $this->set($stationTaskMaterialBox,[
@@ -140,9 +176,28 @@ class StationTaskMaterialBoxService
         }
     }
 
-    function markHasTaken($stationTaskMaterialBox){
+    function markHasTaken($stationTaskMaterialBox)
+    {
         //TODO: 标记 料箱位置(需要其字段存在)$stationTaskMaterialBox['materialBox']['position']
+
+        $this->instant($this->stationCacheShelfGridService,'StationCacheShelfGridService');
+
+        // 料箱从缓存架上拿走
+        if($stationTaskMaterialBox['station']['stationType']['name']=='缓存架'){
+            $stationTaskMaterialBox['status'] = '完成';
+
+            $json = json_encode([
+                'station_id'=>$stationTaskMaterialBox['station_id'],
+                'code' => $stationTaskMaterialBox['materialBox']['code'],
+                'status' => '完成'
+            ]);
+            $grids = StationCacheShelfGrid::query()->where(['station_id'=>$stationTaskMaterialBox['station_id'],'material_box_id'=>$stationTaskMaterialBox['material_box_id']])->get();
+            $this->stationCacheShelfGridService->cancelTask($grids);
+            broadcast(new BroadcastToStation($stationTaskMaterialBox['station_id'], $json));
+            $this->markProcessed($stationTaskMaterialBox);
+        }
     }
+
     function processNextQueued(StationTaskMaterialBox $stationTaskMaterialBox_lastProcessed){
         $station_id=$stationTaskMaterialBox_lastProcessed['station_id'];
         $stationTaskMaterialBox_next=StationTaskMaterialBox::query()
@@ -155,11 +210,17 @@ class StationTaskMaterialBoxService
         }
         return $stationTaskMaterialBox_next;
     }
+
     function markProcessed(StationTaskMaterialBox $stationTaskMaterialBox){
-        $stationTaskMaterialBox['status'] = '完成';
-        $stationTaskMaterialBox['station_id'] = 4;
+        $stationTaskMaterialBox['status'] = '处理完';
         $stationTaskMaterialBox->update();
     }
+    function getNotProcessedSiblings($stationTaskMaterialBox){
+        return StationTaskMaterialBox::query()
+            ->whereNotIn('status',['完成'])
+            ->where('station_task_id',$stationTaskMaterialBox['station_task_id'])
+            ->get();
+    }
 
     /**
      * 每波次仅将最老的作务标为“处理中”,其他置入队列;
@@ -171,26 +232,26 @@ class StationTaskMaterialBoxService
         $this->instant($this->stationTaskService,'StationTaskService');
         $stationTaskMaterialBoxes =
             (function()use($stationTaskMaterialBox_orBoxes){
-            if (get_class($stationTaskMaterialBox_orBoxes)==StationTaskMaterialBox::class){
-                return collect([$stationTaskMaterialBox_orBoxes]);
-            }
-            return collect($stationTaskMaterialBox_orBoxes);
-        })();
+                if (get_class($stationTaskMaterialBox_orBoxes)==StationTaskMaterialBox::class){
+                    return collect([$stationTaskMaterialBox_orBoxes]);
+                }
+                return collect($stationTaskMaterialBox_orBoxes);
+            })();
         $stationTaskMaterialBoxes_grouped=
             ($按时间从前往后排出顺序=function ()use(&$stationTaskMaterialBoxes){
-            return $stationTaskMaterialBoxes
-                ->sortBy('id')
-                ->groupBy('station_task_batch_id');
-        })();
+                return $stationTaskMaterialBoxes
+                    ->sortBy('id')
+                    ->groupBy('station_task_batch_id');
+            })();
         $stationTaskMaterialBoxes_grouped->each(function(&$groupByBatch){
-            ($将所要标记的箱任务先放在队列里=function()use(&$groupByBatch){
+            ($将所要标记的箱任务先放在队列里=function()use(&$groupByBatch){
                 $groupByBatch->each(function (&$stationTaskMaterialBox){
                     $stationTaskMaterialBox['status']='处理队列';
                 });
             })();
-            ($如果之前没有处理中则标记第一个为处理目标,持久化=function()use(&$groupByBatch){
-                $taskBatchId=$groupByBatch[0]['station_task_batch_id'];
-                $processing=$this->getProcessing_byTaskBatch($taskBatchId);
+            ($如果之前没有处理中则标记第一个为处理目标,准备持久化=function()use(&$groupByBatch){
+                $stationId=$groupByBatch[0]['station_id'];
+                $processing=$this->getProcessing_byStationId($stationId);
                 if(!$processing){
                     $groupByBatch[0]['status']='处理中';
                     $groupByBatch[0]->update();
@@ -219,11 +280,11 @@ class StationTaskMaterialBoxService
             );
     }
 
-    function getProcessing_byTaskBatch($stationTaskBatch_id)
+    function getProcessing_byStationId($stationId)
     {
         //这里不能用缓存,因为更新会非常快
         return StationTaskMaterialBox::query()
-            ->where('station_task_batch_id',$stationTaskBatch_id)
+            ->where('station_id',$stationId)
             ->where('status','处理中')
             ->first();
     }
@@ -237,7 +298,7 @@ class StationTaskMaterialBoxService
         switch (get_class($stationTaskMaterialBoxes_orBox)){
             case MaterialBox::class:
             case StationTaskMaterialBox::class:
-            throw new ErrorException('料箱异常'.json_encode($stationTaskMaterialBoxes_orBox->toJson()));
+                throw new ErrorException('料箱异常'.json_encode($stationTaskMaterialBoxes_orBox->toJson()));
         }
     }
 
@@ -246,7 +307,7 @@ class StationTaskMaterialBoxService
         $stationTaskMaterialBox->load('station.stationType');
         if($isBatching=(
             $stationTaskMaterialBox['station_task_batch_id'] &&
-                $stationTaskMaterialBox['station']['stationType']['name'] == '料箱监视器')
+            $stationTaskMaterialBox['station']['stationType']['name'] == '料箱监视器')
         ){
             return '分波次';
         }

+ 17 - 1
app/Services/StationTaskService.php

@@ -81,13 +81,29 @@ class StationTaskService
         }
     }
 
+    function getProcessing()
+    {
+        return StationTask::query()
+            ->where(['status'=>'处理中'])
+            ->first();
+    }
     function markProcessing_byIds($ids)
     {
         if(!$ids)$ids=[];
         if(!is_array($ids))$ids=[$ids];
+
+        $hasProcessing=StationTask::query()
+            ->whereNotIn('id', $ids)
+            ->where(['status'=>'处理中'])
+            ->get('id')->isNotEmpty();
+
+        $status = '处理中';
+        if($hasProcessing)
+            $status = '处理队列';
+
         StationTask::query()
             ->whereIn('id',$ids)
-            ->update(['status'=>'处理中']);
+            ->update(['status'=>$status]);
     }
     function markProcessed_byIds($ids)
     {

+ 3 - 3
app/Services/StoreService.php

@@ -87,7 +87,7 @@ class StoreService
         if (count($asnHerders)<1) return;
         $last_time = $asnHerders->first()['edittime'];
         $last_records = $asnHerders->where('edittime', $last_time);
-        $this->createStore($asnHerders);
+        $this->createStore($asnHerders,"update");
         $this->updateStore($asnHerders);
         $this->createStoreRejected($asnHerders);
         $this->deleteCacheKey($update_set, $update_keys);
@@ -95,7 +95,7 @@ class StoreService
         $this->setAsnLastSyncAt($updated_at, $last_time);
     }
 
-    public function createStore($asnHerders)
+    public function createStore($asnHerders,$isUpdate=null)
     {
         if (!$asnHerders) return null;
         /** @var OwnerService $ownerService */
@@ -126,7 +126,7 @@ class StoreService
         /** @var RejectedBillService $rejectedBillService */
         $rejectedBillService = app(RejectedBillService::class);
         $rejectedBillService->syncLoadedStatusByAsnHerder($asnHerders);
-        $this->pushJob($asnHerders);
+        if (!$isUpdate)$this->pushJob($asnHerders);
         unset($asnHerders, $owners_code_map, $warehouses_code_map);
     }
 

+ 8 - 1
app/Station.php

@@ -2,13 +2,15 @@
 
 namespace App;
 
+use App\Traits\ModelTimeFormat;
 use Illuminate\Database\Eloquent\Model;
-
+use Illuminate\Database\Eloquent\Relations\HasMany;
 use App\Traits\ModelLogChanging;
 
 class Station extends Model
 {
     use ModelLogChanging;
+    use ModelTimeFormat;
 
     protected $fillable=['name', 'code', 'station_type_id','sequence','parent_id'];
 
@@ -28,4 +30,9 @@ class Station extends Model
     public function stationTypeBinMonitor(){
         return $this->hasOne(StationTypeBinMonitor::class);
     }
+
+    public function grids(): HasMany
+    {
+        return $this->hasMany(StationCacheShelfGrid::class);
+    }
 }

+ 61 - 0
app/StationCacheShelfGrid.php

@@ -0,0 +1,61 @@
+<?php
+
+namespace App;
+
+use Illuminate\Database\Eloquent\Model;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
+use App\Traits\ModelLogChanging;
+
+class StationCacheShelfGrid extends Model
+{
+    use ModelLogChanging;
+
+    protected $fillable = ['station_id', 'material_box_id', 'grid_id', 'status'];
+
+    public static $status = [
+        '0' => '无',
+        '1' => '待处理',
+    ];
+
+    public function station(): BelongsTo
+    {
+        return $this->belongsTo(Station::class);
+    }
+
+    public function materialBox(): BelongsTo
+    {
+        return $this->belongsTo(MaterialBox::class);
+    }
+
+    /**
+     * 根据格口计算位置
+     * @param Station $station
+     * @param StationCacheShelfGrid $grid
+     * @return string
+     */
+    public static function getLocation(Station $station, StationCacheShelfGrid $grid): string
+    {
+        $code = $station['code'];
+        $grid_id = $grid['grid_id'];
+        $row = 2 - round($grid_id / 3) + 1;
+        $col = 2 - ($grid_id % 3) + 1;
+        return 'HAI' . $code . '-0' . $col . '-0' . $row;
+    }
+
+    /**
+     * 根据位置计算 grid_id 和 station code
+     * @param $code
+     * @return array|false[]
+     */
+    public static function getGridByCode($code): array
+    {
+        $arr = [];
+        preg_match('/^HAI([\w\.\ ]+)-0([0-9]+)-0([0-9]+)/',$code,$arr);
+        if(count($arr)==0)return [false,false,null,null];
+        $stationCode =$arr[1] ?? false;
+        $col = $arr[2] ?? 0;  // 列
+        $row = $arr[3] ?? 0;  // 行
+        $gridId = ($row-1)*3 + (3-$col);
+        return [$stationCode,$gridId,$row,$col];
+    }
+}

+ 1 - 1
app/StationTask.php

@@ -10,7 +10,7 @@ class StationTask extends Model
 {
     use ModelLogChanging;
 
-    protected $fillable = ['status'];
+    protected $fillable = ['status','station_id'];
 
     public function tasks()
     {

+ 11 - 0
bootstrap/cache/packages.php

@@ -46,6 +46,17 @@
       'Image' => 'Intervention\\Image\\Facades\\Image',
     ),
   ),
+  'laravel/horizon' => 
+  array (
+    'providers' => 
+    array (
+      0 => 'Laravel\\Horizon\\HorizonServiceProvider',
+    ),
+    'aliases' => 
+    array (
+      'Horizon' => 'Laravel\\Horizon\\Horizon',
+    ),
+  ),
   'laravel/tinker' => 
   array (
     'providers' => 

+ 29 - 25
bootstrap/cache/services.php

@@ -28,19 +28,21 @@
     24 => 'Facade\\Ignition\\IgnitionServiceProvider',
     25 => 'Fideloper\\Proxy\\TrustedProxyServiceProvider',
     26 => 'Intervention\\Image\\ImageServiceProvider',
-    27 => 'Laravel\\Tinker\\TinkerServiceProvider',
-    28 => 'Laravel\\Ui\\UiServiceProvider',
-    29 => 'Maatwebsite\\Excel\\ExcelServiceProvider',
-    30 => 'Carbon\\Laravel\\ServiceProvider',
-    31 => 'NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider',
-    32 => 'Overtrue\\LaravelPinyin\\ServiceProvider',
-    33 => 'Te7aHoudini\\LaravelTrix\\LaravelTrixServiceProvider',
-    34 => 'Yajra\\Oci8\\Oci8ServiceProvider',
-    35 => 'App\\Providers\\AppServiceProvider',
-    36 => 'App\\Providers\\AuthServiceProvider',
-    37 => 'App\\Providers\\BroadcastServiceProvider',
-    38 => 'App\\Providers\\EventServiceProvider',
-    39 => 'App\\Providers\\RouteServiceProvider',
+    27 => 'Laravel\\Horizon\\HorizonServiceProvider',
+    28 => 'Laravel\\Tinker\\TinkerServiceProvider',
+    29 => 'Laravel\\Ui\\UiServiceProvider',
+    30 => 'Maatwebsite\\Excel\\ExcelServiceProvider',
+    31 => 'Carbon\\Laravel\\ServiceProvider',
+    32 => 'NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider',
+    33 => 'Overtrue\\LaravelPinyin\\ServiceProvider',
+    34 => 'Te7aHoudini\\LaravelTrix\\LaravelTrixServiceProvider',
+    35 => 'Yajra\\Oci8\\Oci8ServiceProvider',
+    36 => 'App\\Providers\\AppServiceProvider',
+    37 => 'App\\Providers\\AuthServiceProvider',
+    38 => 'App\\Providers\\BroadcastServiceProvider',
+    39 => 'App\\Providers\\EventServiceProvider',
+    40 => 'App\\Providers\\HorizonServiceProvider',
+    41 => 'App\\Providers\\RouteServiceProvider',
   ),
   'eager' => 
   array (
@@ -59,18 +61,20 @@
     12 => 'Facade\\Ignition\\IgnitionServiceProvider',
     13 => 'Fideloper\\Proxy\\TrustedProxyServiceProvider',
     14 => 'Intervention\\Image\\ImageServiceProvider',
-    15 => 'Laravel\\Ui\\UiServiceProvider',
-    16 => 'Maatwebsite\\Excel\\ExcelServiceProvider',
-    17 => 'Carbon\\Laravel\\ServiceProvider',
-    18 => 'NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider',
-    19 => 'Overtrue\\LaravelPinyin\\ServiceProvider',
-    20 => 'Te7aHoudini\\LaravelTrix\\LaravelTrixServiceProvider',
-    21 => 'Yajra\\Oci8\\Oci8ServiceProvider',
-    22 => 'App\\Providers\\AppServiceProvider',
-    23 => 'App\\Providers\\AuthServiceProvider',
-    24 => 'App\\Providers\\BroadcastServiceProvider',
-    25 => 'App\\Providers\\EventServiceProvider',
-    26 => 'App\\Providers\\RouteServiceProvider',
+    15 => 'Laravel\\Horizon\\HorizonServiceProvider',
+    16 => 'Laravel\\Ui\\UiServiceProvider',
+    17 => 'Maatwebsite\\Excel\\ExcelServiceProvider',
+    18 => 'Carbon\\Laravel\\ServiceProvider',
+    19 => 'NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider',
+    20 => 'Overtrue\\LaravelPinyin\\ServiceProvider',
+    21 => 'Te7aHoudini\\LaravelTrix\\LaravelTrixServiceProvider',
+    22 => 'Yajra\\Oci8\\Oci8ServiceProvider',
+    23 => 'App\\Providers\\AppServiceProvider',
+    24 => 'App\\Providers\\AuthServiceProvider',
+    25 => 'App\\Providers\\BroadcastServiceProvider',
+    26 => 'App\\Providers\\EventServiceProvider',
+    27 => 'App\\Providers\\HorizonServiceProvider',
+    28 => 'App\\Providers\\RouteServiceProvider',
   ),
   'deferred' => 
   array (

+ 25 - 0
ci/executeCommand.sh

@@ -0,0 +1,25 @@
+#!/bin/bash
+
+
+update_path=/var/www/update
+#重启队列
+#sudo bash runServes.sh
+
+
+
+# shellcheck disable=SC2045
+# shellcheck disable=SC2006
+for file in `ls $update_path`
+     do
+        if [ "composer.json" = "$file" ]; then
+            sudo composer install
+        elif [ "package.json" == "$file" ]; then
+            sudo yarn install
+        elif [ "resources" == "$file" ]; then
+            sudo yarn run prod
+        elif [ "database" == "$file" ]; then
+            sudo php artisan migrate
+        fi
+    done
+
+

+ 35 - 0
ci/judgePush.sh

@@ -0,0 +1,35 @@
+#!/bin/bash
+
+#push 变动文件目录
+update_path=/var/www/update
+#git 文件路径
+path=$1
+# shellcheck disable=SC2164
+cd "$update_path"
+# shellcheck disable=SC2035
+rm -rf *
+
+#将项目文件中3分钟之前改动的文件存到update文件
+# shellcheck disable=SC2038
+# shellcheck disable=SC2185
+#find -mmin -3 |grep -v .git|xargs -I {} cp -r {} -u -t $update_path
+#find -mtime -1 |grep -v .git|grep -v vendor|xargs -I {} cp -r {} /var/www/update/
+
+# shellcheck disable=SC2045
+# shellcheck disable=SC2006
+#for file in `ls $update_path`
+#     do
+#        if [ "composer.json" = "$file" ]; then
+#            sudo composer install
+#        fi
+#    done
+
+for menu in `cd "$path" && find -type f -mmin -5 |grep -v .git|grep -v vendor`
+    do
+    #判断文件夹是否存在
+        if [ $(dirname "${menu#./}") != "." ]; then
+             mkdir -p $(dirname "${menu#./}") && cp "$path"/"${menu#./}" /var/www/update/$(dirname "${menu#./}")
+            else
+                cp -r "$path"/"${menu#./}" /var/www/update
+        fi
+    done

+ 6 - 1
composer.json

@@ -22,6 +22,7 @@
         "intervention/image": "^2.5",
         "kitetail/zttp": "^0.6.0",
         "laravel/framework": "7.*",
+        "laravel/horizon": "^4.3",
         "laravel/tinker": "^2.0",
         "laravel/ui": "^2.0",
         "maatwebsite/excel": "^3.1",
@@ -44,7 +45,11 @@
     "config": {
         "optimize-autoloader": true,
         "preferred-install": "dist",
-        "sort-packages": true
+        "sort-packages": true,
+        "platform": {
+            "ext-pcntl": "7.2",
+            "ext-posix": "7.2"
+        }
     },
     "extra": {
         "laravel": {

文件差異過大導致無法顯示
+ 312 - 164
composer.lock


+ 1 - 0
config/app.php

@@ -173,6 +173,7 @@ return [
         App\Providers\AuthServiceProvider::class,
          App\Providers\BroadcastServiceProvider::class,
         App\Providers\EventServiceProvider::class,
+        App\Providers\HorizonServiceProvider::class,
         App\Providers\RouteServiceProvider::class,
     ],
 

+ 5 - 0
config/haiRou.php

@@ -0,0 +1,5 @@
+<?php
+
+return [
+    "波次防重叠时间_秒" => 1200,
+];

+ 192 - 0
config/horizon.php

@@ -0,0 +1,192 @@
+<?php
+
+use Illuminate\Support\Str;
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | Horizon Domain
+    |--------------------------------------------------------------------------
+    |
+    | This is the subdomain where Horizon will be accessible from. If this
+    | setting is null, Horizon will reside under the same domain as the
+    | application. Otherwise, this value will serve as the subdomain.
+    |
+    */
+
+    'domain' => null,
+
+    /*
+    |--------------------------------------------------------------------------
+    | Horizon Path
+    |--------------------------------------------------------------------------
+    |
+    | This is the URI path where Horizon will be accessible from. Feel free
+    | to change this path to anything you like. Note that the URI will not
+    | affect the paths of its internal API that aren't exposed to users.
+    |
+    */
+
+    'path' => 'horizon',
+
+    /*
+    |--------------------------------------------------------------------------
+    | Horizon Redis Connection
+    |--------------------------------------------------------------------------
+    |
+    | This is the name of the Redis connection where Horizon will store the
+    | meta information required for it to function. It includes the list
+    | of supervisors, failed jobs, job metrics, and other information.
+    |
+    */
+
+    'use' => 'default',
+
+    /*
+    |--------------------------------------------------------------------------
+    | Horizon Redis Prefix
+    |--------------------------------------------------------------------------
+    |
+    | This prefix will be used when storing all Horizon data in Redis. You
+    | may modify the prefix when you are running multiple installations
+    | of Horizon on the same server so that they don't have problems.
+    |
+    */
+
+    'prefix' => env(
+        'HORIZON_PREFIX',
+        Str::slug(env('APP_NAME', 'laravel'), '_').'_horizon:'
+    ),
+
+    /*
+    |--------------------------------------------------------------------------
+    | Horizon Route Middleware
+    |--------------------------------------------------------------------------
+    |
+    | These middleware will get attached onto each Horizon route, giving you
+    | the chance to add your own middleware to this list or change any of
+    | the existing middleware. Or, you can simply stick with this list.
+    |
+    */
+
+    'middleware' => ['web'],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Queue Wait Time Thresholds
+    |--------------------------------------------------------------------------
+    |
+    | This option allows you to configure when the LongWaitDetected event
+    | will be fired. Every connection / queue combination may have its
+    | own, unique threshold (in seconds) before this event is fired.
+    |
+    */
+
+    'waits' => [
+        'redis:default' => 60,
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Job Trimming Times
+    |--------------------------------------------------------------------------
+    |
+    | Here you can configure for how long (in minutes) you desire Horizon to
+    | persist the recent and failed jobs. Typically, recent jobs are kept
+    | for one hour while all failed jobs are stored for an entire week.
+    |
+    */
+
+    'trim' => [
+        'recent' => 60,
+        'pending' => 60,
+        'completed' => 60,
+        'recent_failed' => 10080,
+        'failed' => 10080,
+        'monitored' => 10080,
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Metrics
+    |--------------------------------------------------------------------------
+    |
+    | Here you can configure how many snapshots should be kept to display in
+    | the metrics graph. This will get used in combination with Horizon's
+    | `horizon:snapshot` schedule to define how long to retain metrics.
+    |
+    */
+
+    'metrics' => [
+        'trim_snapshots' => [
+            'job' => 24,
+            'queue' => 24,
+        ],
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Fast Termination
+    |--------------------------------------------------------------------------
+    |
+    | When this option is enabled, Horizon's "terminate" command will not
+    | wait on all of the workers to terminate unless the --wait option
+    | is provided. Fast termination can shorten deployment delay by
+    | allowing a new instance of Horizon to start while the last
+    | instance will continue to terminate each of its workers.
+    |
+    */
+
+    'fast_termination' => false,
+
+    /*
+    |--------------------------------------------------------------------------
+    | Memory Limit (MB)
+    |--------------------------------------------------------------------------
+    |
+    | This value describes the maximum amount of memory the Horizon worker
+    | may consume before it is terminated and restarted. You should set
+    | this value according to the resources available to your server.
+    |
+    */
+
+    'memory_limit' => 64,
+
+    /*
+    |--------------------------------------------------------------------------
+    | Queue Worker Configuration
+    |--------------------------------------------------------------------------
+    |
+    | Here you may define the queue worker settings used by your application
+    | in all environments. These supervisors and settings handle all your
+    | queued jobs and will be provisioned by Horizon during deployment.
+    |
+    */
+
+    'environments' => [
+        'production' => [
+            'supervisor-1' => [
+                'connection' => 'redis',
+                'queue' => ['default'],
+                'balance' => 'auto',
+                'minProcesses' => 2,
+                'maxProcesses' => 25,
+                'balanceMaxShift' => 1,
+                'balanceCooldown' => 3,
+                'tries' => 3,
+            ],
+        ],
+
+        'local' => [
+            'supervisor-1' => [
+                'connection' => 'redis',
+                'queue' => ['default'],
+                'balance' => 'simple',
+                'processes' => 3,
+                'tries' => 1,
+                'nice' => 0,
+            ],
+        ],
+    ],
+];

+ 304 - 0
config/jwt.php

@@ -0,0 +1,304 @@
+<?php
+
+/*
+ * This file is part of jwt-auth.
+ *
+ * (c) Sean Tymon <tymon148@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | JWT Authentication Secret
+    |--------------------------------------------------------------------------
+    |
+    | Don't forget to set this in your .env file, as it will be used to sign
+    | your tokens. A helper command is provided for this:
+    | `php artisan jwt:secret`
+    |
+    | Note: This will be used for Symmetric algorithms only (HMAC),
+    | since RSA and ECDSA use a private/public key combo (See below).
+    |
+    */
+
+    'secret' => env('JWT_SECRET'),
+
+    /*
+    |--------------------------------------------------------------------------
+    | JWT Authentication Keys
+    |--------------------------------------------------------------------------
+    |
+    | The algorithm you are using, will determine whether your tokens are
+    | signed with a random string (defined in `JWT_SECRET`) or using the
+    | following public & private keys.
+    |
+    | Symmetric Algorithms:
+    | HS256, HS384 & HS512 will use `JWT_SECRET`.
+    |
+    | Asymmetric Algorithms:
+    | RS256, RS384 & RS512 / ES256, ES384 & ES512 will use the keys below.
+    |
+    */
+
+    'keys' => [
+
+        /*
+        |--------------------------------------------------------------------------
+        | Public Key
+        |--------------------------------------------------------------------------
+        |
+        | A path or resource to your public key.
+        |
+        | E.g. 'file://path/to/public/key'
+        |
+        */
+
+        'public' => env('JWT_PUBLIC_KEY'),
+
+        /*
+        |--------------------------------------------------------------------------
+        | Private Key
+        |--------------------------------------------------------------------------
+        |
+        | A path or resource to your private key.
+        |
+        | E.g. 'file://path/to/private/key'
+        |
+        */
+
+        'private' => env('JWT_PRIVATE_KEY'),
+
+        /*
+        |--------------------------------------------------------------------------
+        | Passphrase
+        |--------------------------------------------------------------------------
+        |
+        | The passphrase for your private key. Can be null if none set.
+        |
+        */
+
+        'passphrase' => env('JWT_PASSPHRASE'),
+
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | JWT time to live
+    |--------------------------------------------------------------------------
+    |
+    | Specify the length of time (in minutes) that the token will be valid for.
+    | Defaults to 1 hour.
+    |
+    | You can also set this to null, to yield a never expiring token.
+    | Some people may want this behaviour for e.g. a mobile app.
+    | This is not particularly recommended, so make sure you have appropriate
+    | systems in place to revoke the token if necessary.
+    | Notice: If you set this to null you should remove 'exp' element from 'required_claims' list.
+    |
+    */
+
+    'ttl' => env('JWT_TTL', 60),
+
+    /*
+    |--------------------------------------------------------------------------
+    | Refresh time to live
+    |--------------------------------------------------------------------------
+    |
+    | Specify the length of time (in minutes) that the token can be refreshed
+    | within. I.E. The user can refresh their token within a 2 week window of
+    | the original token being created until they must re-authenticate.
+    | Defaults to 2 weeks.
+    |
+    | You can also set this to null, to yield an infinite refresh time.
+    | Some may want this instead of never expiring tokens for e.g. a mobile app.
+    | This is not particularly recommended, so make sure you have appropriate
+    | systems in place to revoke the token if necessary.
+    |
+    */
+
+    'refresh_ttl' => env('JWT_REFRESH_TTL', 20160),
+
+    /*
+    |--------------------------------------------------------------------------
+    | JWT hashing algorithm
+    |--------------------------------------------------------------------------
+    |
+    | Specify the hashing algorithm that will be used to sign the token.
+    |
+    | See here: https://github.com/namshi/jose/tree/master/src/Namshi/JOSE/Signer/OpenSSL
+    | for possible values.
+    |
+    */
+
+    'algo' => env('JWT_ALGO', 'HS256'),
+
+    /*
+    |--------------------------------------------------------------------------
+    | Required Claims
+    |--------------------------------------------------------------------------
+    |
+    | Specify the required claims that must exist in any token.
+    | A TokenInvalidException will be thrown if any of these claims are not
+    | present in the payload.
+    |
+    */
+
+    'required_claims' => [
+        'iss',
+        'iat',
+        'exp',
+        'nbf',
+        'sub',
+        'jti',
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Persistent Claims
+    |--------------------------------------------------------------------------
+    |
+    | Specify the claim keys to be persisted when refreshing a token.
+    | `sub` and `iat` will automatically be persisted, in
+    | addition to the these claims.
+    |
+    | Note: If a claim does not exist then it will be ignored.
+    |
+    */
+
+    'persistent_claims' => [
+        // 'foo',
+        // 'bar',
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Lock Subject
+    |--------------------------------------------------------------------------
+    |
+    | This will determine whether a `prv` claim is automatically added to
+    | the token. The purpose of this is to ensure that if you have multiple
+    | authentication models e.g. `App\User` & `App\OtherPerson`, then we
+    | should prevent one authentication request from impersonating another,
+    | if 2 tokens happen to have the same id across the 2 different models.
+    |
+    | Under specific circumstances, you may want to disable this behaviour
+    | e.g. if you only have one authentication model, then you would save
+    | a little on token size.
+    |
+    */
+
+    'lock_subject' => true,
+
+    /*
+    |--------------------------------------------------------------------------
+    | Leeway
+    |--------------------------------------------------------------------------
+    |
+    | This property gives the jwt timestamp claims some "leeway".
+    | Meaning that if you have any unavoidable slight clock skew on
+    | any of your servers then this will afford you some level of cushioning.
+    |
+    | This applies to the claims `iat`, `nbf` and `exp`.
+    |
+    | Specify in seconds - only if you know you need it.
+    |
+    */
+
+    'leeway' => env('JWT_LEEWAY', 0),
+
+    /*
+    |--------------------------------------------------------------------------
+    | Blacklist Enabled
+    |--------------------------------------------------------------------------
+    |
+    | In order to invalidate tokens, you must have the blacklist enabled.
+    | If you do not want or need this functionality, then set this to false.
+    |
+    */
+
+    'blacklist_enabled' => env('JWT_BLACKLIST_ENABLED', true),
+
+    /*
+    | -------------------------------------------------------------------------
+    | Blacklist Grace Period
+    | -------------------------------------------------------------------------
+    |
+    | When multiple concurrent requests are made with the same JWT,
+    | it is possible that some of them fail, due to token regeneration
+    | on every request.
+    |
+    | Set grace period in seconds to prevent parallel request failure.
+    |
+    */
+
+    'blacklist_grace_period' => env('JWT_BLACKLIST_GRACE_PERIOD', 0),
+
+    /*
+    |--------------------------------------------------------------------------
+    | Cookies encryption
+    |--------------------------------------------------------------------------
+    |
+    | By default Laravel encrypt cookies for security reason.
+    | If you decide to not decrypt cookies, you will have to configure Laravel
+    | to not encrypt your cookie token by adding its name into the $except
+    | array available in the middleware "EncryptCookies" provided by Laravel.
+    | see https://laravel.com/docs/master/responses#cookies-and-encryption
+    | for details.
+    |
+    | Set it to true if you want to decrypt cookies.
+    |
+    */
+
+    'decrypt_cookies' => false,
+
+    /*
+    |--------------------------------------------------------------------------
+    | Providers
+    |--------------------------------------------------------------------------
+    |
+    | Specify the various providers used throughout the package.
+    |
+    */
+
+    'providers' => [
+
+        /*
+        |--------------------------------------------------------------------------
+        | JWT Provider
+        |--------------------------------------------------------------------------
+        |
+        | Specify the provider that is used to create and decode the tokens.
+        |
+        */
+
+        'jwt' => Tymon\JWTAuth\Providers\JWT\Lcobucci::class,
+
+        /*
+        |--------------------------------------------------------------------------
+        | Authentication Provider
+        |--------------------------------------------------------------------------
+        |
+        | Specify the provider that is used to authenticate users.
+        |
+        */
+
+        'auth' => Tymon\JWTAuth\Providers\Auth\Illuminate::class,
+
+        /*
+        |--------------------------------------------------------------------------
+        | Storage Provider
+        |--------------------------------------------------------------------------
+        |
+        | Specify the provider that is used to store tokens in the blacklist.
+        |
+        */
+
+        'storage' => Tymon\JWTAuth\Providers\Storage\Illuminate::class,
+
+    ],
+
+];

+ 7 - 0
config/sync,php.php

@@ -0,0 +1,7 @@
+<?php
+return [
+    'order_tracking_import' => [
+        'import' => 20,
+        'start_at'=> '',
+    ],
+];

+ 1 - 0
database/factories/OracleActAllocationDetailsFactory.php

@@ -7,6 +7,7 @@ use Faker\Generator as Faker;
 
 $factory->define(OracleActAllocationDetails::class, function (Faker $faker) {
     return [
+        'allocationdetailsid'=>$faker->uuid,
         'orderno' => $faker->uuid,
         'customerid' => $faker->name,
         'sku'=>$faker->name,

+ 14 - 0
database/factories/OracleDocOrderSerialNoFactory.php

@@ -0,0 +1,14 @@
+<?php
+
+/** @var \Illuminate\Database\Eloquent\Factory $factory */
+
+use App\OracleDocOrderSerialNo;
+use Faker\Generator as Faker;
+
+$factory->define(OracleDocOrderSerialNo::class, function (Faker $faker) {
+    return [
+        'serialno' => $faker->uuid,
+        'sku' => $faker->name,
+        'allocationdetailsid' => $faker->uuid,
+    ];
+});

+ 12 - 0
database/factories/OrderPackageCommoditySerialNumberFactory.php

@@ -0,0 +1,12 @@
+<?php
+
+/** @var \Illuminate\Database\Eloquent\Factory $factory */
+
+use App\OrderPackageCommoditySerialNumber;
+use Faker\Generator as Faker;
+
+$factory->define(OrderPackageCommoditySerialNumber::class, function (Faker $faker) {
+    return [
+        //
+    ];
+});

+ 15 - 0
database/factories/StationCacheShelfGridFactory.php

@@ -0,0 +1,15 @@
+<?php
+
+/** @var \Illuminate\Database\Eloquent\Factory $factory */
+
+use App\StationCacheShelfGrid;
+use Faker\Generator as Faker;
+
+$factory->define(StationCacheShelfGrid::class, function (Faker $faker) {
+    return [
+        'station_id' => $faker->numberBetween(0,1),
+        'material_box_id' => $faker->numberBetween(0,100),
+        'grid_id' => 1,
+        'status' => 0
+    ];
+});

+ 2 - 1
database/factories/StationFactory.php

@@ -7,6 +7,7 @@ use Faker\Generator as Faker;
 
 $factory->define(Station::class, function (Faker $faker) {
     return [
-        //
+        'name' => $faker->name,
+        'code' => $faker->name,
     ];
 });

+ 69 - 0
database/migrations/2021_04_13_140632_add_station_type_and_add_station.php

@@ -0,0 +1,69 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use App\Station;
+use App\StationType;
+use App\Authority;
+
+class AddStationTypeAndAddStation extends Migration
+{
+
+    public $stationTypeNames = ['缓存架'];
+
+    public $stationNames = ['缓存架01', '缓存架02'];
+
+    public $authorityName = ['站管理-缓存架'];
+
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        foreach ($this->stationTypeNames as $name) {
+            if (StationType::query()->where('name', $name)->exists()) continue;
+            StationType::query()->create(['name' => $name]);
+        }
+
+        $stationType = StationType::query()->where('name', '缓存架')->first();
+        foreach ($this->stationNames as $stationName) {
+            if (Station::query()->where('name', $stationName)->exists()) continue;
+            Station::query()->create([
+                'name' => $stationName,
+                'code' => $stationName,
+                'station_type_id' => $stationType['id'],
+            ]);
+        }
+
+        foreach ($this->authorityName as $name) {
+            if(Authority::query()->where('name',$name)->exists())continue;
+            Authority::query()->create(['name'=>$name,
+                'alias_name'=>$name]);
+        }
+
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        foreach ($this->stationNames as $stationName) {
+            if (!Station::query()->where('name', $stationName)->exists()) continue;
+            Station::query()->where('name', $stationName)->delete();
+        }
+
+        foreach ($this->stationTypeNames as $name) {
+            if (!Station::query()->where('name', $name)->exists()) continue;
+            Station::query()->where('name', $name)->delete();
+        }
+
+        foreach ($this->authorityName as $name) {
+            if(!Authority::query()->where('name',$name)->exists())continue;
+            Authority::query()->where('name',$name)->delete();
+        }
+    }
+}

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

@@ -0,0 +1,35 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+class CreateStationCacheShelfGridsTable extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::create('station_cache_shelf_grids', function (Blueprint $table) {
+            $table->id();
+            $table->bigInteger('station_id')->comment('站');
+            $table->bigInteger('material_box_id')->nullable()->comment('料箱');
+            $table->tinyInteger('grid_id')->comment('编号');
+            $table->tinyInteger('status')->comment('状态');
+            $table->timestamps();
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::dropIfExists('station_cache_shelf_grids');
+    }
+}

+ 33 - 0
database/migrations/2021_04_23_174700_create_order_package_commodity_serial_numbers_table.php

@@ -0,0 +1,33 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+class CreateOrderPackageCommoditySerialNumbersTable extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::create('order_package_commodity_serial_numbers', function (Blueprint $table) {
+            $table->id();
+            $table->bigInteger('order_package_commodity_id')->comment('快递包裹');
+            $table->string('serial_number')->unique()->default(null)->comment('序列号');
+            $table->timestamps();
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::dropIfExists('order_package_commodity_serial_numbers');
+    }
+}

+ 32 - 0
database/migrations/2021_04_25_185620_change_index_of_station_task_batches_field_batch_id.php

@@ -0,0 +1,32 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+class ChangeIndexOfStationTaskBatchesFieldBatchId extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::table('station_task_batches', function (Blueprint $table) {
+            $table->unique(['batch_id','status'],'s_t_b_unique_batch_id_status');
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::table('station_task_batches', function (Blueprint $table) {
+            $table->dropColumn('s_t_b_unique_batch_id_status');
+        });
+    }
+}

+ 16 - 0
database/seeds/OrderPackageCommoditySerialNumberSeeder.php

@@ -0,0 +1,16 @@
+<?php
+
+use Illuminate\Database\Seeder;
+
+class OrderPackageCommoditySerialNumberSeeder extends Seeder
+{
+    /**
+     * Run the database seeds.
+     *
+     * @return void
+     */
+    public function run()
+    {
+        //
+    }
+}

+ 16 - 0
database/seeds/StationCacheShelfGridSeeder.php

@@ -0,0 +1,16 @@
+<?php
+
+use Illuminate\Database\Seeder;
+
+class StationCacheShelfGridSeeder extends Seeder
+{
+    /**
+     * Run the database seeds.
+     *
+     * @return void
+     */
+    public function run()
+    {
+        //
+    }
+}

文件差異過大導致無法顯示
+ 7 - 0
public/vendor/horizon/app-dark.css


文件差異過大導致無法顯示
+ 7 - 0
public/vendor/horizon/app.css


文件差異過大導致無法顯示
+ 1 - 0
public/vendor/horizon/app.js


二進制
public/vendor/horizon/img/favicon.png


文件差異過大導致無法顯示
+ 1 - 0
public/vendor/horizon/img/horizon.svg


+ 806 - 0
public/vendor/horizon/img/sprite.svg

@@ -0,0 +1,806 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <symbol viewBox="0 0 20 20" id="zondicon-add-outline">
+        <path d="M11 9h4v2h-4v4H9v-4H5V9h4V5h2v4zm-1 11a10 10 0 1 1 0-20 10 10 0 0 1 0 20zm0-2a8 8 0 1 0 0-16 8 8 0 0 0 0 16z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-add-solid">
+        <path d="M11 9V5H9v4H5v2h4v4h2v-4h4V9h-4zm-1 11a10 10 0 1 1 0-20 10 10 0 0 1 0 20z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-adjust">
+        <path d="M10 2v16a8 8 0 1 0 0-16zm0 18a10 10 0 1 1 0-20 10 10 0 0 1 0 20z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-airplane">
+        <path d="M8.4 12H2.8L1 15H0V5h1l1.8 3h5.6L6 0h2l4.8 8H18a2 2 0 1 1 0 4h-5.2L8 20H6l2.4-8z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-align-center">
+        <path d="M1 1h18v2H1V1zm0 8h18v2H1V9zm0 8h18v2H1v-2zM4 5h12v2H4V5zm0 8h12v2H4v-2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-align-justified">
+        <path d="M1 1h18v2H1V1zm0 8h18v2H1V9zm0 8h18v2H1v-2zM1 5h18v2H1V5zm0 8h18v2H1v-2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-align-left">
+        <path d="M1 1h18v2H1V1zm0 8h18v2H1V9zm0 8h18v2H1v-2zM1 5h12v2H1V5zm0 8h12v2H1v-2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-align-right">
+        <path d="M1 1h18v2H1V1zm0 8h18v2H1V9zm0 8h18v2H1v-2zM7 5h12v2H7V5zm0 8h12v2H7v-2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-anchor">
+        <path d="M4.34 15.66A7.97 7.97 0 0 0 9 17.94V10H5V8h4V5.83a3 3 0 1 1 2 0V8h4v2h-4v7.94a7.97 7.97 0 0 0 4.66-2.28l-1.42-1.42h5.66l-2.83 2.83a10 10 0 0 1-14.14 0L.1 14.24h5.66l-1.42 1.42zM10 4a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-announcement">
+        <path d="M3 6c0-1.1.9-2 2-2h8l4-4h2v16h-2l-4-4H5a2 2 0 0 1-2-2H1V6h2zm8 9v5H8l-1.67-5H5v-2h8v2h-2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-apparel">
+        <path d="M7 0H6L0 3v6l4-1v12h12V8l4 1V3l-6-3h-1a3 3 0 0 1-6 0z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-arrow-outline-down">
+        <path d="M10 20a10 10 0 1 1 0-20 10 10 0 0 1 0 20zm0-2a8 8 0 1 0 0-16 8 8 0 0 0 0 16zm-2-8V5h4v5h3l-5 5-5-5h3z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-arrow-outline-left">
+        <path d="M0 10a10 10 0 1 1 20 0 10 10 0 0 1-20 0zm2 0a8 8 0 1 0 16 0 8 8 0 0 0-16 0zm8-2h5v4h-5v3l-5-5 5-5v3z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-arrow-outline-right">
+        <path d="M20 10a10 10 0 1 1-20 0 10 10 0 0 1 20 0zm-2 0a8 8 0 1 0-16 0 8 8 0 0 0 16 0zm-8 2H5V8h5V5l5 5-5 5v-3z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-arrow-outline-up">
+        <path d="M10 0a10 10 0 1 1 0 20 10 10 0 0 1 0-20zm0 2a8 8 0 1 0 0 16 8 8 0 0 0 0-16zm2 8v5H8v-5H5l5-5 5 5h-3z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-arrow-thick-down">
+        <path d="M7 10V2h6v8h5l-8 8-8-8h5z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-arrow-thick-left">
+        <path d="M10 13h8V7h-8V2l-8 8 8 8v-5z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-arrow-thick-right">
+        <path d="M10 7H2v6h8v5l8-8-8-8v5z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-arrow-thick-up">
+        <path d="M7 10v8h6v-8h5l-8-8-8 8h5z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-arrow-thin-down">
+        <path d="M9 16.172l-6.071-6.071-1.414 1.414L10 20l.707-.707 7.778-7.778-1.414-1.414L11 16.172V0H9z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-arrow-thin-left">
+        <path d="M3.828 9l6.071-6.071-1.414-1.414L0 10l.707.707 7.778 7.778 1.414-1.414L3.828 11H20V9H3.828z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-arrow-thin-right">
+        <path d="M16.172 9l-6.071-6.071 1.414-1.414L20 10l-.707.707-7.778 7.778-1.414-1.414L16.172 11H0V9z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-arrow-thin-up">
+        <path d="M9 3.828L2.929 9.899 1.515 8.485 10 0l.707.707 7.778 7.778-1.414 1.414L11 3.828V20H9V3.828z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-at-symbol">
+        <path d="M13.6 13.47A4.99 4.99 0 0 1 5 10a5 5 0 0 1 8-4V5h2v6.5a1.5 1.5 0 0 0 3 0V10a8 8 0 1 0-4.42 7.16l.9 1.79A10 10 0 1 1 20 10h-.18.17v1.5a3.5 3.5 0 0 1-6.4 1.97zM10 13a3 3 0 1 0 0-6 3 3 0 0 0 0 6z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-attachment">
+        <path d="M15 3H7a7 7 0 1 0 0 14h8v-2H7A5 5 0 0 1 7 5h8a3 3 0 0 1 0 6H7a1 1 0 0 1 0-2h8V7H7a3 3 0 1 0 0 6h8a5 5 0 0 0 0-10z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-backspace">
+        <path d="M0 10l7-7h13v14H7l-7-7zm14.41 0l2.13-2.12-1.42-1.42L13 8.6l-2.12-2.13-1.42 1.42L11.6 10l-2.13 2.12 1.42 1.42L13 11.4l2.12 2.13 1.42-1.42L14.4 10z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-badge">
+        <path d="M10 12a6 6 0 1 1 0-12 6 6 0 0 1 0 12zm0-3a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm4 2.75V20l-4-4-4 4v-8.25a6.97 6.97 0 0 0 8 0z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-battery-full">
+        <path d="M0 6c0-1.1.9-2 2-2h16a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V6zm2 0v8h16V6H2zm1 1h4v6H3V7zm5 0h4v6H8V7zm5 0h4v6h-4V7z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-battery-half">
+        <path d="M0 6c0-1.1.9-2 2-2h16a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V6zm2 0v8h16V6H2zm1 1h4v6H3V7zm5 0h4v6H8V7z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-battery-low">
+        <path d="M0 6c0-1.1.9-2 2-2h16a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V6zm2 0v8h16V6H2zm1 1h4v6H3V7z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-beverage">
+        <path d="M9 18v-7L0 2V0h20v2l-9 9v7l5 1v1H4v-1l5-1zm2-10a2 2 0 1 0 0-4 2 2 0 0 0 0 4z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-block">
+        <path d="M0 10a10 10 0 1 1 20 0 10 10 0 0 1-20 0zm16.32-4.9L5.09 16.31A8 8 0 0 0 16.32 5.09zm-1.41-1.42A8 8 0 0 0 3.68 14.91L14.91 3.68z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-bluetooth">
+        <path d="M9.41 0l6 6-4 4 4 4-6 6H9v-7.59l-3.3 3.3-1.4-1.42L8.58 10l-4.3-4.3L5.7 4.3 9 7.58V0h.41zM11 4.41V7.6L12.59 6 11 4.41zM12.59 14L11 12.41v3.18L12.59 14z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-bolt">
+        <path d="M13 8V0L8.11 5.87 3 12h4v8L17 8h-4z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-book-reference">
+        <path d="M6 4H5a1 1 0 1 1 0-2h11V1a1 1 0 0 0-1-1H4a2 2 0 0 0-2 2v16c0 1.1.9 2 2 2h12a2 2 0 0 0 2-2V5a1 1 0 0 0-1-1h-7v8l-2-2-2 2V4z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-bookmark">
+        <path d="M2 2c0-1.1.9-2 2-2h12a2 2 0 0 1 2 2v18l-8-4-8 4V2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-bookmark-outline">
+        <path d="M2 2c0-1.1.9-2 2-2h12a2 2 0 0 1 2 2v18l-8-4-8 4V2zm2 0v15l6-3 6 3V2H4z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-bookmark-outline-add">
+        <path d="M2 2c0-1.1.9-2 2-2h12a2 2 0 0 1 2 2v18l-8-4-8 4V2zm2 0v15l6-3 6 3V2H4zm5 5V5h2v2h2v2h-2v2H9V9H7V7h2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-border-all">
+        <path d="M11 11v6h6v-6h-6zm0-2h6V3h-6v6zm-2 2H3v6h6v-6zm0-2V3H3v6h6zm-8 9V1h18v18H1v-1z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-border-bottom">
+        <path d="M1 1h2v2H1V1zm0 4h2v2H1V5zm0 4h2v2H1V9zm0 4h2v2H1v-2zm0 4h18v2H1v-2zM5 1h2v2H5V1zm0 8h2v2H5V9zm4-8h2v2H9V1zm0 4h2v2H9V5zm0 4h2v2H9V9zm0 4h2v2H9v-2zm4-12h2v2h-2V1zm0 8h2v2h-2V9zm4-8h2v2h-2V1zm0 4h2v2h-2V5zm0 4h2v2h-2V9zm0 4h2v2h-2v-2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-border-horizontal">
+        <path d="M1 1h2v2H1V1zm0 4h2v2H1V5zm0 4h18v2H1V9zm0 4h2v2H1v-2zm0 4h2v2H1v-2zM5 1h2v2H5V1zm0 16h2v2H5v-2zM9 1h2v2H9V1zm0 4h2v2H9V5zm0 8h2v2H9v-2zm0 4h2v2H9v-2zm4-16h2v2h-2V1zm0 16h2v2h-2v-2zm4-16h2v2h-2V1zm0 4h2v2h-2V5zm0 8h2v2h-2v-2zm0 4h2v2h-2v-2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-border-inner">
+        <path d="M9 9V1h2v8h8v2h-8v8H9v-8H1V9h8zM1 1h2v2H1V1zm0 4h2v2H1V5zm0 8h2v2H1v-2zm0 4h2v2H1v-2zM5 1h2v2H5V1zm0 16h2v2H5v-2zm8-16h2v2h-2V1zm0 16h2v2h-2v-2zm4-16h2v2h-2V1zm0 4h2v2h-2V5zm0 8h2v2h-2v-2zm0 4h2v2h-2v-2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-border-left">
+        <path d="M1 1h2v18H1V1zm4 0h2v2H5V1zm0 8h2v2H5V9zm0 8h2v2H5v-2zM9 1h2v2H9V1zm0 4h2v2H9V5zm0 4h2v2H9V9zm0 4h2v2H9v-2zm0 4h2v2H9v-2zm4-16h2v2h-2V1zm0 8h2v2h-2V9zm0 8h2v2h-2v-2zm4-16h2v2h-2V1zm0 4h2v2h-2V5zm0 4h2v2h-2V9zm0 4h2v2h-2v-2zm0 4h2v2h-2v-2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-border-none">
+        <path d="M1 1h2v2H1V1zm0 4h2v2H1V5zm0 4h2v2H1V9zm0 4h2v2H1v-2zm0 4h2v2H1v-2zM5 1h2v2H5V1zm0 8h2v2H5V9zm0 8h2v2H5v-2zM9 1h2v2H9V1zm0 4h2v2H9V5zm0 4h2v2H9V9zm0 4h2v2H9v-2zm0 4h2v2H9v-2zm4-16h2v2h-2V1zm0 8h2v2h-2V9zm0 8h2v2h-2v-2zm4-16h2v2h-2V1zm0 4h2v2h-2V5zm0 4h2v2h-2V9zm0 4h2v2h-2v-2zm0 4h2v2h-2v-2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-border-outer">
+        <path d="M2 19H1V1h18v18H2zm1-2h14V3H3v14zm10-8h2v2h-2V9zM9 9h2v2H9V9zM5 9h2v2H5V9zm4-4h2v2H9V5zm0 8h2v2H9v-2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-border-right">
+        <path d="M5 1h2v2H5V1zm0 8h2v2H5V9zm0 8h2v2H5v-2zM9 1h2v2H9V1zm0 4h2v2H9V5zm0 4h2v2H9V9zm0 4h2v2H9v-2zm0 4h2v2H9v-2zm4-16h2v2h-2V1zm0 8h2v2h-2V9zm0 8h2v2h-2v-2zM1 1h2v2H1V1zm0 4h2v2H1V5zm0 4h2v2H1V9zm0 4h2v2H1v-2zm0 4h2v2H1v-2zM17 1h2v18h-2V1z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-border-top">
+        <path d="M1 1h18v2H1V1zm0 4h2v2H1V5zm0 4h2v2H1V9zm0 4h2v2H1v-2zm0 4h2v2H1v-2zm4-8h2v2H5V9zm0 8h2v2H5v-2zM9 5h2v2H9V5zm0 4h2v2H9V9zm0 4h2v2H9v-2zm0 4h2v2H9v-2zm4-8h2v2h-2V9zm0 8h2v2h-2v-2zm4-12h2v2h-2V5zm0 4h2v2h-2V9zm0 4h2v2h-2v-2zm0 4h2v2h-2v-2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-border-vertical">
+        <path d="M1 1h2v2H1V1zm0 4h2v2H1V5zm0 4h2v2H1V9zm0 4h2v2H1v-2zm0 4h2v2H1v-2zM5 1h2v2H5V1zm0 8h2v2H5V9zm0 8h2v2H5v-2zM9 1h2v18H9V1zm4 0h2v2h-2V1zm0 8h2v2h-2V9zm0 8h2v2h-2v-2zm4-16h2v2h-2V1zm0 4h2v2h-2V5zm0 4h2v2h-2V9zm0 4h2v2h-2v-2zm0 4h2v2h-2v-2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-box">
+        <path d="M0 2C0 .9.9 0 2 0h16a2 2 0 0 1 2 2v2H0V2zm1 3h18v13a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V5zm6 2v2h6V7H7z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-brightness-down">
+        <path d="M10 13a3 3 0 1 1 0-6 3 3 0 0 1 0 6zM9 4a1 1 0 1 1 2 0 1 1 0 1 1-2 0zm4.54 1.05a1 1 0 1 1 1.41 1.41 1 1 0 1 1-1.41-1.41zM16 9a1 1 0 1 1 0 2 1 1 0 1 1 0-2zm-1.05 4.54a1 1 0 1 1-1.41 1.41 1 1 0 1 1 1.41-1.41zM11 16a1 1 0 1 1-2 0 1 1 0 1 1 2 0zm-4.54-1.05a1 1 0 1 1-1.41-1.41 1 1 0 1 1 1.41 1.41zM4 11a1 1 0 1 1 0-2 1 1 0 1 1 0 2zm1.05-4.54a1 1 0 1 1 1.41-1.41 1 1 0 1 1-1.41 1.41z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-brightness-up">
+        <path d="M10 14a4 4 0 1 1 0-8 4 4 0 0 1 0 8zM9 1a1 1 0 1 1 2 0v2a1 1 0 1 1-2 0V1zm6.65 1.94a1 1 0 1 1 1.41 1.41l-1.4 1.4a1 1 0 1 1-1.41-1.41l1.4-1.4zM18.99 9a1 1 0 1 1 0 2h-1.98a1 1 0 1 1 0-2h1.98zm-1.93 6.65a1 1 0 1 1-1.41 1.41l-1.4-1.4a1 1 0 1 1 1.41-1.41l1.4 1.4zM11 18.99a1 1 0 1 1-2 0v-1.98a1 1 0 1 1 2 0v1.98zm-6.65-1.93a1 1 0 1 1-1.41-1.41l1.4-1.4a1 1 0 1 1 1.41 1.41l-1.4 1.4zM1.01 11a1 1 0 1 1 0-2h1.98a1 1 0 1 1 0 2H1.01zm1.93-6.65a1 1 0 1 1 1.41-1.41l1.4 1.4a1 1 0 1 1-1.41 1.41l-1.4-1.4z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-browser-window">
+        <path d="M0 3c0-1.1.9-2 2-2h16a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V3zm2 2v12h16V5H2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-browser-window-new">
+        <path d="M9 10V8h2v2h2v2h-2v2H9v-2H7v-2h2zM0 3c0-1.1.9-2 2-2h16a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V3zm2 2v12h16V5H2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-browser-window-open">
+        <path d="M0 3c0-1.1.9-2 2-2h16a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V3zm2 2v12h16V5H2zm8 3l4 5H6l4-5z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-bug">
+        <path d="M15.3 14.89l2.77 2.77a1 1 0 0 1 0 1.41 1 1 0 0 1-1.41 0l-2.59-2.58A5.99 5.99 0 0 1 11 18V9.04a1 1 0 0 0-2 0V18a5.98 5.98 0 0 1-3.07-1.51l-2.59 2.58a1 1 0 0 1-1.41 0 1 1 0 0 1 0-1.41l2.77-2.77A5.95 5.95 0 0 1 4.07 13H1a1 1 0 1 1 0-2h3V8.41L.93 5.34a1 1 0 0 1 0-1.41 1 1 0 0 1 1.41 0l2.1 2.1h11.12l2.1-2.1a1 1 0 0 1 1.41 0 1 1 0 0 1 0 1.41L16 8.41V11h3a1 1 0 1 1 0 2h-3.07c-.1.67-.32 1.31-.63 1.89zM15 5H5a5 5 0 1 1 10 0z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-buoy">
+        <path d="M17.16 6.42a8.03 8.03 0 0 0-3.58-3.58l-1.34 2.69a5.02 5.02 0 0 1 2.23 2.23l2.69-1.34zm0 7.16l-2.69-1.34a5.02 5.02 0 0 1-2.23 2.23l1.34 2.69a8.03 8.03 0 0 0 3.58-3.58zM6.42 2.84a8.03 8.03 0 0 0-3.58 3.58l2.69 1.34a5.02 5.02 0 0 1 2.23-2.23L6.42 2.84zM2.84 13.58a8.03 8.03 0 0 0 3.58 3.58l1.34-2.69a5.02 5.02 0 0 1-2.23-2.23l-2.69 1.34zM10 20a10 10 0 1 1 0-20 10 10 0 0 1 0 20zm0-7a3 3 0 1 0 0-6 3 3 0 0 0 0 6z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-calculator">
+        <path d="M2 2c0-1.1.9-2 2-2h12a2 2 0 0 1 2 2v16a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V2zm3 1v2h10V3H5zm0 4v2h2V7H5zm4 0v2h2V7H9zm4 0v2h2V7h-2zm-8 4v2h2v-2H5zm4 0v2h2v-2H9zm4 0v6h2v-6h-2zm-8 4v2h2v-2H5zm4 0v2h2v-2H9z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-calendar">
+        <path d="M1 4c0-1.1.9-2 2-2h14a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V4zm2 2v12h14V6H3zm2-6h2v2H5V0zm8 0h2v2h-2V0zM5 9h2v2H5V9zm0 4h2v2H5v-2zm4-4h2v2H9V9zm0 4h2v2H9v-2zm4-4h2v2h-2V9zm0 4h2v2h-2v-2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-camera">
+        <path d="M0 6c0-1.1.9-2 2-2h3l2-2h6l2 2h3a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V6zm10 10a5 5 0 1 0 0-10 5 5 0 0 0 0 10zm0-2a3 3 0 1 1 0-6 3 3 0 0 1 0 6z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-chart">
+        <path d="M4.13 12H4a2 2 0 1 0 1.8 1.11L7.86 10a2.03 2.03 0 0 0 .65-.07l1.55 1.55a2 2 0 1 0 3.72-.37L15.87 8H16a2 2 0 1 0-1.8-1.11L12.14 10a2.03 2.03 0 0 0-.65.07L9.93 8.52a2 2 0 1 0-3.72.37L4.13 12zM0 4c0-1.1.9-2 2-2h16a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V4z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-chart-bar">
+        <path d="M1 10h3v10H1V10zM6 0h3v20H6V0zm5 8h3v12h-3V8zm5-4h3v16h-3V4z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-chart-pie">
+        <path d="M19.95 11A10 10 0 1 1 9 .05V11h10.95zm-.08-2.6H11.6V.13a10 10 0 0 1 8.27 8.27z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-chat-bubble-dots">
+        <path d="M10 15l-4 4v-4H2a2 2 0 0 1-2-2V3c0-1.1.9-2 2-2h16a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2h-8zM5 7v2h2V7H5zm4 0v2h2V7H9zm4 0v2h2V7h-2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-checkmark">
+        <path d="M0 11l2-2 5 5L18 3l2 2L7 18z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-checkmark-outline">
+        <path d="M2.93 17.07A10 10 0 1 1 17.07 2.93 10 10 0 0 1 2.93 17.07zm12.73-1.41A8 8 0 1 0 4.34 4.34a8 8 0 0 0 11.32 11.32zM6.7 9.29L9 11.6l4.3-4.3 1.4 1.42L9 14.4l-3.7-3.7 1.4-1.42z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-cheveron-down">
+        <path d="M9.293 12.95l.707.707L15.657 8l-1.414-1.414L10 10.828 5.757 6.586 4.343 8z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-cheveron-left">
+        <path d="M7.05 9.293L6.343 10 12 15.657l1.414-1.414L9.172 10l4.242-4.243L12 4.343z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-cheveron-outline-down">
+        <path d="M20 10a10 10 0 1 1-20 0 10 10 0 0 1 20 0zM10 2a8 8 0 1 0 0 16 8 8 0 0 0 0-16zm-.7 10.54L5.75 9l1.41-1.41L10 10.4l2.83-2.82L14.24 9 10 13.24l-.7-.7z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-cheveron-outline-left">
+        <path d="M10 20a10 10 0 1 1 0-20 10 10 0 0 1 0 20zm8-10a8 8 0 1 0-16 0 8 8 0 0 0 16 0zM7.46 9.3L11 5.75l1.41 1.41L9.6 10l2.82 2.83L11 14.24 6.76 10l.7-.7z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-cheveron-outline-right">
+        <path d="M10 0a10 10 0 1 1 0 20 10 10 0 0 1 0-20zM2 10a8 8 0 1 0 16 0 8 8 0 0 0-16 0zm10.54.7L9 14.25l-1.41-1.41L10.4 10 7.6 7.17 9 5.76 13.24 10l-.7.7z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-cheveron-outline-up">
+        <path d="M0 10a10 10 0 1 1 20 0 10 10 0 0 1-20 0zm10 8a8 8 0 1 0 0-16 8 8 0 0 0 0 16zm.7-10.54L14.25 11l-1.41 1.41L10 9.6l-2.83 2.8L5.76 11 10 6.76l.7.7z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-cheveron-right">
+        <path d="M12.95 10.707l.707-.707L8 4.343 6.586 5.757 10.828 10l-4.242 4.243L8 15.657l4.95-4.95z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-cheveron-up">
+        <path d="M10.707 7.05L10 6.343 4.343 12l1.414 1.414L10 9.172l4.243 4.242L15.657 12z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-clipboard">
+        <path d="M7.03 2.6a3 3 0 0 1 5.94 0L15 3v1h1a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2h1V3l2.03-.4zM5 6H4v12h12V6h-1v1H5V6zm5-2a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-close">
+        <path d="M10 8.586L2.929 1.515 1.515 2.929 8.586 10l-7.071 7.071 1.414 1.414L10 11.414l7.071 7.071 1.414-1.414L11.414 10l7.071-7.071-1.414-1.414L10 8.586z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-close-outline">
+        <path d="M2.93 17.07A10 10 0 1 1 17.07 2.93 10 10 0 0 1 2.93 17.07zm1.41-1.41A8 8 0 1 0 15.66 4.34 8 8 0 0 0 4.34 15.66zm9.9-8.49L11.41 10l2.83 2.83-1.41 1.41L10 11.41l-2.83 2.83-1.41-1.41L8.59 10 5.76 7.17l1.41-1.41L10 8.59l2.83-2.83 1.41 1.41z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-close-solid">
+        <path d="M2.93 17.07A10 10 0 1 1 17.07 2.93 10 10 0 0 1 2.93 17.07zM11.4 10l2.83-2.83-1.41-1.41L10 8.59 7.17 5.76 5.76 7.17 8.59 10l-2.83 2.83 1.41 1.41L10 11.41l2.83 2.83 1.41-1.41L11.41 10z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-cloud">
+        <path d="M16.88 9.1A4 4 0 0 1 16 17H5a5 5 0 0 1-1-9.9V7a3 3 0 0 1 4.52-2.59A4.98 4.98 0 0 1 17 8c0 .38-.04.74-.12 1.1z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-cloud-upload">
+        <path d="M16.88 9.1A4 4 0 0 1 16 17H5a5 5 0 0 1-1-9.9V7a3 3 0 0 1 4.52-2.59A4.98 4.98 0 0 1 17 8c0 .38-.04.74-.12 1.1zM11 11h3l-4-4-4 4h3v3h2v-3z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-code">
+        <path d="M.7 9.3l4.8-4.8 1.4 1.42L2.84 10l4.07 4.07-1.41 1.42L0 10l.7-.7zm18.6 1.4l.7-.7-5.49-5.49-1.4 1.42L17.16 10l-4.07 4.07 1.41 1.42 4.78-4.78z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-coffee">
+        <path d="M4 11H2a2 2 0 0 1-2-2V5c0-1.1.9-2 2-2h2V1h14v10a4 4 0 0 1-4 4H8a4 4 0 0 1-4-4zm0-2V5H2v4h2zm-2 8v-1h18v1l-4 2H6l-4-2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-cog">
+        <path d="M3.94 6.5L2.22 3.64l1.42-1.42L6.5 3.94c.52-.3 1.1-.54 1.7-.7L9 0h2l.8 3.24c.6.16 1.18.4 1.7.7l2.86-1.72 1.42 1.42-1.72 2.86c.3.52.54 1.1.7 1.7L20 9v2l-3.24.8c-.16.6-.4 1.18-.7 1.7l1.72 2.86-1.42 1.42-2.86-1.72c-.52.3-1.1.54-1.7.7L11 20H9l-.8-3.24c-.6-.16-1.18-.4-1.7-.7l-2.86 1.72-1.42-1.42 1.72-2.86c-.3-.52-.54-1.1-.7-1.7L0 11V9l3.24-.8c.16-.6.4-1.18.7-1.7zM10 13a3 3 0 1 0 0-6 3 3 0 0 0 0 6z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-color-palette">
+        <path d="M9 20v-1.7l.01-.24L15.07 12h2.94c1.1 0 1.99.89 1.99 2v4a2 2 0 0 1-2 2H9zm0-3.34V5.34l2.08-2.07a1.99 1.99 0 0 1 2.82 0l2.83 2.83a2 2 0 0 1 0 2.82L9 16.66zM0 1.99C0 .9.89 0 2 0h4a2 2 0 0 1 2 2v16a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2zM4 17a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-compose">
+        <path d="M2 4v14h14v-6l2-2v10H0V2h10L8 4H2zm10.3-.3l4 4L8 16H4v-4l8.3-8.3zm1.4-1.4L16 0l4 4-2.3 2.3-4-4z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-computer-desktop">
+        <path d="M7 17H2a2 2 0 0 1-2-2V2C0 .9.9 0 2 0h16a2 2 0 0 1 2 2v13a2 2 0 0 1-2 2h-5l4 2v1H3v-1l4-2zM2 2v11h16V2H2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-computer-laptop">
+        <path d="M18 16h2v1a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1v-1h2V4c0-1.1.9-2 2-2h12a2 2 0 0 1 2 2v12zM4 4v9h12V4H4zm4 11v1h4v-1H8z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-conversation">
+        <path d="M17 11v3l-3-3H8a2 2 0 0 1-2-2V2c0-1.1.9-2 2-2h10a2 2 0 0 1 2 2v7a2 2 0 0 1-2 2h-1zm-3 2v2a2 2 0 0 1-2 2H6l-3 3v-3H2a2 2 0 0 1-2-2V8c0-1.1.9-2 2-2h2v3a4 4 0 0 0 4 4h6z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-credit-card">
+        <path d="M18 6V4H2v2h16zm0 4H2v6h16v-6zM0 4c0-1.1.9-2 2-2h16a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V4zm4 8h4v2H4v-2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-currency-dollar">
+        <path d="M10 20a10 10 0 1 1 0-20 10 10 0 0 1 0 20zm1-5h1a3 3 0 0 0 0-6H7.99a1 1 0 0 1 0-2H14V5h-3V3H9v2H8a3 3 0 1 0 0 6h4a1 1 0 1 1 0 2H6v2h3v2h2v-2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-dashboard">
+        <path d="M10 20a10 10 0 1 1 0-20 10 10 0 0 1 0 20zm-5.6-4.29a9.95 9.95 0 0 1 11.2 0 8 8 0 1 0-11.2 0zm6.12-7.64l3.02-3.02 1.41 1.41-3.02 3.02a2 2 0 1 1-1.41-1.41z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-date-add">
+        <path d="M15 2h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V4c0-1.1.9-2 2-2h2V0h2v2h6V0h2v2zM3 6v12h14V6H3zm6 5V9h2v2h2v2h-2v2H9v-2H7v-2h2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-dial-pad">
+        <path d="M5 4a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm5 0a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm5 0a2 2 0 1 1 0-4 2 2 0 0 1 0 4zM5 9a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm5 0a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm5 0a2 2 0 1 1 0-4 2 2 0 0 1 0 4zM5 14a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm5 0a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm0 6a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm5-6a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-directions">
+        <path d="M10 0l10 10-10 10L0 10 10 0zM6 10v3h2v-3h3v3l4-4-4-4v3H8a2 2 0 0 0-2 2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-document">
+        <path d="M4 18h12V6h-4V2H4v16zm-2 1V0h12l4 4v16H2v-1z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-document-add">
+        <path d="M9 10V8h2v2h2v2h-2v2H9v-2H7v-2h2zm-5 8h12V6h-4V2H4v16zm-2 1V0h12l4 4v16H2v-1z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-dots-horizontal-double">
+        <path d="M10 9a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm0 6a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-dots-horizontal-triple">
+        <path d="M10 12a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm0-6a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm0 12a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-download">
+        <path d="M13 8V2H7v6H2l8 8 8-8h-5zM0 18h20v2H0v-2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-duplicate">
+        <path d="M6 6V2c0-1.1.9-2 2-2h10a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2h-4v4a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V8c0-1.1.9-2 2-2h4zm2 0h4a2 2 0 0 1 2 2v4h4V2H8v4zM2 8v10h10V8H2zm4 4v-2h2v2h2v2H8v2H6v-2H4v-2h2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-edit-copy">
+        <path d="M6 6V2c0-1.1.9-2 2-2h10a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2h-4v4a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V8c0-1.1.9-2 2-2h4zm2 0h4a2 2 0 0 1 2 2v4h4V2H8v4zM2 8v10h10V8H2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-edit-crop">
+        <path d="M14 16H6a2 2 0 0 1-2-2V6H0V4h4V0h2v14h14v2h-4v4h-2v-4zm0-3V6H7V4h7a2 2 0 0 1 2 2v7h-2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-edit-cut">
+        <path d="M9.77 11.5l5.34 3.91c.44.33 1.24.59 1.79.59H20L6.89 6.38A3.5 3.5 0 1 0 5.5 8.37L7.73 10 5.5 11.63a3.5 3.5 0 1 0 1.38 1.99l2.9-2.12zM3.5 7a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zm0 9a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zM15.1 4.59A3.53 3.53 0 0 1 16.9 4H20l-7.5 5.5L10.45 8l4.65-3.41z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-edit-pencil">
+        <path d="M12.3 3.7l4 4L4 20H0v-4L12.3 3.7zm1.4-1.4L16 0l4 4-2.3 2.3-4-4z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-education">
+        <path d="M3.33 8L10 12l10-6-10-6L0 6h10v2H3.33zM0 8v8l2-2.22V9.2L0 8zm10 12l-5-3-2-1.2v-6l7 4.2 7-4.2v6L10 20z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-envelope">
+        <path d="M18 2a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V4c0-1.1.9-2 2-2h16zm-4.37 9.1L20 16v-2l-5.12-3.9L20 6V4l-10 8L0 4v2l5.12 4.1L0 14v2l6.37-4.9L10 14l3.63-2.9z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-exclamation-outline">
+        <path d="M2.93 17.07A10 10 0 1 1 17.07 2.93 10 10 0 0 1 2.93 17.07zm12.73-1.41A8 8 0 1 0 4.34 4.34a8 8 0 0 0 11.32 11.32zM9 5h2v6H9V5zm0 8h2v2H9v-2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-exclamation-solid">
+        <path d="M2.93 17.07A10 10 0 1 1 17.07 2.93 10 10 0 0 1 2.93 17.07zM9 5v6h2V5H9zm0 8v2h2v-2H9z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-explore">
+        <path d="M10 20a10 10 0 1 1 0-20 10 10 0 0 1 0 20zM7.88 7.88l-3.54 7.78 7.78-3.54 3.54-7.78-7.78 3.54zM10 11a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-factory">
+        <path d="M10.5 20H0V7l5 3.33V7l5 3.33V7l5 3.33V0h5v20h-9.5z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-fast-forward">
+        <path d="M1 5l9 5-9 5V5zm9 0l9 5-9 5V5z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-fast-rewind">
+        <path d="M19 5v10l-9-5 9-5zm-9 0v10l-9-5 9-5z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-film">
+        <path d="M0 4c0-1.1.9-2 2-2h16a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V4zm6 0v12h8V4H6zM2 5v2h2V5H2zm0 4v2h2V9H2zm0 4v2h2v-2H2zm14-8v2h2V5h-2zm0 4v2h2V9h-2zm0 4v2h2v-2h-2zM8 7l5 3-5 3V7z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-filter">
+        <path d="M12 12l8-8V0H0v4l8 8v8l4-4v-4z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-flag">
+        <path d="M7.667 12H2v8H0V0h12l.333 2H20l-3 6 3 6H8l-.333-2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-flashlight">
+        <path d="M13 7v11a2 2 0 0 1-2 2H9a2 2 0 0 1-2-2V7L5 5V3h10v2l-2 2zM9 8v1a1 1 0 1 0 2 0V8a1 1 0 0 0-2 0zM5 0h10v2H5V0z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-folder">
+        <path d="M0 4c0-1.1.9-2 2-2h7l2 2h7a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V4z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-folder-outline">
+        <path d="M0 4c0-1.1.9-2 2-2h7l2 2h7a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V4zm2 2v10h16V6H2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-folder-outline-add">
+        <path d="M0 4c0-1.1.9-2 2-2h7l2 2h7a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V4zm2 2v10h16V6H2zm7 4V8h2v2h2v2h-2v2H9v-2H7v-2h2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-format-bold">
+        <path d="M3 19V1h8a5 5 0 0 1 3.88 8.16A5.5 5.5 0 0 1 11.5 19H3zm7.5-8H7v5h3.5a2.5 2.5 0 1 0 0-5zM7 4v4h3a2 2 0 1 0 0-4H7z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-format-italic">
+        <path d="M8 1h9v2H8V1zm3 2h3L8 17H5l6-14zM2 17h9v2H2v-2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-format-text-size">
+        <path d="M16 9v8h-2V9h-4V7h10v2h-4zM8 5v12H6V5H0V3h15v2H8z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-format-underline">
+        <path d="M16 9A6 6 0 1 1 4 9V1h3v8a3 3 0 0 0 6 0V1h3v8zM2 17h16v2H2v-2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-gift">
+        <path d="M14.83 4H20v6h-1v10H1V10H0V4h5.17A3 3 0 0 1 10 .76 3 3 0 0 1 14.83 4zM8 10H3v8h5v-8zm4 0v8h5v-8h-5zM8 6H2v2h6V6zm4 0v2h6V6h-6zM8 4a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm4 0a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-globe">
+        <path d="M10 20a10 10 0 1 1 0-20 10 10 0 0 1 0 20zm2-2.25a8 8 0 0 0 4-2.46V9a2 2 0 0 1-2-2V3.07a7.95 7.95 0 0 0-3-1V3a2 2 0 0 1-2 2v1a2 2 0 0 1-2 2v2h3a2 2 0 0 1 2 2v5.75zm-4 0V15a2 2 0 0 1-2-2v-1h-.5A1.5 1.5 0 0 1 4 10.5V8H2.25A8.01 8.01 0 0 0 8 17.75z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-hand-stop">
+        <path d="M17 16a4 4 0 0 1-4 4H7a4 4 0 0 1-4-4.01V4a1 1 0 0 1 1-1 1 1 0 0 1 1 1v6h1V2a1 1 0 0 1 1-1 1 1 0 0 1 1 1v8h1V1a1 1 0 1 1 2 0v9h1V2a1 1 0 0 1 1-1 1 1 0 0 1 1 1v13h1V9a1 1 0 0 1 1-1h1v8z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-hard-drive">
+        <path d="M2 2c0-1.1.9-2 2-2h12a2 2 0 0 1 2 2v16a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V2zm10.4 5.6A5 5 0 1 0 15 12V5l-2.6 2.6zM10 14a2 2 0 1 1 0-4 2 2 0 0 1 0 4zM6 3v2h4V3H6zM4 3a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm0 16a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm12 0a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm0-16a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-headphones">
+        <path d="M16 8A6 6 0 1 0 4 8v11H2a2 2 0 0 1-2-2v-4a2 2 0 0 1 2-2V8a8 8 0 1 1 16 0v3a2 2 0 0 1 2 2v4a2 2 0 0 1-2 2h-2V8zm-4 2h3v10h-3V10zm-7 0h3v10H5V10z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-heart">
+        <path d="M10 3.22l-.61-.6a5.5 5.5 0 0 0-7.78 7.77L10 18.78l8.39-8.4a5.5 5.5 0 0 0-7.78-7.77l-.61.61z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-home">
+        <path d="M8 20H3V10H0L10 0l10 10h-3v10h-5v-6H8v6z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-hot">
+        <path d="M10 0s8 7.58 8 12a8 8 0 1 1-16 0c0-1.5.91-3.35 2.12-5.15A3 3 0 0 0 10 6V0zM8 0a3 3 0 1 0 0 6V0z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-hour-glass">
+        <path d="M3 18a7 7 0 0 1 4-6.33V8.33A7 7 0 0 1 3 2H1V0h18v2h-2a7 7 0 0 1-4 6.33v3.34A7 7 0 0 1 17 18h2v2H1v-2h2zM5 2a5 5 0 0 0 4 4.9V10h2V6.9A5 5 0 0 0 15 2H5z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-inbox">
+        <path d="M0 2C0 .9.9 0 2 0h16a2 2 0 0 1 2 2v16a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2zm14 12h4V2H2v12h4c0 1.1.9 2 2 2h4a2 2 0 0 0 2-2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-inbox-check">
+        <path d="M0 2C0 .9.9 0 2 0h16a2 2 0 0 1 2 2v16a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2zm14 12h4V2H2v12h4c0 1.1.9 2 2 2h4a2 2 0 0 0 2-2zM5 9l2-2 2 2 4-4 2 2-6 6-4-4z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-inbox-download">
+        <path d="M0 2C0 .9.9 0 2 0h16a2 2 0 0 1 2 2v16a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2zm14 12h4V2H2v12h4c0 1.1.9 2 2 2h4a2 2 0 0 0 2-2zM9 8V5h2v3h3l-4 4-4-4h3z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-inbox-full">
+        <path d="M14 14h4V2H2v12h4c0 1.1.9 2 2 2h4a2 2 0 0 0 2-2zM0 2C0 .9.9 0 2 0h16a2 2 0 0 1 2 2v16a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2zm4 2h12v2H4V4zm0 3h12v2H4V7zm0 3h12v2H4v-2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-indent-decrease">
+        <path d="M1 1h18v2H1V1zm6 8h12v2H7V9zm-6 8h18v2H1v-2zM7 5h12v2H7V5zm0 8h12v2H7v-2zM5 6v8l-4-4 4-4z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-indent-increase">
+        <path d="M1 1h18v2H1V1zm6 8h12v2H7V9zm-6 8h18v2H1v-2zM7 5h12v2H7V5zm0 8h12v2H7v-2zM1 6l4 4-4 4V6z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-information-outline">
+        <path d="M2.93 17.07A10 10 0 1 1 17.07 2.93 10 10 0 0 1 2.93 17.07zm12.73-1.41A8 8 0 1 0 4.34 4.34a8 8 0 0 0 11.32 11.32zM9 11V9h2v6H9v-4zm0-6h2v2H9V5z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-information-solid">
+        <path d="M2.93 17.07A10 10 0 1 1 17.07 2.93 10 10 0 0 1 2.93 17.07zM9 11v4h2V9H9v2zm0-6v2h2V5H9z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-key">
+        <path d="M12.26 11.74L10 14H8v2H6v2l-2 2H0v-4l8.26-8.26a6 6 0 1 1 4 4zm4.86-4.62A3 3 0 0 0 15 2a3 3 0 0 0-2.12.88l4.24 4.24z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-keyboard">
+        <path d="M0 6c0-1.1.9-2 2-2h16a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V6zm2 0v2h2V6H2zm1 3v2h2V9H3zm-1 3v2h2v-2H2zm3 0v2h10v-2H5zm11 0v2h2v-2h-2zM6 9v2h2V9H6zm3 0v2h2V9H9zm3 0v2h2V9h-2zm3 0v2h2V9h-2zM5 6v2h2V6H5zm3 0v2h2V6H8zm3 0v2h2V6h-2zm3 0v2h4V6h-4z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-layers">
+        <path d="M10 1l10 6-10 6L0 7l10-6zm6.67 10L20 13l-10 6-10-6 3.33-2L10 15l6.67-4z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-library">
+        <path d="M0 6l10-6 10 6v2H0V6zm0 12h20v2H0v-2zm2-2h16v2H2v-2zm0-8h4v8H2V8zm6 0h4v8H8V8zm6 0h4v8h-4V8z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-light-bulb">
+        <path d="M7 13.33a7 7 0 1 1 6 0V16H7v-2.67zM7 17h6v1.5c0 .83-.67 1.5-1.5 1.5h-3A1.5 1.5 0 0 1 7 18.5V17zm2-5.1V14h2v-2.1a5 5 0 1 0-2 0z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-link">
+        <path d="M9.26 13a2 2 0 0 1 .01-2.01A3 3 0 0 0 9 5H5a3 3 0 0 0 0 6h.08a6.06 6.06 0 0 0 0 2H5A5 5 0 0 1 5 3h4a5 5 0 0 1 .26 10zm1.48-6a2 2 0 0 1-.01 2.01A3 3 0 0 0 11 15h4a3 3 0 0 0 0-6h-.08a6.06 6.06 0 0 0 0-2H15a5 5 0 0 1 0 10h-4a5 5 0 0 1-.26-10z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-list-add">
+        <path d="M15 9h-3v2h3v3h2v-3h3V9h-3V6h-2v3zM0 3h10v2H0V3zm0 8h10v2H0v-2zm0-4h10v2H0V7zm0 8h10v2H0v-2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-list-bullet">
+        <path d="M1 4h2v2H1V4zm4 0h14v2H5V4zM1 9h2v2H1V9zm4 0h14v2H5V9zm-4 5h2v2H1v-2zm4 0h14v2H5v-2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-load-balancer">
+        <path d="M17 12h-6v4h1v4H8v-4h1v-4H3v4h1v4H0v-4h1v-4a2 2 0 0 1 2-2h6V6H7V0h6v6h-2v4h6a2 2 0 0 1 2 2v4h1v4h-4v-4h1v-4z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-location">
+        <path d="M10 20S3 10.87 3 7a7 7 0 1 1 14 0c0 3.87-7 13-7 13zm0-11a2 2 0 1 0 0-4 2 2 0 0 0 0 4z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-location-current">
+        <path d="M0 0l20 8-8 4-2 8z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-location-food">
+        <path d="M18 11v7a2 2 0 0 1-4 0v-5h-2V3a3 3 0 0 1 3-3h3v11zM4 10a2 2 0 0 1-2-2V1a1 1 0 0 1 2 0v4h1V1a1 1 0 0 1 2 0v4h1V1a1 1 0 0 1 2 0v7a2 2 0 0 1-2 2v8a2 2 0 0 1-4 0v-8z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-location-gas-station">
+        <path d="M13 18h1v2H0v-2h1V2c0-1.1.9-2 2-2h8a2 2 0 0 1 2 2v16zM3 2v6h8V2H3zm10 8h1a2 2 0 0 1 2 2v3a1 1 0 0 0 2 0v-5l-2-2V6l-2-2 1-1 5 5v7a3 3 0 0 1-6 0v-3h-1v-2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-location-hotel">
+        <path d="M2 12h18v6h-2v-2H2v2H0V2h2v10zm8-6h8a2 2 0 0 1 2 2v3H10V6zm-4 5a3 3 0 1 1 0-6 3 3 0 0 1 0 6z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-location-marina">
+        <path d="M8 1.88V0h2v16h10l-4 4H2l-2-4h8v-2H0v-.26A24.03 24.03 0 0 0 8 1.88zM19.97 14H10v-.36A11.94 11.94 0 0 0 10 .36v-.2A16.01 16.01 0 0 1 19.97 14z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-location-park">
+        <path d="M5.33 12.77A4 4 0 1 1 3 5.13V5a4 4 0 0 1 5.71-3.62 3.5 3.5 0 0 1 6.26 1.66 2.5 2.5 0 0 1 2 2.08 4 4 0 1 1-2.7 7.49A5.02 5.02 0 0 1 12 14.58V18l2 1v1H6v-1l2-1v-3l-2.67-2.23zM5 10l3 3v-3H5z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-location-restroom">
+        <path d="M12 16H9l2-4.5V9c0-1.1.9-2 2-2h2a2 2 0 0 1 2 2v2.5l2 4.5h-3v4h-4v-4zm-5-3h2V9a2 2 0 0 0-2-2H3a2 2 0 0 0-2 2v4h2v7h4v-7zM5 6a3 3 0 1 1 0-6 3 3 0 0 1 0 6zm9 0a3 3 0 1 1 0-6 3 3 0 0 1 0 6z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-location-shopping">
+        <path d="M16 6v2h2l2 12H0L2 8h2V6a6 6 0 1 1 12 0zm-2 0a4 4 0 1 0-8 0v2h8V6zM4 10v2h2v-2H4zm10 0v2h2v-2h-2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-lock-closed">
+        <path d="M4 8V6a6 6 0 1 1 12 0v2h1a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2v-8c0-1.1.9-2 2-2h1zm5 6.73V17h2v-2.27a2 2 0 1 0-2 0zM7 6v2h6V6a3 3 0 0 0-6 0z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-lock-open">
+        <path d="M4 8V6a6 6 0 1 1 12 0h-3v2h4a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2v-8c0-1.1.9-2 2-2h1zm5 6.73V17h2v-2.27a2 2 0 1 0-2 0zM7 6v2h6V6a3 3 0 0 0-6 0z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-map">
+        <path d="M0 0l6 4 8-4 6 4v16l-6-4-8 4-6-4V0zm7 6v11l6-3V3L7 6z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-menu">
+        <path d="M0 3h20v2H0V3zm0 6h20v2H0V9zm0 6h20v2H0v-2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-mic">
+        <path d="M9 18v-1.06A8 8 0 0 1 2 9h2a6 6 0 1 0 12 0h2a8 8 0 0 1-7 7.94V18h3v2H6v-2h3zM6 4a4 4 0 1 1 8 0v5a4 4 0 1 1-8 0V4z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-minus-outline">
+        <path d="M10 20a10 10 0 1 1 0-20 10 10 0 0 1 0 20zm0-2a8 8 0 1 0 0-16 8 8 0 0 0 0 16zm5-9v2H5V9h10z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-minus-solid">
+        <path d="M10 20a10 10 0 1 1 0-20 10 10 0 0 1 0 20zm5-11H5v2h10V9z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-mobile-devices">
+        <path d="M17 6V5h-2V2H3v14h5v4h3.25H11a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6zm-5.75 14H3a2 2 0 0 1-2-2V2c0-1.1.9-2 2-2h12a2 2 0 0 1 2 2v4a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2h-5.75zM11 8v8h6V8h-6zm3 11a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-mood-happy">
+        <path d="M10 20a10 10 0 1 1 0-20 10 10 0 0 1 0 20zm0-2a8 8 0 1 0 0-16 8 8 0 0 0 0 16zM6.5 9a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zm7 0a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zm2.16 3a6 6 0 0 1-11.32 0h11.32z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-mood-sad">
+        <path d="M10 20a10 10 0 1 1 0-20 10 10 0 0 1 0 20zm0-2a8 8 0 1 0 0-16 8 8 0 0 0 0 16zM6.5 9a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zm7 0a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zm2.16 6H4.34a6 6 0 0 1 11.32 0z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-mouse">
+        <path d="M4 9V6A6 6 0 0 1 9 .08V9H4zm0 2v3a6 6 0 1 0 12 0v-3H4zm12-2V6a6 6 0 0 0-5-5.92V9h5z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-music-album">
+        <path d="M0 0h20v20H0V0zm10 18a8 8 0 1 0 0-16 8 8 0 0 0 0 16zm0-5a3 3 0 1 1 0-6 3 3 0 0 1 0 6z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-music-artist">
+        <path d="M15.75 8l-3.74-3.75a3.99 3.99 0 0 1 6.82-3.08A4 4 0 0 1 15.75 8zm-13.9 7.3l9.2-9.19 2.83 2.83-9.2 9.2-2.82-2.84zm-1.4 2.83l2.11-2.12 1.42 1.42-2.12 2.12-1.42-1.42zM10 15l2-2v7h-2v-5z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-music-notes">
+        <path d="M20 2.5V0L6 2v12.17A3 3 0 0 0 5 14H3a3 3 0 0 0 0 6h2a3 3 0 0 0 3-3V5.71L18 4.3v7.88a3 3 0 0 0-1-.17h-2a3 3 0 0 0 0 6h2a3 3 0 0 0 3-3V2.5z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-music-playlist">
+        <path d="M16 17a3 3 0 0 1-3 3h-2a3 3 0 0 1 0-6h2a3 3 0 0 1 1 .17V1l6-1v4l-4 .67V17zM0 3h12v2H0V3zm0 4h12v2H0V7zm0 4h12v2H0v-2zm0 4h6v2H0v-2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-navigation-more">
+        <path d="M4 12a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm6 0a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm6 0a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-network">
+        <path d="M10 20a10 10 0 1 1 0-20 10 10 0 0 1 0 20zm7.75-8a8.01 8.01 0 0 0 0-4h-3.82a28.81 28.81 0 0 1 0 4h3.82zm-.82 2h-3.22a14.44 14.44 0 0 1-.95 3.51A8.03 8.03 0 0 0 16.93 14zm-8.85-2h3.84a24.61 24.61 0 0 0 0-4H8.08a24.61 24.61 0 0 0 0 4zm.25 2c.41 2.4 1.13 4 1.67 4s1.26-1.6 1.67-4H8.33zm-6.08-2h3.82a28.81 28.81 0 0 1 0-4H2.25a8.01 8.01 0 0 0 0 4zm.82 2a8.03 8.03 0 0 0 4.17 3.51c-.42-.96-.74-2.16-.95-3.51H3.07zm13.86-8a8.03 8.03 0 0 0-4.17-3.51c.42.96.74 2.16.95 3.51h3.22zm-8.6 0h3.34c-.41-2.4-1.13-4-1.67-4S8.74 3.6 8.33 6zM3.07 6h3.22c.2-1.35.53-2.55.95-3.51A8.03 8.03 0 0 0 3.07 6z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-news-paper">
+        <path d="M16 2h4v15a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V0h16v2zm0 2v13a1 1 0 0 0 1 1 1 1 0 0 0 1-1V4h-2zM2 2v15a1 1 0 0 0 1 1h11.17a2.98 2.98 0 0 1-.17-1V2H2zm2 8h8v2H4v-2zm0 4h8v2H4v-2zM4 4h8v4H4V4z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-notifications">
+        <path d="M4 8a6 6 0 0 1 4.03-5.67 2 2 0 1 1 3.95 0A6 6 0 0 1 16 8v6l3 2v1H1v-1l3-2V8zm8 10a2 2 0 1 1-4 0h4z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-notifications-outline">
+        <path d="M6 8v7h8V8a4 4 0 1 0-8 0zm2.03-5.67a2 2 0 1 1 3.95 0A6 6 0 0 1 16 8v6l3 2v1H1v-1l3-2V8a6 6 0 0 1 4.03-5.67zM12 18a2 2 0 1 1-4 0h4z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-paste">
+        <path d="M10.5 20H2a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2h1V3l2.03-.4a3 3 0 0 1 5.94 0L13 3v1h1a2 2 0 0 1 2 2v1h-2V6h-1v1H3V6H2v12h5v2h3.5zM8 4a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm2 4h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2h-8a2 2 0 0 1-2-2v-8c0-1.1.9-2 2-2zm0 2v8h8v-8h-8z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-pause">
+        <path d="M5 4h3v12H5V4zm7 0h3v12h-3V4z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-pause-outline">
+        <path d="M2.93 17.07A10 10 0 1 1 17.07 2.93 10 10 0 0 1 2.93 17.07zm12.73-1.41A8 8 0 1 0 4.34 4.34a8 8 0 0 0 11.32 11.32zM7 6h2v8H7V6zm4 0h2v8h-2V6z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-pause-solid">
+        <path d="M2.93 17.07A10 10 0 1 1 17.07 2.93 10 10 0 0 1 2.93 17.07zM7 6v8h2V6H7zm4 0v8h2V6h-2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-pen-tool">
+        <path d="M11 9.27V0l6 11-4 6H7l-4-6L9 0v9.27a2 2 0 1 0 2 0zM6 18h8v2H6v-2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-phone">
+        <path d="M20 18.35V19a1 1 0 0 1-1 1h-2A17 17 0 0 1 0 3V1a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v4c0 .56-.31 1.31-.7 1.7L3.16 8.84c1.52 3.6 4.4 6.48 8 8l2.12-2.12c.4-.4 1.15-.71 1.7-.71H19a1 1 0 0 1 .99 1v3.35z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-photo">
+        <path d="M0 4c0-1.1.9-2 2-2h16a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V4zm11 9l-3-3-6 6h16l-5-5-2 2zm4-4a2 2 0 1 0 0-4 2 2 0 0 0 0 4z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-php-elephant">
+        <path fill-rule="evenodd"
+              d="M10 12v8A10 10 0 0 1 8.17.17L10 2h5a5 5 0 0 1 5 4.99v9.02A4 4 0 0 1 16 20v-2a2 2 0 1 0 0-4h-4l-2-2zm5.5-3a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-pin">
+        <path d="M11 12h6v-1l-3-1V2l3-1V0H3v1l3 1v8l-3 1v1h6v7l1 1 1-1v-7z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-play">
+        <path d="M4 4l12 6-12 6z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-play-outline">
+        <path d="M2.93 17.07A10 10 0 1 1 17.07 2.93 10 10 0 0 1 2.93 17.07zm12.73-1.41A8 8 0 1 0 4.34 4.34a8 8 0 0 0 11.32 11.32zM7 6l8 4-8 4V6z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-plugin">
+        <path d="M20 14v4a2 2 0 0 1-2 2h-4v-2a2 2 0 0 0-2-2 2 2 0 0 0-2 2v2H6a2 2 0 0 1-2-2v-4H2a2 2 0 0 1-2-2 2 2 0 0 1 2-2h2V6c0-1.1.9-2 2-2h4V2a2 2 0 0 1 2-2 2 2 0 0 1 2 2v2h4a2 2 0 0 1 2 2v4h-2a2 2 0 0 0-2 2 2 2 0 0 0 2 2h2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-portfolio">
+        <path d="M9 12H1v6a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-6h-8v2H9v-2zm0-1H0V5c0-1.1.9-2 2-2h4V2a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v1h4a2 2 0 0 1 2 2v6h-9V9H9v2zm3-8V2H8v1h4z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-printer">
+        <path d="M4 16H0V6h20v10h-4v4H4v-4zm2-4v6h8v-6H6zM4 0h12v5H4V0zM2 8v2h2V8H2zm4 0v2h2V8H6z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-pylon">
+        <path d="M17.4 18H20v2H0v-2h2.6L8 0h4l5.4 18zm-3.2-4H5.8l-1.2 4h10.8l-1.2-4zm-2.4-8H8.2L7 10h6l-1.2-4z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-question">
+        <path d="M10 20a10 10 0 1 1 0-20 10 10 0 0 1 0 20zm2-13c0 .28-.21.8-.42 1L10 9.58c-.57.58-1 1.6-1 2.42v1h2v-1c0-.29.21-.8.42-1L13 9.42c.57-.58 1-1.6 1-2.42a4 4 0 1 0-8 0h2a2 2 0 1 1 4 0zm-3 8v2h2v-2H9z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-queue">
+        <path d="M0 2h20v4H0V2zm0 8h20v2H0v-2zm0 6h20v2H0v-2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-radar">
+        <path d="M12 10a2 2 0 0 1-3.41 1.41A2 2 0 0 1 10 8V0a9.97 9.97 0 0 1 10 10h-8zm7.9 1.41A10 10 0 1 1 8.59.1v2.03a8 8 0 1 0 9.29 9.29h2.02zm-4.07 0a6 6 0 1 1-7.25-7.25v2.1a3.99 3.99 0 0 0-1.4 6.57 4 4 0 0 0 6.56-1.42h2.1z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-radio">
+        <path d="M20 9v9a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V8c0-1.1.9-2 2-2h13.8L.74 1.97 1.26.03 20 5.06V9zm-5 9a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM2 8v2h16V8H2zm1.5 10a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3zm5 0a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3zm6.5-1a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-refresh">
+        <path d="M10 3v2a5 5 0 0 0-3.54 8.54l-1.41 1.41A7 7 0 0 1 10 3zm4.95 2.05A7 7 0 0 1 10 17v-2a5 5 0 0 0 3.54-8.54l1.41-1.41zM10 20l-4-4 4-4v8zm0-12V0l4 4-4 4z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-reload">
+        <path d="M14.66 15.66A8 8 0 1 1 17 10h-2a6 6 0 1 0-1.76 4.24l1.42 1.42zM12 10h8l-4 4-4-4z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-reply">
+        <path d="M15 17v-2.99A4 4 0 0 0 11 10H8v5L2 9l6-6v5h3a6 6 0 0 1 6 6v3h-2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-reply-all">
+        <path d="M18 17v-2.99A4 4 0 0 0 14 10h-3v5L5 9l6-6v5h3a6 6 0 0 1 6 6v3h-2zM6 6V3L0 9l6 6v-3L3 9l3-3z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-repost">
+        <path d="M5 4a2 2 0 0 0-2 2v6H0l4 4 4-4H5V6h7l2-2H5zm10 4h-3l4-4 4 4h-3v6a2 2 0 0 1-2 2H6l2-2h7V8z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-save-disk">
+        <path d="M0 2C0 .9.9 0 2 0h14l4 4v14a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2zm5 0v6h10V2H5zm6 1h3v4h-3V3z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-screen-full">
+        <path d="M2.8 15.8L0 13v7h7l-2.8-2.8 4.34-4.32-1.42-1.42L2.8 15.8zM17.2 4.2L20 7V0h-7l2.8 2.8-4.34 4.32 1.42 1.42L17.2 4.2zm-1.4 13L13 20h7v-7l-2.8 2.8-4.32-4.34-1.42 1.42 4.33 4.33zM4.2 2.8L7 0H0v7l2.8-2.8 4.32 4.34 1.42-1.42L4.2 2.8z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-search">
+        <path d="M12.9 14.32a8 8 0 1 1 1.41-1.41l5.35 5.33-1.42 1.42-5.33-5.34zM8 14A6 6 0 1 0 8 2a6 6 0 0 0 0 12z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-send">
+        <path d="M0 0l20 10L0 20V0zm0 8v4l10-2L0 8z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-servers">
+        <path d="M0 2C0 .9.9 0 2 0h16a2 2 0 0 1 2 2v2a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2zm0 7c0-1.1.9-2 2-2h16a2 2 0 0 1 2 2v2a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V9zm0 7c0-1.1.9-2 2-2h16a2 2 0 0 1 2 2v2a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2zM12 2v2h2V2h-2zm4 0v2h2V2h-2zm-4 7v2h2V9h-2zm4 0v2h2V9h-2zm-4 7v2h2v-2h-2zm4 0v2h2v-2h-2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-share">
+        <path d="M4 10c0-1.1.9-2 2-2h8c1.1 0 2 .9 2 2v8c0 1.1-.9 2-2 2H6c-1.1 0-2-.9-2-2v-8zm2 0v8h8v-8h-2V8H8v2H6zm3-6.17V16h2V3.83l3.07 3.07 1.42-1.41L10 0l-.7.7-4.8 4.8 1.42 1.4L9 3.84z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-share-alt">
+        <path d="M5.08 12.16A2.99 2.99 0 0 1 0 10a3 3 0 0 1 5.08-2.16l8.94-4.47a3 3 0 1 1 .9 1.79L5.98 9.63a3.03 3.03 0 0 1 0 .74l8.94 4.47A2.99 2.99 0 0 1 20 17a3 3 0 1 1-5.98-.37l-8.94-4.47z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-shield">
+        <path d="M19 11a7.5 7.5 0 0 1-3.5 5.94L10 20l-5.5-3.06A7.5 7.5 0 0 1 1 11V3c3.38 0 6.5-1.12 9-3 2.5 1.89 5.62 3 9 3v8zm-9 1.08l2.92 2.04-1.03-3.41 2.84-2.15-3.56-.08L10 5.12 8.83 8.48l-3.56.08L8.1 10.7l-1.03 3.4L10 12.09z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-shopping-cart">
+        <path d="M4 2h16l-3 9H4a1 1 0 1 0 0 2h13v2H4a3 3 0 0 1 0-6h.33L3 5 2 2H0V0h3a1 1 0 0 1 1 1v1zm1 18a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm10 0a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-show-sidebar">
+        <path d="M7 3H2v14h5V3zm2 0v14h9V3H9zM0 3c0-1.1.9-2 2-2h16a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V3zm3 1h3v2H3V4zm0 3h3v2H3V7zm0 3h3v2H3v-2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-shuffle">
+        <path d="M6.59 12.83L4.4 15c-.58.58-1.59 1-2.4 1H0v-2h2c.29 0 .8-.2 1-.41l2.17-2.18 1.42 1.42zM16 4V1l4 4-4 4V6h-2c-.29 0-.8.2-1 .41l-2.17 2.18L9.4 7.17 11.6 5c.58-.58 1.59-1 2.41-1h2zm0 10v-3l4 4-4 4v-3h-2c-.82 0-1.83-.42-2.41-1l-8.6-8.59C2.8 6.21 2.3 6 2 6H0V4h2c.82 0 1.83.42 2.41 1l8.6 8.59c.2.2.7.41.99.41h2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-star-full">
+        <path d="M10 15l-5.878 3.09 1.123-6.545L.489 6.91l6.572-.955L10 0l2.939 5.955 6.572.955-4.756 4.635 1.123 6.545z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-station">
+        <path d="M9 11.73a2 2 0 1 1 2 0V20H9v-8.27zm5.24 2.51l-1.41-1.41A3.99 3.99 0 0 0 10 6a4 4 0 0 0-2.83 6.83l-1.41 1.41a6 6 0 1 1 8.49 0zm2.83 2.83l-1.41-1.41a8 8 0 1 0-11.31 0l-1.42 1.41a10 10 0 1 1 14.14 0z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-step-backward">
+        <path d="M4 5h3v10H4V5zm12 0v10l-9-5 9-5z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-step-forward">
+        <path d="M13 5h3v10h-3V5zM4 5l9 5-9 5V5z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-stethoscope">
+        <path d="M17 10.27V4.99a1 1 0 0 0-2 0V15a5 5 0 0 1-10 0v-1.08A6 6 0 0 1 0 8V2C0 .9.9 0 2 0h1a1 1 0 0 1 1 1 1 1 0 0 1-1 1H2v6a4 4 0 1 0 8 0V2H9a1 1 0 0 1-1-1 1 1 0 0 1 1-1h1a2 2 0 0 1 2 2v6a6 6 0 0 1-5 5.92V15a3 3 0 0 0 6 0V5a3 3 0 0 1 6 0v5.27a2 2 0 1 1-2 0z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-store-front">
+        <path d="M18 9.87V20H2V9.87a4.25 4.25 0 0 0 3-.38V14h10V9.5a4.26 4.26 0 0 0 3 .37zM3 0h4l-.67 6.03A3.43 3.43 0 0 1 3 9C1.34 9 .42 7.73.95 6.15L3 0zm5 0h4l.7 6.3c.17 1.5-.91 2.7-2.42 2.7h-.56A2.38 2.38 0 0 1 7.3 6.3L8 0zm5 0h4l2.05 6.15C19.58 7.73 18.65 9 17 9a3.42 3.42 0 0 1-3.33-2.97L13 0z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-stroke-width">
+        <path d="M0 0h20v5H0V0zm0 7h20v4H0V7zm0 6h20v3H0v-3zm0 5h20v2H0v-2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-subdirectory-left">
+        <path d="M18 12v1H8v5l-6-6 6-6v5h8V2h2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-subdirectory-right">
+        <path d="M3.5 13H12v5l6-6-6-6v5H4V2H2v11z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-swap">
+        <path d="M9 6a4 4 0 1 1 8 0v8h3l-4 4-4-4h3V6a2 2 0 0 0-2-2 2 2 0 0 0-2 2v8a4 4 0 1 1-8 0V6H0l4-4 4 4H5v8a2 2 0 0 0 2 2 2 2 0 0 0 2-2V6z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-tablet">
+        <path d="M2 2c0-1.1.9-2 2-2h12a2 2 0 0 1 2 2v16a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V2zm2 0v14h12V2H4zm6 17a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-tag">
+        <path d="M0 10V2l2-2h8l10 10-10 10L0 10zm4.5-4a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-target">
+        <path d="M17.94 11H13V9h4.94A8 8 0 0 0 11 2.06V7H9V2.06A8 8 0 0 0 2.06 9H7v2H2.06A8 8 0 0 0 9 17.94V13h2v4.94A8 8 0 0 0 17.94 11zM10 20a10 10 0 1 1 0-20 10 10 0 0 1 0 20z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-text-box">
+        <path d="M0 0h6v6H0V0zm2 2v2h2V2H2zm12-2h6v6h-6V0zm2 2v2h2V2h-2zm-2 12h6v6h-6v-6zm2 2v2h2v-2h-2zM0 14h6v6H0v-6zm2 2v2h2v-2H2zM6 2h8v2H6V2zm0 14h8v2H6v-2zM16 6h2v8h-2V6zM2 6h2v8H2V6zm5 1h6v2H7V7zm2 2h2v4H9V9z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-text-decoration">
+        <path d="M12 5h-2v12H8V3h8v2h-2v12h-2V5zM8 3a4 4 0 1 0 0 8V3z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-thermometer">
+        <path d="M9 11.17V7h2v4.17a3 3 0 1 1-2 0zm-1-.63a4 4 0 1 0 4 0V4a2 2 0 1 0-4 0v6.53zM6 9.53V4a4 4 0 0 1 8 0v5.53A5.99 5.99 0 0 1 10 20 6 6 0 0 1 6 9.53z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-thumbs-down">
+        <path d="M11 20a2 2 0 0 1-2-2v-6H2a2 2 0 0 1-2-2V8l2.3-6.12A3.11 3.11 0 0 1 5 0h8a2 2 0 0 1 2 2v8l-3 7v3h-1zm6-10V0h3v10h-3z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-thumbs-up">
+        <path d="M11 0h1v3l3 7v8a2 2 0 0 1-2 2H5c-1.1 0-2.31-.84-2.7-1.88L0 12v-2a2 2 0 0 1 2-2h7V2a2 2 0 0 1 2-2zm6 10h3v10h-3V10z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-ticket">
+        <path d="M20 12v5H0v-5a2 2 0 1 0 0-4V3h20v5a2 2 0 1 0 0 4zM3 5v10h14V5H3zm7 7.08l-2.92 2.04L8.1 10.7 5.27 8.56l3.56-.08L10 5.12l1.17 3.36 3.56.08-2.84 2.15 1.03 3.4L10 12.09z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-time">
+        <path d="M10 20a10 10 0 1 1 0-20 10 10 0 0 1 0 20zm0-2a8 8 0 1 0 0-16 8 8 0 0 0 0 16zm-1-7.59V4h2v5.59l3.95 3.95-1.41 1.41L9 10.41z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-timer">
+        <path d="M16.32 7.1A8 8 0 1 1 9 4.06V2h2v2.06c1.46.18 2.8.76 3.9 1.62l1.46-1.46 1.42 1.42-1.46 1.45zM10 18a6 6 0 1 0 0-12 6 6 0 0 0 0 12zM7 0h6v2H7V0zm5.12 8.46l1.42 1.42L10 13.4 8.59 12l3.53-3.54z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-translate">
+        <path d="M7.41 9l2.24 2.24-.83 2L6 10.4l-3.3 3.3-1.4-1.42L4.58 9l-.88-.88c-.53-.53-1-1.3-1.3-2.12h2.2c.15.28.33.53.51.7l.89.9.88-.88C7.48 6.1 8 4.84 8 4H0V2h5V0h2v2h5v2h-2c0 1.37-.74 3.15-1.7 4.12L7.4 9zm3.84 8L10 20H8l5-12h2l5 12h-2l-1.25-3h-5.5zm.83-2h3.84L14 10.4 12.08 15z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-trash">
+        <path d="M6 2l2-2h4l2 2h4v2H2V2h4zM3 6h14l-1 14H4L3 6zm5 2v10h1V8H8zm3 0v10h1V8h-1z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-travel-bus">
+        <path d="M13 18H7v1a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1v-1a2 2 0 0 1-2-2V2c0-1.1.9-2 2-2h12a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2v1a1 1 0 0 1-1 1h-1a1 1 0 0 1-1-1v-1zM4 5v6h5V5H4zm7 0v6h5V5h-5zM5 2v1h10V2H5zm.5 14a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3zm9 0a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-travel-car">
+        <path d="M2 14v-3H1a1 1 0 0 1-1-1 1 1 0 0 1 1-1h1l4-7h8l4 7h1a1 1 0 0 1 1 1 1 1 0 0 1-1 1h-1v6a1 1 0 0 1-1 1h-1a1 1 0 0 1-1-1v-1H5v1a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1v-3zm13.86-5L13 4H7L4.14 9h11.72zM5.5 14a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3zm9 0a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-travel-case">
+        <path d="M14 5h2v14H4V5h2V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v1zm3 0h1a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2h-1V5zM3 5v14H2a2 2 0 0 1-2-2V7c0-1.1.9-2 2-2h1zm5-1v1h4V4H8z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-travel-taxi-cab">
+        <path d="M12 3h2l4 7h1a1 1 0 0 1 1 1 1 1 0 0 1-1 1h-1v6a1 1 0 0 1-1 1h-1a1 1 0 0 1-1-1v-1H5v1a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1v-6H1a1 1 0 0 1-1-1 1 1 0 0 1 1-1h1l4-7h2V1h4v2zm3.86 7L13 5H7l-2.86 5h11.72zM5.5 15a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3zm9 0a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-travel-train">
+        <path d="M12 18H8l-2 2H3l2-2a2 2 0 0 1-2-2V2c0-1.1.9-2 2-2h10a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2l2 2h-3l-2-2zM5 5v6h10V5H5zm1.5 11a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3zm7 0a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3zM8 2v1h4V2H8z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-travel-walk">
+        <path d="M11 7l1.44 2.16c.31.47 1.01.84 1.57.84H17V8h-3l-1.44-2.16a5.94 5.94 0 0 0-1.4-1.4l-1.32-.88a1.72 1.72 0 0 0-1.7-.04L4 6v5h2V7l2-1-3 14h2l2.35-7.65L11 14v6h2v-8l-2.7-2.7L11 7zm1-3a2 2 0 1 0 0-4 2 2 0 0 0 0 4z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-trophy">
+        <path d="M15 9a3 3 0 0 0 3-3h2a5 5 0 0 1-5.1 5 5 5 0 0 1-3.9 3.9V17l5 2v1H4v-1l5-2v-2.1A5 5 0 0 1 5.1 11H5a5 5 0 0 1-5-5h2a3 3 0 0 0 3 3V4H2v2H0V2h5V0h10v2h5v4h-2V4h-3v5z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-tuning">
+        <path d="M17 16v4h-2v-4h-2v-3h6v3h-2zM1 9h6v3H1V9zm6-4h6v3H7V5zM3 0h2v8H3V0zm12 0h2v12h-2V0zM9 0h2v4H9V0zM3 12h2v8H3v-8zm6-4h2v12H9V8z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-upload">
+        <path d="M13 10v6H7v-6H2l8-8 8 8h-5zM0 18h20v2H0v-2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-usb">
+        <path d="M15 8v2h-4V4h2l-3-4-3 4h2v8H5V9.73a2 2 0 1 0-2 0V12a2 2 0 0 0 2 2h4v2.27a2 2 0 1 0 2 0V12h4a2 2 0 0 0 2-2V8h1V4h-4v4h1z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-user">
+        <path d="M5 5a5 5 0 0 1 10 0v2A5 5 0 0 1 5 7V5zM0 16.68A19.9 19.9 0 0 1 10 14c3.64 0 7.06.97 10 2.68V20H0v-3.32z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-user-add">
+        <path d="M2 6H0v2h2v2h2V8h2V6H4V4H2v2zm7 0a3 3 0 0 1 6 0v2a3 3 0 0 1-6 0V6zm11 9.14A15.93 15.93 0 0 0 12 13c-2.91 0-5.65.78-8 2.14V18h16v-2.86z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-user-group">
+        <path d="M7 8a4 4 0 1 1 0-8 4 4 0 0 1 0 8zm0 1c2.15 0 4.2.4 6.1 1.09L12 16h-1.25L10 20H4l-.75-4H2L.9 10.09A17.93 17.93 0 0 1 7 9zm8.31.17c1.32.18 2.59.48 3.8.92L18 16h-1.25L16 20h-3.96l.37-2h1.25l1.65-8.83zM13 0a4 4 0 1 1-1.33 7.76 5.96 5.96 0 0 0 0-7.52C12.1.1 12.53 0 13 0z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-user-solid-circle">
+        <path d="M10 20a10 10 0 1 1 0-20 10 10 0 0 1 0 20zM7 6v2a3 3 0 1 0 6 0V6a3 3 0 1 0-6 0zm-3.65 8.44a8 8 0 0 0 13.3 0 15.94 15.94 0 0 0-13.3 0z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-user-solid-square">
+        <path d="M0 2C0 .9.9 0 2 0h16a2 2 0 0 1 2 2v16a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2zm7 4v2a3 3 0 1 0 6 0V6a3 3 0 1 0-6 0zm11 9.14A15.93 15.93 0 0 0 10 13c-2.91 0-5.65.78-8 2.14V18h16v-2.86z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-vector">
+        <path d="M12 4h4.27a2 2 0 1 1 0 2h-2.14a9 9 0 0 1 4.84 7.25 2 2 0 1 1-2 .04 7 7 0 0 0-4.97-6V8H8v-.71a7 7 0 0 0-4.96 6 2 2 0 1 1-2-.04A9 9 0 0 1 5.86 6H3.73a2 2 0 1 1 0-2H8V3h4v1z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-video-camera">
+        <path d="M16 7l4-4v14l-4-4v3a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V4c0-1.1.9-2 2-2h12a2 2 0 0 1 2 2v3zm-8 7a4 4 0 1 0 0-8 4 4 0 0 0 0 8zm0-2a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-view-carousel">
+        <path d="M16 16v2H4v-2H0V4h4V2h12v2h4v12h-4zM14 5.5V4H6v12h8V5.5zm2 .5v8h2V6h-2zM4 6H2v8h2V6z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-view-column">
+        <path d="M12 4H8v12h4V4zm2 0v12h4V4h-4zM6 4H2v12h4V4zM0 2h20v16H0V2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-view-hide">
+        <path d="M12.81 4.36l-1.77 1.78a4 4 0 0 0-4.9 4.9l-2.76 2.75C2.06 12.79.96 11.49.2 10a11 11 0 0 1 12.6-5.64zm3.8 1.85c1.33 1 2.43 2.3 3.2 3.79a11 11 0 0 1-12.62 5.64l1.77-1.78a4 4 0 0 0 4.9-4.9l2.76-2.75zm-.25-3.99l1.42 1.42L3.64 17.78l-1.42-1.42L16.36 2.22z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-view-list">
+        <path d="M0 3h20v2H0V3zm0 4h20v2H0V7zm0 4h20v2H0v-2zm0 4h20v2H0v-2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-view-show">
+        <path d="M.2 10a11 11 0 0 1 19.6 0A11 11 0 0 1 .2 10zm9.8 4a4 4 0 1 0 0-8 4 4 0 0 0 0 8zm0-2a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-view-tile">
+        <path d="M0 0h9v9H0V0zm2 2v5h5V2H2zm-2 9h9v9H0v-9zm2 2v5h5v-5H2zm9-13h9v9h-9V0zm2 2v5h5V2h-5zm-2 9h9v9h-9v-9zm2 2v5h5v-5h-5z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-volume-down">
+        <path d="M7 7H3v6h4l5 5V2L7 7zm8.54 6.54l-1.42-1.42a3 3 0 0 0 0-4.24l1.42-1.42a4.98 4.98 0 0 1 0 7.08z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-volume-mute">
+        <path d="M9 7H5v6h4l5 5V2L9 7z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-volume-off">
+        <path d="M15 8.59l-2.12-2.13-1.42 1.42L13.6 10l-2.13 2.12 1.42 1.42L15 11.4l2.12 2.13 1.42-1.42L16.4 10l2.13-2.12-1.42-1.42L15 8.6zM4 7H0v6h4l5 5V2L4 7z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-volume-up">
+        <path d="M5 7H1v6h4l5 5V2L5 7zm11.36 9.36l-1.41-1.41a6.98 6.98 0 0 0 0-9.9l1.41-1.41a8.97 8.97 0 0 1 0 12.72zm-2.82-2.82l-1.42-1.42a3 3 0 0 0 0-4.24l1.42-1.42a4.98 4.98 0 0 1 0 7.08z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-wallet">
+        <path d="M0 4c0-1.1.9-2 2-2h15a1 1 0 0 1 1 1v1H2v1h17a1 1 0 0 1 1 1v10a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V4zm16.5 9a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-watch">
+        <path d="M11 9h2v2H9V7h2v2zm-5.82 6.08a6.98 6.98 0 0 1 0-10.16L6 0h8l.82 4.92a6.98 6.98 0 0 1 0 10.16L14 20H6l-.82-4.92zM10 15a5 5 0 1 0 0-10 5 5 0 0 0 0 10z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-wrench">
+        <path d="M6.47 9.8A5 5 0 0 1 .2 3.22l3.95 3.95 2.82-2.83L3.03.41a5 5 0 0 1 6.4 6.68l10 10-2.83 2.83L6.47 9.8z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-zoom-in">
+        <path fill-rule="evenodd"
+              d="M12.9 14.32a8 8 0 1 1 1.41-1.41l5.35 5.33-1.42 1.42-5.33-5.34zM8 14A6 6 0 1 0 8 2a6 6 0 0 0 0 12zM7 7V5h2v2h2v2H9v2H7V9H5V7h2z"/>
+    </symbol>
+    <symbol viewBox="0 0 20 20" id="zondicon-zoom-out">
+        <path fill-rule="evenodd"
+              d="M12.9 14.32a8 8 0 1 1 1.41-1.41l5.35 5.33-1.42 1.42-5.33-5.34zM8 14A6 6 0 1 0 8 2a6 6 0 0 0 0 12zM5 7h6v2H5V7z"/>
+    </symbol>
+</svg>

+ 5 - 0
public/vendor/horizon/mix-manifest.json

@@ -0,0 +1,5 @@
+{
+    "/app.js": "/app.js?id=a2e36b7a4f248973b22b",
+    "/app.css": "/app.css?id=9ce01eaaba790566b895",
+    "/app-dark.css": "/app-dark.css?id=821c845f9bf3b7853c33"
+}

+ 1 - 1
resources/js/utilities/barcode.js

@@ -6,7 +6,7 @@ window.setBarcode = function setBarcode(val, dom='barcode', width=2, height=40,
     JsBarcode(dom, val, {
         format: 'CODE39',
         lineColor: '#000',
-        background: '#EBEEF5',
+        background: '#ffffff',
         width: width,
         height: height,
         displayValue: isShowValue

+ 34 - 0
resources/scripts/git-hooks/pre-push

@@ -0,0 +1,34 @@
+#!/usr/bin/php
+
+<?php
+
+// 获取应用名称
+$projectName = basename(getcwd());
+
+echo PHP_EOL;
+echo '开始执行单元测试...'.PHP_EOL;
+exec('phpunit Unit', $output, $returnCode);
+exec('phpunit Services', $output, $returnCode);
+exec('phpunit Feature', $output, $returnCode);
+exec('phpunit Inventory', $output, $returnCode);
+exec('phpunit OrderIssues', $output, $returnCode);
+exec('phpunit StoreService', $output, $returnCode);
+
+printf("测试结果: ", $projectName);
+echo PHP_EOL;
+
+if ($returnCode !== 0) {
+//    printf(" ( %s ) %s%2\$s", json_encode($output), PHP_EOL);
+    print_r("<pre>");
+    print_r($output);
+    printf(" x 测试不通过,Git Push 已终止!\n");
+
+    // 停止 Git
+    exit(1);
+}
+
+echo ' √ 测试通过!'.PHP_EOL;
+echo PHP_EOL;
+
+// 继续执行 Git
+exit(0);

+ 1 - 1
resources/views/demand/_create.blade.php

@@ -1,5 +1,5 @@
 @auth
-<div id="demand-div">
+<div id="demand-div" class="d-none">
     <div class="container-fluid position-absolute" style="min-width:10px;z-index:300;top: 100px;">
         <div class="float-right position-absolute" style="right: 50px" >
             <button class="btn-circle btn-outline-primary" type="button" id="dropdownMenu2" data-toggle="dropdown" style="width: 45px;height: 45px"

+ 1 - 1
resources/views/demand/_createjs.blade.php

@@ -17,9 +17,9 @@
             this.authoritiesFilter = JSON.parse(JSON.stringify(this.authorities));
             this.getAuthority();
             this.setUnClaimDemandRatio();
+            $('#demand-div').removeClass('d-none');
         },
         mounted() {
-            // this.setUnClaimDemandRatio();
         },
         methods: {
             /** 筛选 */

+ 78 - 34
resources/views/order/tracking/index.blade.php

@@ -11,7 +11,9 @@
                     <div class="modal-dialog modal-dialog-centered modal-lg" role="document">
                         <div class="modal-content">
                             <div class="modal-header row form-inline">
-                                <input type="text" v-model="name" class="form-control form-control-sm col-5 offset-3" placeholder="搜索货主,点击下方块添加" />
+                                <label>
+                                    <input type="text" v-model="name" class="form-control form-control-sm col-5 offset-3" placeholder="搜索货主,点击下方块添加">
+                                </label>
                                 <button class="btn btn-sm btn-info col-2" @click="seekOwner()">搜索</button>
                                 <label class="col-2"></label>
                             </div>
@@ -50,7 +52,9 @@
                 <table class="table table-sm table-striped table-bordered table-hover card-body p-0 m-0" id="headerParent">
                     <tr class="text-center" >
                         <th>
-                            <input id="all" type="checkbox" @click="checkAll($event)"/>
+                            <label>
+                                <input id="all" type="checkbox" @click="checkAll($event)">
+                            </label>
                         </th>
                         <th style="min-width: 50px">序号</th>
                         <th class="td-warm" style="min-width: 75px">公司</th>
@@ -71,6 +75,7 @@
                         <th class="td-yellow">运输单号</th>
                         <th class="td-yellow" style="min-width: 120px">到达城市</th>
                         <th class="td-yellow" style="min-width: 120px">照片</th>
+                        <th class="td-yellow" style="min-width: 120px">序列号</th>
                         <th class="td-calm" style="min-width: 200px">应送达时间</th>
                         <th class="td-calm" style="min-width: 120px">是否赶上卡班</th>
                         <th class="td-calm" style="min-width: 120px">到货情况</th>
@@ -81,13 +86,16 @@
                     <tr v-for="(trackOrder,index) in trackOrders" class="table-body text-center"  :key="trackOrder.id"
                         @click="selectTr===index+1?selectTr=0:selectTr=index+1" :class="selectTr===index+1?'focusing' : ''">
                         <td>
+                            <label for="all"></label>
                             <input id="all" type="checkbox" :value="trackOrder.id" v-model="checkData">
                         </td>
                         <td>@{{ index+1 }}</td>
                         <td class="text-decoration-none td-warm">@{{ trackOrder.ownerName }}</td>
                         <td class="td-warm">
                             <span v-if="trackOrder.web_order_number">
-                                <input class="form-control form-control-sm" :value="trackOrder.order_client_code === trackOrder.web_order_number ? '' : trackOrder.order_client_code " @change="warehouseUpdate($event,'order_client_code',trackOrder)" >
+                                <label>
+                                    <input class="form-control form-control-sm" :value="trackOrder.order_client_code === trackOrder.web_order_number ? '' : trackOrder.order_client_code " @change="warehouseUpdate($event,'order_client_code',trackOrder)" >
+                                </label>
                             </span>
                             <span v-else>@{{ trackOrder.order_client_code }}</span>
 {{--                            <span v-else>--}}
@@ -104,14 +112,18 @@
 
                         <td class="td-warm">
                             @can('订单管理-跟踪-仓库编辑')
-                                <input class="form-control form-control-sm" :value="trackOrder.web_order_number" @change="warehouseUpdate($event,'web_order_number',trackOrder)" >
+                                <label>
+                                    <input class="form-control form-control-sm" :value="trackOrder.web_order_number" @change="warehouseUpdate($event,'web_order_number',trackOrder)" >
+                                </label>
                             @else
                                 @{{ trackOrder.web_order_number }}
                             @endcan
                         </td>
                         <td class="td-warm">
                             @can('订单管理-跟踪-仓库编辑')
-                                <input class="form-control form-control-sm" type="datetime-local" :value="trackOrder.pick_up_at | dateLocal" @change="warehouseUpdate($event,'pick_up_at',trackOrder)" >
+                                <label>
+                                    <input type="datetime-local" class="form-control form-control-sm"  :value="trackOrder.pick_up_at | dateLocal" @change="warehouseUpdate($event,'pick_up_at',trackOrder)" >
+                                </label>
                             @else
                                 @{{ trackOrder.pick_up_at }}
                             @endcan
@@ -127,7 +139,9 @@
 {{--                        </td>--}}
                         <td class="td-yellow">
                             @can('订单管理-跟踪-仓库编辑')
+                            <label>
                                 <input class="form-control form-control-sm" :value="trackOrder.client" @change="warehouseUpdate($event,'client',trackOrder)" >
+                            </label>
                             @else
                                 @{{ trackOrder.client }}
                             @endcan
@@ -137,7 +151,9 @@
                         <td class="td-yellow">@{{ trackOrder.sku.skuAmount }}</td>
                         <td class="td-yellow " data-toggle="tooltip" >
                             @can('订单管理-跟踪-仓库编辑')
-                                <input class="form-control form-control-sm" :value="trackOrder.order_remark" @change="warehouseUpdate($event,'order_remark',trackOrder)" data-toggle="tooltip" data-placement="bottom" :title="trackOrder.order_remark">
+                                <label>
+                                    <input class="form-control form-control-sm" :value="trackOrder.order_remark" @change="warehouseUpdate($event,'order_remark',trackOrder)" data-toggle="tooltip" data-placement="bottom" :title="trackOrder.order_remark">
+                                </label>
                             @else
                                 @{{ trackOrder.order_remark }}
                             @endcan
@@ -146,7 +162,9 @@
                         <td class="td-yellow">@{{ trackOrder.bulk |millimeterToCubic}}</td>
                         <td class="td-yellow">
                             @can('订单管理-跟踪-仓库编辑')
-                                <input class="form-control form-control-sm" :value="trackOrder.pallet_total" @change="warehouseUpdate($event,'pallet_total',trackOrder)" >
+                                <label>
+                                    <input class="form-control form-control-sm" :value="trackOrder.pallet_total" @change="warehouseUpdate($event,'pallet_total',trackOrder)" >
+                                </label>
                             @else
                                 @{{ trackOrder.pallet_total }}
                             @endcan
@@ -178,57 +196,80 @@
                         <td class="td-yellow">
                             <div class="text-center" @mouseleave="removeCommonImg('common_img_'+trackOrder.id)" @mouseenter="commonImg('img_'+trackOrder.id,trackOrder)">
                             @can('订单管理-跟踪-物流公司编辑')
-                                <img v-if="trackOrder.upload_file" :id="'img_'+trackOrder.id" :data-src="trackOrder.upload_file.url+'-thumbnail.'+trackOrder.upload_file.type" src="{{url('icon/img404-thumbnail.jpg')}}">
+                                <img v-if="trackOrder.upload_file" :id="'img_'+trackOrder.id" :data-src="trackOrder.upload_file.url+'-thumbnail.'+trackOrder.upload_file.type" src="{{url('icon/img404-thumbnail.jpg')}}" alt="">
                                 <input v-else class="btn btn-sm btn-outline-secondary" type="button" @click="certiimg(trackOrder.id)" value="上传照片">
-                                <input type="file" @change="submitFile($event,trackOrder)" :id="trackOrder.id+'input'"
-                                       style="display: none" accept="image/gif,image/jpeg,image/jpg,image/png,image/svg"/>
+                                <input type="file" @change="submitFile($event,trackOrder)" :id="trackOrder.id+'input'" alt=""
+                                       style="display: none" accept="image/gif,image/jpeg,image/jpg,image/png,image/svg">
                             @else
-                                <img v-if="trackOrder.upload_file" :id="'img_'+trackOrder.id" :data-scr="trackOrder.upload_file.url" src="{{url('icon/img404-thumbnail.jpg')}}">
+                                <img v-if="trackOrder.upload_file" :id="'img_'+trackOrder.id" :data-scr="trackOrder.upload_file.url" src="{{url('icon/img404-thumbnail.jpg')}}" alt="">
                             @endcan
                             </div>
                         </td>
+                        <td class="td-yellow">
+                            <template v-if="trackOrder.commodities['serial_numbers'].length > 0">
+                                    <template v-for="serialNumber in trackOrder.commodities['serial_numbers']">
+                                        <transition name="fade">
+                                        <p v-show="trackOrder.displayed || trackOrder.commodities['serial_numbers'].length === 1">@{{ serialNumber.serial_number }}</p>
+                                        </transition>
+                                    </template>
+                                <p class="text-primary m-0 p-0" v-if="trackOrder.commodities.serial_numbers.length !== 1" @click="trackOrder.displayed = !trackOrder.displayed" style="cursor: pointer">@{{ trackOrder.displayed ? '点击隐藏' : '点击展开' }}</p>
+                            </template>
+                        </td>
                         <td class="td-calm">
                             @can('订单管理-跟踪-物流公司编辑')
-                                <input class="form-control form-control-sm" type="date"  @change="logisticUpdate($event,'planning_sent_at',trackOrder)"  :value="trackOrder.planning_sent_at | date" >
+
+                                <label>
+                                    <input class="form-control form-control-sm" type="date"  @change="logisticUpdate($event,'planning_sent_at',trackOrder)"  :value="trackOrder.planning_sent_at | date" >
+                                </label>
                             @else
                                 @{{ trackOrder.planning_sent_at }}
                             @endcan
                         </td>
                         <td class="td-calm">
                             @can('订单管理-跟踪-物流公司编辑')
-                                <select class="form-control form-control-sm" :value="trackOrder.is_on_duty_shift" @change="logisticUpdate($event,'is_on_duty_shift',trackOrder)" >
-                                    <option v-for="item in isOnDutyShift" :value="item">@{{ item }}</option>
-                                </select>
+                                <label>
+                                    <select class="form-control form-control-sm" :value="trackOrder.is_on_duty_shift" @change="logisticUpdate($event,'is_on_duty_shift',trackOrder)" >
+                                        <option v-for="item in isOnDutyShift" :value="item">@{{ item }}</option>
+                                    </select>
+                                </label>
                             @else
                                 @{{ trackOrder.is_on_duty_shift }}
                             @endcan
                         </td>
                         <td class="td-calm">
                             @can('订单管理-跟踪-物流公司编辑')
+                                <label>
                                 <select class="form-control form-control-sm" :value="trackOrder.is_arrival" @change="logisticUpdate($event,'is_arrival',trackOrder)" >
                                     <option v-for="item in isArrival" :value="item">@{{ item }}</option>
                                 </select>
+                                </label>
                             @else
                                 @{{ trackOrder.is_arrival }}
                             @endcan
                         </td>
                         <td class="td-calm">
                             @can('订单管理-跟踪-物流公司编辑')
-                                <input class="form-control form-control-sm" type="date"  @change="logisticUpdate($event,'signed_at',trackOrder)" :value="trackOrder.signed_at | date">
+                                <label >
+                                    <input class="form-control form-control-sm" type="date"  @change="logisticUpdate($event,'signed_at',trackOrder)" :value="trackOrder.signed_at | date">
+                                </label>
                             @else
                                 @{{ trackOrder.signed_at }}
                             @endcan
                         </td>
                         <td class="td-calm">
                             @can('订单管理-跟踪-物流公司编辑')
-                                <input class="form-control form-control-sm" @change="logisticUpdate($event,'receive_bill_status',trackOrder)" :value="trackOrder.receive_bill_status">
+                                <label>
+                                    <input class="form-control form-control-sm" @change="logisticUpdate($event,'receive_bill_status',trackOrder)" :value="trackOrder.receive_bill_status">
+                                </label>
                             @else
                                 @{{ trackOrder.receive_bill_status }}
                             @endcan
                         </td>
                         <td class="td-calm" data-toggle="tooltip">
                             @can('订单管理-跟踪-物流公司编辑')
-                                <input class="form-control form-control-sm" @change="logisticUpdate($event,'remark',trackOrder)" :value="trackOrder.remark" data-toggle="tooltip" data-placement="bottom" :title="trackOrder.remark">
+                                <label>
+                                    <input class="form-control form-control-sm" @change="logisticUpdate($event,'remark',trackOrder)" :value="trackOrder.remark" data-toggle="tooltip" data-placement="bottom" :title="trackOrder.remark">
+                                </label>
                             @else
                                 @{{ trackOrder.remark }}
                             @endcan
@@ -267,10 +308,17 @@
 @section('lastScript')
     <script type="text/javascript" src="{{mix('js/queryForm/export.js')}}"></script>
     <script type="text/javascript" src="{{mix('js/queryForm/queryForm.js')}}"></script>
+
     <style>
         .btn-border{
             border:1px solid rgba(108, 117, 125,0.3);
         }
+        .fade-enter-active, .fade-leave-active {
+            transition: opacity .3s;
+        }
+        .fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
+            opacity: 0;
+        }
     </style>
     <script>
         let tracking_vue = new Vue({
@@ -300,6 +348,9 @@
                 selectTr:''
             },
             mounted:function(){
+                this.trackOrders.forEach(function(item,index,self){
+                    self[index]['displayed'] = false;
+                });
                 this.imgs=Array.from(document.getElementById('tracking_div').querySelectorAll('img'));
                 this.lazy();
                 if (this.imgs&&this.imgs.length>0){
@@ -356,11 +407,7 @@
             watch:{
                 checkData: {
                     handler() {
-                        if (this.checkData.length === this.trackOrders.length && this.checkData.length !== 0) {
-                            document.querySelector('#all').checked = true;
-                        } else {
-                            document.querySelector('#all').checked = false;
-                        }
+                        document.querySelector('#all').checked = this.checkData.length === this.trackOrders.length && this.checkData.length !== 0;
                     },
                     deep: true
                 },
@@ -368,9 +415,9 @@
             methods:{
                 checkAll(e){
                     if (e.target.checked) {
-                        this.trackOrders.forEach((el, i) => {
-                            if (this.checkData.indexOf(el.id) === -1)
-                                this.checkData.push(el.id);
+                        this.trackOrders.forEach((el) => {
+                            if (this.checkData.indexOf(el['id']) === -1)
+                                this.checkData.push(el['id']);
                         });
                     } else {
                         this.checkData = [];
@@ -426,7 +473,7 @@
                 },
                 hideInput(e){
                     let input = $(e.target);
-                    let button = input.prev('button').show();
+                    input.prev('button').show();
                     input.addClass('d-none');
                 },
                 warehouseUpdate(e,param,trackingOrder){
@@ -497,7 +544,6 @@
                     this.trackOrders.forEach(function(trackOrder){
                         if(parseInt(data['id']) === trackOrder['id'] ){
                             trackOrder[data['param']] = data['value'];
-                            return;
                         }
                     });
                 },
@@ -525,7 +571,7 @@
                         value='新杰物流'
                     }
                     let data = {id:tackOrder.id,param:'logistic_id',value:value}
-                    let _this = this
+
                     axios.post('{{url('apiLocal/order/tracking/updateLogisticId')}}',data).then(function (response) {
                         tempTip.setDuration(2000);
                         if(response.data.success){
@@ -570,7 +616,6 @@
                                     },1);
                                 }
                             })
-                            console.log(_this.imgs);
                             tempTip.showSuccess('上传成功!')
                         }else{
                             tempTip.show(response.data.error)
@@ -630,7 +675,7 @@
                         if(response.data.success){
                             tempTip.showSuccess('删除成功!')
                             let _orderTracking = _this.trackOrders.find(function(_orderTrack){
-                                return id ==_orderTrack.id;
+                                return id === _orderTrack['id'];
                             });
                             if(_orderTracking===null){return}
                             _this.trackOrders.forEach(function (_orderTrack) {
@@ -669,9 +714,8 @@
                     return value
                 },
                 number:function(value){
-                    var toFixedNum = Number(value).toFixed(3);
-                    var realVal = toFixedNum.substring(0, toFixedNum.toString().length - 1);
-                    return realVal;
+                    let toFixedNum = Number(value).toFixed(3);
+                    return toFixedNum.substring(0, toFixedNum.toString().length - 1);
                 }
             },
 

+ 4 - 4
resources/views/procurement/procurement/index.blade.php

@@ -67,6 +67,9 @@
                     <input class="checkItem" type="checkbox" :value="procurement.id">
                 </td>
                 <td><span>@{{ procurement.code }}</span></td>
+                <td>
+                    <span :class="[procurement.status==10?'text-danger font-weight-bold':procurement.status==9?'text-success font-weight-bold':'']"  >@{{ procurement_status[procurement.status] }}</span>
+                </td>
                 <td><span v-if="procurement.owner_material.owner">@{{ procurement.owner_material.owner.name }}</span></td>
                 <td>@{{ procurement_type[procurement.type] }}</td>
 {{--                <td><span v-if="procurement.owner_material.owner">@{{ procurement.owner_material.owner.customer? procurement.owner_material.owner.customer.company_name:'' }}</span></td>--}}
@@ -97,9 +100,6 @@
                 <td><span>@{{ procurement.unit_price }}</span></td>
                 <td><span v-if="procurement.deliver_amount">@{{ procurement.deliver_amount }}</span></td>
                 <td><span>@{{ procurement.quantity*procurement.unit_price }}</span></td>
-                <td>
-                    <span :class="[procurement.status==10?'text-danger font-weight-bold':procurement.status==9?'text-success font-weight-bold':'']"  >@{{ procurement_status[procurement.status] }}</span>
-                </td>
                 <td><span v-if="procurement.owner_material.owner">@{{ procurement.owner_material.owner.customer? procurement.owner_material.owner.customer.phone:'' }}</span></td>
                 <td>
                     <span v-if="procurement_status[procurement.status]!='订单取消'">
@@ -202,6 +202,7 @@
                 this.form.init();
                 let column = [
                     {name: 'code', value: '采购编号', neglect: true,class: 'text-center'},
+                    {name: '采购单状态', value: '采购单状态', neglect: true,class: 'text-center'},
                     {name: 'owner_id', value: '项目', class: 'text-center'},
                     {name: 'type', value: '单据类型', class: 'text-center'},
                     // {name: 'company_name', value: '采购公司', class: 'text-center'},
@@ -217,7 +218,6 @@
                     {name: 'unit_price', value: '销售单价(元)', neglect: true,class: 'text-center'},
                     {name: '送货数量', value: '送货数量', neglect: true,class: 'text-center'},
                     {name: '销售总价', value: '销售总价(元)', neglect: true,class: 'text-center'},
-                    {name: '采购单状态', value: '采购单状态', neglect: true,class: 'text-center'},
                     {name: '联系方式', value: '联系方式', neglect: true,class: 'text-center'},
                     {name: '操作', value: '操作', neglect: true,class: 'text-center'},
                 ];

+ 5 - 4
resources/views/rejected/importRejectedNumber.blade.php

@@ -2,7 +2,8 @@
 @section('title')退回件-导入@endsection
 @section('content')
     <div id="nav2">
-        @component('order.issue.menu')@endcomponent
+        @component('rejected.menu')@endcomponent
+        @component('rejected.search.menu')@endcomponent
     </div>
     <div class="container-fluid" id="issue_import_div">
         <div class="card col-md-8 offset-md-2">
@@ -11,7 +12,7 @@
                     <div class="form-group row text-center">
                         <div class="col-12 text-danger">
                             退回单号修改<br/>
-                            【退回单号,原单单号】 必填项;【到付金额】 非必填项<br/>
+                            【退回单号,单号】 必填项;【到付金额】 非必填项<br/>
                             <span class="text-muted" style="opacity:0.7">
                                 如果在导入后出现部分数据导入失败,修改对应错误的数据继续导入即可,
                             </span>
@@ -63,8 +64,8 @@
         let listVue = new Vue({
             el:'#issue_import_div',
             data:{
-                rows:['退回单号','原单单号','到付金额'],
-                requiredRows:['退回单号','原单单号','到付金额'],
+                rows:['退回单号','单号','到付金额'],
+                requiredRows:['退回单号','单号','到付金额'],
                 popoverContent:'',
                 pasteDataText: '',
             },

+ 2 - 2
resources/views/rejected/search/general.blade.php

@@ -86,10 +86,10 @@
                         <td style="min-width: 900px">
                             <div class="w-100" :class="rejectedBill.items.length>1 ? 'up' : ''" :id="'rejected-'+rejectedBill.id">
                                 <div class="row" v-for="item in rejectedBill.items">
-                                    <div class="col-2 border border-1">
+                                    <div class="col-2 border border-1" style="overflow-x: hidden">
                                         <div class="w-100 text-overflow-warp-200 warp-min-200">@{{item.barcode_goods}}</div>
                                     </div>
-                                    <div class="col-2 border border-1">
+                                    <div class="col-2 border border-1" style="overflow-x: hidden">
                                         <div class="w-100 text-overflow-warp-200 warp-min-200">@{{item.name_goods}}</div>
                                     </div>
                                     <div class="col-1 border border-1">@{{item.amount}}</div>

+ 42 - 0
resources/views/station/cachingShelf/list/_fillBox.blade.php

@@ -0,0 +1,42 @@
+<div class="modal face" id="box">
+    <div class="modal-dialog ":class="boxClass">
+        <div class="modal-content">
+            <div class="modal-header">
+                <div class="modal-title">入料箱</div>
+                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+                    <span aria-hidden="true">&times;</span>
+                </button>
+            </div>
+            <div class="modal-body">
+                <div class="form-group">
+                    <label for="material-box">扫描条码</label>
+                    <div class="form-inline">
+                        <input type="text" id="material-box" class="form-control col" @keydown.enter="pushMaterialBox()" v-model="materialBoxCode">
+                    </div>
+                </div>
+                <hr>
+                <div class="container">
+                    <template v-if="currentStation.data ">
+                        <div class="row mt-1 mb-1" v-for="(rows,rowI) in currentStation.data">
+                            <template v-if="rows.length !== 0">
+                                <div class="col" v-for="(item,colI) in rows" :y="item.row" :x="item.col">
+                                    <textarea class="form-control font-weight-bold"
+                                              style="font-size: 35px"
+                                              @keyup.enter="pushBoxToStation(item.row,item.col,$event)"
+                                              :value="item.code"
+                                              :placeholder="item.index"
+                                              :class="item.code? 'border-success ':''"
+                                        ></textarea>
+                                </div>
+                            </template>
+                        </div>
+                    </template>
+                </div>
+                <div class="modal-footer">
+                    <button class="btn btn-primary" data-dismiss="modal">关闭</button>
+                    <button class="btn btn-primary" @click="clearCurrentStation">清空</button>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>

+ 61 - 0
resources/views/station/cachingShelf/list/_table.blade.php

@@ -0,0 +1,61 @@
+<table class="table table-striped table-md table-hover" id="table">
+    <thead>
+        <tr>
+            <th>
+                设备id
+            </th>
+            <th>
+                入料箱
+            </th>
+            <th>
+                名称
+            </th>
+            <th>
+                父级设备
+            </th>
+            <th>
+                硬件id
+            </th>
+            <th>
+                备注
+            </th>
+            <th>
+                录入时间
+            </th>
+        </tr>
+        <template v-if="stations.length > 0">
+            <tr v-for="(station,i) in stations" @click="selectTr===i+1?selectTr=0:selectTr=i+1" :class="selectTr===i+1?'focusing' : ''">
+                <td>
+                    @{{ station.id }}
+                </td>
+                <td>
+                    <button class="btn btn-primary" @click="showFeedBox(station)">入料箱</button>
+                </td>
+                <td>
+                    @{{ station.name }}
+                </td>
+                <td>
+                    @{{ station.parent ? station.parent.name  : ''}}
+                </td>
+                <td>
+                    @{{ station.parent ? station.parent.id  : ''}}
+                </td>
+                <td>
+                    @{{ station.sequence ? station.sequence : ''}}
+                </td>
+                <td>
+                    @{{ station.created_at }}
+                </td>
+            </tr>
+        </template>
+        <template v-else>
+            <tr>
+                <td colspan="2">
+                    <div class="alert alert-info">
+                        查询数据为空
+                    </div>
+                </td>
+            </tr>
+        </template>
+    </thead>
+</table>

+ 190 - 0
resources/views/station/cachingShelf/list/index.blade.php

@@ -0,0 +1,190 @@
+@extends('layouts.app')
+
+@section('title','列表')
+
+@section('content')
+    <div id="nav2">
+        @component('station.menu')@endcomponent
+        @component('station.cachingShelf.menu')@endcomponent
+    </div>
+    <div class="container-fluid d-none" id="list">
+        @include('station.cachingShelf.list._table')
+        @include('station.cachingShelf.list._fillBox')
+    </div>
+@endsection
+
+@section('lastScript')
+    <script>
+        new Vue({
+            el: '#list',
+            data: {
+                stations: {!! $stations->toJson() !!}['data'],
+                selectTr: null,
+                currentEcho:null,
+                materialBoxCode: null,      // 料箱编码
+                currentStation: {           // 当前Station
+                    id: null,               // Station->id
+                    data: [[], [], []],     // 格口
+                },
+                boxClass: 'modal-sm',
+                broadcastName: null,
+                channelName: ".App\\Events\\BroadcastToStation",
+
+            },
+            mounted() {
+                this.initCacheShelfBox();
+                this.initModalBox();
+                $('#list').removeClass('d-none');
+            },
+            methods: {
+                /** 1初始化缓存架格口 */
+                initCacheShelfBox() {
+                    this.currentStation.data.forEach(function (item, index, self) {
+                        let boxRow = new Array(3);
+                        for (let i = 0; i < boxRow.length; i++) {
+                            let row =  2 - index + 1;
+                            let col =  2 - i + 1;
+                            let myIndex = index * 3 + i + 1;
+                            boxRow[i] = {x: index, y: i, row: row, col: col, code: null, index: myIndex};
+                        }
+                        self[index] = boxRow;
+                    });
+                },
+                /** 2初始化modal大小 */
+                initModalBox() {
+                    if (window.screen.width >= 520) this.boxClass = 'modal-sm';
+                    if (window.screen.width >= 720) this.boxClass = 'modal-md';
+                    if (window.screen.width >= 960) this.boxClass = 'modal-lg';
+                    if (window.screen.width >= 1200) this.boxClass = 'modal-xl';
+                },
+                /** 展示modal */
+                showFeedBox(station) {
+                    this.currentStation.id = station['id'];
+                    this.broadcastName = "{{config('database.redis.options.prefix')}}" + 'station-' + station['id'];
+                    this.initCacheGrid();
+                    $('#box').modal('show');
+                },
+                /** 清空格口号 */
+                clearCurrentStation() {
+                    this.currentStation.data.forEach(function (item, index, items) {
+                        items[index].forEach(function (item, index, self) {
+                            self[index]['code'] = null;
+                        })
+                    });
+                    this.$forceUpdate();
+                },
+                /** 初始化格口 */
+                initCacheGrid() {
+                    let url = '{{url('apiLocal/station/cacheShelf/getTasks')}}'+'/'+this.currentStation.id;
+                    window.tempTip.setIndex(1099);
+                    window.tempTip.setDuration(3000);
+                    window.axios.get(url).then(res => {
+                        if (res.data.data) {
+                            if (res.data.data.length === 0) return;
+                            for (let i = 0; i < res.data.data.length; i++) {
+                                if(res.data.data.length === i)break;
+                                let grid = res.data.data[i];
+                                let gridIndex = grid['grid_id'];
+                                let row = Math.round(gridIndex / 3);
+                                let col = gridIndex % 3 -1;
+                                let code = grid['material_box'] ? grid['material_box']['code'] : null;
+                                if(grid['status'] === 1) this.$set(this.currentStation.data[row][col], 'code', code);
+                            }
+                            this.$forceUpdate();
+                            return;
+                        }
+                        window.tempTip.show('获取缓存货架信息失败');
+                    }).catch(err => {
+                        window.tempTip.show('获取缓存货架信息异常:' + err);
+                    });
+                },
+                /** 扫码 1、料箱扫码 */
+                pushMaterialBox($e) {
+                    let code = this.materialBoxCode;
+                    if (code === null) {
+                        $('#material-box').focus();
+                    }
+                    // 自动排号
+                    this.pushBoxToStation(null,null,$e);
+                },
+                /** 扫码 2、填写九宫格 */
+                pushBoxToStation(x = null, y = null, $e = null) {
+                    let index = null;
+                    // 自动排号和自动发送
+                    // 选排
+                    if (x !== null && y !== null) {
+                        let code = $($e.target).val();
+                        index = this.currentStation.data[x][y]['index'];
+                        this.fillBox(code,index,x,y);
+                        return;
+                    }
+                    // 自动排号
+                    let arr = [];
+                    this.currentStation.data.forEach(function (items) {
+                        items.forEach(function (item) {
+                            if (item['code'] === null) {
+                                arr.push(item);
+                            }
+                        });
+                    })
+                    x = arr[0]['x'];
+                    y = arr[0]['y'];
+                    index = arr[0]['index'];
+                    this.fillBox(this.materialBoxCode,index,x,y);
+                },
+                /** 扫码 4:填充页面 */
+                fillBox(code,index,x,y){
+                    this.$set(this.currentStation.data[x][y], 'code', this.materialBoxCode);
+                    this.lightOn(this.currentStation.id,code,index,x,y);
+                    this.$forceUpdate();
+                },
+                /** 扫码 3、亮灯 */
+                lightOn(id,code,index,x,y) {
+                    let url = '{{url('apiLocal/station/cacheShelf/lightOn')}}';
+                    let data = {id:id, code:code, index:index, x:x, y:y};
+                    window.tempTip.setDuration(2000);
+                    window.tempTip.setIndex(1999);
+                    window.axios.post(url,data).then(res=>{
+                        if(res.data.success){
+                            window.tempTip.showSuccess(res.data.data);
+                            this.materialBoxCode = null;
+                            return ;
+                        }
+                        window.tempTip.show('亮灯异常:'+res.data.data);
+                    }).catch(err=>{
+                        window.tempTip.show(err);
+                    });
+                },
+                /** 校验是否存在 */
+                checkMaterialBoxCodeExist(code) {
+                    let bool = false;
+                    for (let x = 0; x < this.currentStation.data.length; x++) {
+                        if (!isArray(this.currentStation.data[x])) {
+                            continue;
+                        }
+                        if (this.currentStation.data[x].length > 0) {
+                            for (let y = 0; y < this.currentStation.data[y].length; y++) {
+                                if (this.currentStation.data[x][y]['code'] === code) return true;
+                            }
+                        }
+                    }
+                    return bool;
+                },
+                removeMaterialBox(x,y){
+                    this.$set(this.currentStation.data[x][y],'code',null);
+                },
+                /** 监听 【灭灯】 */
+                _listenBroadcast() {
+                    initEcho();
+                    if(this.currentEcho)this.currentEcho.disconnect();
+                    this.currentEcho = window.Echo.channel(this.broadcastName).listen(this.channelName, (msg) => {
+                        let json = JSON.parse(msg.json);
+                        if (!json || json.length === 0) return;
+                        this.removeMaterialBox(json['x'],json['y']);
+                    });
+                },
+            }
+        })
+    </script>
+@endsection
+

+ 9 - 0
resources/views/station/cachingShelf/menu.blade.php

@@ -0,0 +1,9 @@
+<div class="container-fluid nav3">
+    <div class="card">
+        <ul class="nav nav-pills">
+            <li class="nav-item">
+                <a target="station/cachingShelf/index" class="nav-link" href="{{url('station/cachingShelf/index')}}" :class="{active:isActive('cachingShelf',2)}">列表</a>
+            </li>
+        </ul>
+    </div>
+</div>

+ 1 - 1
resources/views/station/menu.blade.php

@@ -8,7 +8,7 @@
                 </li> @endcan
             @can('站管理-缓存架')
                 <li class="nav-item">
-                    <a target="station/cachingShelf/index" class="nav-link" href="{{url('station/monitor/index')}}" :class="{active:isActive('monitor',2)}">缓存架</a>
+                    <a target="station/cachingShelf/index" class="nav-link" href="{{url('station/cachingShelf/index')}}" :class="{active:isActive('cachingShelf',2)}">缓存架</a>
                 </li> @endcan
         </ul>
     </div>

+ 5 - 9
resources/views/transport/waybill/index.blade.php

@@ -85,13 +85,9 @@
                         </td>
                     @endcan
                     <td :class="[waybill.status=='已审核'?'text-success':'']"><span>@{{waybill.status}}</span></td>
-                    <td class="td-warm text-muted toptd">
-                        <div>
-                            <div v-if="waybill.remark" class="bg-light-yellow  text-danger top"  data-toggle="tooltip" style="opacity: 0.1;position: absolute;z-index: 1"
-                            >置顶备注:@{{  waybill.remark }}</div>
-                        @{{ i+1 }}
-                        </div>
-                    </td>
+                    <td class="td-warm text-muted toptd position-static">
+                        <div v-if="waybill.remark" class="bg-light-yellow  text-danger top position-absolute"  data-toggle="tooltip" style="opacity: 0.1;z-index: 1">置顶备注:@{{  waybill.remark }}</div>
+                        @{{ i+1 }}</td>
                     <td class="td-warm text-muted"><span>@{{waybill.created_at}}</span></td>
                     <td class="td-warm text-muted">
                         <div>
@@ -266,9 +262,9 @@
         .top{
             padding-top: 0;
             padding-left: 10px;
-            margin-top: -4px;
+            margin-top: -32px;
             margin-left: -5px;
-            line-height: 55px;
+            line-height: 50px;
 
 
             position: absolute;

+ 9 - 0
routes/apiLocal.php

@@ -181,3 +181,12 @@ Route::group(['prefix'=>'demand'],function(){
 });
 
 Route::get('/authority/get','AuthorityController@getAuthoritiesApi')->name('authority.getAuthoritiesApi');
+
+/** 缓存架 */
+Route::group(['prefix' => 'station'],function(){
+    Route::group(['prefix'=>'cacheShelf'],function(){
+        Route::post('pushTask','CacheShelfController@pushTaskApi')->name('station.cacheShelf.pushTaskApi');
+        Route::post('lightOn','CacheShelfController@lightOnApi')->name('station.cacheShelf.lightOnApi');
+        Route::get('getTasks/{id}','CacheShelfController@getTasksApi')->name('station.cacheShelf.getTasksApi');
+    });
+});

+ 3 - 0
routes/web.php

@@ -804,6 +804,9 @@ Route::group(['prefix'=>'station'],function(){
         Route::get('/index','StationController@monitorIndex');
         Route::get('/{station}','StationController@monitorShow');
     });
+    Route::group(['prefix'=>'cachingShelf'],function(){
+        Route::get('/index','CacheShelfController@index');
+    });
 });
 /** 控制台 */
 Route::group(['prefix'=>'control'],function () {

+ 1 - 1
syncProject.sh

@@ -3,7 +3,7 @@ Ip="was.baoshi56.com"
 username="haozi"
 password="haozi"
 files=("app" "config" "database" "public" "resources" "routes" "webpack.mix.js" "composer.json" "tests")
-src=/var/www/bswas_test
+src=/var/www/bswas_test_
 dsc=/var/www/was
 cd $src
 for i in ${files[*]}

+ 49 - 0
tests/Services/CacheShelfService/GetTasksTest.php

@@ -0,0 +1,49 @@
+<?php
+
+
+namespace Tests\Services\CacheShelfService;
+
+use App\Services\CacheShelfService;
+use App\Station;
+use App\StationCacheShelfGrid;
+use Tests\TestCase;
+
+class GetTasksTest extends TestCase
+{
+    /** @var CacheShelfService $service */
+    protected $service;
+
+    protected $data = [];
+
+    protected function setup(): void
+    {
+        parent::setup();
+
+        $this->service = app(CacheShelfService::class);
+        $this->data['station'] = factory(Station::class)->create();
+        $collect = collect();
+        for ($i = 1; $i <= 8; $i++) {
+            $collect->push(StationCacheShelfGrid::query()->create([
+                'station_id' => $this->data['station']['id'] ?? '',
+                'grid_id' => $i,
+                'status' => 1,
+            ]));
+        }
+        $this->data['grids'] = $collect;
+    }
+
+    public function testGetTasks()
+    {
+        $grids = $this->service->getTasks($this->data['station']);
+        $this->assertEquals(count($grids), count($this->data['grids']));
+    }
+
+    protected function tearDown(): void
+    {
+        Station::query()->where('id', $this->data['station']['id'])->delete();
+        StationCacheShelfGrid::query()->where('station_id', $this->data['station']['id'])->delete();
+        parent::tearDown(); // TODO: Change the autogenerated stub
+    }
+
+
+}

+ 70 - 0
tests/Services/CacheShelfService/LightOffTaskTest.php

@@ -0,0 +1,70 @@
+<?php
+
+
+namespace Tests\Services\CacheShelfService;
+
+use App\MaterialBox;
+use App\Services\CacheShelfService;
+use App\Services\ForeignHaiRoboticsService;
+use App\Services\StationCacheShelfGridService;
+use App\Station;
+use App\StationCacheShelfGrid;
+use App\StationTaskMaterialBox;
+use App\Traits\TestMockSubServices;
+use Tests\TestCase;
+
+
+class LightOffTaskTest extends TestCase
+{
+    use TestMockSubServices;
+
+    /** @var CacheShelfService $service */
+    protected $service;
+    /** @var ForeignHaiRoboticsService $foreignHaiRoboticsService  */
+    protected $foreignHaiRoboticsService;
+
+    protected $data = [];
+
+    protected function setup(): void
+    {
+        parent::setup();
+        $this->service = $this->subMock([
+            'class' => CacheShelfService::class,
+            'subService' => [
+                'serviceName' => 'foreignHaiRoboticsService',
+                'class' => ForeignHaiRoboticsService::class,
+                'methods'=>[
+                    'controlHaiRobot' =>true
+                ]
+            ]
+        ]);
+        $row = 2;
+        $col = 1;
+        $gridIndex = ($row-1)*3 + (3-$col);
+        $this->data['station'] = factory(Station::class)->create();
+        $this->data['materialBox'] = factory(MaterialBox::class)->create();
+        $this->data['stationCacheShelfGrid'] = factory(StationCacheShelfGrid::class)
+            ->create(['station_id' => $this->data['station']['id'],'grid_id'=>$gridIndex,'material_box_id'=>$this->data['materialBox']['id']]);
+        $this->data['locCode'] = 'HAI'.$this->data['station']['code'].'-0'.$col.'-0'.$row;
+        $this->data['PTLAction'] = 0;
+    }
+
+    public function testLightOffTask()
+    {
+        $this->service->lightOffTask($this->data['locCode'],$this->data['PTLAction']);
+        $grid = StationCacheShelfGrid::query()->where('id',$this->data['stationCacheShelfGrid']['id'])->first();
+        $task = StationTaskMaterialBox::query()->where('station_id',$this->data['station']['id'])->where('material_box_id',$this->data['materialBox']['id'])->first();
+        $this->assertNotEmpty($task);
+        $this->assertEquals($task['status'],'处理中');
+        $this->assertEquals($grid['material_id'],null);
+    }
+
+    protected function tearDown(): void
+    {
+        Station::query()->where('id',$this->data['station']['id'])->delete();
+        StationCacheShelfGrid::query()->where('id',$this->data['stationCacheShelfGrid']['id'])->delete();
+        MaterialBox::query()->where('id',$this->data['materialBox']['id'])->delete();
+        StationTaskMaterialBox::query()->where('station_id',$this->data['station']['id'])->where('material_box_id',$this->data['materialBox'])->delete();
+        parent::tearDown(); // TODO: Change the autogenerated stub
+    }
+}

+ 88 - 0
tests/Services/CacheShelfService/PutBinToStoreTest.php

@@ -0,0 +1,88 @@
+<?php
+
+
+namespace Tests\Services\CacheShelfService;
+use App\MaterialBox;
+use App\Services\CacheShelfService;
+use App\Services\ForeignHaiRoboticsService;
+use App\Services\StationCacheShelfGridService;
+use App\Services\StationTaskMaterialBoxService;
+use App\Station;
+use App\StationCacheShelfGrid;
+use App\StationTaskMaterialBox;
+use App\Traits\TestMockSubServices;
+use Tests\TestCase;
+
+
+class PutBinToStoreTest extends TestCase
+{
+    use TestMockSubServices;
+    /** @var CacheShelfService $cacheShelfService */
+    protected $cacheShelfService;
+    protected $data =[];
+    protected function setup(): void
+    {
+        parent::setup(); // todo: change the autogenerated stub
+        $this->data['station'] = factory(Station::class)->create(['name' => 'test', 'code'=> 'test']);
+        $this->data['materialBox'] = factory(MaterialBox::class)->create();
+        $this->data['stationTaskMaterialBox'] = factory(StationTaskMaterialBox::class)->create([
+            'station_id' => $this->data['station']['id'],
+            'material_box_id' => $this->data['materialBox']['id'],
+            'status' => 1
+        ]);
+        $this->data['stationCacheShelfGrid'] = factory(StationCacheShelfGrid::class)->create([
+            'station_id'=>$this->data['station']['id'],
+            'material_box_id'=>$this->data['materialBox']['id'],
+            'status' => 1
+        ]);
+
+
+
+        $this->cacheShelfService = $this->subMock([
+            'class' => CacheShelfService::class,
+            'subService' => [
+//                [
+//                    'serviceName' => 'stationTaskMaterialBoxService',
+//                    'class' => StationTaskMaterialBoxService::class,
+//                    'methods' => [
+//                        'createByStationMaterialBox' => $this->data['stationTaskMaterialBox']
+//                    ]
+//                ],
+                [
+                    'serviceName' => 'stationCacheShelfGridService',
+                    'class' => StationCacheShelfGridService::class,
+                    'methods' => [
+                        'processGrid' => null
+                    ]
+                ],
+                [
+                    'serviceName' => 'foreignHaiRoboticsService',
+                    'class' => ForeignHaiRoboticsService::class,
+                    'methods' => [
+//                        'putBinToStore_fromCacheShelf' => true,
+                        'controlHaiRobot' => true
+                    ]
+                ]
+            ]
+        ]);
+    }
+
+    public function testPutBinToStore()
+    {
+        $bool = $this->cacheShelfService->putBinToStore($this->data['station'],$this->data['materialBox'],$this->data['stationCacheShelfGrid']);
+        $boxTask = StationTaskMaterialBox::query()->where('station_id',$this->data['station']['id'])->where('material_box_id',$this->data['materialBox']['id'])->first();
+        $this->assertTrue($bool);
+        $this->assertNotEmpty($boxTask);
+        $this->assertEquals($boxTask['status'],'待处理');
+    }
+
+    protected function tearDown(): void
+    {
+        Station::query()->where('id',$this->data['station']['id'])->delete();
+        MaterialBox::query()->where('id',$this->data['materialBox']['id'])->delete();
+        StationCacheShelfGrid::query()->where('id',$this->data['stationCacheShelfGrid']['id'])->delete();
+        StationTaskMaterialBox::query()->where('id',$this->data['stationTaskMaterialBox']['id'])->delete();
+        parent::tearDown();
+    }
+
+}

+ 62 - 0
tests/Services/ForeignHaiRoboticsService/PutBinToStoreFromCacheShelfTest.php

@@ -0,0 +1,62 @@
+<?php
+
+
+namespace Tests\Services\ForeignHaiRoboticsService;
+
+use App\Exceptions\ErrorException;
+use App\MaterialBox;
+use App\Services\ForeignHaiRoboticsService;
+use App\Services\StationService;
+use App\Station;
+use App\StationCacheShelfGrid;
+use App\StationTaskMaterialBox;
+use App\Traits\TestMockSubServices;
+use Tests\TestCase;
+
+class PutBinToStoreFromCacheShelfTest extends TestCase
+{
+    use TestMockSubServices;
+
+    /** @var ForeignHaiRoboticsService $service */
+    protected $service;
+    protected $data = [];
+
+    protected function setUp(): void
+    {
+        parent::setUp();
+        $this->service = $this->subMock([
+            'class' => ForeignHaiRoboticsService::class,
+            'subService' => [
+                'serviceName' => 'StationService',
+                'class' => StationService::class,
+                'methods' => [
+                    'controlHaiRobot' => true
+                ],
+            ]
+        ]);
+        $this->data['station'] = factory(Station::class)->create();
+        $this->data['materialBox'] = factory(MaterialBox::class)->create();
+        $this->data['grids'] = factory(StationCacheShelfGrid::class)->create();
+        $this->data['stationTaskMaterialBox'] = factory(StationTaskMaterialBox::class)
+            ->create(['station_id'=>$this->data['station']['id']]);
+        $this->data['station']->setRelation('grids',collect([$this->data['grids']]));
+        $this->data['stationTaskMaterialBox']->setRelation('station', $this->data['station']);
+        $this->data['stationTaskMaterialBox']->setRelation('materialBox', $this->data['materialBox']);
+    }
+
+    public function testSuccess1()
+    {
+        $success = $this->service->putBinToStore_fromCacheShelf($this->data['stationTaskMaterialBox']);
+        $this->assertEquals($success,true);
+    }
+
+
+    protected function tearDown(): void
+    {
+        Station::query()->where('id',$this->data['station']['id'])->delete();
+        StationCacheShelfGrid::query()->where('id',$this->data['grids']['id'])->delete();
+        MaterialBox::query()->where('id',$this->data['materialBox']['id'])->delete();
+        StationTaskMaterialBox::query()->where('id',$this->data['stationTaskMaterialBox']['id'])->delete();
+        parent::tearDown();
+    }
+}

+ 94 - 0
tests/Services/OrderPackageCommoditySerialNumberService/GetCreateModelBy.php

@@ -0,0 +1,94 @@
+<?php
+
+
+namespace Tests\Services\OrderPackageCommoditySerialNumberService;
+
+use App\Commodity;
+use App\OracleActAllocationDetails;
+use App\OracleDOCOrderHeader;
+use App\OracleDocOrderSerialNo;
+use App\Order;
+use App\OrderPackage;
+use App\OrderPackageCommodities;
+use App\Services\OrderPackageCommoditySerialNumberService;
+use \Tests\TestCase;
+
+class GetCreateModelBy extends TestCase
+{
+    /** @var OrderPackageCommoditySerialNumberService $service */
+    protected $service ;
+    public $data = [];
+
+    protected function setUp(): void
+    {
+        parent::setUp(); // TODO: Change the autogenerated stub
+        $this->service = app(OrderPackageCommoditySerialNumberService::class);
+        $orderHeaders = factory(OracleDOCOrderHeader::class)->times(3)->make();
+        $count= 0;
+
+        foreach ($orderHeaders as $orderHeader) {
+            $actAllocationDetails =  factory(OracleActAllocationDetails::class)->times(3)->make(['orderno'=>$orderHeader['orderno']]);
+            foreach ($actAllocationDetails as $actAllocationDetail) {
+                $oracleDocOrderSerialNos = factory(OracleDocOrderSerialNo::class)->times(3)
+                    ->make(['allocationdetailsid'=>$actAllocationDetail['allocationdetailsid'],'sku'=>$actAllocationDetail['sku']]);
+                $count+=3;
+                $actAllocationDetail->setRelation('oracleDocOrderSerialNos',$oracleDocOrderSerialNos);
+            }
+            $orderHeader->setRelation('actAllocationDetails',$actAllocationDetails);
+        }
+        $orders = [];
+        $orderPackages = [];
+        $commodities = [];
+        $orderPackageCommodities = [];
+        foreach ($orderHeaders as $orderHeader) {
+            $order = factory(Order::class)->create(['code'=>$orderHeader['orderno']]);
+            $orders[] = $order;
+            foreach ($orderHeader['actAllocationDetails'] as $actAllocationDetail) {
+                $commodity = factory(Commodity::class)->create(['sku'=>$actAllocationDetail['sku']]);
+                $orderPackage =  factory(OrderPackage::class)->create(['order_id' => $order['id'],'logistic_number'=> $actAllocationDetail['picktotraceid']]);
+                $orderPackageCommodity = factory(OrderPackageCommodities::class)
+                    ->create(['order_package_id'=>$orderPackage['id'],'commodity_id'=>$commodity['id'],'amount'=>$actAllocationDetail['qty_each']]);
+                $orderPackageCommodities[] = $orderPackageCommodity;
+                $orderPackages[] =$orderPackage;
+                $commodities[] = $commodity;
+            }
+        }
+        $this->data['count'] = $count;
+        $this->data['orders'] = $orders;
+        $this->data['orderPackages'] = $orderPackages;
+        $this->data['orderPackages'] = $orderPackages;
+        $this->data['commodities'] = $commodities;
+        $this->data['orderPackageCommodities'] = $orderPackageCommodities;
+        $this->data['orderHeaders'] = $orderHeaders;
+    }
+
+    public function testGetInnerParams()
+    {
+        $orderQuery = Order::query()->selectRaw('id')->whereIn('code', data_get($this->data['orderHeaders'], '*.orderno'));
+        $orderPackages = OrderPackage::query()->with(['commodities' => function ($query) {
+            $query->with('commodity', 'serialNumbers');
+        }])->whereIn('Order_Packages.Order_id', $orderQuery)->get();
+//        $this->tearDown();
+        $innerParams = $this->service->getCreateModelBy($this->data['orderHeaders'],$orderPackages);
+        foreach ($this->data['orderHeaders'] as $orderHeader){
+            foreach ($orderHeader['actAllocationDetails'] as $actAllocationDetail) {
+                $logisticNumber = $actAllocationDetail['picktotraceid'];
+                $sku = $actAllocationDetail['sku'];
+                foreach ($actAllocationDetail['oracleDocOrderSerialNos'] as $oracleDocOrderSerialNo){
+                    $serialno = $oracleDocOrderSerialNo['serialno'];
+                    $this->assertNotEmpty($innerParams[$logisticNumber][$sku][$serialno]);
+                }
+            }
+        }
+        $this->assertNotEmpty($innerParams);
+    }
+
+    protected function tearDown():void
+    {
+        Order::query()->whereIn('id',data_get($this->data['orders'],'*.id'))->delete();
+        OrderPackage::query()->whereIn('logistic_number',data_get($this->data['orderPackages'],'*.logistic_number'))->delete();
+        OrderPackageCommodities::query()->whereIn('id',data_get($this->data['orderPackageCommodities'],'*.id'))->delete();
+        Commodity::query()->whereIn('id',data_get($this->data['commodities'],'*.id'))->delete();
+        parent::tearDown();
+    }
+}

+ 38 - 0
tests/Services/StationCacheShelfGrid/GetGridByCodeTest.php

@@ -0,0 +1,38 @@
+<?php
+
+
+namespace Tests\Services\StationCacheShelfGrid;
+
+use App\Services\StationCacheShelfGridService;
+use App\StationCacheShelfGrid;
+use Tests\TestCase;
+
+class GetGridByCodeTest extends TestCase
+{
+    protected $data = [];
+
+    protected function setup(): void
+    {
+        parent::setUp();
+        $this->data['stationCode'] = 'station001';
+        $this->data['grid'] = 6;
+        $this->data['x'] = 2;
+        $this->data['y'] = 3;
+        $this->data['code'] = 'HAI'.$this->data['stationCode'].'-0'.$this->data['y'].'-0'.$this->data['x'];
+
+    }
+
+    function testGetGridByCodeTest()
+    {
+        list($stationCode, $gridId, $x, $y) = StationCacheShelfGrid::getGridByCode($this->data['code']);
+        $this->assertEquals($stationCode,$this->data['stationCode']);
+        $this->assertEquals($gridId,$this->data['grid']);
+        $this->assertEquals($x,$this->data['x']);
+        $this->assertEquals($y,$this->data['y']);
+    }
+
+    protected function tearDown(): void
+    {
+        parent::tearDown();
+    }
+}

+ 39 - 0
tests/Services/StationCacheShelfGrid/GetLocationTest.php

@@ -0,0 +1,39 @@
+<?php
+
+
+namespace Tests\Services\StationCacheShelfGrid;
+
+use App\Station;
+use App\StationCacheShelfGrid;
+use Tests\TestCase;
+
+class GetLocationTest extends TestCase
+{
+
+    protected $data = [];
+
+    protected function setUp(): void
+    {
+        parent::setUp();
+        $row = 3;
+        $col = 2;
+        $this->data['grid_id'] = ($row-1)*3+$col;
+        $this->data['station'] = factory(Station::class)->create();
+        $this->data['grid'] = factory(StationCacheShelfGrid::class)->create(['station_id'=>$this->data['station']['id'],'grid_id'=>$this->data['grid_id']]);
+        $this->data['code'] = 'HAI'.$this->data['station']['code'].'-0'. (2 - $col + 1).'-0'.(2-$row+1);
+    }
+
+    function testGetLocation()
+    {
+        $location = StationCacheShelfGrid::getLocation($this->data['station'],$this->data['grid']);
+        $this->assertEquals($this->data['grid_id'],$this->data['grid']['grid_id']);
+        $this->assertEquals($this->data['code'],$location);
+    }
+
+    protected function tearDown(): void
+    {
+        Station::query()->where('id',$this->data['station']['id'])->delete();
+        StationCacheShelfGrid::query()->where('id',$this->data['grid']['id'])->delete();
+        parent::tearDown();
+    }
+}

+ 39 - 0
tests/Services/StationCacheShelfGridService/CancelTaskTest.php

@@ -0,0 +1,39 @@
+<?php
+
+
+namespace Tests\Services\StationCacheShelfGridService;
+
+use App\Services\StationCacheShelfGridService;
+use App\StationCacheShelfGrid;
+use App\Traits\TestMockSubServices;
+use Tests\TestCase;
+
+class CancelTaskTest extends TestCase
+{
+    use TestMockSubServices;
+    /** @var StationCacheShelfGridService $stationCacheShelfGridService */
+    protected $stationCacheShelfGridService;
+    protected $data =[];
+
+    protected function setup(): void
+    {
+        parent::setUp();
+        $this->stationCacheShelfGridService = app(StationCacheShelfGridService::class);
+        $this->data['grids'] = factory(StationCacheShelfGrid::class)->times(3)->create();
+
+    }
+
+    public function testCancelTask()
+    {
+        $this->stationCacheShelfGridService->cancelTask($this->data['grids']);
+        $grids = StationCacheShelfGrid::query()->whereIn('id',data_get($this->data['grids'],'*.id'))->whereNull('material_box_id')->where('status',0)->count();
+        $this->assertEquals($grids, count($this->data['grids']));
+    }
+
+    protected function tearDown(): void
+    {
+        StationCacheShelfGrid::query()->where('id',data_get($this->data['grids'],'*.id'))->delete();
+        parent::tearDown();
+    }
+
+}

+ 47 - 0
tests/Services/StationCacheShelfGridService/LightOffTest.php

@@ -0,0 +1,47 @@
+<?php
+
+
+namespace Tests\Services\StationCacheShelfGridService;
+use App\Services\StationCacheShelfGridService;
+use App\Station;
+use App\StationCacheShelfGrid;
+use Tests\TestCase;
+
+class LightOffTest extends TestCase
+{
+    /** @var StationCacheShelfGridService $stationCacheShelfGridService */
+    protected $stationCacheShelfGridService;
+    protected $data = [];
+
+    protected function setup(): void
+    {
+        parent::setUp();
+        $this->stationCacheShelfGridService = app(StationCacheShelfGridService::class);
+        $row = 2;
+        $col = 1;
+        $gridIndex = ($row-1)*3 + (3-$col);
+        $this->data['station'] = factory(Station::class)->create();
+        $this->data['stationCacheShelfGrid'] = factory(StationCacheShelfGrid::class)->create(['station_id' => $this->data['station']['id'],'grid_id'=>$gridIndex]);
+        $this->data['locCode'] = 'HAI'.$this->data['station']['code'].'-0'.$col.'-0'.$row;
+        $this->data['PTLAction'] = 0;
+    }
+
+    public function testLightOff()
+    {
+        $this->stationCacheShelfGridService->lightOff($this->data['locCode'],$this->data['PTLAction']);
+        $grid = StationCacheShelfGrid::query()->where('id',$this->data['stationCacheShelfGrid']['id'])->first();
+        $this->assertEquals($grid['id'],$this->data['stationCacheShelfGrid']['id']);
+        $this->assertEquals($grid['station']['id'],$this->data['stationCacheShelfGrid']['station_id']);
+        $this->assertEquals($grid['material_box_id'],null);
+        $this->assertEquals($grid['status'],0);
+
+    }
+
+    protected function tearDown(): void
+    {
+        Station::query()->where('id',$this->data['station']['id'])->delete();
+        StationCacheShelfGrid::query()->where('id',$this->data['stationCacheShelfGrid']['id'])->delete();
+        parent::tearDown(); // TODO: Change the autogenerated stub
+    }
+
+}

+ 42 - 0
tests/Services/StationCacheShelfGridService/ProcessGridTest.php

@@ -0,0 +1,42 @@
+<?php
+
+
+namespace Tests\Services\StationCacheShelfGridService;
+use App\MaterialBox;
+use App\Services\StationCacheShelfGridService;
+use App\Station;
+use App\StationCacheShelfGrid;
+use Tests\TestCase;
+
+
+class ProcessGridTest extends TestCase
+{
+    /** @var StationCacheShelfGridService $stationCacheShelfGridService */
+    protected $stationCacheShelfGridService;
+    protected $data = [];
+
+    protected function setup(): void
+    {
+        parent::setUp();
+        $this->stationCacheShelfGridService = app(StationCacheShelfGridService::class);
+        $this->data['station'] = factory(Station::class)->create();
+        $this->data['materialBox'] = factory(MaterialBox::class)->create();
+        $this->data['stationCacheShelfGrid'] = factory(StationCacheShelfGrid::class)->create();
+    }
+
+    public function test()
+    {
+        $this->stationCacheShelfGridService->processGrid($this->data['stationCacheShelfGrid'],$this->data['station'],$this->data['materialBox']);
+        $grid = StationCacheShelfGrid::query()->where('id',$this->data['stationCacheShelfGrid']['id'])->first();
+        $this->assertEquals($grid['id'],$this->data['stationCacheShelfGrid']['id']);
+        $this->assertEquals($grid['station_id'],$this->data['station']['id']);
+        $this->assertEquals($grid['material_box_id'],$this->data['materialBox']['id']);
+    }
+    protected function tearDown(): void
+    {
+        Station::query()->where('id',$this->data['station']['id'])->delete();
+        MaterialBox::query()->where('id',$this->data['materialBox']['id'])->delete();
+        StationCacheShelfGrid::query()->where('id',$this->data['stationCacheShelfGrid']['id'])->delete();
+        parent::tearDown();
+    }
+}

+ 39 - 0
tests/webApi/cacheShelf.http

@@ -0,0 +1,39 @@
+###
+# 亮灯
+POST http://bswas/api/station/cacheShelf/lightOn
+Content-Type: application/json
+
+{
+    "id": "",
+    "code": "",
+    "index": "",
+    "x": "x",
+    "y": "y"
+}
+
+###
+
+###
+# 拍灯任务
+POST http://bswas/api/station/cacheShelf/pushTask
+Content-Type: application/json
+
+{
+    "id": "",
+    "code": "",
+    "index": ""
+}
+
+###
+
+###
+#获取任务
+GET http://bswas/api/station/cacheShelf/getTasks
+Content-Type: application/json
+
+{
+    "id": ""
+}
+
+###
+

部分文件因文件數量過多而無法顯示