mirror of
https://gitee.com/ShopeX/OMS
synced 2026-04-17 11:05:30 +08:00
1. 【新增】售后单售后原因类型支持搜索
2. 【新增】手工创建订单折扣可输入正数 3. 【优化】盘点申请单确认 4. 【修复】采购退货单模拟出库失败问题 5. 【新增】订单金额客户实付与结算金额 6. 【优化】仓库发货统计报表物料名称显示 7. 【优化】自有仓储虚拟发货逻辑 8. 【修复】基础物料分类管理问题
This commit is contained in:
@@ -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());
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
21
app/finance/lib/finder/gaptype.php
Normal file
21
app/finance/lib/finder/gaptype.php
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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'];
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
460
app/finance/lib/monthly/report/items/import.php
Normal file
460
app/finance/lib/monthly/report/items/import.php
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user