Files
OMS/app/finance/lib/monthly/report/items.php
2026-01-04 19:08:31 +08:00

429 lines
18 KiB
PHP

<?php
/**
* Copyright 2012-2026 ShopeX (https://www.shopex.cn)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class finance_monthly_report_items {
private $monthly_report;
private $gap_list;
/**
* 获取ItemId
* @param mixed $sdf sdf
* @param mixed $insert insert
* @return mixed 返回结果
*/
public function getItemId($sdf, $insert = true) {
if(!$sdf['order_bn']) {
return 0;
}
$shop_id = $sdf['channel_id'];
$time = $sdf['trade_time'];
if(!isset($this->monthly_report[$shop_id]))
{
$mdlMonthlyReport = app::get('finance')->model('monthly_report');
$mr = $mdlMonthlyReport->getList('monthly_id,begin_time,end_time',array('shop_id'=>$shop_id,'status|lthan'=>2));
$this->monthly_report[$shop_id] = array_column($mr, null, 'monthly_id');
}
$mr = $this->monthly_report[$shop_id];
$oMRI = app::get('finance')->model('monthly_report_items');
$items = $oMRI->getList('id,monthly_id', ['order_bn'=>$sdf['order_bn']]);
if($items) {
foreach($items as $v) {
if($mr[$v['monthly_id']])
{
return $v['id'];
}
}
}
if(!$insert) {
return 0;
}
foreach ($mr as $v)
{
if($v['begin_time'] <= $time and $v['end_time'] >= $time )
{
$data = [
'order_bn' => $sdf['order_bn'],
'monthly_id' => $v['monthly_id'],
];
$rs = $oMRI->insert($data);
if($rs) {
return $data['id'];
}
return $oMRI->db_dump($data, 'id')['id'];
}
}
return 0;
}
/**
* doAutoVerificate
* @param mixed $itemId ID
* @param mixed $shop_id ID
* @return mixed 返回值
*/
public function doAutoVerificate($itemId, $shop_id) {
if(empty($itemId)) {
return [false, ['msg'=>'缺少单据']];
}
list($rs, $rsData) = $this->doGapRule($itemId, $shop_id);
$oMRI = app::get('finance')->model('monthly_report_items');
if(app::get('finance')->model('bill')->db_dump(['status|noequal'=>'2', 'monthly_item_id'=>$itemId], 'bill_id')
|| app::get('finance')->model('ar')->db_dump(['status|noequal'=>'2', 'monthly_item_id'=>$itemId], 'ar_id')
) {
$oMRI->update(['gap_type'=>'', 'memo'=>$rsData['msg'], 'verification_status'=>'1'], ['id'=>$itemId]);
} else {
$upData = ['verification_status'=>'2', 'memo'=>'', 'gap_type'=>''];
if($rsData['msg']) {
$upData['memo'] = $rsData['msg'];
}
if($rsData['gap_type']) {
$upData['gap_type'] = $rsData['gap_type'];
}
$oMRI->update($upData, ['id'=>$itemId]);
}
$servicelist = kernel::servicelist('financebase.reportitem.doAutoVerificate.after');
foreach ($servicelist as $instance) {
if (method_exists($instance, 'after_verificate')){
$instance->after_verificate($itemId);
}
}
return [$rs, $rsData];
}
/**
* doGapRule
* @param mixed $itemId ID
* @param mixed $shop_id ID
* @return mixed 返回值
*/
public function doGapRule($itemId, $shop_id) {
if(!isset($this->gap_list)) {
$this->gap_list = array();
$mdlBillVerificationError = app::get('financebase')->model('bill_verification_error');
foreach ($mdlBillVerificationError->getList('rule,is_verify,verify_mode,shop_id,name', array(), 0, -1, 'priority desc') as $key => $value) {
$this->gap_list[$value['shop_id']][] = $value;
}
}
# 三位小数
$diff = $this->getDiffMoney($itemId);
$gap_list = $this->gap_list[$shop_id] ? : [];
$z = $p = $pz = true;
foreach ($gap_list as $gap) {
if($gap['verify_mode'] == '1') {
##收入
if($z) {
$diff_money = $diff['yingshou_money'] - $diff['shishou_money'];
$diff_money = sprintf('%.3f', $diff_money);
list($zrs, $zrsData) = $this->gapCalc($gap, $diff_money, $itemId, 'z');
if($zrsData['msg'] != '未匹配到核销规则') {
$z = false;
}
$zrsData['msg'] = '收入:'.$zrsData['msg'];
}
#退款
if($p) {
$diff_money = $diff['yingtui_money'] - $diff['shitui_money'];
$diff_money = sprintf('%.3f', $diff_money);
list($prs, $prsData) = $this->gapCalc($gap, $diff_money, $itemId, 'p');
if($prsData['msg'] != '未匹配到核销规则') {
$p = false;
}
$prsData['msg'] = '退款:'.$prsData['msg'];
}
#缺少实收实退
if($diff['shishou_money'] == 0 && $diff['shitui_money'] == 0 && $pz) {
$diff_money = $diff['yingtui_money'] + $diff['yingshou_money'];
$diff_money = sprintf('%.3f', $diff_money);
list($pzrs, $pzrsData) = $this->gapCalc($gap, $diff_money, $itemId, 'ying');
if($pzrsData['msg'] != '未匹配到核销规则') {
$pz = false;
}
$pzrsData['msg'] = '应收应退:'.$pzrsData['msg'];
}
if(!$z && !$p && !$pz) {
$rsData = $zrsData;
$rsData['msg'] .= $prsData['msg'];
$rsData['msg'] .= $pzrsData['msg'];
return [($zrs && $prs && $pzrs), $rsData];
}
} else {
$diff_money = $diff['gap'];
$diff_money = sprintf('%.3f', $diff_money);
list($rs, $rsData) = $this->gapCalc($gap, $diff_money, $itemId);
if($rsData['msg'] != '未匹配到核销规则') {
return [$rs, $rsData];
}
}
}
return [false, ['msg'=>$gap_list ? '未匹配到核销规则' : '未配置核销规则']];
}
/**
* gapCalc
* @param mixed $gap gap
* @param mixed $diff_money diff_money
* @param mixed $itemId ID
* @param mixed $compare compare
* @return mixed 返回值
*/
public function gapCalc($gap, $diff_money, $itemId, $compare = 'order') {
$expression = array();
foreach ($gap['rule'] as $rule) {
$expression[] = str_replace('{field}', $diff_money, $this->get_comp($rule['operator'], $rule['operand']));
}
$expression = implode(' && ', array_filter($expression));
$result = false;
eval("\$result=($expression);");
if ($result) {
// 强制核销
if ($gap['is_verify'] == 1) {
$sdf = [
'gap_type' => $gap['name'],
'verification_status' => $diff_money == 0 ? 1 : 2,
'compare' => $compare
];
list($verRs, $rsData) = $this->doVerificate($itemId, $sdf);
$rsData['gap_type'] = $sdf['gap_type'];
return [$verRs, $rsData];
}
return [false, ['msg'=>$gap['name'] . '未开启核销']];
}
return [false, ['msg' => '未匹配到核销规则']];
}
/**
* 获取DiffMoney
* @param mixed $itemId ID
* @return mixed 返回结果
*/
public function getDiffMoney($itemId) {
$arlist = app::get('finance')->model('ar')->getList('money,type,trade_time', ['monthly_item_id' => $itemId]);
$upData = ['yingshou_money'=>0, 'yingtui_money'=>0, 'refund_only_money'=>0,'shishou_money'=>0,'shitui_money'=>0];
foreach ($arlist as $arRow) {
if($arRow['money'] > 0) {
$upData['ship_time'] = $arRow['trade_time'];
$upData['yingshou_money'] += $arRow['money'];
} else {
$upData['reship_time'] = $arRow['trade_time'];
$upData['yingtui_money'] += $arRow['money'];
if($arRow['type'] == kernel::single('finance_ar')->get_type_by_name('售后仅退款')) {
$upData['refund_only_money'] += $arRow['money'];
}
}
}
$upData['xiaotui_total'] = $upData['yingshou_money'] + $upData['yingtui_money'];
$billList = app::get('finance')->model('bill')->getList('money,trade_time', ['monthly_item_id' => $itemId]);
foreach ($billList as $billRow) {
if($billRow['money'] > 0) {
$upData['shishou_trade_time'] = $billRow['trade_time'];
$upData['shishou_money'] += $billRow['money'];
} else {
$upData['shitui_trade_time'] = $billRow['trade_time'];
$upData['shitui_money'] += $billRow['money'];
}
}
$upData['shouzhi_total'] = $upData['shishou_money'] + $upData['shitui_money'];
$upData['gap'] = $upData['xiaotui_total'] - $upData['shouzhi_total'];
app::get('finance')->model('monthly_report_items')->update($upData, ['id'=>$itemId]);
return $upData;
}
/**
* 获取_comp
* @param mixed $type type
* @param mixed $var var
* @return mixed 返回结果
*/
public function get_comp($type, $var)
{
$comp = array(
'nequal' => '{field}==' . $var,
'than' => '{field}> ' . $var,
'lthan' => '{field}< ' . $var,
'bthan' => '{field}>=' . $var,
'sthan' => '{field}<=' . $var,
'between' => '{field}>=' . $var[0] . ' && ' . ' {field}<=' . $var[1],
);
return $comp[$type];
}
/**
* doVerificate
* @param mixed $itemId ID
* @param mixed $sdf sdf
* @return mixed 返回值
*/
public function doVerificate($itemId, $sdf) {
$verification_time = time();
$filter = ['status|noequal'=>'2', 'monthly_item_id'=>$itemId];
if($sdf['compare'] == 'z') {
$filter['money|than'] = 0;
} elseif($sdf['compare'] == 'p') {
$filter['money|lthan'] = 0;
}
$mdlBill = app::get('finance')->model('bill');
if($sdf['compare'] == 'ying') {
$bill_data = [];
} else {
$bill_data = $mdlBill->getList('bill_id,bill_bn,money,memo', $filter);
}
foreach ($bill_data as $key => $value) {
// 更新核销流水表
$data = array();
$data['verification_time'] = $verification_time;
$data['status'] = 2;
$data['confirm_money'] = $value['money'];
$data['unconfirm_money'] = 0;
$data['verification_status'] = $sdf['verification_status'];
$data['gap_type'] = $sdf['gap_type'];
$data['memo'] = ($sdf['compare'] == 'z' ? '按收入核销' : ($sdf['compare'] == 'p' ? '按退款核销' : '按订单核销')) . ' ' . $value['memo'];
$data['auto_flag'] = 1;
$mdlBill->update($data,array('bill_id'=>$value['bill_id'],'status|noequal'=>'2'));
}
$mdlAr = app::get('finance')->model('ar');
$ar_data = $mdlAr->getList('ar_id, money', $filter);
foreach ($ar_data as $key => $value) {
$data = array();
if(in_array($sdf['compare'], ['order','ying']) && empty($bill_data)) {
$data['verification_flag'] = 1;
}
$data['verification_time'] = $verification_time;
$data['verification_status'] = $sdf['verification_status'];
$data['gap_type'] = $sdf['gap_type'];
$data['status'] = 2;
$data['confirm_money'] = $value['money'];
$data['unconfirm_money'] = 0;
$data['memo'] = ($sdf['compare'] == 'z' ? '按收入核销' : ($sdf['compare'] == 'p' ? '按退款核销' : '按订单核销')) . ' ' . '实收实退单据号:'. implode('|', array_column($bill_data,'bill_bn')) ;
$data['auto_flag'] = 1;
$mdlAr->update($data,array('ar_id'=>$value['ar_id'],'status|noequal'=>'2'));
}
return [true, ['msg'=>'核销成功']];
}
/**
* dealArMatchReport
* @param mixed $ar_id ID
* @return mixed 返回值
*/
public function dealArMatchReport($ar_id) {
$oMonthlyReportItems = kernel::single('finance_monthly_report_items');
$oAr = app::get('finance')->model('ar');
$arRow = $oAr->db_dump($ar_id, 'ar_id,ar_bn,order_bn,channel_id,trade_time,money,monthly_item_id,channel_id,ar_type');
if(empty($arRow)) {
return [false, ['msg'=>'缺少单据']];
}
if($arRow['monthly_item_id']) {
return [false, ['msg'=>$arRow['ar_bn'].'已经匹配账期']];
}
$init_time = app::get('finance')->getConf('finance_setting_init_time');
if($init_time['according'] == 'shi_shou') {
$itemId = $oMonthlyReportItems->getItemId($arRow, false);
if(empty($itemId) && $arRow['ar_type'] == 1) {
$order = app::get('ome')->model('orders')->db_dump(['order_bn'=>$arRow['order_bn']], 'source_status');
if($order['source_status'] == 'TRADE_CLOSED') {
$itemId = $oMonthlyReportItems->getItemId($arRow);
$arRows = $oAr->getList('ar_id',['order_bn'=>$arRow['order_bn'], 'monthly_item_id'=>0, 'ar_id|noequal'=>$arRow['ar_id']]);
if($arRows) {
foreach($arRows as $v) {
$this->dealArMatchReport($v['ar_id']);
}
}
}
}
} else {
$itemId = $oMonthlyReportItems->getItemId($arRow);
$oBill = app::get('finance')->model('bill');
$billRows = $oBill->getList('bill_id',['order_bn'=>$arRow['order_bn'], 'monthly_item_id'=>0]);
if($billRows) {
foreach($billRows as $v) {
$this->dealBillMatchReport($v['bill_id']);
}
}
}
if($itemId) {
kernel::database()->beginTransaction();
$sql = 'select id, monthly_id, yingshou_money, yingtui_money, xiaotui_total, shouzhi_total
from sdb_finance_monthly_report_items
where id="'.$itemId.'" limit 1 for update';
$itemMRI = kernel::database()->select($sql);
$itemMRI = $itemMRI[0];
$main = [];
$main['monthly_id'] = $itemMRI['monthly_id'];
$main['monthly_item_id'] = $itemId;
$oAr->update($main, ['ar_id'=>$arRow['ar_id']]);
kernel::single('finance_monthly_report_items')->doAutoVerificate($itemId, $arRow['channel_id']);
kernel::database()->commit();
return [true, ['msg' => $arRow['ar_bn'].'匹配成功', 'monthly_id'=>$itemMRI['monthly_id']]];
}
return [false, ['msg' => $arRow['ar_bn'].'匹配失败']];
}
/**
* dealBillMatchReport
* @param mixed $bill_id ID
* @return mixed 返回值
*/
public function dealBillMatchReport($bill_id) {
$oMonthlyReportItems = kernel::single('finance_monthly_report_items');
$oBill = app::get('finance')->model('bill');
$billRow = $oBill->db_dump($bill_id, 'bill_id,bill_bn,order_bn,channel_id,trade_time,money,monthly_item_id,channel_id');
if(empty($billRow)) {
return [false, ['msg'=>'缺少单据']];
}
if($billRow['monthly_item_id']) {
return [false, ['msg'=>$billRow['bill_bn'].'已经匹配账期']];
}
$init_time = app::get('finance')->getConf('finance_setting_init_time');
if($init_time['according'] == 'shi_shou') {
$itemId = $oMonthlyReportItems->getItemId($billRow);
$oAr = app::get('finance')->model('ar');
$arRows = $oAr->getList('ar_id',['order_bn'=>$billRow['order_bn'], 'monthly_item_id'=>0]);
if($arRows) {
foreach($arRows as $v) {
$this->dealArMatchReport($v['ar_id']);
}
}
} else {
$itemId = $oMonthlyReportItems->getItemId($billRow, false);
}
if($itemId) {
kernel::database()->beginTransaction();
$sql = 'select id, monthly_id, xiaotui_total, shishou_money, shitui_money, shouzhi_total
from sdb_finance_monthly_report_items
where id="'.$itemId.'" limit 1 for update';
$itemMRI = kernel::database()->select($sql);
$itemMRI = $itemMRI[0];
$main = [];
$main['monthly_id'] = $itemMRI['monthly_id'];
$main['monthly_item_id'] = $itemId;
$oBill->update($main, ['bill_id'=>$billRow['bill_id']]);
kernel::single('finance_monthly_report_items')->doAutoVerificate($itemId, $billRow['channel_id']);
kernel::database()->commit();
return [true, ['msg' => $billRow['bill_bn'].'匹配成功', 'monthly_id'=>$itemMRI['monthly_id']]];
}
return [false, ['msg' => $billRow['bill_bn'].'匹配失败']];
}
}