风险与内控管理优化: 异常账单服务端分页/筛选/收银员搜索/合计, 收银员TOP15按异常账单数排序, 双击跳转明细, MonthPicker靠右, 侧边栏底部间距
This commit is contained in:
@@ -152,7 +152,7 @@ router.get('/dow-traffic', async (req: AuthRequest, res) => {
|
||||
const storeName = req.query.store as string
|
||||
const month = parseMonth(req)
|
||||
const params: any[] = [month]
|
||||
let where = `WHERE c175 IS NOT NULL AND c175 != '' AND c175 >= $1 AND c175 < $1::date + interval '1 month'`
|
||||
let where = `WHERE c175 IS NOT NULL AND c175 != '' AND c175::timestamp >= $1::date AND c175::timestamp < $1::date + interval '1 month'`
|
||||
if (storeName) {
|
||||
params.push(storeName)
|
||||
where += ` AND c003 = $${params.length}`
|
||||
@@ -455,7 +455,7 @@ router.get('/scheduling-suggestion', async (req: AuthRequest, res) => {
|
||||
count(*) AS bills
|
||||
FROM bill_records
|
||||
WHERE c003 = $1 AND c175 IS NOT NULL AND c175 != ''
|
||||
AND c175 >= $4 AND c175 < $4::date + interval '1 month'
|
||||
AND c175::timestamp >= $4::date AND c175::timestamp < $4::date + interval '1 month'
|
||||
GROUP BY hour, day_type, bill_date
|
||||
),
|
||||
hourly_stats AS (
|
||||
@@ -721,12 +721,18 @@ router.get('/employee-analysis', async (req: AuthRequest, res) => {
|
||||
params.push(storeName)
|
||||
where += ` AND org_level5 = $${params.length}`
|
||||
}
|
||||
params.push(month)
|
||||
const monthParam = `$${params.length}`
|
||||
// 将 2026-04 转为 2026年4月 格式匹配 salary_period
|
||||
const [yr, mo] = month.split('-')
|
||||
const periodLabel = `${parseInt(yr)}年${parseInt(mo)}月`
|
||||
params.push(periodLabel)
|
||||
where += ` AND salary_period = $${params.length}`
|
||||
|
||||
const countResult = await query(`SELECT count(*) FROM salary_detail_records ${where}`, params)
|
||||
const total = countResult.rows[0].count
|
||||
|
||||
params.push(month)
|
||||
const monthParam = `$${params.length}`
|
||||
|
||||
params.push(pageSize, offset)
|
||||
const result = await query(`
|
||||
SELECT employee_code,
|
||||
@@ -767,6 +773,9 @@ router.get('/employee-analysis', async (req: AuthRequest, res) => {
|
||||
// 岗位薪资对比
|
||||
router.get('/position-salary-compare', async (req: AuthRequest, res) => {
|
||||
try {
|
||||
const month = parseMonth(req)
|
||||
const [yr, mo] = month.split('-')
|
||||
const periodLabel = `${parseInt(yr)}年${parseInt(mo)}月`
|
||||
const result = await query(`
|
||||
SELECT position,
|
||||
count(*) AS emp_count,
|
||||
@@ -781,10 +790,11 @@ router.get('/position-salary-compare', async (req: AuthRequest, res) => {
|
||||
FROM salary_detail_records
|
||||
WHERE org_level2 = '西部马华品牌门店'
|
||||
AND position IS NOT NULL AND position != ''
|
||||
AND salary_period = $1
|
||||
GROUP BY position
|
||||
HAVING count(*) >= 5
|
||||
ORDER BY avg_gross DESC
|
||||
`)
|
||||
`, [periodLabel])
|
||||
sendSuccess(res, result.rows)
|
||||
} catch (err: any) {
|
||||
sendError(res, err.message)
|
||||
@@ -795,6 +805,8 @@ router.get('/position-salary-compare', async (req: AuthRequest, res) => {
|
||||
router.get('/turnover-stats', async (req: AuthRequest, res) => {
|
||||
try {
|
||||
const month = parseMonth(req)
|
||||
const [yr, mo] = month.split('-')
|
||||
const periodLabel = `${parseInt(yr)}年${parseInt(mo)}月`
|
||||
const result = await query(`
|
||||
SELECT org_level5 AS store_name,
|
||||
count(*) AS total_emp,
|
||||
@@ -804,9 +816,10 @@ router.get('/turnover-stats', async (req: AuthRequest, res) => {
|
||||
round(count(*) FILTER (WHERE hire_date IS NOT NULL AND hire_date >= $1)::numeric / nullif(count(*), 0) * 100, 2) AS new_hire_rate
|
||||
FROM salary_detail_records
|
||||
WHERE org_level2 = '西部马华品牌门店' AND org_level5 IS NOT NULL AND org_level5 != ''
|
||||
AND salary_period = $2
|
||||
GROUP BY org_level5
|
||||
ORDER BY turnover_rate DESC NULLS LAST
|
||||
`, [month])
|
||||
`, [month, periodLabel])
|
||||
sendSuccess(res, result.rows)
|
||||
} catch (err: any) {
|
||||
sendError(res, err.message)
|
||||
|
||||
Reference in New Issue
Block a user