feat(admin): add bulk actions and pending-record deletion to creator income
- Always render the checkbox column and bulk bar; disable with a reason instead of hiding when settlement is off or the page has no pending rows - Replace the batch-settle button with a bulk action select (settle / delete) plus apply, matching the charge cards and invite codes pages - Add per-row delete for pending records and Revenue::delete_pending(), guarded by status = 'pending' so settled ledger-backed rows are never removed - Report done / failed counts for bulk settle and delete Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
b25ebc5fff
commit
1d6bc8f7cc
5 files changed
+146
-48
No files matched your search
@@ -12,11 +12,12 @@
|
||||
.aksrt-income-page .aksrt-income-filters__id { width: 96px; }
|
||||
.aksrt-income-filters__submit { display: inline-flex; gap: 8px; }
|
||||
|
||||
/* ---------- 批量结算 ---------- */
|
||||
.aksrt-income-batch { margin: 0; }
|
||||
.aksrt-income-batch__count { color: var(--an-muted); font-size: 12px; }
|
||||
.aksrt-income-batch__count.is-active { color: var(--an-text); font-weight: 500; }
|
||||
.aksrt-income-page .aksrt-income-batch .button:disabled { opacity: .6; cursor: not-allowed; }
|
||||
/* ---------- 批量操作 ---------- */
|
||||
.aksrt-income-bulk { margin: 0; }
|
||||
.aksrt-income-bulk__count { color: var(--an-muted); font-size: 12px; font-variant-numeric: tabular-nums; }
|
||||
.aksrt-income-bulk__count.is-active { color: var(--an-text); font-weight: 500; }
|
||||
.aksrt-income-page .aksrt-income-bulk .button:disabled { opacity: .6; cursor: not-allowed; }
|
||||
.aksrt-income-table .check-column input:disabled { cursor: not-allowed; opacity: .45; }
|
||||
|
||||
/* ---------- 分成表 ---------- */
|
||||
.aksrt-income-table { min-width: 1040px; table-layout: fixed; }
|
||||
@@ -25,8 +26,8 @@
|
||||
.aksrt-income-table .column-source { width: 24%; }
|
||||
.aksrt-income-table .column-base { width: 14%; }
|
||||
.aksrt-income-table .column-amount { width: 12%; }
|
||||
.aksrt-income-table .column-status { width: 13%; }
|
||||
.aksrt-income-table .column-actions { width: 9%; }
|
||||
.aksrt-income-table .column-status { width: 12%; }
|
||||
.aksrt-income-table .column-actions { width: 13%; }
|
||||
.aksrt-income-table td,
|
||||
.aksrt-income-table tbody th { vertical-align: top; }
|
||||
.aksrt-income-id { color: var(--an-text); font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 12px; font-weight: 600; }
|
||||
@@ -40,7 +41,8 @@ a.aksrt-income-post:focus { color: var(--an-primary); }
|
||||
.aksrt-income-table .column-base,
|
||||
.aksrt-income-table .column-amount { font-variant-numeric: tabular-nums; }
|
||||
|
||||
/* 行内结算:单按钮 POST 表单 */
|
||||
/* 行内操作:每个按钮是一个独立 POST 表单 */
|
||||
.aksrt-income-actions { display: flex; flex-wrap: wrap; gap: 6px; }
|
||||
.aksrt-income-action { display: inline-flex; margin: 0; }
|
||||
.aksrt-income-action .button[disabled] { opacity: .6; cursor: progress; }
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/** 创作分成:批量勾选计数、全选、结算二次确认 + 防重复提交。仅本页入队。 */
|
||||
/** 创作分成:勾选计数、全选、批量操作(结算 / 删除)、二次确认 + 防重复提交。仅本页入队。 */
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
|
||||
var checkAll = root.querySelector('[data-aksrt-income-check-all]');
|
||||
var checks = Array.from(root.querySelectorAll('[data-aksrt-income-check]'));
|
||||
var batchSubmit = root.querySelector('[data-aksrt-income-batch-submit]');
|
||||
var bulkAction = root.querySelector('[data-aksrt-income-bulk-action]');
|
||||
var bulkSubmit = root.querySelector('[data-aksrt-income-bulk-submit]');
|
||||
var selected = root.querySelector('[data-aksrt-income-selected]');
|
||||
var emptyText = selected ? selected.textContent : '';
|
||||
|
||||
@@ -17,12 +18,12 @@
|
||||
|
||||
function sync() {
|
||||
var count = checkedCount();
|
||||
if (batchSubmit) batchSubmit.disabled = count === 0;
|
||||
if (bulkSubmit) bulkSubmit.disabled = count === 0 || !bulkAction || bulkAction.value === '';
|
||||
if (selected) {
|
||||
selected.textContent = count > 0 ? '已选 ' + count + ' 条' : emptyText;
|
||||
selected.classList.toggle('is-active', count > 0);
|
||||
}
|
||||
if (checkAll) {
|
||||
if (checkAll && !checkAll.disabled) {
|
||||
checkAll.checked = count > 0 && count === checks.length;
|
||||
checkAll.indeterminate = count > 0 && count < checks.length;
|
||||
}
|
||||
@@ -35,8 +36,18 @@
|
||||
});
|
||||
}
|
||||
checks.forEach(function (box) { box.addEventListener('change', sync); });
|
||||
if (bulkAction) bulkAction.addEventListener('change', sync);
|
||||
sync();
|
||||
|
||||
/** 单行表单的确认文案在 form 上;批量表单按所选操作取 option 上的文案。 */
|
||||
function confirmMessage(form) {
|
||||
if (bulkAction && form.contains(bulkAction)) {
|
||||
var option = bulkAction.selectedOptions[0];
|
||||
return option ? option.getAttribute('data-confirm') : '';
|
||||
}
|
||||
return form.getAttribute('data-aksrt-confirm');
|
||||
}
|
||||
|
||||
root.addEventListener('submit', function (event) {
|
||||
var form = event.target.closest('form');
|
||||
if (!form) return;
|
||||
@@ -44,7 +55,7 @@
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
var message = form.getAttribute('data-aksrt-confirm');
|
||||
var message = confirmMessage(form);
|
||||
if (message && !window.confirm(message.replace('{count}', String(checkedCount())))) {
|
||||
event.preventDefault();
|
||||
return;
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
* 支付中心「创作分成」:分成记录列表、单条与批量结算。
|
||||
*
|
||||
* 列表渲染在 PayIncomeList(flat sibling trait,PSR-4 要求与宿主类同目录);这里保留入口、
|
||||
* admin-post 处理器与共用的展示助手。页面样式在 assets/css/admin/income.css,
|
||||
* 结算确认、全选与防重复提交在 assets/js/admin-income.js。
|
||||
* admin-post 处理器(结算 / 删除 / 批量操作)与共用的展示助手。页面样式在 assets/css/admin/income.css,
|
||||
* 批量操作、二次确认、全选与防重复提交在 assets/js/admin-income.js。
|
||||
*
|
||||
* @package AKSRT
|
||||
*/
|
||||
@@ -35,7 +35,8 @@ class PayIncome {
|
||||
}
|
||||
add_action('admin_enqueue_scripts', [$this, 'enqueue_assets']);
|
||||
add_action('admin_post_aksrt_pay_settle_income', [$this, 'handle_settle_income']);
|
||||
add_action('admin_post_aksrt_pay_batch_settle_income', [$this, 'handle_batch_settle_income']);
|
||||
add_action('admin_post_aksrt_pay_delete_income', [$this, 'handle_delete_income']);
|
||||
add_action('admin_post_aksrt_pay_bulk_income', [$this, 'handle_bulk_income']);
|
||||
}
|
||||
|
||||
public function enqueue_assets(): void {
|
||||
@@ -79,18 +80,43 @@ class PayIncome {
|
||||
$this->redirect_back(['aksrt_pay_msg' => $ok ? 'income_settled' : 'income_failed']);
|
||||
}
|
||||
|
||||
public function handle_batch_settle_income(): void {
|
||||
/** 单条删除:只删待结算记录,已结算记录由 Revenue::delete_pending() 的状态条件挡住。 */
|
||||
public function handle_delete_income(): void {
|
||||
$this->ensure_cap();
|
||||
check_admin_referer('aksrt_pay_batch_settle_income');
|
||||
$id = isset($_POST['revenue_id']) ? (int) $_POST['revenue_id'] : 0;
|
||||
check_admin_referer('aksrt_pay_delete_income_' . $id);
|
||||
|
||||
$deleted = $id > 0 ? \AKSRT\Payment\Revenue::get_instance()->delete_pending([$id]) : 0;
|
||||
$this->redirect_back(['aksrt_pay_msg' => $deleted > 0 ? 'income_deleted' : 'income_delete_failed', 'done' => $deleted]);
|
||||
}
|
||||
|
||||
/** 批量操作:bulk_action = settle | delete,作用于勾选的记录。 */
|
||||
public function handle_bulk_income(): void {
|
||||
$this->ensure_cap();
|
||||
check_admin_referer('aksrt_pay_bulk_income');
|
||||
|
||||
$action = isset($_POST['bulk_action']) ? sanitize_key((string) wp_unslash($_POST['bulk_action'])) : '';
|
||||
$raw_ids = isset($_POST['revenue_ids']) ? wp_unslash($_POST['revenue_ids']) : [];
|
||||
$ids = is_array($raw_ids)
|
||||
? array_values(array_unique(array_filter(array_map('intval', $raw_ids), static fn (int $id): bool => $id > 0)))
|
||||
: [];
|
||||
if (!in_array($action, ['settle', 'delete'], true)) {
|
||||
$this->redirect_back(['aksrt_pay_msg' => 'income_bulk_invalid']);
|
||||
}
|
||||
if ($ids === []) {
|
||||
$this->redirect_back(['aksrt_pay_msg' => 'income_none_selected']);
|
||||
}
|
||||
|
||||
if ($action === 'delete') {
|
||||
$deleted = \AKSRT\Payment\Revenue::get_instance()->delete_pending($ids);
|
||||
$failed = count($ids) - $deleted;
|
||||
$this->redirect_back([
|
||||
'aksrt_pay_msg' => $deleted === 0 ? 'income_delete_failed' : ($failed > 0 ? 'income_delete_partial' : 'income_deleted'),
|
||||
'done' => $deleted,
|
||||
'failed' => $failed,
|
||||
]);
|
||||
}
|
||||
|
||||
$operation_key = $this->request_operation_key('admin-revenue-settle-batch');
|
||||
$result = $operation_key !== ''
|
||||
? \AKSRT\Payment\Revenue::get_instance()->settle_batch($ids, get_current_user_id(), $operation_key)
|
||||
@@ -100,7 +126,7 @@ class PayIncome {
|
||||
|
||||
$this->redirect_back([
|
||||
'aksrt_pay_msg' => $settled === 0 ? 'income_failed' : ($failed > 0 ? 'income_batch_partial' : 'income_batch_settled'),
|
||||
'settled' => $settled,
|
||||
'done' => $settled,
|
||||
'failed' => $failed,
|
||||
]);
|
||||
}
|
||||
@@ -124,7 +150,7 @@ class PayIncome {
|
||||
if (is_string($referer) && $referer !== '') {
|
||||
parse_str((string) wp_parse_url($referer, PHP_URL_QUERY), $parsed);
|
||||
if (($parsed['page'] ?? '') === self::PAGE_SLUG) {
|
||||
unset($parsed['page'], $parsed['aksrt_pay_msg'], $parsed['settled'], $parsed['failed']);
|
||||
unset($parsed['page'], $parsed['aksrt_pay_msg'], $parsed['done'], $parsed['failed']);
|
||||
$query = $parsed;
|
||||
}
|
||||
}
|
||||
@@ -179,7 +205,7 @@ class PayIncome {
|
||||
return;
|
||||
}
|
||||
$code = sanitize_key((string) wp_unslash($_GET['aksrt_pay_msg']));
|
||||
$settled = max(0, (int) ($_GET['settled'] ?? 0));
|
||||
$done = max(0, (int) ($_GET['done'] ?? 0));
|
||||
$failed = max(0, (int) ($_GET['failed'] ?? 0));
|
||||
// 码 => [文案, notice 类型]
|
||||
$map = [
|
||||
@@ -187,16 +213,29 @@ class PayIncome {
|
||||
'income_batch_settled' => [sprintf(
|
||||
/* translators: %d settled count */
|
||||
__('已结算 %d 条分成记录。', 'aksrt'),
|
||||
$settled
|
||||
$done
|
||||
), 'success'],
|
||||
'income_batch_partial' => [sprintf(
|
||||
/* translators: 1 settled count 2 failed count */
|
||||
__('已结算 %1$d 条,%2$d 条失败(记录状态可能已变化),可刷新后重试。', 'aksrt'),
|
||||
$settled,
|
||||
$done,
|
||||
$failed
|
||||
), 'warning'],
|
||||
'income_none_selected' => [__('请先勾选要结算的待结算记录。', 'aksrt'), 'warning'],
|
||||
'income_none_selected' => [__('请先勾选待结算记录。', 'aksrt'), 'warning'],
|
||||
'income_bulk_invalid' => [__('请先选择批量操作。', 'aksrt'), 'warning'],
|
||||
'income_failed' => [__('分成结算失败:记录可能已被结算,或分成功能未启用。', 'aksrt'), 'error'],
|
||||
'income_deleted' => [sprintf(
|
||||
/* translators: %d deleted count */
|
||||
__('已删除 %d 条待结算记录。', 'aksrt'),
|
||||
$done
|
||||
), 'success'],
|
||||
'income_delete_partial' => [sprintf(
|
||||
/* translators: 1 deleted count 2 skipped count */
|
||||
__('已删除 %1$d 条,%2$d 条未删除(仅待结算记录可删除,可能已被结算)。', 'aksrt'),
|
||||
$done,
|
||||
$failed
|
||||
), 'warning'],
|
||||
'income_delete_failed' => [__('删除失败:仅待结算记录可删除,记录可能已被结算或不存在。', 'aksrt'), 'error'],
|
||||
];
|
||||
if (!isset($map[$code])) {
|
||||
return;
|
||||
|
||||
@@ -61,17 +61,16 @@ trait PayIncomeList {
|
||||
$this->render_status_views($filters, $query_args, $counts);
|
||||
$this->render_filter_card($filters);
|
||||
|
||||
$can_settle = $policy['enabled'];
|
||||
$has_pending = $can_settle && in_array('pending', array_column($items, 'status'), true);
|
||||
$can_settle = $policy['enabled'];
|
||||
$has_pending = in_array('pending', array_column($items, 'status'), true);
|
||||
|
||||
NativeUi::table_card_open(__('分成记录', 'aksrt'), $has_pending ? $this->batch_form_html() : '');
|
||||
// 复选框列与批量栏始终渲染;不可用时禁用并用 title 说明原因,而不是整块隐藏。
|
||||
NativeUi::table_card_open(__('分成记录', 'aksrt'), $this->bulk_form_html($can_settle, $has_pending));
|
||||
echo '<table class="wp-list-table widefat striped aksrt-income-table">';
|
||||
echo '<caption class="screen-reader-text">' . esc_html__('分成记录', 'aksrt') . '</caption><thead><tr>';
|
||||
echo '<td class="manage-column column-cb check-column">';
|
||||
if ($has_pending) {
|
||||
echo '<input type="checkbox" data-aksrt-income-check-all aria-label="' . esc_attr__('全选本页待结算记录', 'aksrt') . '" />';
|
||||
}
|
||||
echo '</td>';
|
||||
echo '<td class="manage-column column-cb check-column">'
|
||||
. '<input type="checkbox" data-aksrt-income-check-all aria-label="' . esc_attr__('全选本页待结算记录', 'aksrt') . '"'
|
||||
. ($has_pending ? '' : ' disabled title="' . esc_attr__('本页没有待结算记录', 'aksrt') . '"') . ' /></td>';
|
||||
foreach ([
|
||||
'record' => __('记录 / 产生时间', 'aksrt'),
|
||||
'author' => __('作者', 'aksrt'),
|
||||
@@ -111,12 +110,11 @@ trait PayIncomeList {
|
||||
$pending = $status === 'pending';
|
||||
|
||||
echo '<tr>';
|
||||
echo '<th scope="row" class="check-column">';
|
||||
if ($pending && $can_settle) {
|
||||
echo '<input type="checkbox" form="aksrt-income-batch" name="revenue_ids[]" value="' . esc_attr((string) $id) . '" data-aksrt-income-check aria-label="'
|
||||
. esc_attr(sprintf(__('选择分成记录 #%d', 'aksrt'), $id)) . '" />';
|
||||
}
|
||||
echo '</th>';
|
||||
// 已结算记录已入账到钱包,不可结算也不可删除:复选框禁用占位,保持列对齐。
|
||||
echo '<th scope="row" class="check-column">'
|
||||
. '<input type="checkbox" form="aksrt-income-bulk" name="revenue_ids[]" value="' . esc_attr((string) $id) . '"'
|
||||
. ($pending ? ' data-aksrt-income-check' : ' disabled title="' . esc_attr__('已结算记录不可操作', 'aksrt') . '"')
|
||||
. ' aria-label="' . esc_attr(sprintf(__('选择分成记录 #%d', 'aksrt'), $id)) . '" /></th>';
|
||||
|
||||
echo '<td class="column-record"><span class="aksrt-income-id">#' . esc_html((string) $id) . '</span>'
|
||||
. '<div class="aksrt-income-sub">' . esc_html((string) ($row['created_at'] ?? '')) . '</div></td>';
|
||||
@@ -148,8 +146,11 @@ trait PayIncomeList {
|
||||
. $this->status_hint($row, $cycle_days) . '</td>';
|
||||
|
||||
echo '<td class="column-actions">';
|
||||
if ($pending && $can_settle) {
|
||||
echo $this->settle_form_html($id); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
if ($pending) {
|
||||
echo '<div class="aksrt-income-actions">'
|
||||
. ($can_settle ? $this->settle_form_html($id) : '')
|
||||
. $this->delete_form_html($id)
|
||||
. '</div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
} else {
|
||||
echo '<span class="aksrt-native-muted">—</span>';
|
||||
}
|
||||
@@ -300,18 +301,41 @@ trait PayIncomeList {
|
||||
return ['' => __('全部订单状态', 'aksrt')] + \AKSRT\Core\DomainModel::order_status_labels();
|
||||
}
|
||||
|
||||
/** 批量结算表单只放提交按钮;表格里的复选框通过 form="aksrt-income-batch" 关联进来。 */
|
||||
private function batch_form_html(): string {
|
||||
return '<form id="aksrt-income-batch" class="aksrt-native-bulk aksrt-income-batch" method="post" action="' . esc_url(admin_url('admin-post.php')) . '"'
|
||||
. ' data-aksrt-confirm="' . esc_attr__('确认立即结算选中的 {count} 条分成?金额会转入作者钱包,不可撤销。', 'aksrt') . '">'
|
||||
. '<input type="hidden" name="action" value="aksrt_pay_batch_settle_income" />'
|
||||
/**
|
||||
* 批量操作栏:已选计数 + 「批量操作」下拉 + 应用。表格里的复选框通过 form="aksrt-income-bulk" 关联进来。
|
||||
* 各操作的确认文案挂在 option 的 data-confirm 上,由 admin-income.js 按所选操作取用。
|
||||
*/
|
||||
private function bulk_form_html(bool $can_settle, bool $has_pending): string {
|
||||
$settle_attrs = $can_settle
|
||||
? ' data-confirm="' . esc_attr__('确认立即结算选中的 {count} 条分成?金额会转入作者钱包,不可撤销。', 'aksrt') . '"'
|
||||
: ' disabled';
|
||||
$settle_label = $can_settle ? __('立即结算', 'aksrt') : __('立即结算(分成功能未启用)', 'aksrt');
|
||||
|
||||
return '<form id="aksrt-income-bulk" class="aksrt-native-bulk aksrt-income-bulk" method="post" action="' . esc_url(admin_url('admin-post.php')) . '">'
|
||||
. '<input type="hidden" name="action" value="aksrt_pay_bulk_income" />'
|
||||
. $this->operation_key_field()
|
||||
. wp_nonce_field('aksrt_pay_batch_settle_income', '_wpnonce', true, false)
|
||||
. '<span class="aksrt-income-batch__count" data-aksrt-income-selected aria-live="polite">' . esc_html__('未选择记录', 'aksrt') . '</span>'
|
||||
. '<button type="submit" class="button button-primary" disabled data-aksrt-income-batch-submit>' . esc_html__('结算选中', 'aksrt') . '</button>'
|
||||
. wp_nonce_field('aksrt_pay_bulk_income', '_wpnonce', true, false)
|
||||
. '<span class="aksrt-income-bulk__count" data-aksrt-income-selected aria-live="polite">'
|
||||
. esc_html($has_pending ? __('未选择记录', 'aksrt') : __('本页没有待结算记录', 'aksrt')) . '</span>'
|
||||
. '<label class="screen-reader-text" for="aksrt-income-bulk-action">' . esc_html__('批量操作', 'aksrt') . '</label>'
|
||||
. '<select name="bulk_action" id="aksrt-income-bulk-action" data-aksrt-income-bulk-action>'
|
||||
. '<option value="">' . esc_html__('批量操作', 'aksrt') . '</option>'
|
||||
. '<option value="settle"' . $settle_attrs . '>' . esc_html($settle_label) . '</option>'
|
||||
. '<option value="delete" data-confirm="' . esc_attr__('确认删除选中的 {count} 条待结算分成?删除后作者不会获得这些分成,不可恢复。', 'aksrt') . '">' . esc_html__('删除', 'aksrt') . '</option>'
|
||||
. '</select>'
|
||||
. '<button type="submit" class="button" disabled data-aksrt-income-bulk-submit>' . esc_html__('应用', 'aksrt') . '</button>'
|
||||
. '</form>';
|
||||
}
|
||||
|
||||
private function delete_form_html(int $id): string {
|
||||
return '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="aksrt-income-action"'
|
||||
. ' data-aksrt-confirm="' . esc_attr(sprintf(__('确认删除待结算分成记录 #%d?删除后作者不会获得这笔分成,不可恢复。', 'aksrt'), $id)) . '">'
|
||||
. '<input type="hidden" name="action" value="aksrt_pay_delete_income" />'
|
||||
. '<input type="hidden" name="revenue_id" value="' . esc_attr((string) $id) . '" />'
|
||||
. wp_nonce_field('aksrt_pay_delete_income_' . $id, '_wpnonce', true, false)
|
||||
. '<button type="submit" class="button button-small is-danger">' . esc_html__('删除', 'aksrt') . '</button></form>';
|
||||
}
|
||||
|
||||
private function settle_form_html(int $id): string {
|
||||
return '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="aksrt-income-action"'
|
||||
. ' data-aksrt-confirm="' . esc_attr(sprintf(__('确认立即结算分成记录 #%d?金额会转入作者钱包,不可撤销。', 'aksrt'), $id)) . '">'
|
||||
|
||||
@@ -171,6 +171,28 @@ class Revenue {
|
||||
return ['settled' => $settled, 'failed' => $failed];
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete pending records. Settled records are never deleted: their amount is already
|
||||
* credited to the wallet ledger, which references the record id. The status guard in
|
||||
* the DELETE makes a concurrent settlement win (settle_one locks the row first).
|
||||
*
|
||||
* @param array<int,int> $record_ids
|
||||
* @return int Number of records deleted.
|
||||
*/
|
||||
public function delete_pending(array $record_ids): int {
|
||||
global $wpdb;
|
||||
$ids = array_slice(array_values(array_unique(array_filter(array_map('intval', $record_ids), static fn (int $id): bool => $id > 0))), 0, 100);
|
||||
if (!$ids) {
|
||||
return 0;
|
||||
}
|
||||
$placeholders = implode(', ', array_fill(0, count($ids), '%d'));
|
||||
$deleted = $wpdb->query($wpdb->prepare(
|
||||
"DELETE FROM {$this->table()} WHERE status = %s AND id IN ({$placeholders})",
|
||||
array_merge(['pending'], $ids)
|
||||
));
|
||||
return max(0, (int) $deleted);
|
||||
}
|
||||
|
||||
/**
|
||||
* Settle all pending records whose rolling earning cycle has ended.
|
||||
*
|
||||
|
||||
Reference in new issue
Block a user