1. 【新增】售后单售后原因类型支持搜索

2. 【新增】手工创建订单折扣可输入正数

3. 【优化】盘点申请单确认

4. 【修复】采购退货单模拟出库失败问题

5. 【新增】订单金额客户实付与结算金额

6. 【优化】仓库发货统计报表物料名称显示

7. 【优化】自有仓储虚拟发货逻辑

8. 【修复】基础物料分类管理问题
This commit is contained in:
chenping
2026-04-01 11:59:17 +08:00
parent 9341122827
commit 61783b7d01
754 changed files with 46179 additions and 5700 deletions

View File

@@ -86,6 +86,7 @@ class finance_ar
'charge_time' => $sdf['charge_time'],
'money' => (empty($sdf['money']) ? 0 : $sdf['money']),
'actually_money' => (empty($sdf['actually_money']) ? 0 : $sdf['actually_money']),
'platform_amount' => (empty($sdf['platform_amount']) ? 0 : $sdf['platform_amount']),
'serial_number' => $sdf['serial_number'],
'unique_id' => $sdf['unique_id'],
'addon' => serialize($addon),
@@ -112,6 +113,7 @@ class finance_ar
'num' => $v['num'],
'money' => (empty($v['money']) ? 0 : $v['money']),
'actually_money' => (empty($v['actually_money']) ? 0 : $v['actually_money']),
'platform_amount' => (empty($v['platform_amount']) ? 0 : $v['platform_amount']),
);
if (!$receitemObj->insert($items)) {
$res = array('status' => 'fail', 'msg' => '明细数据保存失败:'.$receitemObj->db->errorinfo());

View File

@@ -120,6 +120,8 @@ class finance_cronjob_tradeScript
$arrShop = app::get('ome')->model('shop')->getList('shop_id, shop_type, name');
$arrShop = array_column($arrShop, null, 'shop_id');
$init_time = app::get('finance')->getConf('finance_setting_init_time');
$orders = array();
$sales_items = array();
if ($list) {
foreach ($list as $k => $v) {
if($init_time['ying_shou'] == 'settlement') {
@@ -129,6 +131,9 @@ class finance_cronjob_tradeScript
} else {
$sale_amount = $v['sale_amount'];
}
// 计算平台承担金额 = 结算金额 - 客户实付金额
$platform_amount = bcsub($v['settlement_amount'], $v['actually_amount'], 3);
$sales[$v['sale_id']] = array(
'sale_time' => $v['sale_time'],
'delivery_time' => $v['ship_time'],
@@ -136,6 +141,7 @@ class finance_cronjob_tradeScript
'sale_amount' => bcsub($sale_amount, $v['cost_freight'], 3), //$v['sale_amount'],
'delivery_cost' => $v['cost_freight'],
'actually_amount' => $v['actually_amount'],
'platform_amount' => $platform_amount,
'sale_bn' => $v['sale_bn'],
'iostock_type' => &$orders[$v['order_id']]['iostock_type'],
'sales_items' => &$sales_items[$v['sale_id']],
@@ -191,12 +197,16 @@ class finance_cronjob_tradeScript
} elseif($init_time['ying_shou'] == 'actually') {
$v['sales_amount'] = $v['actually_amount'];
}
// 计算平台承担金额 = 结算金额 - 客户实付金额
$item_platform_amount = bcsub($v['settlement_amount'], $v['actually_amount'], 3);
$sales_items[$v['sale_id']][] = array(
'bn' => $v['bn'],
'name' => $v['name'],
'nums' => $v['nums'],
'sales_amount' => $v['sales_amount'],
'actually_amount' => $v['actually_amount'],
'platform_amount' => $item_platform_amount,
'shop_name' => $sales[$v['sale_id']]['shop_name'],
'shop_id' => $sales[$v['sale_id']]['shop_id'],
'order_id' => $sales[$v['sale_id']]['order_id'],
@@ -554,6 +564,9 @@ class finance_cronjob_tradeScript
!$relate_order_bn and $relate_order_bn = $sdf['diff_order_bn'];
!$relate_order_bn and $relate_order_bn = $sdf['order_bn'];
// 计算平台承担金额 = 结算金额 - 客户实付金额(退款为负数)
$platform_amount = sprintf('%.3f', ($sdf['settlement_amount'] - $sdf['actually_amount']) * -1);
$ar_data = array(
'ar_bn' => $oBillAr->gen_ar_bn(),
'trade_time' => $sdf['aftersale_time'],
@@ -572,6 +585,7 @@ class finance_cronjob_tradeScript
'charge_time' => time(),
'money' => $money,
'actually_money' => sprintf('%.2f', $sdf['actually_amount'] * -1),
'platform_amount' => $platform_amount,
'serial_number' => $sdf['aftersale_bn'],
'unique_id' => md5($sdf['aftersale_bn']),
'ar_type' => 1,
@@ -590,7 +604,7 @@ class finance_cronjob_tradeScript
throw new Exception('插入ar单据表失败');
}
$aftersale_items = $mdlSalesAftersaleItems->getList('bn,num,refunded,settlement_amount,actually_amount,product_name', array(
$aftersale_items = $mdlSalesAftersaleItems->getList('bn,num,refunded,settlement_amount,actually_amount,platform_amount,product_name', array(
'aftersale_id' => $sdf['aftersale_id'],
'return_type' => ['return','refunded','refuse'],
));
@@ -605,6 +619,9 @@ class finance_cronjob_tradeScript
));
}
foreach ($aftersale_items as $v) {
// 计算平台承担金额 = 结算金额 - 客户实付金额(退款为负数)
$item_platform_amount = sprintf('%.3f', ($v['settlement_amount'] - $v['actually_amount']) * -1);
$ar_item = array(
'ar_id' => $ar_id,
'bn' => $v['bn'],
@@ -612,6 +629,7 @@ class finance_cronjob_tradeScript
'num' => $v['num'],
'money' => sprintf('%.2f', $v['refunded'] * -1),
'actually_money' => sprintf('%.2f', $v['actually_amount'] * -1),
'platform_amount' => $item_platform_amount,
);
if($init_time['ying_tui'] == 'settlement' && $sdf['return_type'] != 'refund') {
$ar_item['money'] = sprintf('%.2f', $v['settlement_amount'] * -1);

View File

@@ -112,7 +112,7 @@ class finance_finder_bill{
return $render->fetch("settlement/verification_logs.html");
}
public $column_monthly_id = '账期名称';
public $column_monthly_id_order = 1;
public $column_monthly_id_width = 180;

View File

@@ -0,0 +1,21 @@
<?php
/**
* ============================
* @Author: yaokangming
* @describe: 财务差异类型查找器
* ============================
*/
class finance_finder_gaptype {
public $addon_cols = "";
public $column_edit = "操作";
public $column_edit_width = 120;
public $column_edit_order = 1;
public function column_edit($row){
$btn = [];
$btn[] = '<a class="lnk" target="dialog::{width:600,height:300,title:\'编辑\'}" href="index.php?app=finance&ctl=gaptype&act=edit&p[0]='.$row['id'].'&finder_id='.$_GET['_finder']['finder_id'].'&finder_vid='.$_GET['finder_vid'].'">编辑</a>';
return implode('|', $btn);
}
}

View File

@@ -16,11 +16,12 @@
*/
class finance_finder_monthly_report_items{
var $addon_cols = "order_bn,monthly_id";
var $addon_cols = "order_bn,monthly_id,sales_gap,refund_gap";
var $column_edit = "操作";
var $column_edit_width = "150";
var $column_edit_order=5;
function column_edit($row){
$confhref = '';
$row['monthly_id'] = $row[$this->col_prefix.'monthly_id'];

View File

@@ -52,6 +52,7 @@ class finance_iostocksales
$data['sale_amount'] = (empty($data['sale_amount']) ? 0 : $data['sale_amount']);
$data['delivery_cost'] = (empty($data['delivery_cost']) ? 0 : $data['delivery_cost']);
$data['actually_amount'] = (empty($data['actually_amount']) ? 0 : $data['actually_amount']);
$data['platform_amount'] = (empty($data['platform_amount']) ? 0 : $data['platform_amount']);
//sdf
$return_sdf = array();
@@ -66,6 +67,7 @@ class finance_iostocksales
$return_sdf['sale_money'] = $data['sale_amount']; //商品成交金额
$return_sdf['fee_money'] = $data['delivery_cost']; //运费
$return_sdf['actually_money'] = $data['actually_amount']; //客户实付
$return_sdf['platform_amount'] = $data['platform_amount']; //平台补贴
$return_sdf['money'] = $return_sdf['sale_money'] + $return_sdf['fee_money']; //商品成交金额+运费
$return_sdf['serial_number'] = $data['sale_bn']; //默认销售单据号,没有自定义规则 todo
$return_sdf['charge_status'] = 1; //默认已记账
@@ -94,6 +96,7 @@ class finance_iostocksales
$aTmp['num'] = abs($val['nums']);
$aTmp['money'] = $val['sales_amount'];
$aTmp['actually_money'] = $val['actually_amount'];
$aTmp['platform_amount'] = $val['platform_amount'];
$return_sale_itmes[] = $aTmp;
}
return $return_sale_itmes;

View File

@@ -210,17 +210,23 @@ class finance_monthly_report
}
}
$ar_list = $mdlMonthlyReport->db->select('select sum(money) as amount,monthly_id,ar_type from sdb_finance_ar where monthly_id in ('.implode(',', $monthly_list).') and charge_status = 1 group by monthly_id,ar_type');
$ar_list = $mdlMonthlyReport->db->select('select sum(money) as amount,sum(actually_money) as actually_amount,sum(platform_amount) as platform_amount,monthly_id,ar_type from sdb_finance_ar where monthly_id in ('.implode(',', $monthly_list).') and charge_status = 1 group by monthly_id,ar_type');
if($ar_list)
{
foreach ($ar_list as $v) {
if($v['ar_type'])
{
$monthly_amount[$v['monthly_id']]['ar_out_amount'] = $v['amount'];
// 累计退款客户实付和退款平台承担金额(只计算流出)
$monthly_amount[$v['monthly_id']]['refund_actually_amount'] = $v['actually_amount'];
$monthly_amount[$v['monthly_id']]['refund_platform_amount'] = $v['platform_amount'];
}
else
{
$monthly_amount[$v['monthly_id']]['ar_in_amount'] = $v['amount'];
// 累计客户实付和平台承担金额(只计算流入)
$monthly_amount[$v['monthly_id']]['actually_amount'] = $v['actually_amount'];
$monthly_amount[$v['monthly_id']]['platform_amount'] = $v['platform_amount'];
}
}
}
@@ -232,6 +238,10 @@ class finance_monthly_report
$data['bill_out_amount'] = isset($monthly_amount[$monthly_id]['bill_out_amount']) ? $monthly_amount[$monthly_id]['bill_out_amount'] : 0;
$data['ar_in_amount'] = isset($monthly_amount[$monthly_id]['ar_in_amount']) ? $monthly_amount[$monthly_id]['ar_in_amount'] : 0;
$data['ar_out_amount'] = isset($monthly_amount[$monthly_id]['ar_out_amount']) ? $monthly_amount[$monthly_id]['ar_out_amount'] : 0;
$data['actually_amount'] = isset($monthly_amount[$monthly_id]['actually_amount']) ? $monthly_amount[$monthly_id]['actually_amount'] : 0;
$data['platform_amount'] = isset($monthly_amount[$monthly_id]['platform_amount']) ? $monthly_amount[$monthly_id]['platform_amount'] : 0;
$data['refund_actually_amount'] = isset($monthly_amount[$monthly_id]['refund_actually_amount']) ? $monthly_amount[$monthly_id]['refund_actually_amount'] : 0;
$data['refund_platform_amount'] = isset($monthly_amount[$monthly_id]['refund_platform_amount']) ? $monthly_amount[$monthly_id]['refund_platform_amount'] : 0;
$mdlMonthlyReport->update($data,array('monthly_id'=>$monthly_id));
}
}

View File

@@ -81,10 +81,15 @@ class finance_monthly_report_items {
}
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')
$billInfo = [];
if($billInfo = app::get('finance')->model('bill')->db_dump(['status|noequal'=>'2', 'monthly_item_id'=>$itemId])
|| 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]);
$upData = ['gap_type'=>'', 'memo'=>$rsData['msg'], 'verification_status'=>'1'];
if($billInfo && $billInfo['fee_item'] == '国补金额' && $billInfo['money']>0){
$upData['gov_shishou_total_upset_sql'] = '`gov_shishou_total`+'.$billInfo['money'];
}
$oMRI->update($upData, ['id'=>$itemId]);
} else {
$upData = ['verification_status'=>'2', 'memo'=>'', 'gap_type'=>''];
if($rsData['msg']) {
@@ -94,6 +99,9 @@ class finance_monthly_report_items {
$upData['gap_type'] = $rsData['gap_type'];
}
$oMRI->update($upData, ['id'=>$itemId]);
// 更新核对状态
kernel::single('finance_verification')->updateArVerifyStatus($itemId);
}
$servicelist = kernel::servicelist('financebase.reportitem.doAutoVerificate.after');
@@ -217,15 +225,21 @@ class finance_monthly_report_items {
* @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];
$arlist = app::get('finance')->model('ar')->getList('money,type,trade_time,actually_money,platform_amount', ['monthly_item_id' => $itemId]);
$upData = ['yingshou_money'=>0, 'yingtui_money'=>0, 'refund_only_money'=>0,'shishou_money'=>0,'shitui_money'=>0,'actually_amount'=>0,'platform_amount'=>0,'refund_actually_amount'=>0,'refund_platform_amount'=>0];
foreach ($arlist as $arRow) {
if($arRow['money'] > 0) {
$upData['ship_time'] = $arRow['trade_time'];
$upData['yingshou_money'] += $arRow['money'];
// 累计客户实付和平台承担金额(只计算收入)
$upData['actually_amount'] += $arRow['actually_money'];
$upData['platform_amount'] += $arRow['platform_amount'];
} else {
$upData['reship_time'] = $arRow['trade_time'];
$upData['yingtui_money'] += $arRow['money'];
// 累计退款客户实付和退款平台承担金额(只计算退款)
$upData['refund_actually_amount'] += $arRow['actually_money'];
$upData['refund_platform_amount'] += $arRow['platform_amount'];
if($arRow['type'] == kernel::single('finance_ar')->get_type_by_name('售后仅退款')) {
$upData['refund_only_money'] += $arRow['money'];
}
@@ -244,6 +258,8 @@ class finance_monthly_report_items {
}
$upData['shouzhi_total'] = $upData['shishou_money'] + $upData['shitui_money'];
$upData['gap'] = $upData['xiaotui_total'] - $upData['shouzhi_total'];
$upData['sales_gap'] = $upData['yingshou_money'] - $upData['shishou_money'];
$upData['refund_gap'] = $upData['yingtui_money'] - $upData['shitui_money'];
app::get('finance')->model('monthly_report_items')->update($upData, ['id'=>$itemId]);
return $upData;
}

View File

@@ -0,0 +1,460 @@
<?php
class finance_monthly_report_items_import implements omecsv_data_split_interface
{
/**
* 导入模板标题定义
*/
const IMPORT_TITLE = [
['label' => '*:账单名称', 'col' => 'monthly_date'],
['label' => '*:订单号', 'col' => 'order_bn'],
['label' => '*:差异类型', 'col' => 'gap_type'],
];
public $column_num = 3; // 表头列数
public $current_key = null; // 切片标识
private $oSchema = []; // 表头定义
private $ioTitle = []; // 输入输出标题
// 静态缓存变量,减少数据库查询
private static $gapNames = []; // 有效的差异类型名称缓存
private static $monthlyReportCache = []; // 账单信息缓存
/**
* 初始化缓存数据
*/
private function initCache()
{
// 如果缓存为空,则初始化
if (empty(self::$gapNames)) {
$mdlGap = app::get('financebase')->model('gap');
$gapList = $mdlGap->getList('gap_name', ['status' => '1']);
self::$gapNames = array_column($gapList, 'gap_name');
}
}
/**
* 获取账单信息(带缓存)
*/
private function getMonthlyReportInfo($monthly_date, $monthly_id = '')
{
// 如果缓存中没有,则查询数据库
$cacheKey = $monthly_date . '_' . $monthly_id;
if (!isset(self::$monthlyReportCache[$cacheKey])) {
$mdlMonthlyReport = app::get('finance')->model('monthly_report');
// 使用monthly_id和monthly_date一起查询确保唯一性
$monthlyInfo = $mdlMonthlyReport->db_dump([
'monthly_id' => $monthly_id,
'monthly_date' => $monthly_date
], 'monthly_id');
self::$monthlyReportCache[$cacheKey] = $monthlyInfo;
}
return self::$monthlyReportCache[$cacheKey];
}
/**
* 获取表头定义(接口要求的方法)
*/
public function getTitle($filter = null, $ioType = 'csv')
{
return array_column(self::IMPORT_TITLE, 'label');
}
/**
* 文件格式检查
*/
public function checkFile($file_name, $file_type, $queue_data)
{
// 获取monthly_id参数
$monthly_id = $queue_data['monthly_id'] ?? '';
if (!$monthly_id) {
return [false, '缺少monthly_id参数'];
}
$ioType = kernel::single('omecsv_io_split_' . $file_type);
$rows = $ioType->getData($file_name, 0, -1);
$title = $this->getTitle();
// 检查表头
$plateTitle = $rows[0];
foreach ($title as $v) {
if (array_search($v, $plateTitle) === false) {
return [false, '文件模板错误:列【' . $v . '】未包含在' . implode('、', $plateTitle)];
}
}
// 3. 获取数据行并预处理(跳过第一行标题)
$dataRows = array_slice($rows, 1);
if (empty($dataRows)) {
return [false, '没有数据行'];
}
// 预处理:过滤空行
$dataRows = $this->filterEmptyRows($dataRows);
if (empty($dataRows)) {
return [false, '没有有效的数据行'];
}
// 4. 验证数据行
$oSchema = array_column(self::IMPORT_TITLE, 'label', 'col');
$sdf = [];
$errmsg = [];
foreach ($dataRows as $rowIndex => $row) {
$rowNum = $rowIndex + 2; // 行号从2开始第1行是标题
// 将行数据与标题对应
$titleKey = array();
$_title = $plateTitle;
foreach ($plateTitle as $k => $t) {
$titleKey[$k] = array_search($t, $oSchema);
if ($titleKey[$k] === false) {
unset($titleKey[$k]);
unset($row[$k]);
unset($_title[$k]);
}
}
$_title = array_values($_title);
$row = array_values($row);
// 如果当前行的数据长于标题,截取标题长度的数据
if (count($row) > count($titleKey)) {
$row = array_splice($row, 0, count($titleKey));
}
$rowData = array_combine($titleKey, $row);
if (!$rowData) {
return [false, "{$rowNum}行:标题列数(" . count($plateTitle) . ")与数据列数(" . count($row) . ")不匹配"];
}
// 验证必填字段
foreach ($title as $v) {
$field = array_search($v, $oSchema);
if ($field && empty(trim($rowData[$field]))) {
return [false, "{$rowNum}行:{$v}不能为空"];
}
}
// 格式化数据
$this->_formatData($rowData);
$sdf[] = $rowData;
}
if ($errmsg) {
return [false, '文件内容错误信息:' . implode('<br/>', $errmsg)];
}
// 业务验证
list($dataList, $errMsg) = $this->validateData($sdf, true, $monthly_id);
if ($errMsg) {
return [false, '文件内容错误信息:' . implode('<br/>', $errMsg)];
}
return [true, '文件模板匹配', $rows[0]];
}
/**
* 数据处理主方法
*/
public function process($cursor_id, $params, &$errmsg)
{
set_time_limit(0);
@ini_set('memory_limit', '128M');
$oFunc = kernel::single('omecsv_func');
$queueMdl = app::get('omecsv')->model('queue');
// 获取monthly_id参数
$monthly_id = $params['queue_data']['monthly_id'] ?? '';
if (!$monthly_id) {
$errmsg[] = '缺少monthly_id参数';
return [false];
}
$oFunc->writelog('处理任务-开始', 'import', $params);
// 业务逻辑处理
$data = $params['data'];
// 使用params中的title这是实际的CSV标题行
$title = $params['title'];
$sdf = [];
$offset = intval($data['offset']) + 1; // 文件行数 行数默认从1开始
$splitCount = 0; // 执行行数
// 去掉第一行标题,只保留数据行
$data = array_slice($data, 1);
// 预处理:过滤空行
$data = $this->filterEmptyRows($data);
// 记录预处理结果
$oFunc->writelog('预处理结果', 'import', [
'原始数据行数' => count($params['data']),
'过滤后数据行数' => count($data)
]);
// 使用过滤后的数据 - 参考订单导入的数据对齐逻辑
if ($data) {
$oSchema = array_column(self::IMPORT_TITLE, 'label', 'col');
foreach ($data as $row) {
// 将行数据与标题对应 - 参考订单导入的逻辑
$titleKey = array();
$_title = $title;
foreach ($title as $k => $t) {
$titleKey[$k] = array_search($t, $oSchema);
if ($titleKey[$k] === false) {
unset($titleKey[$k]);
unset($row[$k]);
unset($_title[$k]);
}
}
$_title = array_values($_title);
$row = array_values($row);
// 如果当前行的数据长于标题,截取标题长度的数据
if (count($row) > count($titleKey)) {
$row = array_splice($row, 0, count($titleKey));
}
$rowData = array_combine($titleKey, $row);
if ($rowData) {
// 格式化数据
$this->_formatData($rowData);
$sdf[] = $rowData;
$splitCount++;
} else {
array_push($errmsg, "{$offset}行:数据格式错误");
}
$offset++;
}
}
unset($data);
// 数据验证
if ($sdf) {
list($validData, $validateErrMsg) = $this->validateData($sdf, true, $monthly_id);
if ($validateErrMsg) {
$errmsg = array_merge($errmsg, $validateErrMsg);
}
// 保存数据
if ($validData) {
list($result, $msgList) = $this->saveData($validData);
if ($msgList) {
$errmsg = array_merge($errmsg, $msgList);
}
$queueMdl->update(['split_count' => count($validData)], ['queue_id' => $cursor_id]);
}
}
$oFunc->writelog('处理任务-完成', 'import', 'Done');
return [true];
}
/**
* 数据格式转换
*/
public function getSdf($row, $offset, $title)
{
$row = array_map('trim', $row);
$oSchema = array_column(self::IMPORT_TITLE, 'label', 'col');
$titleKey = [];
foreach ($title as $k => $t) {
$titleKey[$k] = array_search($t, $oSchema);
if ($titleKey[$k] === false) {
return ['status' => false, 'msg' => '未定义字段`' . $t . '`'];
}
}
$res = ['status' => true, 'data' => [], 'msg' => ''];
if ($this->column_num <= count($row) && $row[0] != '*:表头标识') {
$tmp = array_combine($titleKey, $row);
// 必填字段验证
foreach ($tmp as $k => $v) {
if (strpos($k, '*:') === 0 && !$v) {
$res['status'] = false;
$res['msg'] = sprintf("LINE %d : %s 不能为空!", $offset, $oSchema[$k]);
return $res;
}
}
$res['data'] = $tmp;
}
return $res;
}
/**
* 数据格式化
*/
public function _formatData(&$data)
{
foreach ($data as $k => $str) {
$data[$k] = str_replace(["\r\n", "\r", "\n", "\t"], "", $str);
}
}
/**
* 过滤空行
* @param $rows 数据行数组
* @return array 过滤后的数据行数组
*/
private function filterEmptyRows($rows)
{
$filteredRows = [];
foreach ($rows as $row) {
// 检查行是否为空(所有列都是空值)
$isEmpty = true;
if (is_array($row)) {
foreach ($row as $cell) {
if (!empty(trim($cell))) {
$isEmpty = false;
break;
}
}
}
// 如果遇到第一个空行,就停止处理
if ($isEmpty) {
break;
}
$filteredRows[] = $row;
}
return $filteredRows;
}
/**
* 数据验证(合并了业务逻辑验证)
*/
public function validateData($data, $is_check = true, $monthly_id = '')
{
// 初始化缓存
$this->initCache();
$errMsg = [];
$validData = [];
$mdlMonthlyReportItems = app::get('finance')->model('monthly_report_items');
foreach ($data as $key => $item) {
// 先验证差异类型是否存在且有效(使用缓存,最快验证)
if (!in_array($item['gap_type'], self::$gapNames)) {
$errMsg[] = sprintf('数据验证失败,行号:%d - 差异类型【%s】不存在或已失效', $key + 1, $item['gap_type']);
continue;
}
// 验证账单名称是否存在使用monthly_id和monthly_date一起查询确保唯一性
$monthlyInfo = $this->getMonthlyReportInfo($item['monthly_date'], $monthly_id);
if (!$monthlyInfo) {
$errMsg[] = sprintf('数据验证失败,行号:%d - 账单名称【%s】与当前账单不匹配', $key + 1, $item['monthly_date']);
continue;
}
// 验证订单号是否存在
$itemInfo = $mdlMonthlyReportItems->db_dump([
'monthly_id' => $monthly_id,
'order_bn' => $item['order_bn']
], 'id');
if (!$itemInfo) {
$errMsg[] = sprintf('数据验证失败,行号:%d - 订单号【%s】在指定账单中不存在', $key + 1, $item['order_bn']);
continue;
}
// 保存验证通过的数据用于后续处理
$item['monthly_id'] = $monthly_id;
$item['item_id'] = $itemInfo['id'];
$validData[] = $item;
}
return [$validData, $errMsg];
}
/**
* 保存数据
*/
public function saveData($contents)
{
$errMsg = [];
foreach ($contents as $data) {
// 开启事务
kernel::database()->beginTransaction();
try {
// 更新三个表的差异类型
$mdlBill = app::get('finance')->model('bill');
$mdlAr = app::get('finance')->model('ar');
$mdlItem = app::get('finance')->model('monthly_report_items');
// 更新 bill 表
$mdlBill->update(
['gap_type' => $data['gap_type']],
['order_bn' => $data['order_bn'], 'monthly_id' => $data['monthly_id']]
);
// 更新 ar 表
$mdlAr->update(
['gap_type' => $data['gap_type']],
['order_bn' => $data['order_bn'], 'monthly_id' => $data['monthly_id']]
);
// 更新 monthly_report_items 表
$mdlItem->update(
['gap_type' => $data['gap_type']],
['id' => $data['item_id']]
);
// 提交事务
kernel::database()->commit();
} catch (Exception $e) {
// 回滚事务
kernel::database()->rollBack();
$errMsg[] = $e->getMessage();
}
}
return [true, $errMsg];
}
/**
* 切片检测
*/
public function is_split($row)
{
$is_split = false;
if ($row['0'] !== $this->current_key) {
if ($this->current_key !== null) {
$is_split = true;
}
$this->current_key = $row['0'];
}
return $is_split;
}
/**
* 配置信息(可选方法)
* 如果不实现此方法,系统将默认走队列处理
* 实现此方法并设置 max_direct_count > 0 时,数据量小于等于此值将直接处理
*/
public function getConfig($key = null)
{
$config = [
'page_size' => 100, // 每页处理数量
'max_direct_count' => 50, // 50条以内直接处理超过则走队列
];
return $key ? $config[$key] : $config;
}
}

View File

@@ -337,11 +337,23 @@ class finance_verification
return $res;
}
$oMRI = app::get('finance')->model('monthly_report_items');
$servicelist = kernel::servicelist('financebase.reportitem.doAutoVerificate.after');
foreach($monthly_item_id as $itemId) {
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(['memo'=>$memo, 'verification_status'=>'2'], ['id'=>$itemId]);
// 更新核对状态
$this->updateArVerifyStatus($itemId);
foreach ($servicelist as $instance) {
if (method_exists($instance, 'after_verificate')){
$instance->after_verificate($itemId);
}
}
}
}
return $res;
@@ -612,4 +624,44 @@ class finance_verification
return $res;
}
/**
* 更新核对状态
* 根据monthly_item_id查找sdb_finance_bill表数据匹配sdb_financebase_bill表的unique_id
* 如果匹配到且ar_verify_status='2',则更新为'1'
*
* @param int $itemId 月度报告项目ID
* @return bool 更新是否成功
*/
public function updateArVerifyStatus($itemId)
{
if (empty($itemId)) {
return false;
}
// 第一步:查询符合条件的 unique_id 列表
$sql1 = "SELECT unique_id
FROM sdb_finance_bill
WHERE monthly_item_id = '{$itemId}'";
$uniqueIds = kernel::database()->select($sql1);
if (empty($uniqueIds)) {
return true; // 没有需要更新的记录
}
// 提取 unique_id 数组
$ids = array();
foreach ($uniqueIds as $row) {
$ids[] = $row['unique_id'];
}
$idsStr = "'" . implode("','", $ids) . "'";
// 第二步:更新 sdb_financebase_bill 表
$sql2 = "UPDATE sdb_financebase_bill
SET ar_verify_status = '1'
WHERE unique_id IN ({$idsStr})
AND ar_verify_status = '2'";
return kernel::database()->exec($sql2);
}
}