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.

474 lines
16 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<?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()
{
dd($this->paramedic_id,$this->project);
if ($this->paramedic_id
&& $this->project->agreement == 1
&& strtotime($this->created_at) >= strtotime(Orders::AGREEMENT_START_DATE)
&& ($this->orderAgreements->isEmpty() || ($this->orderAgreements->isNotEmpty() && $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;
}
}