|
|
<?php
|
|
|
|
|
|
namespace App\Models;
|
|
|
|
|
|
use App\Customer;
|
|
|
use Carbon\Carbon;
|
|
|
use Illuminate\Support\Facades\DB;
|
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
|
|
class OrderItems extends SoftDeletesModel
|
|
|
{
|
|
|
protected $table = "order_items";
|
|
|
const PREV_MONTH = "往月服务单";
|
|
|
|
|
|
protected $appends = ["paid_status"];
|
|
|
|
|
|
public function getPaidStatusAttribute()
|
|
|
{
|
|
|
if ($this->total == 0) {
|
|
|
return "未服务";
|
|
|
}
|
|
|
if ($this->paid_at) {
|
|
|
$next_month = date("m", strtotime("+1 month", strtotime(date("Y-m", strtotime($this->paid_at)))));
|
|
|
return "已扣款,{$next_month}月结工资";
|
|
|
} else {
|
|
|
return "未扣款";
|
|
|
}
|
|
|
}
|
|
|
|
|
|
public function order()
|
|
|
{
|
|
|
return $this->belongsTo(Orders::class);
|
|
|
}
|
|
|
|
|
|
public function siblings()
|
|
|
{
|
|
|
return $this->hasManyThrough(OrderItems::class, Orders::class, "id", "order_id", "order_id", "id");
|
|
|
}
|
|
|
|
|
|
public function customer()
|
|
|
{
|
|
|
return $this->hasOneThrough(Customer::class, Orders::class, "id", "id", "order_id", "customer_id");
|
|
|
}
|
|
|
|
|
|
public function product()
|
|
|
{
|
|
|
return $this->hasOneThrough(Product::class, ProductItems::class, "id", "id", "product_item_id", "product_id")
|
|
|
->withoutGlobalScope(\App\Scopes\AdminProjectScope::class)
|
|
|
->withTrashedParents();
|
|
|
}
|
|
|
|
|
|
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)->withTrashed();
|
|
|
}
|
|
|
|
|
|
public function room()
|
|
|
{
|
|
|
return $this->hasOneThrough(Room::class, Bed::class, "id", "id", "bed_id", "room_id")->withTrashed();
|
|
|
}
|
|
|
|
|
|
public function building()
|
|
|
{
|
|
|
return $this->hasOneThrough(Building::class, Bed::class, "id", "id", "bed_id", "building_id")->withTrashed();
|
|
|
}
|
|
|
|
|
|
public function area()
|
|
|
{
|
|
|
return $this->hasOneThrough(Area::class, Bed::class, "id", "id", "bed_id", "area_id")->withTrashed();
|
|
|
}
|
|
|
|
|
|
public function paramedic()
|
|
|
{
|
|
|
return $this->belongsTo(Paramedic::class)->withTrashed();
|
|
|
}
|
|
|
|
|
|
public function createItem($order_id, $service_date)
|
|
|
{
|
|
|
$record = (new OrderItems())->where([
|
|
|
"order_id" => $order_id,
|
|
|
"service_date" => $service_date
|
|
|
])->first();
|
|
|
if ($record) {
|
|
|
return $record;
|
|
|
}
|
|
|
|
|
|
$order = (new Orders())->find($order_id);
|
|
|
$patient_quantity = (new Orders())
|
|
|
->where("status", Orders::STATUS_ONGOING)
|
|
|
->where("paramedic_id", $order->paramedic_id)
|
|
|
->count();
|
|
|
|
|
|
$order_item = [
|
|
|
"order_id" => $order->id,
|
|
|
"product_item_id" => $order->product_item_id,
|
|
|
"product_paramedic_level_id" => $order->product_paramedic_level_id,
|
|
|
"bed_id" => $order->bed_id,
|
|
|
"paramedic_id" => $order->paramedic_id,
|
|
|
"service_date" => $service_date,
|
|
|
"patient_quantity" => $patient_quantity,
|
|
|
"total" => $order->price,
|
|
|
"factors" => $order->factors
|
|
|
];
|
|
|
|
|
|
return $this->create($order_item);
|
|
|
}
|
|
|
|
|
|
public function updateTodayItem($order_id)
|
|
|
{
|
|
|
$service_date = date("Y-m-d");
|
|
|
$record = (new OrderItems())->where([
|
|
|
"order_id" => $order_id,
|
|
|
"service_date" => $service_date
|
|
|
])->first();
|
|
|
if (!$record) {
|
|
|
return [
|
|
|
"status" => 0,
|
|
|
"msg" => "没找到当日子订单"
|
|
|
];
|
|
|
}
|
|
|
if ($record->paid_at) {
|
|
|
return [
|
|
|
"status" => 0,
|
|
|
"msg" => "当日子订单已扣款,请移步至子订单修改"
|
|
|
];
|
|
|
}
|
|
|
|
|
|
$order = (new Orders())->find($order_id);
|
|
|
$patient_quantity = (new Orders())
|
|
|
->where("status", Orders::STATUS_ONGOING)
|
|
|
->where("paramedic_id", $order->paramedic_id)
|
|
|
->count();
|
|
|
|
|
|
$update = [
|
|
|
"product_item_id" => $order->product_item_id,
|
|
|
"product_paramedic_level_id" => $order->product_paramedic_level_id,
|
|
|
"bed_id" => $order->bed_id,
|
|
|
"paramedic_id" => $order->paramedic_id,
|
|
|
"patient_quantity" => $patient_quantity,
|
|
|
"total" => $order->price,
|
|
|
"factors" => $order->factors
|
|
|
];
|
|
|
|
|
|
$record->update($update);
|
|
|
|
|
|
return [
|
|
|
"status" => 1,
|
|
|
"msg" => "当日子订单同步成功"
|
|
|
];
|
|
|
}
|
|
|
|
|
|
public function calculateFee()
|
|
|
{
|
|
|
$factors = json_decode($this->factors);
|
|
|
$factor_texts = [];
|
|
|
if ($factors) {
|
|
|
foreach ($factors as $factor_item) {
|
|
|
if ($factor_item->used_for_fee) {
|
|
|
$add_text = "(管理费{$factor_item->fee_percent}%+{$factor_item->fee})";
|
|
|
} else {
|
|
|
$add_text = "";
|
|
|
}
|
|
|
$factor_texts[] = "{$factor_item->factor_name}:{$factor_item->factor_item_name}{$add_text}";
|
|
|
}
|
|
|
}
|
|
|
$this->factor_texts = $factor_texts;
|
|
|
|
|
|
// 标志变量:是否成功计算了管理费
|
|
|
$feeCalculated = false;
|
|
|
|
|
|
if (!$this->product) {
|
|
|
// 没有 product 时设置默认值(不扣除管理费)
|
|
|
$this->paramedic_total = $this->total ?? 0;
|
|
|
$this->fee = 0;
|
|
|
return $this;
|
|
|
}
|
|
|
|
|
|
switch ($this->product->fee_type) {
|
|
|
case "factor":
|
|
|
$factor = collect($factors)->filter(function ($item) {
|
|
|
return $item->used_for_fee;
|
|
|
})->first();
|
|
|
if (!$factor) {
|
|
|
//todo:检查factors为什么没有进去、缺失的factors已经手工补录进去了
|
|
|
$factorModel = (new Factor())
|
|
|
->with("factorItems")
|
|
|
->where("product_id", $this->product->id)
|
|
|
->where("used_for_fee", 1)
|
|
|
->first();
|
|
|
if ($factorModel && $factorModel->factorItems) {
|
|
|
$factor = $factorModel->factorItems->first();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (!$factor) {
|
|
|
// 找不到因子时设置默认值(不扣除管理费)
|
|
|
$this->paramedic_total = $this->total ?? 0;
|
|
|
$this->fee = 0;
|
|
|
return $this;
|
|
|
}
|
|
|
|
|
|
//todo:考虑不同项目的情况,目前是全部统一
|
|
|
if (!$this->order) {
|
|
|
// 没有 order 时设置默认值(不扣除管理费)
|
|
|
$this->paramedic_total = $this->total ?? 0;
|
|
|
$this->fee = 0;
|
|
|
return $this;
|
|
|
}
|
|
|
|
|
|
$holidays = cache("holidays_" . $this->order->project_id);
|
|
|
if (!$holidays) {
|
|
|
$holidays = Holiday::where("project_id", $this->order->project_id)->get()->keyBy("date")->toArray();
|
|
|
cache(['holidays' . $this->order->project_id => $holidays], now()->addSeconds(90)); //只保存较短时间,省却了更新节假日时的缓存更新机制
|
|
|
}
|
|
|
|
|
|
if (in_array($this->service_date, array_keys($holidays))) {
|
|
|
$fee = $factor->fee_percent * ($this->total / $holidays[$this->service_date]["price_ratio"]) / 100 + $factor->fee - $this->fee_free;
|
|
|
} else {
|
|
|
$fee = $factor->fee_percent * $this->total / 100 + $factor->fee - $this->fee_free;
|
|
|
}
|
|
|
$paramedic_total = $this->total - $fee;
|
|
|
$this->paramedic_total = $paramedic_total;
|
|
|
$this->fee = $fee;
|
|
|
$feeCalculated = true;
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
// 兜底逻辑:如果管理费没有被计算(通常是 fee_type 不是 "factor" 的情况),使用默认值
|
|
|
if (!$feeCalculated) {
|
|
|
$this->paramedic_total = $this->total ?? 0;
|
|
|
$this->fee = 0;
|
|
|
}
|
|
|
|
|
|
return $this;
|
|
|
}
|
|
|
|
|
|
public function autoCheckout()
|
|
|
{
|
|
|
$threshold = 50;
|
|
|
$last_id = cache("last_auto_checkout_order_item_id", 0);
|
|
|
Log::channel("daily_auto_checkout")->info("Last id:" . $last_id);
|
|
|
|
|
|
// 获取所有状态为1的项目
|
|
|
$projects = Project::where("status", 1)->pluck("id")->toArray();
|
|
|
|
|
|
$unpaid_order_items = (new OrderItems())
|
|
|
->whereHas("order", function ($query) use ($projects) {
|
|
|
$query->where("status", Orders::STATUS_ONGOING)
|
|
|
->whereIn("project_id", $projects);
|
|
|
})
|
|
|
->where("total", ">", 0)
|
|
|
->whereNull("paid_at")
|
|
|
->where("id", ">", $last_id)
|
|
|
->with("customer");
|
|
|
|
|
|
$unpaid_order_items = $unpaid_order_items
|
|
|
->orderBy("id")
|
|
|
->limit($threshold)
|
|
|
->get();
|
|
|
if (!$unpaid_order_items->count()) {
|
|
|
cache(['last_auto_checkout_order_item_id' => null], now()->addSeconds(90));
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
Log::channel("daily_auto_checkout")->info("From " . $unpaid_order_items->first()->id . " to " . $unpaid_order_items->last()->id);
|
|
|
foreach ($unpaid_order_items as $item) {
|
|
|
$customer = $item->customer;
|
|
|
if ($customer->balance < $item->total || $item->total == 0) {
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
$item->update(["paid_at" => date("Y-m-d H:i:s")]);
|
|
|
|
|
|
$balance = $customer->balance - $item->total;
|
|
|
(new Balance())->create([
|
|
|
"customer_id" => $customer->id,
|
|
|
"order_id" => $item->order_id,
|
|
|
"belongs_type" => get_class($item),
|
|
|
"belongs_id" => $item->id,
|
|
|
"money" => -$item->total,
|
|
|
"balance" => $balance
|
|
|
]);
|
|
|
|
|
|
$customer->update(["balance" => $balance]);
|
|
|
Log::channel("daily_auto_checkout")->info($item->id . "扣款成功");
|
|
|
}
|
|
|
cache(['last_auto_checkout_order_item_id' => $unpaid_order_items->last()->id], now()->addSeconds(90));
|
|
|
}
|
|
|
|
|
|
public function balance()
|
|
|
{
|
|
|
return $this->hasOne(Balance::class, "belongs_id")->where("belongs_type", self::class);
|
|
|
}
|
|
|
}
|