226 lines
9.5 KiB
PHP
226 lines
9.5 KiB
PHP
<?php
|
||
|
||
namespace App\Models\Business;
|
||
|
||
use App\Abstracts\Model;
|
||
|
||
class FundForecast extends Model
|
||
{
|
||
protected $table = 'business_fund_forecast';
|
||
|
||
public const BALANCE_FIELDS = [
|
||
'balance_bank', 'balance_jd', 'balance_douyin', 'balance_dewu',
|
||
'balance_taobao_alipay', 'balance_wechat',
|
||
];
|
||
|
||
public const INCOME_FIELDS = [
|
||
'income_jd', 'income_douyin', 'income_dewu', 'income_taobao_alipay',
|
||
'income_goods_payment', 'income_other',
|
||
];
|
||
|
||
public const EXPENSE_FIELDS = [
|
||
'expense_supplier_goods', 'expense_courier', 'expense_paper_lamination',
|
||
'expense_edge_band', 'expense_daily_reimbursement', 'expense_taobao_red_packet',
|
||
'expense_board', 'expense_store_top_up', 'expense_planting', 'expense_salary',
|
||
'expense_tax', 'expense_store_spend',
|
||
];
|
||
|
||
public const SUMMARY_FIELDS = [
|
||
'previous_month_balance', 'forecast_month_income', 'forecast_month_expense',
|
||
'gap_surplus', 'has_gap',
|
||
];
|
||
|
||
public const PAGE_FIELDS = [
|
||
'budget_month', 'company_name', 'region', 'report_status',
|
||
...self::BALANCE_FIELDS,
|
||
...self::INCOME_FIELDS,
|
||
...self::EXPENSE_FIELDS,
|
||
...self::SUMMARY_FIELDS,
|
||
];
|
||
|
||
protected $fillable = [
|
||
'company_id',
|
||
...self::PAGE_FIELDS,
|
||
// Legacy fields remain fillable so old records and rollback exports stay compatible.
|
||
'actual_month', 'store_balance_budget', 'store_balance_actual', 'bank_balance_budget',
|
||
'bank_balance_actual', 'other_balance_budget', 'other_balance_actual',
|
||
'expected_monthly_return_budget', 'expected_monthly_return_actual',
|
||
'income_total_budget', 'income_total_actual', 'prior_unpaid_budget',
|
||
'prior_unpaid_actual', 'advance_budget', 'advance_actual', 'supplier_budget',
|
||
'supplier_actual', 'spot_settlement_budget', 'spot_settlement_actual',
|
||
'courier_budget', 'courier_actual', 'paper_factory_budget', 'paper_factory_actual',
|
||
'edge_band_budget', 'edge_band_actual', 'board_budget', 'board_actual',
|
||
'salary_budget', 'salary_actual', 'daily_expense_budget', 'daily_expense_actual',
|
||
'planting_budget', 'planting_actual', 'red_packet_budget', 'red_packet_actual',
|
||
'corporate_deduction_budget', 'corporate_deduction_actual',
|
||
'expense_total_budget', 'expense_total_actual', 'bank_reservation',
|
||
'net_balance_budget', 'net_balance_actual',
|
||
];
|
||
|
||
protected $attributes = [
|
||
'report_status' => '待填报',
|
||
];
|
||
|
||
protected $casts = [
|
||
'company_id' => 'integer',
|
||
'has_gap' => 'boolean',
|
||
'balance_bank' => 'decimal:2',
|
||
'balance_jd' => 'decimal:2',
|
||
'balance_douyin' => 'decimal:2',
|
||
'balance_dewu' => 'decimal:2',
|
||
'balance_taobao_alipay' => 'decimal:2',
|
||
'balance_wechat' => 'decimal:2',
|
||
'income_jd' => 'decimal:2',
|
||
'income_douyin' => 'decimal:2',
|
||
'income_dewu' => 'decimal:2',
|
||
'income_taobao_alipay' => 'decimal:2',
|
||
'income_goods_payment' => 'decimal:2',
|
||
'income_other' => 'decimal:2',
|
||
'expense_supplier_goods' => 'decimal:2',
|
||
'expense_courier' => 'decimal:2',
|
||
'expense_paper_lamination' => 'decimal:2',
|
||
'expense_edge_band' => 'decimal:2',
|
||
'expense_daily_reimbursement' => 'decimal:2',
|
||
'expense_taobao_red_packet' => 'decimal:2',
|
||
'expense_board' => 'decimal:2',
|
||
'expense_store_top_up' => 'decimal:2',
|
||
'expense_planting' => 'decimal:2',
|
||
'expense_salary' => 'decimal:2',
|
||
'expense_tax' => 'decimal:2',
|
||
'expense_store_spend' => 'decimal:2',
|
||
'previous_month_balance' => 'decimal:2',
|
||
'forecast_month_income' => 'decimal:2',
|
||
'forecast_month_expense' => 'decimal:2',
|
||
'gap_surplus' => 'decimal:2',
|
||
];
|
||
|
||
protected static function booted(): void
|
||
{
|
||
static::creating(function (self $forecast): void {
|
||
if (empty($forecast->company_id)) {
|
||
$forecast->company_id = company_id() ?: 1;
|
||
}
|
||
});
|
||
|
||
static::saving(function (self $forecast): void {
|
||
$balance = self::sumFields($forecast, self::BALANCE_FIELDS);
|
||
$income = self::sumFields($forecast, self::INCOME_FIELDS);
|
||
$expense = self::sumFields($forecast, self::EXPENSE_FIELDS);
|
||
$gap = round($balance + $income - $expense, 2);
|
||
|
||
$forecast->previous_month_balance = $balance;
|
||
$forecast->forecast_month_income = $income;
|
||
$forecast->forecast_month_expense = $expense;
|
||
$forecast->gap_surplus = $gap;
|
||
$forecast->has_gap = $gap < 0;
|
||
});
|
||
}
|
||
|
||
private static function sumFields(self $forecast, array $fields): float
|
||
{
|
||
return array_reduce(
|
||
$fields,
|
||
fn (float $sum, string $field): float => round($sum + round((float) ($forecast->{$field} ?? 0), 2), 2),
|
||
0.0,
|
||
);
|
||
}
|
||
|
||
public static function columnLabels(): array
|
||
{
|
||
return [
|
||
'company_name' => '公司名称',
|
||
'region' => '所属区域',
|
||
'report_status' => '填报状态',
|
||
'budget_month' => '预算月份',
|
||
'actual_month' => '实际月份',
|
||
'store_balance_budget' => '店铺余额(预算)',
|
||
'store_balance_actual' => '店铺余额(实际)',
|
||
'bank_balance_budget' => '银行余额(预算)',
|
||
'bank_balance_actual' => '银行余额(实际)',
|
||
'other_balance_budget' => '其他余额(预算)',
|
||
'other_balance_actual' => '其他余额(实际)',
|
||
'expected_monthly_return_budget' => '预计回款(预算)',
|
||
'expected_monthly_return_actual' => '预计回款(实际)',
|
||
'income_total_budget' => '收入合计(预算)',
|
||
'income_total_actual' => '收入合计(实际)',
|
||
'prior_unpaid_budget' => '往期未付(预算)',
|
||
'prior_unpaid_actual' => '往期未付(实际)',
|
||
'advance_budget' => '借支(预算)',
|
||
'advance_actual' => '借支(实际)',
|
||
'supplier_budget' => '供应商(预算)',
|
||
'supplier_actual' => '供应商(实际)',
|
||
'spot_settlement_budget' => '现结(预算)',
|
||
'spot_settlement_actual' => '现结(实际)',
|
||
'courier_budget' => '快递(预算)',
|
||
'courier_actual' => '快递(实际)',
|
||
'paper_factory_budget' => '纸厂压贴(预算)',
|
||
'paper_factory_actual' => '纸厂压贴(实际)',
|
||
'edge_band_budget' => '封边条(预算)',
|
||
'edge_band_actual' => '封边条(实际)',
|
||
'board_budget' => '板材(预算)',
|
||
'board_actual' => '板材(实际)',
|
||
'salary_budget' => '工资(预算)',
|
||
'salary_actual' => '工资(实际)',
|
||
'daily_expense_budget' => '日常费用(预算)',
|
||
'daily_expense_actual' => '日常费用(实际)',
|
||
'planting_budget' => '种菜(预算)',
|
||
'planting_actual' => '种菜(实际)',
|
||
'red_packet_budget' => '红包(预算)',
|
||
'red_packet_actual' => '红包(实际)',
|
||
'corporate_deduction_budget' => '对公扣款(预算)',
|
||
'corporate_deduction_actual' => '对公扣款(实际)',
|
||
'expense_total_budget' => '支出合计(预算)',
|
||
'expense_total_actual' => '支出合计(实际)',
|
||
'bank_reservation' => '银行预留',
|
||
'net_balance_budget' => '净结余(预算)',
|
||
'net_balance_actual' => '净结余(实际)',
|
||
'balance_bank' => '银行',
|
||
'balance_jd' => '京东',
|
||
'balance_douyin' => '抖音',
|
||
'balance_dewu' => '得物',
|
||
'balance_taobao_alipay' => '淘系支付宝',
|
||
'balance_wechat' => '微信',
|
||
'income_jd' => '京东',
|
||
'income_douyin' => '抖音',
|
||
'income_dewu' => '得物',
|
||
'income_taobao_alipay' => '淘系支付宝',
|
||
'income_goods_payment' => '货款',
|
||
'income_other' => '其他收入',
|
||
'expense_supplier_goods' => '供应商货款',
|
||
'expense_courier' => '快递款',
|
||
'expense_paper_lamination' => '纸厂压贴',
|
||
'expense_edge_band' => '封边条',
|
||
'expense_daily_reimbursement' => '日常报销',
|
||
'expense_taobao_red_packet' => '淘宝红包',
|
||
'expense_board' => '板材',
|
||
'expense_store_top_up' => '店铺充值',
|
||
'expense_planting' => '种菜',
|
||
'expense_salary' => '工资',
|
||
'expense_tax' => '税金',
|
||
'expense_store_spend' => '店铺花费(暂估10%)',
|
||
'previous_month_balance' => '上月末总余额',
|
||
'forecast_month_income' => '预测本月收入',
|
||
'forecast_month_expense' => '预测本月支付',
|
||
'gap_surplus' => '缺口/盈余',
|
||
'has_gap' => '是否有缺口',
|
||
];
|
||
}
|
||
|
||
public static function fieldTypes(): array
|
||
{
|
||
return array_replace(
|
||
array_fill_keys([
|
||
...self::BALANCE_FIELDS,
|
||
...self::INCOME_FIELDS,
|
||
...self::EXPENSE_FIELDS,
|
||
'previous_month_balance', 'forecast_month_income',
|
||
'forecast_month_expense', 'gap_surplus',
|
||
], 'currency'),
|
||
[
|
||
'report_status' => 'select',
|
||
'has_gap' => 'boolean',
|
||
],
|
||
);
|
||
}
|
||
}
|