status) { case self::STATUS_UNCONFIRMED: $text = self::TEXT_UNCONFIRMED; return $pure_text !== false ? $text : "$text"; break; case self::STATUS_UNASSIGNED: $text = self::TEXT_UNASSIGNED; return $pure_text !== false ? $text : "$text"; break; case self::STATUS_ONGOING: $text = self::TEXT_ONGOING; return $pure_text !== false ? $text : "$text"; break; case self::STATUS_FINISHED: $text = self::TEXT_FINISHED; return $pure_text !== false ? $text : "$text"; break; default: return $this->status; } } public function getStatusNameAttribute() { return $this->getStatusLabelAttribute(); } protected static function booted() { static::addGlobalScope(new AdminProjectScope()); static::updating(function (Orders $order) { if ($order->isClean()) return; $dirty = $order->getDirty(); foreach ($dirty as $k => $v) { $original_v = $order->getOriginal($k); if (is_numeric($v) && is_numeric($original_v) && floatval($v) == floatval($original_v)) { unset($dirty[$k]); } } if (empty($dirty)) { return; } $guards = array_keys((array)config("auth.guards")); $user = null; foreach ($guards as $guard) { if ($user) continue; $user = Auth::guard($guard)->user(); } $last_batch = (new OrderAudit())->max("batch"); $batch = (int)$last_batch + 1; $audits = []; foreach ($dirty as $k => $v) { $audits[] = [ "batch" => $batch, "field_name" => $k, "old_value" => $order->getOriginal($k), "new_value" => $v, "operator_type" => ($user ? get_class($user) : null), "operator_id" => ($user ? $user->id : null), ]; } $order->audits()->createMany($audits); }); } public function scopeOfCustomer($query, $customer_id) { return $query->whereIn('customer_id', (array)$customer_id); } public function scopeOfManager($query, $manager_id) { return $query->whereIn('manager_id', (array)$manager_id); } public function scopeOfProject($query, $project_id) { return $query->whereIn('project_id', (array)$project_id); } public function audits() { return $this->hasMany(OrderAudit::class, "order_id", "id"); } public function firstItem() { return $this->hasOne(OrderItems::class, "order_id")->with([ "productItem", "productParamedicLevel", "bed", "paramedic" ]); } public function lastItem() { return $this->hasOne(OrderItems::class, "order_id")->with([ "productItem", "productParamedicLevel", "bed", "paramedic" ])->orderBy("id", "desc"); } public function orderItems() { return $this->hasMany(OrderItems::class, "order_id")->with([ "productItem", "productParamedicLevel", "bed", "paramedic" ]); } public function handlingApprovalItem() { return $this->morphOne(ApprovalItems::class, "belongs")->whereHas("approval", function ($query) { $query->where("status", Approval::STATUS_HANDLING)->whereRaw(" TIMESTAMPDIFF(SECOND,DATE_FORMAT(NOW(),'%Y-%m-%d %H:%i:%s'),`expire_at`) > 0 "); })->orderBy("id", "desc"); } public function lastApproval() { return $this->hasOne(Approval::class, "order_id", "id")->orderBy("id", "desc"); } public function customer() { return $this->belongsTo(Customer::class)->select(["id", "name", "mobile", "username", "sex", "balance", "openid", "unionid"]); } public function patient() { return $this->belongsTo(Patient::class); } public function project() { return $this->belongsTo(Project::class); } public function product() { return $this->belongsTo(Product::class); } public function manager() { return $this->belongsTo(Manager::class)->select(["id", "name", "mobile", "username", "sex", "openid", "unionid"]); } public function productItem() { return $this->belongsTo(ProductItems::class); } public function productParamedicLevel() { return $this->belongsTo(ProductParamedicLevel::class); } public function paramedicLevel() { return $this->hasOneThrough(ParamedicLevel::class, ProductParamedicLevel::class, "id", "id", "product_paramedic_level_id", "paramedic_level_id"); } public function bed() { return $this->belongsTo(Bed::class); } public function room() { return $this->hasOneThrough(Room::class, Bed::class, "id", "id", "bed_id", "room_id"); } public function area() { return $this->hasOneThrough(Area::class, Bed::class, "id", "id", "bed_id", "area_id"); } public function building() { return $this->hasOneThrough(Building::class, Bed::class, "id", "id", "bed_id", "building_id"); } public function paramedic() { return $this->belongsTo(Paramedic::class); } public function recharges() { return $this->hasMany(Recharge::class, "order_id", "id")->whereNotNull("paid_at"); } public function refunds() { return $this->hasMany(Refund::class, "order_id", "id")->whereNotNull("paid_at"); } public function getSerial() { if ($this->serial) { return $this; } $daily_index = (new Orders())->where("id", "<", $this->id)->whereRaw("DATEDIFF(`created_at`,'" . $this->created_at . "') = 0")->withTrashed()->count(); $serial = date("Ymd", strtotime($this->created_at)) . sprintf("%06d", ($daily_index + 1)); $this->update(["serial" => $serial]); return $this; } public function refreshTotal() { $order_items = $this->orderItems()->get(); switch ($this->status) { case self::STATUS_UNCONFIRMED: case self::STATUS_UNASSIGNED: case self::STATUS_ONGOING: default: $items_total = $order_items->sum("total"); $price = $this->price; if ($order_items->last()) { $days = max(0, Carbon::parse($order_items->last()->service_date)->diffInDays($this->to_date, false)); } else { $days = max(0, Carbon::parse($this->from_date)->diffInDays($this->to_date, false) + 1); } $un_generated_total = $price * $days; $total = $items_total + $un_generated_total; break; case self::STATUS_FINISHED: $total = $this->orderItems()->sum("total"); } if ($order_items->last()) { if (Carbon::parse($order_items->last()->service_date)->greaterThan($this->to_date)) { $this->to_date = $order_items->last()->service_date; } } $paid_total = $order_items->filter(function ($item) { return $item->paid_at; })->sum("total"); $this->paid_total = $paid_total; $this->total = $total; $this->save(); return $this; } public function determineNeedApprovePriceChange($new_price) { //todo:根据更多变化情形判断 return $this->price != $new_price; } public function getLastDayPriceOfNow() { $lastday_price_rules = $this->product->lastdayCheckoutRules; $price = $this->price; $now_rule = null; foreach ($lastday_price_rules as $rule) { if ($now_rule) { continue; } if (time() < strtotime(date("Y-m-d") . " " . $rule->before_time)) { $now_rule = $rule; } } if ($now_rule) { $price = $now_rule->percent / 100 * $price; } return $price; } public function autoCheckout() { $threshold = 50; $orders = $this::whereIn("status", [self::STATUS_ONGOING]) //已经开始 ->whereRaw("DATEDIFF(`from_date`, now()) <= 0") //今日结束 ->whereRaw("DATEDIFF(`to_date`, now()) = 0") //设定了自动结单的 ->where("auto_checkout", 1) //今日子订单已生成 ->whereHas("orderItems", function ($query) { $query->whereRaw("DATEDIFF(`service_date`, now()) = 0"); }) ->orderBy("id") ->limit($threshold) ->get(); foreach ($orders as $order) { //todo:自动结算订单 } } public function generateTodayOrderItems() { $threshold = 50; DB::enableQueryLog(); if ($this->id) { $unGeneratedOrders = (new Orders())->whereDoesntHave("orderItems", function ($query) { $query->whereRaw("DATEDIFF(`service_date`, now()) = 0"); })->where("id", $this->id)->get(); } else { //获取正在进行中的订单,即使已经到了截止日,只要状态是在进行中的都继续生成 $unGeneratedOrders = (new Orders())->whereIn("status", [self::STATUS_ONGOING]) ->whereRaw("DATEDIFF(`from_date`, now()) <= 0") ->whereDoesntHave("orderItems", function ($query) { $query->whereRaw("DATEDIFF(`service_date`, now()) = 0"); }) ->orderBy("id") ->limit($threshold) ->get(); } foreach ($unGeneratedOrders as $order) { $service_date = date("Y-m-d"); (new OrderItems())->createItem($order->id, $service_date); } } public function requestFactorsToOrderFactors() { $request_factors = (array)json_decode(request()->factors, true); $factors = []; foreach ($request_factors as $factor) { $factor_item = FactorItems::join("factor", "factor_items.factor_id", "=", "factor.id") ->where("factor_items.id", $factor["factor_item_id"]) ->select( "factor_items.id as factor_item_id", "factor_items.name as factor_item_name", "factor_items.price", "factor_items.factor_id", "factor_items.fee", "factor_items.fee_percent", "factor.name as factor_name", "factor.used_for_fee" ) ->first(); $factors[] = $factor_item->toArray(); } return $factors; } public function orderFactorsToRequestArray($order_factors = false) { $order_factors === false ? $order_factors = $this->factors : ""; if (!$order_factors) { return []; } $order_factors = (array)json_decode($order_factors, true); $factors = []; foreach ($order_factors as $factor) { $factors[] = [ "id" => $factor["factor_id"], "factor_item_id" => $factor["factor_item_id"] ]; } return $factors; } public function determineOrderFactorsIsDirty($order_factors = false) { if (!isset(request()->factors)) { return false; } $request_factors = json_decode(request()->factors, true); $request_factors = collect((array)$request_factors)->pluck("factor_item_id", "id")->sortKeys(); $order_factors_to_request_factors = $this->orderFactorsToRequestArray($order_factors); $order_factors_to_request_factors = collect((array)$order_factors_to_request_factors)->pluck("factor_item_id", "id")->sortKeys(); return $request_factors->diffAssoc($order_factors_to_request_factors)->count(); } //获取可用护工 public function getAvailableParamedics() { DB::enableQueryLog(); $request = request(); $bed = (new Bed())->find($request->bed_id); $date = $request->start_date ?? date("Y-m-d"); $model = new Paramedic(); $model = $model->where("project_id", $bed->project_id); $model = $model->where("status", 1); $model = $model->with(["project", "levelInProject"]); $model = $model->withCount(["orders"]); $model = $model->select("id", "name", "avatar", "sex", "birthday", "hometown", "work_years", "paramedic_level_id", "project_id", "has_health_certificate", "has_work_certificate"); //性别筛选:如果是女患者,选择女的护工 if ($request->sex == "女") { $model = $model->where("sex", "女"); } //todo:护工所在病区筛选 //一对N的数量排序 $model->withCount(["orders as ongoing_orders_count" => function ($query) use ($date) { //todo:与日期及自动结单关联 $query->where("status", Orders::STATUS_ONGOING); }]); $model = $model->orderBy("ongoing_orders_count"); //同房间的排序 $model->withCount(["orders as ongoing_orders_count_in_same_room" => function ($query) use ($date, $bed) { //todo:与日期及自动结单关联 $query->where("status", Orders::STATUS_ONGOING)->whereHas("room", function ($query) use ($bed) { $query->whereRaw("`room`.`id` = {$bed->room_id}"); }); }]); $model = $model->orderBy("ongoing_orders_count_in_same_room", "desc"); //同病区其他房间的病床排序 $model->withCount(["orders as ongoing_orders_count_in_same_area" => function ($query) use ($date, $bed) { //todo:与日期及自动结单关联 $query->where("status", Orders::STATUS_ONGOING)->whereHas("area", function ($query) use ($bed) { $query->whereRaw("`area`.`id` = {$bed->area_id}"); }); }]); $model = $model->orderBy("ongoing_orders_count_in_same_area", "desc"); $page_size = 3; $paramedics = $model->limit($page_size)->get(); //价格运算 $factors = (new Orders())->requestFactorsToOrderFactors(); foreach ($paramedics as $paramedic) { $product_item = ProductItems::where("patient_quantity", "<=", $paramedic->ongoing_orders_count + 1)->orderBy("patient_quantity", "desc")->first(); $price = $paramedic->levelInProject->price + $product_item->price; foreach ($factors as $factor) { $price += $factor["price"]; } $paramedic->price = $price; } $result = []; $result[$date] = $paramedics->toArray(); return $result; } }