create or replace package cus_con_deposit_pkg is -- Author : Spencer 3893 -- Created : 2019/10/14 20:51:45 -- Purpose : 保证金 --罚息测算 procedure calc_overdue_dayend(p_calc_date date, p_session_id number, p_user_id number); end cus_con_deposit_pkg; / create or replace package body cus_con_deposit_pkg is g_currency_precision constant number := 2; procedure log(p_log_desc varchar2, p_error_message varchar2) is begin hls_dayend_pkg.log(p_log_desc => p_log_desc, p_error_message => p_error_message); end; procedure f_calc_penalty(p_calc_amount number, p_contract_rec con_contract%rowtype, p_calc_date date, p_penalty_calc_date date, p_user_id number, p_total_penalty_amt out number) is v_penalty_days number; v_penalty_amt number; v_total_penalty_amt number := 0; begin v_penalty_days := trunc(p_calc_date) - trunc(p_penalty_calc_date) - nvl(p_contract_rec.grace_period, 0); -- 未核销金额 * 延迟天数 * 延迟履行金率 if v_penalty_days <= 0 then v_penalty_amt := 0; else v_penalty_days := v_penalty_days + nvl(p_contract_rec.grace_period, 0); v_penalty_amt := p_calc_amount * (v_penalty_days) * p_contract_rec.penalty_rate; end if; /*log(p_log_desc => '未收金额'||to_char(p_cashflow_rec.due_amount - nvl(p_cashflow_rec.received_amount,0)), p_error_message => '延迟天数='||v_penalty_days||',延迟金'||v_penalty_amt); */ v_total_penalty_amt := v_total_penalty_amt + v_penalty_amt; p_total_penalty_amt := round(v_total_penalty_amt, nvl(p_contract_rec.currency_precision, g_currency_precision)); end; procedure calc_penalty(p_cashflow_rec con_contract_cashflow%rowtype, p_contract_rec con_contract%rowtype, p_calc_date date, p_user_id number, p_total_penalty_amt out number) is v_penalty_days number; v_penalty_amt number; v_total_penalty_amt number := 0; v_last_creation_date date; begin select trunc(max(a.penalty_calc_date)) into v_last_creation_date from csh_write_off a where a.cashflow_id = p_cashflow_rec.cashflow_id; --modify by wcs if not (p_cashflow_rec.penalty_process_status = 'NORMAL') then --不满足计算条件 return; end if; --已收部分 for c_write_off in (select a.* from csh_write_off a where a.contract_id = p_contract_rec.contract_id and a.cashflow_id = p_cashflow_rec.cashflow_id and trunc(a.penalty_calc_date) <= trunc(p_calc_date) order by a.penalty_calc_date, a.write_off_date) loop --核销金额 * 延迟天数 * 延迟履行金率 v_penalty_days := trunc(c_write_off.penalty_calc_date) - trunc(nvl(p_cashflow_rec.ln_user_col_d01, p_cashflow_rec.due_date)) - nvl(p_contract_rec.grace_period, 0); if v_penalty_days <= 0 then v_penalty_amt := 0; else v_penalty_days := v_penalty_days + nvl(p_contract_rec.grace_period, 0); v_penalty_amt := c_write_off.write_off_due_amount * v_penalty_days * p_contract_rec.penalty_rate; end if; /*log(p_log_desc => to_char(c_write_off.penalty_calc_date,'YYYYMMDD')||'收款核销'||c_write_off.write_off_due_amount, p_error_message => '延迟天数='||v_penalty_days||',延迟金'||v_penalty_amt); */ v_total_penalty_amt := v_total_penalty_amt + v_penalty_amt; end loop; --未收部分 if nvl(p_cashflow_rec.received_amount, 0) < p_cashflow_rec.due_amount then v_penalty_days := trunc(p_calc_date) - trunc(nvl(p_cashflow_rec.ln_user_col_d01, p_cashflow_rec.due_date)) - nvl(p_contract_rec.grace_period, 0); -- 未核销金额 * 延迟天数 * 延迟履行金率 if v_penalty_days <= 0 then v_penalty_amt := 0; else v_penalty_days := v_penalty_days + nvl(p_contract_rec.grace_period, 0); v_penalty_amt := (p_cashflow_rec.due_amount - nvl(p_cashflow_rec.received_amount, 0)) * (v_penalty_days) * p_contract_rec.penalty_rate; end if; /*log(p_log_desc => '未收金额'||to_char(p_cashflow_rec.due_amount - nvl(p_cashflow_rec.received_amount,0)), p_error_message => '延迟天数='||v_penalty_days||',延迟金'||v_penalty_amt); */ v_total_penalty_amt := v_total_penalty_amt + v_penalty_amt; end if; p_total_penalty_amt := round(v_total_penalty_amt, nvl(p_contract_rec.currency_precision, g_currency_precision)); end; procedure overdue_dayend(p_contract_cashflow_rec con_contract_cashflow%rowtype, p_contract_rec con_contract%rowtype, p_calc_date date, p_session_id number, p_user_id number) is v_total_penalty_amt number; v_total_penalty_amt_tmp number; v_last_creation_date date; v_deposit_calc_overdue_tmp_rec CUS_DEPOSIT_CALC_OVERDUE_TMP%rowtype; begin select trunc(max(a.penalty_calc_date)) into v_last_creation_date from csh_write_off a where a.cashflow_id = p_contract_cashflow_rec.cashflow_id; --计算营业数据overdue --逾期 if p_contract_cashflow_rec.overdue_status = 'Y' then --已逾期 if p_contract_cashflow_rec.due_amount > nvl(p_contract_cashflow_rec.received_amount, 0) then --未收完款 v_deposit_calc_overdue_tmp_rec.calc_id := CUS_DEPOSIT_CALC_OVERDUE_TMP_s.Nextval; v_deposit_calc_overdue_tmp_rec.session_id := p_session_id; v_deposit_calc_overdue_tmp_rec.calc_date := p_calc_date; v_deposit_calc_overdue_tmp_rec.due_date := p_contract_cashflow_rec.due_date; v_deposit_calc_overdue_tmp_rec.contract_id := p_contract_rec.contract_id; v_deposit_calc_overdue_tmp_rec.cashflow_id := p_contract_cashflow_rec.cashflow_id; v_deposit_calc_overdue_tmp_rec.overdue_max_days := trunc(p_calc_date) - trunc(p_contract_cashflow_rec.due_date); v_deposit_calc_overdue_tmp_rec.overdue_amount := p_contract_cashflow_rec.due_amount - nvl(p_contract_cashflow_rec.received_amount, 0); v_deposit_calc_overdue_tmp_rec.overdue_principal := p_contract_cashflow_rec.principal - nvl(p_contract_cashflow_rec.received_principal, 0); v_deposit_calc_overdue_tmp_rec.overdue_interest := p_contract_cashflow_rec.interest - nvl(p_contract_cashflow_rec.received_interest, 0); v_deposit_calc_overdue_tmp_rec.created_by := p_user_id; v_deposit_calc_overdue_tmp_rec.creation_date := sysdate; insert into CUS_DEPOSIT_CALC_OVERDUE_TMP values v_deposit_calc_overdue_tmp_rec; end if; else --未逾期 if p_contract_cashflow_rec.due_amount > nvl(p_contract_cashflow_rec.received_amount, 0) then v_deposit_calc_overdue_tmp_rec.calc_id := CUS_DEPOSIT_CALC_OVERDUE_TMP_s.Nextval; v_deposit_calc_overdue_tmp_rec.session_id := p_session_id; v_deposit_calc_overdue_tmp_rec.calc_date := p_calc_date; v_deposit_calc_overdue_tmp_rec.due_date := p_contract_cashflow_rec.due_date; v_deposit_calc_overdue_tmp_rec.contract_id := p_contract_rec.contract_id; v_deposit_calc_overdue_tmp_rec.cashflow_id := p_contract_cashflow_rec.cashflow_id; v_deposit_calc_overdue_tmp_rec.overdue_max_days := trunc(p_calc_date) - trunc(p_contract_cashflow_rec.due_date); v_deposit_calc_overdue_tmp_rec.overdue_amount := p_contract_cashflow_rec.due_amount - nvl(p_contract_cashflow_rec.received_amount, 0); v_deposit_calc_overdue_tmp_rec.overdue_principal := p_contract_cashflow_rec.principal - nvl(p_contract_cashflow_rec.received_principal, 0); v_deposit_calc_overdue_tmp_rec.overdue_interest := p_contract_cashflow_rec.interest - nvl(p_contract_cashflow_rec.received_interest, 0); v_deposit_calc_overdue_tmp_rec.created_by := p_user_id; v_deposit_calc_overdue_tmp_rec.creation_date := sysdate; insert into CUS_DEPOSIT_CALC_OVERDUE_TMP values v_deposit_calc_overdue_tmp_rec; end if; end if; --计算penalty if p_contract_cashflow_rec.write_off_flag <> 'FULL' then calc_penalty(p_cashflow_rec => p_contract_cashflow_rec, p_contract_rec => p_contract_rec, p_calc_date => p_calc_date, p_total_penalty_amt => v_total_penalty_amt, p_user_id => p_user_id); --会计逾期金额默认营业数据,当会计没核销实际上和营业逾期一致 update CUS_DEPOSIT_CALC_OVERDUE_TMP set OVERDUE_PENALTY_AMT = v_total_penalty_amt, f_overdue_amount = overdue_amount, f_overdue_penalty_amt = v_total_penalty_amt where calc_id = v_deposit_calc_overdue_tmp_rec.calc_id; end if; --计算会计逾期数据,取会计核销的最大期与营业现金流数据比较 for c_f in (select gwo.cf_item, gwo.times, sum(gwo.write_off_amount) write_off_amount, gwo.penalty_calc_date from GLD_WRITE_OFF gwo where /*gwo.create_je_flag = 'Y' AND */ gwo.reversed_flag = 'N' and gwo.contract_id = p_contract_rec.contract_id and gwo.times = (select max(gwo.times) max_times from GLD_WRITE_OFF gwo where /*gwo.create_je_flag = 'Y' AND */ gwo.reversed_flag = 'N' and gwo.contract_id = p_contract_rec.contract_id) group by gwo.cf_item, gwo.times, gwo.penalty_calc_date) loop --会计出凭证的数据同类型同期现金流,并营业应收金额大于会计出凭证的和及营业应收日小于计算日。就视为这一期现金流 会计逾期 if p_contract_cashflow_rec.cf_item = c_f.cf_item and p_contract_cashflow_rec.times = c_f.times and p_contract_cashflow_rec.due_amount > c_f.write_off_amount and p_contract_cashflow_rec.due_date < p_calc_date then --会计已收收计算罚息 f_calc_penalty(p_calc_amount => c_f.write_off_amount, p_contract_rec => p_contract_rec, p_calc_date => p_calc_date, p_penalty_calc_date => c_f.penalty_calc_date, p_user_id => p_user_id, p_total_penalty_amt => v_total_penalty_amt_tmp); --会计未收计算罚息 f_calc_penalty(p_calc_amount => p_contract_cashflow_rec.due_amount - c_f.write_off_amount, p_contract_rec => p_contract_rec, p_calc_date => p_calc_date, p_penalty_calc_date => p_contract_cashflow_rec.due_date, p_user_id => p_user_id, p_total_penalty_amt => v_total_penalty_amt); --更新会计逾期租金=营业应收租金-会计核销租金 update CUS_DEPOSIT_CALC_OVERDUE_TMP t set t.f_overdue_amount = p_contract_cashflow_rec.due_amount - c_f.write_off_amount, t.f_overdue_penalty_amt = round(v_total_penalty_amt_tmp + v_total_penalty_amt, 2) where t.cashflow_id = p_contract_cashflow_rec.cashflow_id and t.contract_id = p_contract_rec.contract_id and t.session_id = p_session_id; --会计出凭证的数据同类型及会计未来期现金流,并营业应收日小于计算日。就视为这一期现金流 会计逾期 elsif p_contract_cashflow_rec.cf_item = c_f.cf_item and p_contract_cashflow_rec.times > c_f.times and p_contract_cashflow_rec.due_date < p_calc_date then --会计未收计算罚息 f_calc_penalty(p_calc_amount => p_contract_cashflow_rec.due_amount, p_contract_rec => p_contract_rec, p_calc_date => p_calc_date, p_penalty_calc_date => p_contract_cashflow_rec.due_date, p_user_id => p_user_id, p_total_penalty_amt => v_total_penalty_amt); --更新会计逾期租金=营业应收租金 update CUS_DEPOSIT_CALC_OVERDUE_TMP t set t.f_overdue_amount = p_contract_cashflow_rec.due_amount, t.f_overdue_penalty_amt = v_total_penalty_amt where t.cashflow_id = p_contract_cashflow_rec.cashflow_id and t.contract_id = p_contract_rec.contract_id and t.session_id = p_session_id; end if; end loop; end; procedure overdue_dayend(p_contract_id number, p_calc_date date, p_session_id number, p_user_id number) is v_contract_rec con_contract%rowtype; begin begin select * into v_contract_rec from con_contract where contract_id = p_contract_id and contract_status = 'INCEPT' for update nowait; exception when no_data_found then return; end; for c_contract_cashflow in (select a.* from con_contract_cashflow a, con_contract_cf_item b where a.contract_id = p_contract_id and a.cf_status = 'RELEASE' and a.due_date <= p_calc_date --只有正常的才计算罚息。 and a.penalty_process_status = 'NORMAL' and a.cf_direction = 'INFLOW' and a.contract_id = b.contract_id and a.cf_item = b.cf_item and b.calc_penalty = 'Y' order by times, due_date for update nowait) loop overdue_dayend(p_contract_cashflow_rec => c_contract_cashflow, p_contract_rec => v_contract_rec, p_calc_date => p_calc_date, p_session_id => p_session_id, p_user_id => p_user_id); end loop; end; --罚息测算 procedure calc_overdue_dayend(p_calc_date date, p_session_id number, p_user_id number) is v_count number; begin -- 同样的罚息测算日如果存在数据,不在进行计算 select count(*) into v_count from CUS_DEPOSIT_CALC_OVERDUE_TMP t where t.calc_date = p_calc_date --and t.session_id = p_session_id ; if v_count >= 1 then null; else -- 之前的计算数据 delete from CUS_DEPOSIT_CALC_OVERDUE_TMP t where t.calc_date <= p_calc_date; --t.session_id = p_session_id; --根据测算日重算罚息 for c_con in (select * from con_contract t where t.contract_status = 'INCEPT') loop overdue_dayend(p_contract_id => c_con.contract_id, p_calc_date => p_calc_date, p_session_id => p_session_id, p_user_id => p_user_id); end loop; end if; end calc_overdue_dayend; end cus_con_deposit_pkg; /