Browse Source

日对账单,回调/下单切换

lowkeyman 8 months ago
parent
commit
ef96eafbee

+ 40 - 26
admin/control/refill_amount_stats.php

@@ -47,7 +47,6 @@ class refill_amount_statsControl extends SystemControl
             'channel_normal' => $settings['channel_normal'] ?? [],
             'merchant' => $settings['merchant'] ?? [],
             'daily_channel' => $settings['daily_channel'] ?? [],
-            'daily_channel_set' => $settings['daily_channel_set'] ?? [],
             'daily_merchant' => $settings['daily_merchant'] ?? []
         ];
     }
@@ -257,25 +256,29 @@ class refill_amount_statsControl extends SystemControl
                 'day' => $_GET['query_day'],
                 'store_name' => $item['store_name'],
                 'name' => $item['name'],
-                'quantity' => 0,
-                'card_value' => 0,
-                'discounted_amount' => 0,
                 'initial_balance' => 0,
                 'repayment_amount' => 0,
-                'manual_amount' => 0,//手动调款+返销,备留字段
-                'ending_balance' => 0,
                 'tag' => ADMIN_NAME,
-                'store_id' => $store_id
+                'store_id' => $store_id,
+                'notify_time' => [
+                    'quantity' => 0,
+                    'card_value' => 0,
+                    'discounted_amount' => 0,
+                ],
+                'order_time' => [
+                    'quantity' => 0,
+                    'card_value' => 0,
+                    'discounted_amount' => 0,
+                ]
             ];
         }
 
         $order_condition['type'] = 'provider';
         $order_condition['time_text'] = $_GET['query_day'];
-        $order_condition['order_time_type'] = 'notify_time';
         $order_condition['cid'] = ['in', $_GET['cid']];
 
         $model_refill_order = Model('refill_order');
-        $stats_list = $model_refill_order->getOrderStatsList($order_condition, 200, '*', 'time_stamp desc, cname asc');
+        $stats_list = $model_refill_order->getOrderStatsListALl($order_condition);
 
         foreach ($stats_list as $item)
         {
@@ -284,9 +287,14 @@ class refill_amount_statsControl extends SystemControl
                 continue;
             }
 
-            $list[$store_id]['quantity'] = $item['success_count'];
-            $list[$store_id]['card_value'] = $item['success_refill_amounts'];
-            $list[$store_id]['discounted_amount'] = $item['success_channel_amounts'];
+            $time_type = $item['order_time_type'];
+            if (!in_array($time_type, ['notify_time', 'order_time'])) {
+                continue;
+            }
+
+            $list[$store_id][$time_type]['quantity'] = $item['success_count'];
+            $list[$store_id][$time_type]['card_value'] = $item['success_refill_amounts'];
+            $list[$store_id][$time_type]['discounted_amount'] = $item['success_channel_amounts'];
         }
 
         $amount_condition['refill_provider.store_id'] = ['in', $_GET['cid']];
@@ -336,21 +344,25 @@ class refill_amount_statsControl extends SystemControl
                 'day' => $_GET['query_day'],
                 'mch_name' => $item['company_name'],
                 'name' => $item['name'],
-                'quantity' => 0,
-                'card_value' => 0,
-                'discounted_amount' => 0,
                 'initial_balance' => 0,
                 'repayment_amount' => 0,
-                'manual_amount' => 0,//手动调款+返销
-                'ending_balance' => 0,
                 'tag' => ADMIN_NAME,
-                'mch_id' => $mchid
+                'mch_id' => $mchid,
+                'notify_time' => [
+                    'quantity' => 0,
+                    'card_value' => 0,
+                    'discounted_amount' => 0,
+                ],
+                'order_time' => [
+                    'quantity' => 0,
+                    'card_value' => 0,
+                    'discounted_amount' => 0,
+                ]
             ];
         }
 
         $order_condition['type'] = 'merchant';
         $order_condition['time_text'] = $_GET['query_day'];
-        $order_condition['order_time_type'] = 'notify_time';
         $order_condition['cid'] = ['in', $_GET['cid']];
 
         $model_refill_order = Model('refill_order');
@@ -363,9 +375,14 @@ class refill_amount_statsControl extends SystemControl
                 continue;
             }
 
-            $list[$store_id]['quantity'] = $item['success_count'];
-            $list[$store_id]['card_value'] = $item['success_refill_amounts'];
-            $list[$store_id]['discounted_amount'] = $item['success_mch_amounts'];
+            $time_type = $item['order_time_type'];
+            if (!in_array($time_type, ['notify_time', 'order_time'])) {
+                continue;
+            }
+
+            $list[$store_id][$time_type]['quantity'] = $item['success_count'];
+            $list[$store_id][$time_type]['card_value'] = $item['success_refill_amounts'];
+            $list[$store_id][$time_type]['discounted_amount'] = $item['success_mch_amounts'];
         }
 
         $start_unixtime = intval(strtotime($_GET['query_day'] . '00:00:00'));
@@ -375,16 +392,13 @@ class refill_amount_statsControl extends SystemControl
         $amount_condition['check_time'] = ['gt', 0];
         $amount_condition['status'] = 2;
         $merchant_model = Model('merchant');
-        $evidence_list = $merchant_model->getRefillEvidence($amount_condition, 200, 'refill_evidence.*,member.available_predeposit', 'refill_evidence.add_time desc', '', true);
+        $evidence_list = $merchant_model->getRefillEvidenceAll($amount_condition, 'refill_evidence.*,member.available_predeposit', 'refill_evidence.add_time desc');
 
         foreach ($evidence_list as $value)
         {
             $mchid = $value['mchid'];
 
             $list[$mchid]['repayment_amount'] += $value['amount'];
-            if (in_array($value['add_type'], [3, 6])) {
-                $list[$mchid]['manual_amount'] += $value['amount'];
-            }
         }
 
         $data = [

+ 32 - 6
admin/templates/default/js/stats-component.js

@@ -466,6 +466,35 @@ class LayuiTableBase {
         });
     }
 
+    switchEvent(switchColumns = [], callback) {
+        const tableItem = $('#' + this.tableId);
+        const trs = tableItem.next('.layui-table-view').find('.layui-table-body tbody tr');
+
+        trs.each(function (i, tr) {
+            for (let clo of switchColumns) {
+                const td = $(tr).find('td[data-field="' + clo + '"]').not('.layui-hide');
+                if (td.length > 0) {
+                    const buttons = td.find('.layui-btn');
+
+                    buttons.off('click').on('click', function (e) {
+                        e.preventDefault();
+                        e.stopPropagation();
+
+                        const currTr = $(this).closest('tr');
+                        const index = currTr.data('index');
+
+                        buttons.removeClass('selected').addClass('unselected');
+                        $(this).removeClass('unselected').addClass('selected');
+
+                        if (callback !== null && typeof callback === 'function') {
+                            callback(index);
+                        }
+                    });
+                }
+            }
+        });
+    }
+
     /*
      * 表格中金额字段样式,小于0的数值设置为红色
      */
@@ -712,12 +741,9 @@ class SelectedChannelComponent extends Component {
 
     async loadData() {
         const loadIndex = layer.load(2, { shade: [0.2, '#000'] });
-
-        if (this.sysData.length === 0) { //系统全量通道数据,仅加载依次
-            const sysChannels = await statsApi.getSysChannels();
-            if (sysChannels.state === true) {
-                this.sysData = sysChannels.list;
-            }
+        const sysChannels = await statsApi.getSysChannels();
+        if (sysChannels.state === true) {
+            this.sysData = sysChannels.list;
         }
 
         const statsSettings = await statsApi.getStatsSettings();

+ 462 - 69
admin/templates/default/refill_amount_stats.daily_statement.php

@@ -168,13 +168,40 @@
     .input-container .layui-input {
         border: 1px solid #d2d2d2;
         padding: 0 11px;
-        width: 200px; /* 你可以根据需要调整宽度 */
+        width: 200px;
         box-sizing: border-box;
     }
 
     .input-container .layui-btn {
         margin-left: 10px;
     }
+
+    .layui-btn.switch-btn {
+        padding: 0 10px;
+        height: 28px;
+        line-height: 28px;
+        margin: 0;
+        font-size: 14px;
+        user-select: none;
+        display: inline-block;
+        border-radius: 0;
+        float: left;
+    }
+
+    .layui-btn.selected {
+        background-color: #1E9FFF;
+        color: white;
+    }
+
+    .layui-btn.unselected {
+        background-color: #E0E0E0;
+        color: black;
+    }
+
+    .layui-btn.switch-btn:hover {
+        cursor: pointer;
+    }
+
 </style>
 
 <div class="page" id="app">
@@ -275,6 +302,13 @@
         return `${year}-${month}-${day}`;
     }
 
+    function getLocalDate(date = new Date()) {
+        const year = date.getFullYear();
+        const month = String(date.getMonth() + 1).padStart(2, '0');
+        const day = String(date.getDate()).padStart(2, '0');
+        return `${year}-${month}-${day}`;
+    };
+
     function addData(obj, date, value) {
         obj[date] = value;
 
@@ -293,8 +327,22 @@
         Object.assign(obj, sortedObj);
     }
 
+    function removeSet(data) {//本系统不缓存其他系统的期初配置,防止缓存过大
+        const dataClone = deepCloneData(data);
+        dataClone.forEach(obj => {
+            if (obj.tag !== systemTag) {
+                delete obj.set_key;
+                delete obj.set;
+                delete obj.set_order;
+            }
+        });
+        return dataClone;
+    };
+
+    let day = getLastDay();
+
     $(function() {
-        const day = getLastDay();
+
         let upstreamOrgData = [], downstreamOrgData = [] ,copyData = {};
 
         // 初始化部分
@@ -349,13 +397,26 @@
 
                 const params =  { cid: storeIds, query_day: day};
                 const providerAmountData = await statsApi.getDailyProviderData(params);
-                const providerAmountOrg = providerAmountData.list;
+                let providerAmountOrg = [];
+                if (providerAmountData.state === true) {
+                    providerAmountOrg = providerAmountData.list;
+                }
 
                 upstreamOrgData = deepCloneData(providerAmountOrg);
 
                 let providerAmount = [];
                 if (!isObjectEmpty(copyData)) {
                     providerAmount = mergeData(providerAmountOrg, copyData.stats_data_channel, 'store_name');
+                    this.dailyChannel.forEach((item, index) => {
+                        if (item.tag !== systemTag) {
+                            const otherItem = copyData.stats_daily_channel.find(citem => {
+                                return citem.tag === item.tag && citem.store_id === item.store_id;
+                            });
+                            if (otherItem !== undefined) {
+                                this.dailyChannel[index] = otherItem;
+                            }
+                        }
+                    });
                 } else {
                     providerAmount = providerAmountOrg;
                 }
@@ -365,18 +426,35 @@
                 const that = this;
 
                 sortData.forEach(function(item) {
-                    const cache = that.dailyChannel.find(function(set) {
+                    const cache = that.dailyChannel.find(set => {
                         return item.tag === set.tag && item.store_id === set.store_id;
                     });
-                    if (cache) {
-                        item.initial_balance = cache.set?.[day] ?? 0;
+
+                    let setKey = 'set'; //setKey='set',显示按回调时间数据,setKey='set_order',显示按下单时间数据
+                    let dataKey = 'notify_time';
+                    let initialBalance = 0;
+                    let quantity = 0;
+                    let cardValue = 0;
+                    let discountedAmount = 0;
+
+                    if (cache !== undefined) {
+                        setKey = cache.set_key || 'set'; //set_key补充:第一版只有回调时间数据,没有该字段,这里做兼容处理
+                        initialBalance = cache[setKey]?.[day] || 0;
+
+                        dataKey = setKey === 'set' ? 'notify_time' : 'order_time';
+                        quantity = item[dataKey].quantity;
+                        cardValue = item[dataKey].card_value;
+                        discountedAmount = item[dataKey].discounted_amount;
                     }
 
-                    item.initial_balance = formatDecimals(item.initial_balance, 4);
-                    item.card_value = formatDecimals(item.card_value, 4);
-                    item.discounted_amount = formatDecimals(item.discounted_amount, 4);
+                    item.set_key = setKey;
+                    item.time_type = dataKey;
+                    item.quantity = quantity;
+                    item.initial_balance = formatDecimals(initialBalance, 4);
+                    item.card_value = formatDecimals(cardValue, 4);
+                    item.discounted_amount = formatDecimals(discountedAmount, 4);
                     item.repayment_amount = formatDecimals(item.repayment_amount, 4);
-                    item.ending_balance = formatDecimals(item.ending_balance, 4);
+                    item.ending_balance = '0';
                 });
 
                 layui.table.render({
@@ -384,7 +462,14 @@
                     data: sortData,
                     limit: sortData.length,
                     cols: [[
-                        {field: 'merge', title: '上游对账', width: 100},
+                        {field: 'time_type', title: '上游对账', width: 150, templet: function (d) {
+                            const notifyClass = d.time_type === 'notify_time' ? 'selected' : 'unselected';
+                            const orderClass = d.time_type === 'order_time' ? 'selected' : 'unselected';
+                            return `
+                            <button class="layui-btn switch-btn ${notifyClass}" data-type="notify_time">回调</button>
+                            <button class="layui-btn switch-btn ${orderClass}" data-type="order_time">下单</button>
+                        `;
+                        }},
                         {field: 'day', title: '日期', width: 120},
                         {field: 'store_name', title: '户名', width: 200},
                         {field: 'initial_balance', title: '期初', width: 150},
@@ -396,19 +481,24 @@
                         {field: 'delete', title: '删除', width: 100, templet: function(d){return '<button class="layui-btn layui-btn-sm delete-btn" data-index="'+d.LAY_TABLE_INDEX+'">删除</button>';}}
                     ]],
                     done: function() {
-                        that.mergeCell();
                         const tableItem = $('#' + that.tableId);
                         const trs = tableItem.next('.layui-table-view').find('.layui-table-body tbody tr');
                         trs.each(function(i, tr) {
                             $(tr).attr('store_id', sortData[i].store_id);
                             $(tr).attr('tag', sortData[i].tag);
+                            $(tr).attr('time_type', sortData[i].time_type);
+                            $(tr).attr('set_key', sortData[i].set_key);
                         });
 
                         that.editEvent(['initial_balance'], function(index) {
                             that.calc(index);
-                            that.saveEdit(index);
+                            that.saveEdit(index, false);
 
-                            statsApi.setStatsSettings(CONSTANTS.KEY_DAILY_CHANNEL, that.dailyChannel);
+                            statsApi.setStatsSettings(CONSTANTS.KEY_DAILY_CHANNEL, removeSet(that.dailyChannel));
+                        });
+
+                        that.switchEvent(['time_type'], function(index) {
+                            that.typeChange(index);
                         });
 
                         for (let i = 0; i < sortData.length; i++) {//计算期末余额
@@ -416,9 +506,9 @@
                         }
 
                         for (let i = 0; i < sortData.length; i++) {//更新期初缓存
-                            that.saveEdit(i);
+                            that.saveEdit(i, true);
                         }
-                        statsApi.setStatsSettings(CONSTANTS.KEY_DAILY_CHANNEL, that.dailyChannel);
+                        statsApi.setStatsSettings(CONSTANTS.KEY_DAILY_CHANNEL, removeSet(that.dailyChannel));
 
                         tableItem.next('.layui-table-view').off('click', '.delete-btn').on('click', '.delete-btn', function (){
                             const index = $(this).data('index');
@@ -428,18 +518,6 @@
                 });
             }
 
-            mergeCell() {
-                const trs = $('#' + this.tableId).next('.layui-table-view').find('.layui-table-body tbody tr');
-                trs.each(function(i, tr) {
-                    const td = $(tr).find('td[data-field="merge"]');
-                    if (i === 0) {
-                        td.attr('rowspan', trs.length);
-                    } else {
-                        td.addClass('layui-hide');
-                    }
-                });
-            }
-
             calc(index) {
                 const tr = $('#' + this.tableId).next('.layui-table-view').find('.layui-table-body tbody tr[data-index="' + index + '"]');
                 const initialBalance = tr.find('td[data-field="initial_balance"]').find('div').text().trim();
@@ -450,36 +528,46 @@
                 tr.find('td[data-field="ending_balance"]').find('div').html(endingBalance);
             }
 
-            saveEdit(index) {
+            saveEdit(index, onlyEnding) {
                 const tr = $('#' + this.tableId).next('.layui-table-view').find('.layui-table-body tbody tr[data-index="' + index + '"]');
                 const initialBalance = tr.find('td[data-field="initial_balance"]').find('div').text().trim();
                 const storeId = tr.attr('store_id');
                 const tag = tr.attr('tag');
+                const setKey = tr.attr('set_key');
                 const endingBalance = tr.find('td[data-field="ending_balance"]').find('div').text().trim();
 
-                this.updateCache(storeId, tag, initialBalance, endingBalance);
+                this.updateCache(storeId, tag, setKey, initialBalance, endingBalance, onlyEnding);
             }
 
-            updateCache(storeId, tag, initialBalance, endingBalance) {
+            updateCache(storeId, tag, setKey, initialBalance, endingBalance, onlyEnding) {
                 const day = queryDayInput.val();
                 if (!isValidDate(day)) {
-                    showErr('数据异常');
+                    showErr('数据异常,请重新加载页面重试');
                     return;
                 }
 
-                //更新今天的期初
                 let cache = this.dailyChannel.find(function (item) {
                     return item.tag === tag && item.store_id === storeId;
                 });
 
-                if (!cache.set) {
-                    cache.set = {};
+                if (cache === undefined) {
+                    showErr('数据异常,请重新加载页面重试');
+                    return;
+                }
+
+                cache.set_key = setKey;
+
+                if (!cache[setKey]) {
+                    cache[setKey] = {};
+                }
+                if (onlyEnding === false) {
+                    //更新今天的期初
+                    addData(cache[setKey], day, initialBalance);
                 }
-                addData(cache.set, day, initialBalance);
 
                 //将今天的期末作为明天的期初
                 const nextDay = getNextDay(day);
-                addData(cache.set, nextDay, endingBalance);
+                addData(cache[setKey], nextDay, endingBalance);
             }
 
             deleteRow(index) {
@@ -497,7 +585,7 @@
                         return ;
                     }
 
-                    await statsApi.setStatsSettings(CONSTANTS.KEY_DAILY_CHANNEL, cache);
+                    await statsApi.setStatsSettings(CONSTANTS.KEY_DAILY_CHANNEL, removeSet(cache));
 
                     if (!isObjectEmpty(copyData)) {
                         //删除导入数据中的项
@@ -510,6 +598,35 @@
                     layer.close(loadIndex);
                 });
             }
+
+            typeChange(index) {
+                const tr = $('#' + this.tableId).next('.layui-table-view').find('.layui-table-body tbody tr[data-index="' + index + '"]');
+                const storeId = tr.attr('store_id');
+                const tag = tr.attr('tag');
+                const setKey = tr.attr('set_key');
+
+                this.updateSetKey(storeId, tag, setKey === 'set' ? 'set_order' : 'set');
+            }
+
+            async updateSetKey(storeId, tag, setKey) {
+                const loadIndex = layer.load(2, { shade: [0.2, '#000'] });
+
+                let cache = this.dailyChannel.find(function (item) {
+                    return item.tag === tag && item.store_id === storeId;
+                });
+
+                if (cache === undefined) {
+                    showErr('数据异常,请重新加载页面重试');
+                    return;
+                }
+
+                cache.set_key = setKey;
+                await statsApi.setStatsSettings(CONSTANTS.KEY_DAILY_CHANNEL, removeSet(this.dailyChannel));
+
+                await this.render();
+
+                layer.close(loadIndex);
+            }
         }
 
         class DownstreamTable extends LayuiTableBase {
@@ -521,7 +638,7 @@
                 const statsSettings = await statsApi.getStatsSettings();
                 this.dailyMerchant = statsSettings[CONSTANTS.KEY_DAILY_MERCHANT] || [];
 
-                const storeIds = this.dailyMerchant
+                const mchIds = this.dailyMerchant
                     .filter(item => item.tag === systemTag)
                     .map(item => item.mch_id)
                     .join(',');
@@ -532,15 +649,28 @@
                     queryDayInput.val(day);
                 }
 
-                const params =  { cid: storeIds, query_day: day};
+                const params =  { cid: mchIds, query_day: day};
                 const mchAmountData = await statsApi.getDailyMerchantData(params);
-                const mchAmountOrg = mchAmountData.list;
+                let mchAmountOrg = [];
+                if (mchAmountData.state === true) {
+                    mchAmountOrg = mchAmountData.list;
+                }
 
                 downstreamOrgData = deepCloneData(mchAmountOrg);
 
                 let mchAmount = [];
                 if (!isObjectEmpty(copyData)) {
                     mchAmount = mergeData(mchAmountOrg, copyData.stats_data_merchant, 'mch_name')
+                    this.dailyMerchant.forEach((item, index) => {
+                        if (item.tag !== systemTag) {
+                            const otherItem = copyData.stats_daily_merchant.find(citem => {
+                                return citem.tag === item.tag && citem.mch_id === item.mch_id;
+                            });
+                            if (otherItem !== undefined) {
+                                this.dailyMerchant[index] = otherItem;
+                            }
+                        }
+                    });
                 } else {
                     mchAmount = mchAmountOrg;
                 }
@@ -553,15 +683,32 @@
                     const cache = that.dailyMerchant.find(function(set) {
                         return item.tag === set.tag && item.mch_id === set.mch_id;
                     });
-                    if (cache) {
-                        item.initial_balance = cache.set?.[day] ?? 0;
+
+                    let setKey = 'set'; //setKey='set',显示按回调时间数据,setKey='set_order',显示按下单时间数据
+                    let dataKey = 'notify_time';
+                    let initialBalance = 0;
+                    let quantity = 0;
+                    let cardValue = 0;
+                    let discountedAmount = 0;
+
+                    if (cache !== undefined) {
+                        setKey = cache.set_key || 'set'; //set_key补充:第一版只有回调时间数据,没有该字段,这里做兼容处理
+                        initialBalance = cache[setKey]?.[day] || 0;
+
+                        dataKey = setKey === 'set' ? 'notify_time' : 'order_time';
+                        quantity = item[dataKey].quantity;
+                        cardValue = item[dataKey].card_value;
+                        discountedAmount = item[dataKey].discounted_amount;
                     }
 
-                    item.initial_balance = formatDecimals(item.initial_balance, 4);
-                    item.card_value = formatDecimals(item.card_value, 4);
-                    item.discounted_amount = formatDecimals(item.discounted_amount, 4);
+                    item.set_key = setKey;
+                    item.time_type = dataKey;
+                    item.quantity = quantity;
+                    item.initial_balance = formatDecimals(initialBalance, 4);
+                    item.card_value = formatDecimals(cardValue, 4);
+                    item.discounted_amount = formatDecimals(discountedAmount, 4);
                     item.repayment_amount = formatDecimals(item.repayment_amount, 4);
-                    item.ending_balance = formatDecimals(item.ending_balance, 4);
+                    item.ending_balance = '0';
                 });
 
                 layui.table.render({
@@ -569,7 +716,14 @@
                     data: sortData,
                     limit: sortData.length,
                     cols: [[
-                        {field: 'merge', title: '下游对账', width: 100},
+                        {field: 'time_type', title: '下游对账', width: 150, templet: function (d) {
+                            const notifyClass = d.time_type === 'notify_time' ? 'selected' : 'unselected';
+                            const orderClass = d.time_type === 'order_time' ? 'selected' : 'unselected';
+                            return `
+                            <button class="layui-btn switch-btn ${notifyClass}" data-type="notify_time">回调</button>
+                            <button class="layui-btn switch-btn ${orderClass}" data-type="order_time">下单</button>
+                        `;
+                        }},
                         {field: 'day', title: '日期', width: 120},
                         {field: 'mch_name', title: '户名', width: 200},
                         {field: 'initial_balance', title: '期初', width: 150},
@@ -587,14 +741,19 @@
                         trs.each(function(i, tr) {
                             $(tr).attr('mch_id', sortData[i].mch_id);
                             $(tr).attr('tag', sortData[i].tag);
-                            $(tr).attr('manual_amount', sortData[i].manual_amount);
+                            $(tr).attr('time_type', sortData[i].time_type);
+                            $(tr).attr('set_key', sortData[i].set_key);
                         });
 
                         that.editEvent(['initial_balance'], function(index) {
                             that.calc(index);
-                            that.saveEdit(index);
+                            that.saveEdit(index, false);
+
+                            statsApi.setStatsSettings(CONSTANTS.KEY_DAILY_MERCHANT, removeSet(that.dailyMerchant));
+                        });
 
-                            statsApi.setStatsSettings(CONSTANTS.KEY_DAILY_MERCHANT, that.dailyMerchant);
+                        that.switchEvent(['time_type'], function(index) {
+                            that.typeChange(index);
                         });
 
                         for (let i = 0; i < sortData.length; i++) {//计算期末余额
@@ -602,9 +761,9 @@
                         }
 
                         for (let i = 0; i < sortData.length; i++) {//更新期初缓存
-                            that.saveEdit(i);
+                            that.saveEdit(i, true);
                         }
-                        statsApi.setStatsSettings(CONSTANTS.KEY_DAILY_MERCHANT, that.dailyMerchant);
+                        statsApi.setStatsSettings(CONSTANTS.KEY_DAILY_MERCHANT, removeSet(that.dailyMerchant));
 
                         tableItem.next('.layui-table-view').off('click', '.delete-btn').on('click', '.delete-btn', function (){
                             const index = $(this).data('index');
@@ -630,43 +789,52 @@
                 const tr = $('#' + this.tableId).next('.layui-table-view').find('.layui-table-body tbody tr[data-index="' + index + '"]');
                 const initialBalance = tr.find('td[data-field="initial_balance"]').find('div').text().trim();
                 const repaymentAmount = tr.find('td[data-field="repayment_amount"]').find('div').text().trim();
-                // const manualAmount = tr.attr('manual_amount').trim();
                 const endingBalance = formatDecimals(parseFloat(initialBalance) + parseFloat(repaymentAmount), 4);
 
                 tr.find('td[data-field="initial_balance"]').find('div').html(formatDecimals(initialBalance, 4));//格式化,保持数字形式一致
                 tr.find('td[data-field="ending_balance"]').find('div').html(endingBalance);
             }
 
-            saveEdit(index) {
+            saveEdit(index, onlyEnding) {
                 const tr = $('#' + this.tableId).next('.layui-table-view').find('.layui-table-body tbody tr[data-index="' + index + '"]');
                 const initialBalance = tr.find('td[data-field="initial_balance"]').find('div').text().trim();
                 const mchId = tr.attr('mch_id');
                 const tag = tr.attr('tag');
+                const setKey = tr.attr('set_key');
                 const endingBalance = tr.find('td[data-field="ending_balance"]').find('div').text().trim();
 
-                this.updateCache(mchId, tag, initialBalance, endingBalance);
+                this.updateCache(mchId, tag, setKey, initialBalance, endingBalance, onlyEnding);
             }
 
-            updateCache(mchId, tag, initialBalance, endingBalance) {
+            updateCache(mchId, tag, setKey, initialBalance, endingBalance, onlyEnding) {
                 const day = queryDayInput.val();
                 if (!isValidDate(day)) {
-                    showErr('数据异常');
                     return;
                 }
 
-                //更新今天的期初
                 let cache = this.dailyMerchant.find(function (item) {
                     return item.tag === tag && item.mch_id === mchId;
                 });
 
-                if (!cache.set) {
-                    cache.set = {};
+                if (cache === undefined) {
+                    showErr('数据异常,请重新加载页面重试');
+                    return;
+                }
+
+                cache.set_key = setKey;
+
+                if (!cache[setKey]) {
+                    cache[setKey] = {};
+                }
+
+                if (onlyEnding === false) {
+                    //更新今天的期初
+                    addData(cache[setKey], day, initialBalance);
                 }
-                addData(cache.set, day, initialBalance);
 
                 //将今天的期末作为明天的期初
                 const nextDay = getNextDay(day);
-                addData(cache.set, nextDay, endingBalance);
+                addData(cache[setKey], nextDay, endingBalance);
             }
 
             deleteRow(index) {
@@ -684,7 +852,7 @@
                         return ;
                     }
 
-                    await statsApi.setStatsSettings(CONSTANTS.KEY_DAILY_MERCHANT, cache);
+                    await statsApi.setStatsSettings(CONSTANTS.KEY_DAILY_MERCHANT, removeSet(cache));
 
                     if (!isObjectEmpty(copyData)) {
                         //删除导入数据中的项
@@ -697,17 +865,227 @@
                     layer.close(loadIndex);
                 });
             }
+
+            typeChange(index) {
+                const tr = $('#' + this.tableId).next('.layui-table-view').find('.layui-table-body tbody tr[data-index="' + index + '"]');
+                const mchId = tr.attr('mch_id');
+                const tag = tr.attr('tag');
+                const setKey = tr.attr('set_key');
+
+                this.updateSetKey(mchId, tag, setKey === 'set' ? 'set_order' : 'set');
+            }
+
+            async updateSetKey(mchId, tag, setKey) {
+                const loadIndex = layer.load(2, { shade: [0.2, '#000'] });
+
+                let cache = this.dailyMerchant.find(item => {
+                    return item.tag === tag && item.mch_id === mchId;
+                });
+
+                if (cache === undefined) {
+                    showErr('数据异常,请重新加载页面重试');
+                    return;
+                }
+
+                cache.set_key = setKey;
+                await statsApi.setStatsSettings(CONSTANTS.KEY_DAILY_MERCHANT, removeSet(this.dailyMerchant));
+
+                await this.render();
+
+                layer.close(loadIndex);
+            }
         }
 
-        const upstreamTable = new UpstreamTable();
-        upstreamTable.render();
+        class FillSet {
+            constructor() {
+                this.channelData = {};
+                this.merchantData = {};
+            }
+            //通道数据
+            async fill() {
+                const statsSettings = await statsApi.getStatsSettings();
+                this.dailyChannel = statsSettings[CONSTANTS.KEY_DAILY_CHANNEL] || [];
+                await this.fillMissingDates(this.dailyChannel, 'channel');
+                await statsApi.setStatsSettings(CONSTANTS.KEY_DAILY_CHANNEL, removeSet(this.dailyChannel));
+
+                this.dailyMerchant = statsSettings[CONSTANTS.KEY_DAILY_MERCHANT] || [];
+                await this.fillMissingDates(this.dailyMerchant, 'merchant');
+                await statsApi.setStatsSettings(CONSTANTS.KEY_DAILY_MERCHANT, removeSet(this.dailyMerchant));
+            }
+
+            async fillMissingDates(data, type) {
+                const today = day;
+
+                const processSets = async (sets, id) => {
+                    //仅保留最近31天的缓存
+                    const last31Days = new Date(today);
+                    last31Days.setDate(last31Days.getDate() - 31);
+
+                    sets = Object.keys(sets)
+                        .filter(date => new Date(date) > last31Days)
+                        .reduce((obj, date) => {
+                            obj[date] = sets[date];
+                            return obj;
+                        }, {});
+
+                    let dates = Object.keys(sets).sort();
+                    let prevDate = dates[0];
+
+                    for (let i = 1; i < dates.length; i++) {
+                        let currentDate = dates[i];
+                        let prev = new Date(prevDate);
+                        let curr = new Date(currentDate);
+
+                        let lastDate = prevDate;
+                        while (((curr - prev) / (1000 * 3600 * 24) > 1)) {
+                            prev.setDate(prev.getDate() + 1);
+                            let newDate = getLocalDate(prev);
+                            if (newDate <= today) {
+                                const prevVal = sets[lastDate] || 0;
+                                if (type === 'channel') {
+                                    sets[newDate] = await channelRepayment(lastDate, prevVal, id);
+                                } else {
+                                    sets[newDate] = await merchantRepayment(lastDate, prevVal, id);
+                                }
+                            }
+                            lastDate = newDate;
+                        }
+
+                        prevDate = currentDate;
+                    }
+
+                    let lastDate = new Date(dates[dates.length - 1]);
+                    let todayDate = new Date(today);
+                    let lastDateStr = getLocalDate(lastDate);
+                    while (lastDate < todayDate) {
+                        lastDate.setDate(lastDate.getDate() + 1);
+                        let newDate = getLocalDate(lastDate);
+                        const prevVal = sets[lastDateStr] || 0;
+                        if (type === 'channel') {
+                            sets[newDate] = await channelRepayment(lastDateStr, prevVal, id);
+                        } else {
+                            sets[newDate] = await merchantRepayment(lastDateStr, prevVal, id);
+                        }
+                        lastDateStr = newDate;
+                    }
+
+                    return sets;
+                };
+
+                const channelRepayment = async (prevDate, prevVal, id) => {
+                    let providerAmountOrg = [];
+
+                    if (this.channelData[prevDate]) {
+                        providerAmountOrg = this.channelData[prevDate];
+                    } else {
+                        const storeIds = this.dailyChannel
+                            .filter(item => item.tag === systemTag)
+                            .map(item => item.store_id)
+                            .join(',');
+
+                        const params =  { cid: storeIds, query_day: prevDate};
+                        const providerAmountData = await statsApi.getDailyProviderData(params);
+                        if (providerAmountData.state === true) {
+                            this.channelData[prevDate] = providerAmountData.list;
+                            providerAmountOrg = this.channelData[prevDate];
+                        }
+                    }
+
+                    let newVal = 0;
+                    if (providerAmountOrg.length > 0) {
+                        const findItem = providerAmountOrg.find(item => {
+                            return item.store_id === id;
+                        });
+
+                        let repaymentAmount = '0';
+                        if (findItem) {
+                            repaymentAmount = findItem.repayment_amount;
+                        }
+                        newVal = parseFloat(prevVal) + parseFloat(repaymentAmount);
+                    }
+
+                    return formatDecimals(newVal, 4);
+                };
 
+                const merchantRepayment = async (prevDate, prevVal, id) => {
+                    let mchAmountOrg = [];
+
+                    if (this.merchantData[prevDate]) {
+                        mchAmountOrg = this.merchantData[prevDate];
+                    } else {
+                        const mchIds = this.dailyMerchant
+                            .filter(item => item.tag === systemTag)
+                            .map(item => item.mch_id)
+                            .join(',');
+
+                        const params =  { cid: mchIds, query_day: prevDate};
+                        const mchAmountData = await statsApi.getDailyMerchantData(params);
+                        if (mchAmountData.state === true) {
+                            this.merchantData[prevDate] = mchAmountData.list;
+                            mchAmountOrg = this.merchantData[prevDate];
+                        }
+                    }
+
+                    let newVal = 0;
+                    if (mchAmountOrg.length > 0) {
+                        const findItem = mchAmountOrg.find(item => {
+                            return item.mch_id === id;
+                        });
+
+                        let repaymentAmount = '0';
+                        if (findItem) {
+                            repaymentAmount = findItem.repayment_amount;
+                        }
+                        newVal = parseFloat(prevVal) + parseFloat(repaymentAmount);
+                    }
+
+                    return formatDecimals(newVal, 4);
+                };
+
+                for (const store of data) {
+                    if (store.tag !== systemTag) {
+                        continue;
+                    }
+
+                    let dataId = '';
+                    if (type === 'channel') {
+                        dataId = store.store_id;
+                    } else {
+                        dataId = store.mch_id;
+                    }
+
+                    if (store.set) {
+                        store.set = await processSets(store.set, dataId);
+                    }
+                    if (store.set_order) {
+                        store.set_order = await processSets(store.set_order, dataId);
+                    }
+                }
+            }
+
+        }
+
+        async function init() {
+            const loadIndex = layer.load(2, { shade: [0.2, '#000'] });
+
+            await fillData.fill();
+            await upstreamTable.render();
+            await downstreamTable.render();
+
+            layer.close(loadIndex);
+        }
+
+        const fillData = new FillSet();
+        const upstreamTable = new UpstreamTable();
         const downstreamTable = new DownstreamTable();
-        downstreamTable.render();
+        init();
 
         $('#btn_search').on('click', async function() {
+            day = $('#query_day').val().trim();
+
             const loadIndex = layer.load(2, { shade: [0.2, '#000'] });
 
+            await fillData.fill();
             await upstreamTable.render();
             await downstreamTable.render();
 
@@ -772,10 +1150,10 @@
                     const dailyChannel = statsSettings[CONSTANTS.KEY_DAILY_CHANNEL] || [];
                     const dailyMerchant = statsSettings[CONSTANTS.KEY_DAILY_MERCHANT] || [];
 
-                    const mergeCacheChannel = mergeData(dailyChannel, copyData.stats_daily_channel || [], 'store_name');
+                    const mergeCacheChannel = mergeData(dailyChannel, removeSet(copyData.stats_daily_channel) || [], 'store_name');
                     await statsApi.setStatsSettings(CONSTANTS.KEY_DAILY_CHANNEL, mergeCacheChannel);
 
-                    const mergeCacheMerchant = mergeData(dailyMerchant, copyData.stats_daily_merchant || [], 'mch_name');
+                    const mergeCacheMerchant = mergeData(dailyMerchant, removeSet(copyData.stats_daily_merchant) || [], 'mch_name');
                     await statsApi.setStatsSettings(CONSTANTS.KEY_DAILY_MERCHANT, mergeCacheMerchant);
 
                     await upstreamTable.render();
@@ -802,6 +1180,21 @@
             });
 
             tempDiv.querySelectorAll('tr').forEach(function(row) {
+                const firstCell = row.querySelector('td:first-child');
+                if (firstCell) {
+                    const buttons = firstCell.querySelectorAll('.layui-btn');
+                    let selectedText = '';
+                    buttons.forEach(function(button) {
+                        if (button.classList.contains('selected')) {
+                            selectedText = button.innerText;
+                        }
+                    });
+                    firstCell.innerHTML = selectedText;
+                }
+            });
+
+
+            tempDiv.querySelectorAll('tr').forEach(function(row) {
                 const cells = row.querySelectorAll('td, th');
                 for (let i = cells.length - 1; i >= cells.length - 1; i--) {
                     cells[i].parentNode.removeChild(cells[i]);

+ 26 - 0
data/model/merchant.model.php

@@ -77,6 +77,32 @@ class merchantModel extends Model
         return $list;
     }
 
+    public function getRefillEvidenceAll($condition, $field = '*', $order = '', $master = false)
+    {
+        $len = 1000;
+
+        $i = 0;
+        $list = [];
+        while (true) {
+            $start = $i * $len;
+            $items = $this->table('refill_evidence,member')
+                ->join('inner')
+                ->on('member.member_id=refill_evidence.member_id')
+                ->field($field)
+                ->where($condition)
+                ->order($order)
+                ->limit("{$start},{$len}")
+                ->master($master)
+                ->select();
+            $list = array_merge($list, $items);
+            if (empty($items) || count($items) < $len) {
+                break;
+            }
+            $i++;
+        }
+        return $list;
+    }
+
     public function getRefillEvidenceInfo($condition, $field = '*', $master = false, $lock = false)
     {
         $result = $this->table('refill_evidence,member')->join('inner')->on('member.member_id=refill_evidence.member_id')->where($condition)->field($field)->master($master)->lock($lock)->find();

+ 23 - 0
data/model/refill_order.model.php

@@ -90,6 +90,29 @@ class refill_orderModel extends Model
         return $list;
     }
 
+    public function getOrderStatsListAll($condition, $field = '*', $order = 'time_stamp desc')
+    {
+        $len = 1000;
+
+        $i = 0;
+        $orders = [];
+        while (true) {
+            $start = $i * $len;
+            $items = $this->table('refill_stats')
+                ->field($field)
+                ->where($condition)
+                ->order($order)
+                ->limit("{$start},{$len}")
+                ->select();
+            $orders = array_merge($orders, $items);
+            if (empty($items) || count($items) < $len) {
+                break;
+            }
+            $i++;
+        }
+        return $orders;
+    }
+
     public function first_item()
     {
         return $this->table('refill_order')->field('order_time')->where(['order_id' => ['gt',0]])->order('order_id asc')->find();

+ 6 - 10
test/TestlAmountStats.php

@@ -49,19 +49,15 @@ class TestlAmountStats extends TestCase
 
     public function testdaily_statement_data()
     {
-        $_GET['query_day'] = '2024-07-10';
-        $_GET['cid'] = '319,326,327,234';
-
-
         $model_refill_order = Model('refill_order');
 
         $condition['type'] = 'provider';
-        $condition['time_text'] = $_GET['query_day'] ?? date("Y-m-d 00:00:00", strtotime("-1 day"));
-        $condition['order_time_type'] = 'notify_time';
-        if (!empty($_GET['cid'])) {
-            $condition['cid'] = ['in', $_GET['cid']];
-        }
+        $condition['time_text'] = '2022-09-01';
+//        $condition['order_time_type'] = 'notify_time';
+//        $condition['cid'] = ['in', '319,326,327,234'];
+
+        $list = $model_refill_order->getOrderStatsListAll($condition, '*', 'time_stamp desc, cname asc');
+
 
-        $stats_list = $model_refill_order->getOrderStatsList($condition, 1000, '*', 'time_stamp desc, cname asc');
     }
 }