You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

429 lines
15 KiB

<?php
namespace App\Models;
use App\Customer;
use App\Manager;
use App\Scopes\AdminProjectScope;
use Carbon\Carbon;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
class Orders extends SoftDeletesModel
{
protected $table = "orders";
const STATUS_UNCONFIRMED = 0;
const STATUS_UNASSIGNED = 10;
const STATUS_ONGOING = 20;
const STATUS_FINISHED = 100;
const TEXT_UNCONFIRMED = "待确认";
const TEXT_UNASSIGNED = "待指派";
const TEXT_ONGOING = "进行中";
const TEXT_FINISHED = "已完成";
protected $appends = ["status_name", "days"];
public function getStatusLabelAttribute($pure_text = true)
{
switch ($this->status) {
case self::STATUS_UNCONFIRMED:
$text = self::TEXT_UNCONFIRMED;
return $pure_text !== false ? $text : "<span class='badge badge-warning'>$text</span>";
break;
case self::STATUS_UNASSIGNED:
$text = self::TEXT_UNASSIGNED;
return $pure_text !== false ? $text : "<span class='badge badge-dark'>$text</span>";
break;
case self::STATUS_ONGOING:
$text = self::TEXT_ONGOING;
return $pure_text !== false ? $text : "<span class='badge badge-primary'>$text</span>";
break;
case self::STATUS_FINISHED:
$text = self::TEXT_FINISHED;
return $pure_text !== false ? $text : "<span class='badge badge-success'>$text</span>";
break;
default:
return $this->status;
}
}
public function isPending()
{
return in_array($this->status, [self::STATUS_UNCONFIRMED, self::STATUS_UNASSIGNED]);
}
public function isOngoing()
{
return $this->status == self::STATUS_ONGOING;
}
public function getStatusNameAttribute()
{
return $this->getStatusLabelAttribute();
}
public function getDaysAttribute()
{
return Carbon::parse($this->from_date)->diffInDays($this->to_date) + 1;
}
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 ($k == "updated_at") {
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");
}
public function lastItem()
{
return $this->hasOne(OrderItems::class, "order_id");
}
public function orderItems()
{
return $this->hasMany(OrderItems::class, "order_id")->with([
"paramedic" => function ($query) {
$query
->select("paramedic.id", "paramedic.name", "paramedic.avatar", "paramedic.sex")
->leftJoin("paramedic_level", "paramedic_level.id", "=", "paramedic.paramedic_level_id")
->addSelect("paramedic_level.name as paramedic_level_name");
},
"bed" => function ($query) {
$query->select("bed.id", "bed.name")
->leftJoin("building", "building.id", "=", "bed.building_id")
->leftJoin("area", "area.id", "=", "bed.area_id")
->leftJoin("room", "room.id", "=", "bed.room_id")
->addSelect("room.name as room_name", "area.name as area_name", "building.name as building_name");
}
]);
}
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")
->select("recharge.id", "recharge.order_id", "recharge.manager_id", "recharge.serial", "recharge.money", "recharge.payment", "recharge.payment_serial", "recharge.remark", "recharge.created_at")
->leftJoin("managers", "managers.id", "=", "recharge.manager_id")
->addSelect("managers.name as manager_name")
->orderBy("id", "desc");
}
public function refunds()
{
return $this->hasMany(Refund::class, "order_id", "id")
->whereNotNull("paid_at")
->orderBy("id", "desc");
}
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_FINISHED:
$total = $order_items->sum("total");
break;
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));
if (Carbon::parse($this->to_date)->diffInDays($order_items->last()->service_date, false) > 0) {
$this->to_date = $order_items->last()->service_date;
}
} 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;
}
$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 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();
$product = (new Project())->find($bed->project_id)->products->first();
foreach ($paramedics as $paramedic) {
$product_item = ProductItems::where("product_id", $product->id)->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;
}
public function getOnlineRefundableRecharge($amount)
{
//todo:根据交易状态是否可以退款、以及多次退款金额是否足够进行更精准筛选
//but:出问题的几率微乎其微可以忽略
$recharge = Recharge::where("order_id", $this->id)
->whereNotNull("paid_at")
->whereIn("payment", ["weixin", "alipay"])
->where("money", ">=", $amount)
->doesntHave("refunds")
->orderBy("id", "desc")
->first();
return $recharge;
}
}