|
|
<?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 = "已完成";
|
|
|
|
|
|
// 协议开启时间点变量
|
|
|
const AGREEMENT_START_DATE = '2025-10-23 00:00:00';
|
|
|
|
|
|
protected $appends = ["status_name", "days"];
|
|
|
|
|
|
/**
|
|
|
* 判断是否要签协议
|
|
|
*/
|
|
|
public function needAgreements()
|
|
|
{
|
|
|
if ($this->paramedic_id
|
|
|
&& $this->project->agreement == 1
|
|
|
&& strtotime($this->created_at) >= strtotime(Orders::AGREEMENT_START_DATE)
|
|
|
&& (empty($this->orderAgreementByLast) || (!empty($this->orderAgreementByLast) && $this->orderAgreementByLast->paramedic_id != $this->paramedic_id))) {
|
|
|
return 1;
|
|
|
}
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
|
|
|
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 orderAgreements()
|
|
|
{
|
|
|
return $this->hasMany(OrderAgreement::class, "order_id", "id");
|
|
|
}
|
|
|
|
|
|
public function orderAgreementByLast()
|
|
|
{
|
|
|
return $this->hasOne(OrderAgreement::class, "order_id", "id")->orderBy("id", "desc");
|
|
|
}
|
|
|
|
|
|
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 wechatpayAccount()
|
|
|
{
|
|
|
return $this->hasOneThrough(WechatpayAccount::class, Project::class, "id", "id", "project_id", "wechatpay_account_id");
|
|
|
}
|
|
|
|
|
|
public function alipayAccount()
|
|
|
{
|
|
|
return $this->hasOneThrough(AlipayAccount::class, Project::class, "id", "id", "project_id", "alipay_account_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();
|
|
|
$_product_item_price = $product_item->price ?? 0;
|
|
|
$_paramedic_price = $paramedic->levelInProject->price ?? 0;
|
|
|
$price = $_product_item_price + $_paramedic_price;
|
|
|
|
|
|
foreach ($factors as $factor) {
|
|
|
$_factor_price = $factor["price"] ?? 0;
|
|
|
$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)
|
|
|
->where("merchant_id", "<>", "1621928535") //忽略微信支付的琳颖账户,2024-08-29
|
|
|
->whereNotNull("paid_at")
|
|
|
->whereIn("payment", ["weixin", "alipay"])
|
|
|
->whereNotNull("merchant_id")
|
|
|
->where("money", ">=", $amount)
|
|
|
->doesntHave("refunds")
|
|
|
->orderBy("id", "desc")
|
|
|
->first();
|
|
|
return $recharge;
|
|
|
}
|
|
|
}
|