230 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			PHP
		
	
	
			
		
		
	
	
			230 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			PHP
		
	
	
<?php
 | 
						||
 | 
						||
namespace app\service;
 | 
						||
 | 
						||
use app\model\Account;
 | 
						||
use app\model\ClockLog;
 | 
						||
use app\model\Log;
 | 
						||
use app\model\OvertimeLog;
 | 
						||
use app\model\PayLog;
 | 
						||
use app\model\PayMonthLog;
 | 
						||
use think\facade\Db;
 | 
						||
use think\Model;
 | 
						||
 | 
						||
/**
 | 
						||
 * 薪资计算
 | 
						||
 *
 | 
						||
 * Class Pay
 | 
						||
 * @package app\service
 | 
						||
 */
 | 
						||
class Pay
 | 
						||
{
 | 
						||
    // 基本逻辑 工人工资=基本工资+加班工资 一个月的工资=有效打卡的天数*基本工资+加班工资(=天工资/6*加班小时)
 | 
						||
    //基本工资在员工入职就要填入
 | 
						||
    //基本工资按天计算,打卡确认一天算一天
 | 
						||
    //加班工资按6小时为一天:天工资÷6小时=每小时工资
 | 
						||
 | 
						||
    /*
 | 
						||
        基本工资按天计算,打卡确认一天算一天,可能会出现的漏洞(本期暂不考虑):
 | 
						||
        晚上加班,如23:10打上班卡,第二天03:10打下班卡,这样就会认为上班2天。
 | 
						||
        若正常一天,必须至少要有上班和下班两次打卡的话,上述则两天都不算上班
 | 
						||
     */
 | 
						||
 | 
						||
 | 
						||
    //            $sql = "
 | 
						||
    //            insert into bee_pay_log(`account_id`, `worksite_id`,`time`, `year`, `month`, `day`)
 | 
						||
    //            select ".$item['account_id'].",".$item['worksite_id'].",".$item['day'].",".$year.",".$month.",".$day." from DUAL
 | 
						||
    //            where not exists (select `account_id`, `worksite_id`,`time` from bee_pay_log where `account_id` = ".$item['account_id']." AND `worksite_id` = ".$item['worksite_id']." AND `time` = ".$item['day'].")
 | 
						||
    //";
 | 
						||
    //            Db::execute($sql);
 | 
						||
 | 
						||
    // 生成工人每个工地月薪资汇总
 | 
						||
 | 
						||
 | 
						||
    /**
 | 
						||
     * 汇总指定日期间的工资
 | 
						||
     * 汇总到pay_log表
 | 
						||
     *
 | 
						||
     * @param  int  $begin  开始时间 为0则不限制 格式为Ymd 如20221103
 | 
						||
     * @param  int  $end  结束时间 为0则不限制 格式为Ymd 如20221103
 | 
						||
     */
 | 
						||
    public static function statistic(int $begin = 0, int $end = 0)
 | 
						||
    {
 | 
						||
        set_time_limit(0);
 | 
						||
        $where = [];
 | 
						||
        if ($begin) {
 | 
						||
            $where[] = ['l.day', '>=', $begin];
 | 
						||
        }
 | 
						||
 | 
						||
        if ($end) {
 | 
						||
            $where[] = ['l.day', '<=', $end];
 | 
						||
        }
 | 
						||
 | 
						||
        // 打卡记录表 每300条处理一次 打卡记录表中同一个indexs只计算一次
 | 
						||
        ClockLog::alias('l')
 | 
						||
            ->leftJoin('account a', 'a.id = l.account_id')
 | 
						||
            ->leftJoin('pay_log pl', 'pl.indexs = l.indexs')
 | 
						||
            ->where('l.is_statistic', ClockLog::COMMON_OFF)
 | 
						||
            ->where('l.status', ClockLog::COMMON_ON)
 | 
						||
            ->where('l.need_statistic', ClockLog::COMMON_ON)
 | 
						||
            ->where('l.role', ClockLog::COMMON_ON)//工人
 | 
						||
            ->where('a.pay', '>', 0)
 | 
						||
            ->where('l.handle_count', '<=', 10) //查询处理次数10次以下
 | 
						||
            ->where($where)
 | 
						||
            ->group('l.indexs')
 | 
						||
            ->fieldRaw('count(l.id),l.id,l.account_id,l.worksite_id,l.created_at,l.day,l.indexs,a.pay,pl.id as pay_log_id,pl.base_amount_count')
 | 
						||
            ->chunk(300, function ($items) {
 | 
						||
                $update     = [];
 | 
						||
                $indexsList = [];
 | 
						||
                $ids = $items->column('id');
 | 
						||
                \think\facade\Log::write($ids);
 | 
						||
                // 每查询一次就增加一次handle_count 避免未处理成功后 死循环一直查询
 | 
						||
                (new ClockLog)->whereIn('id', $ids)->save([
 | 
						||
                    'handle_count' => Db::raw('`handle_count` + 1')
 | 
						||
                ]);
 | 
						||
                foreach ($items as $item) {
 | 
						||
                    if ($item['pay_log_id'] && $item['base_amount_count'] == 0) {
 | 
						||
                        $update[]     = [
 | 
						||
                            'id'                => $item['pay_log_id'],
 | 
						||
                            'amount'            => Db::raw('`amount` + '.$item['pay']),
 | 
						||
                            'base_amount'       => Db::raw('`base_amount` + '.$item['pay']),
 | 
						||
                            'remarks'           => Db::raw('CONCAT_WS(char(10), `remarks`,"基本工资入账 '.$item['pay'].'元")'),
 | 
						||
                            'base_amount_count' => 1,
 | 
						||
                        ];
 | 
						||
                        $indexsList[] = $item['indexs'];
 | 
						||
                    }
 | 
						||
                }
 | 
						||
 | 
						||
                if (!empty($update)) {
 | 
						||
                    //                    \think\facade\Log::write($update);
 | 
						||
                    // 启动事务
 | 
						||
                    Db::startTrans();
 | 
						||
                    try {
 | 
						||
                        (new PayLog())->saveAll($update);
 | 
						||
                        ClockLog::whereIn('indexs', $indexsList)->update(['is_statistic' => ClockLog::COMMON_ON]);
 | 
						||
                        Db::commit();
 | 
						||
                    } catch (\Exception $e) {
 | 
						||
                        Db::rollback();
 | 
						||
                        \think\facade\Log::error('批量统计基本工资失败'.$e->getMessage().' file:'.$e->getFile().' line:'.$e->getLine());
 | 
						||
                    }
 | 
						||
 | 
						||
                }
 | 
						||
            }, 'pl.id', 'desc');
 | 
						||
        // 加班记录表 每300条处理一次
 | 
						||
        OvertimeLog::alias('l')
 | 
						||
            ->leftJoin('account a', 'a.id = l.account_id')
 | 
						||
            ->leftJoin('pay_log pl', 'pl.indexs = l.indexs')
 | 
						||
            ->where('l.is_statistic', ClockLog::COMMON_OFF)
 | 
						||
            ->where('l.status', ClockLog::COMMON_ON)
 | 
						||
            ->where('a.pay', '>', 0)
 | 
						||
            ->where('l.handle_count', '<=', 10) //查询处理次数10次以下
 | 
						||
            ->where($where)
 | 
						||
            ->fieldRaw('l.id,l.account_id,l.worksite_id,l.created_at,l.`day`,l.time,l.indexs,a.pay,pl.id as pay_log_id,pl.overtime_amount_count')
 | 
						||
            ->chunk(300, function ($items) {
 | 
						||
                $update = [];
 | 
						||
                $ids    = [];
 | 
						||
 | 
						||
                $handleIds = $items->column('id');
 | 
						||
                // 每查询一次就增加一次handle_count 避免未处理成功后 死循环一直查询
 | 
						||
                (new OvertimeLog)->whereIn('id', $handleIds)->save([
 | 
						||
                    'handle_count' => Db::raw('`handle_count` + 1')
 | 
						||
                ]);
 | 
						||
                foreach ($items as $item) {
 | 
						||
                    if ($item['pay_log_id']) {
 | 
						||
                        // 加班工资=基本工资/6小时 * 加班小时
 | 
						||
                        $overtimeAmount = round(Math::mul(Math::div($item['pay'], 6), $item['time']), 2);
 | 
						||
                        $update[]       = [
 | 
						||
                            'id'                    => $item['pay_log_id'],
 | 
						||
                            'amount'                => Db::raw('`amount` + '.$overtimeAmount),
 | 
						||
                            'overtime_amount'       => Db::raw('`overtime_amount` + '.$overtimeAmount),
 | 
						||
                            'remarks'               => Db::raw('CONCAT_WS(char(10), `remarks`,"加班工资入账 '.$overtimeAmount.'元")'),
 | 
						||
                            'overtime_amount_count' => Db::raw('`overtime_amount_count` + 1'),
 | 
						||
                        ];
 | 
						||
                        $ids[]          = $item['id'];
 | 
						||
                    }
 | 
						||
                }
 | 
						||
 | 
						||
                //                \think\facade\Log::write($update);
 | 
						||
                if (!empty($update)) {
 | 
						||
                    Db::startTrans();
 | 
						||
                    try {
 | 
						||
                        (new PayLog())->saveAll($update);
 | 
						||
                        OvertimeLog::whereIn('id', $ids)->update(['is_statistic' => OvertimeLog::COMMON_ON]);
 | 
						||
                        Db::commit();
 | 
						||
                    } catch (\Exception $e) {
 | 
						||
                        Db::rollback();
 | 
						||
                        \think\facade\Log::error('批量统计加班工资失败'.$e->getMessage().' file:'.$e->getFile().' line:'.$e->getLine());
 | 
						||
                    }
 | 
						||
                }
 | 
						||
            }, 'l.id', 'desc');
 | 
						||
    }
 | 
						||
 | 
						||
    /**
 | 
						||
     * 生成月份工资记录
 | 
						||
     *
 | 
						||
     * @param  int  $time  生成时间 按月份 格式为年月 如202209 date('Ym')
 | 
						||
     * @return bool
 | 
						||
     */
 | 
						||
    public static function generateMonthLog(int $time = 0): bool
 | 
						||
    {
 | 
						||
        if ($time == 0) {
 | 
						||
            // 默认生成上个月
 | 
						||
            $time = date('Ym', strtotime('last month'));
 | 
						||
        }
 | 
						||
        $timestamp = strtotime($time.'01');
 | 
						||
        $year      = date('Y', $timestamp);
 | 
						||
        $month     = date('m', $timestamp);
 | 
						||
 | 
						||
        $where = [
 | 
						||
            ['year', '=', $year],
 | 
						||
            ['month', '=', $month],
 | 
						||
            ['role', '=', Account::ROLE_WORKER],
 | 
						||
        ];
 | 
						||
 | 
						||
        // 每次统计 都讲之前的结果清理,插入新计算结果
 | 
						||
        $insert = [];
 | 
						||
 | 
						||
        // 每日工资记录(基本工资和加班工资)
 | 
						||
        PayLog::where($where)
 | 
						||
            ->chunk(300, function ($items) use (&$insert, $time, $year, $month) {
 | 
						||
                foreach ($items as $item) {
 | 
						||
                    $indexs = $item['account_id'].'-'.$item['worksite_id'].'-'.$time;
 | 
						||
                    if (!isset($insert[$indexs])) {
 | 
						||
                        $insert[$indexs] = [
 | 
						||
                            'account_id'  => $item['account_id'],
 | 
						||
                            'worksite_id' => $item['worksite_id'],
 | 
						||
                            'indexs'      => $indexs,
 | 
						||
                            'time'        => $time,
 | 
						||
                            'year'        => $year,
 | 
						||
                            'month'       => $month,
 | 
						||
                            'created_at'  => date('Y-m-d H:i:s'),
 | 
						||
 | 
						||
                            'amount'          => $item['amount'],
 | 
						||
                            'base_amount'     => $item['base_amount'],
 | 
						||
                            'overtime_amount' => $item['overtime_amount'],
 | 
						||
                        ];
 | 
						||
                    } else {
 | 
						||
                        $insert[$indexs]['amount']          = $insert[$indexs]['amount'] + $item['amount'];
 | 
						||
                        $insert[$indexs]['base_amount']     = $insert[$indexs]['base_amount'] + $item['base_amount'];
 | 
						||
                        $insert[$indexs]['overtime_amount'] = $insert[$indexs]['overtime_amount'] + $item['overtime_amount'];
 | 
						||
                    }
 | 
						||
                }
 | 
						||
            });
 | 
						||
 | 
						||
        if (!empty($insert)) {
 | 
						||
            //                    \think\facade\Log::write($insert);
 | 
						||
            // 启动事务
 | 
						||
            Db::startTrans();
 | 
						||
            try {
 | 
						||
                PayMonthLog::where('time', $time)->delete();
 | 
						||
                (new PayMonthLog())->insertAll($insert);
 | 
						||
                Db::commit();
 | 
						||
            } catch (\Exception $e) {
 | 
						||
                Db::rollback();
 | 
						||
                \think\facade\Log::error('统计['.$time.']工资失败'.$e->getMessage().' file:'.$e->getFile().' line:'.$e->getLine());
 | 
						||
            }
 | 
						||
        }
 | 
						||
 | 
						||
        return true;
 | 
						||
    }
 | 
						||
} |