package com.brilliance.isc.common.fecism.service.impl;

import com.brilliance.mda.runtime.mda.IStream;
import com.brilliance.mda.runtime.mda.impl.Argument;
import com.brilliance.mda.runtime.mda.impl.StreamImpl;
import com.brilliance.mda.runtime.mda.util.*;
import com.brilliance.isc.bo.*;
import com.brilliance.isc.common.cbsmod.CbsRange;
import com.brilliance.isc.common.cbsmod.service.XrtmodService;
import com.brilliance.isc.common.codetable.service.CodetableService;
import com.brilliance.isc.common.contants.CommonContants;
import com.brilliance.isc.common.context.SettleContext;
import com.brilliance.isc.common.context.SpringEnvContext;
import com.brilliance.isc.common.converter.DtoMapper;
import com.brilliance.isc.common.exception.CommonServiceException;
import com.brilliance.isc.common.fecism.calcfee.IFeeCalculator;
import com.brilliance.isc.common.fecism.service.FecismService;
import com.brilliance.isc.common.fecism.vo.CalcFeeRequest;
import com.brilliance.isc.common.fecism.vo.CalcFeeResponse;
import com.brilliance.isc.common.fecmod.service.FecmodService;
import com.brilliance.isc.common.irtcal.service.IrtcalService;
import com.brilliance.isc.common.irtcal.vo.IrtcalVo;
import com.brilliance.isc.common.setfeg.fee.FeeEntity;
import com.brilliance.isc.common.setfeg.service.SetfegService;
import com.brilliance.isc.common.setfeg.vo.SetfelVo;
import com.brilliance.isc.common.setmod.service.SetmodService;
import com.brilliance.isc.common.setmod.vo.SetmodVo;
import com.brilliance.isc.common.util.StringUtils;
import com.brilliance.isc.common.util.Utils;
import com.brilliance.isc.common.vo.FecVo;
import com.brilliance.isc.mda.dao.*;
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import static com.brilliance.mda.runtime.mda.Constants.*;

@Service("fecismService")
public class FecismServiceImpl implements FecismService {

    private static Logger log = LoggerFactory.getLogger(FecismServiceImpl.class);

    @Autowired
    private CodetableService codetableService;

    @Autowired
    private FecmodService fecmodService;

    @Autowired
    private IrtcalService irtcalService;

    @Autowired
    private SetfegService setfegService;

    @Autowired
    private SetmodService setmodService;

    @Autowired
    private XrtmodService xrtmodService;

    @Autowired
    private CbsRange cbsRange;

    @Autowired
    private FecMapper fecMapper;

    @Autowired
    private FeeMapper feeMapper;

    @Autowired
    private FepMapper fepMapper;

    @Autowired
    private IrsMapper irsMapper;

    @Autowired
    private IrtMapper irtMapper;

    @Autowired
    private PteMapper pteMapper;

    /**
     * source:fecism.0003.script
     */
    @Override
    @JsonIgnore
    public BigDecimal getMinimum(BigDecimal val1, BigDecimal val2) {
        if (MdaUtils.compareTo(val1, val2) < 0) {
            return val1;
        } else {
            return val2;
        }
    }

    /**
     * source:fecism.0004.script
     */
/*WD 不再使用
    @Override
    @JsonIgnore
    @TDMethod
    public void setMoreDetails(IrtcalVo irtcal, IStream stm) {
        irtcalService.initialize(irtcal);
        Datas.loadData(irtcal, stm, "IRTCALDET");
        irtcalService.cutGrids();
}
*/

    /**
     * source:fecism.0005.script
     */
/*WD 不再使用
    @TDMethod
     public void popupMoreDetails() {
        //取得上下文
        IContext ctx = MdaEnv.getContext();
        ctx.setErrorCode(NO_ERROR);
        Systems.popupTitledFrame(this.getIrtcaldet(), "Calculation Details");
    }
*/

    /**
     * source:fecism.0007.script
     */
    @Override
    public BigDecimal clcFecIrtcal(SetfelVo line, IrtcalVo irtcal, SetmodVo setmod, String relcur, BigDecimal relamt, String feecur, Date refdat) {
        MdaUtils.clear(line.getInfdetstm());
        BigDecimal fecamt = new BigDecimal("0");
        // Check valitdity of period if both dates are set (in not calclulation from default dates has to be executed)
        if (!MdaUtils.isEmpty(line.getDat1()) && !MdaUtils.isEmpty(line.getDat2())) {
            if (Dates.diff(line.getDat1(), line.getDat2()) >= 0) {
                line.setInftxt(line.getInftxt() + CR + Strings.getText("Calculation impossible for invalid period"));
                setInfdetstm(line, irtcal);
                return fecamt;
            }
        }

        // if fee has been entered manually, the fee amount has to be entered manually
        if (MdaUtils.compareTo(line.getSrc(), "U") == 0) {
            line.setInftxt(line.getInftxt() + CR + Strings.getText("Please enter amount and dates manually"));
            setInfdetstm(line, irtcal);
            return fecamt;
        }

        // check fees already settled
        BigDecimal amt = new BigDecimal("0.000");
        String cur = "";
        Date dat1 = NULLDATE;
        Date dat2 = NULLDATE;
        Date mindat = NULLDATE;
        Date maxdat = NULLDATE;
        Date maxdatref = NULLDATE;
        BigDecimal oldamt = new BigDecimal("0.000");
        BigDecimal olddifamt = new BigDecimal("0.000");
        int addqtr = 0;
        line.setUnt(0);
        BigDecimal mintot = xrtmodService.amtConvTypes(line.getWrkfec().getMincur(), line.getWrkfec().getMinamttot(), "M", feecur, "M", Dates.today());
        BigDecimal minFeecurAmt = xrtmodService.amtConvTypes(line.getWrkfec().getMincur(), line.getWrkfec().getMinamt(), "M", feecur, "M", Dates.today());
        BigDecimal maxFeecurAmt = xrtmodService.amtConvTypes(line.getWrkfec().getMaxcur(), line.getWrkfec().getMaxamt(), "M", feecur, "M", Dates.today());
        // recalculation of old amount is done outside
        // if just adjust settlement that"s it
        Date dat1reg = null;
        Date dat2reg = null;
        Date begdat = null;
        Date enddat = null;
        int sgn = 0;
        int count = 0;
        String oldinftxt = NULLSTR;
        IStream oldinfdetstm = new StreamImpl();
        Date lastdate = null;
        Date nxtcomdat = null;
        BigDecimal maxnow = null;
        boolean addit = false;
        String pfx = NULLSTR;
        int allin = 0;
        IStream detlin = new StreamImpl();
        BigDecimal uni = null;
        if (Dates.diff(maxdatref, refdat) >= 0 && (!MdaUtils.isEmpty(maxdat)) && MdaUtils.compareTo(line.getModflg(), "d") != 0) {
            line.setDat1(mindat);
            line.setDat2(maxdatref);
        } else {
            // the settlement of the new period starts at the end of the settled period
            if (!MdaUtils.isEmpty(maxdat)) {
                line.setDat1(Dates.add(maxdat, 1));
            }
            // if no end date is set use date according to condition (for amunt tier use + 5 years)
            if (!MdaUtils.isEmpty(line.getDat1())) {
                dat1reg = line.getDat1();
                dat2reg = line.getDat2();
                Argument<Date> dat2regBox = new Argument<Date>("", dat2reg);
                setDat2FromPerflg(line.getWrkfec().getSetperflg(), line.getWrkfec().getPertyp(), dat1reg, dat2regBox, line.getDat2dft(), Utils.getMaxDate());
                dat2reg = dat2regBox.value;
                line.setDat2(dat2reg);
            }
            if (Dates.diff(line.getDat1(), line.getDat2()) < 0) {
                begdat = line.getDat1();
                enddat = line.getDat2();
                sgn = 1;
            } else {
                begdat = line.getDat2();
                if (!MdaUtils.isEmpty(line.getDat1())) {
                    enddat = Dates.add(line.getDat1(), -1);
                } else {
                    enddat = NULLDATE;
                }
                sgn = -1;
                line.setInftxt(line.getInftxt() + CR + Strings.getText("Calculating refund from $1 to $2", begdat, enddat));
            }
            if (Dates.diff(begdat, enddat) < 0 && (!MdaUtils.isEmpty(line.getDat1())) && (!MdaUtils.isEmpty(line.getDat2()))) {
                count = 0;
                oldinftxt = line.getInftxt();
                line.setAllcalflg(NULLSTR);
                Streams.streamSet(oldinfdetstm, line.getInfdetstm());

                while (true) {
                    count = count + 1;
                    if (count > 19) {
                        return fecamt;
                    }
                    line.setInftxt(oldinftxt);
                    Streams.streamSet(line.getInfdetstm(), oldinfdetstm);
                    // set line\\DATREFBEG
                    if (MdaUtils.isEmpty(line.getDatrefbeg())) {
                        // try to get it from PTE
                        line.setDatrefbeg(line.getBegdat());
                        // if not available or empty, take begin date of calculation
                        if (MdaUtils.isEmpty(line.getDatrefbeg())) {
                            line.setDatrefbeg(begdat);
                        } else {
                            // else if date is not in sequence default to begin of calculation aswell
                            if (Strings.len(line.getWrkfec().getPertyp()) == 6) {
                                lastdate = line.getDatrefbeg();
                                while (Dates.diff(lastdate, begdat) < 0) {
                                    lastdate = irtcalService.getNextPeriod(line.getDatrefbeg(), lastdate, line.getWrkfec().getPertyp());
                                }
                                if (Dates.diff(lastdate, begdat) > 0) {
                                    line.setDatrefbeg(begdat);
                                }
                            }
                        }
                    }
                    if ("E".equals(line.getWrkfec().getSettyp())) {
                        // check for next due date for caculations in arreas
                        // If current due date is not reached in calulation, end date is the due date and nothing settled in this transaction
                        if (Dates.diff(enddat, refdat) > 0) {
                            fecamt = new BigDecimal("0");
                            line.setDat2(NULLDATE);
                            line.setNxtcomdat(enddat);
                            line.setInftxt(line.getInftxt() + CR + Strings.getText("next settlement is due at $1.", line.getNxtcomdat()));
                            return fecamt;
                        } else {
                            // calculate due date of next settlement:
                            nxtcomdat = NULLDATE;
                            Argument<Date> nxtcomdatBox = new Argument<Date>("", nxtcomdat);
                            setDat2FromPerflg(line.getWrkfec().getSetperflg(), line.getWrkfec().getPertyp(), enddat, nxtcomdatBox, line.getDat2dft(), Utils.getMaxDate());
                            nxtcomdat = nxtcomdatBox.value;
                            line.setNxtcomdat(nxtcomdat);
                            if (Dates.diff(line.getNxtcomdat(), line.getDat2()) > 0) {
                                line.setInftxt(line.getInftxt() + CR + Strings.getText("next settlement is due at $1.", line.getNxtcomdat()));
                            }
                        }
                    }
                    // DO5BLB.000002 - for reference amount calculation without new amounts
                    if (MdaUtils.compareTo(line.getWrkfec().getClcdifflg(), "L") == 0) {
                        setIrtcal(line, setmod, irtcal, relcur, line.getDatrefbeg(), begdat, enddat, false);
                    } else {
                        setIrtcal(line, setmod, irtcal, relcur, line.getDatrefbeg(), begdat, enddat, true);
                    }
                    try {
                        fecamt = Decimals.mul(irtcalService.calcAllCur(irtcal, feecur, minFeecurAmt, maxFeecurAmt), sgn);
                        line.setErrcod(0);
                        line.setErrtxt(NULLSTR);
                        maxnow = xrtmodService.amtConvTypes(line.getWrkfec().getMincur(), line.getWrkfec().getAmtsetall(), "M", feecur, "M", Dates.today());
                        if (!line.isModified("basamt")) {
                            line.setBasamt(new BigDecimal("0"));
                            for (int i = 0; i < irtcal.getIrdele().size(); i++) {
                                if (MdaUtils.compareTo(irtcal.getIrdele().get(i).getBasamt(), line.getBasamt()) > 0) {
                                    line.setBasamt(irtcal.getIrdele().get(i).getBasamt());
                                }
                            }
                        }
                        amt = new BigDecimal("0");
                        line.setUnt(0);
                        addit = true;
                        line.setAllcalflg("");
                        irtcalService.irtGrdsAdjustForShow(irtcal);
                        // show IRD aswell
                        for (int i = 0; i < irtcal.getIrdele().size(); i++) {
                            if (MdaUtils.compareTo(irtcal.getIrdele().get(i).getAmt(), 0) != 0) {
                                line.setInftxt(line.getInftxt() + CR + Strings.getText("- $1-$2 $3 units at rate $4 on $5 $6 \\n  => $5 $7", Formats.fmtDateShort(irtcal.getIrdele().get(i).getBegdat()), Formats.fmtDateShort(irtcal.getIrdele().get(i).getEnddat()), irtcal.getIrdele().get(i).getUni(), irtcal.getIrdele().get(i).getRte(), irtcal.getIrpcur(), Utils.fmtAmount(irtcal.getIrdele().get(i).getBasamt(), irtcal.getIrpcur()), Utils.fmtAmount(irtcal.getIrdele().get(i).getAmt(), irtcal.getIrpcur())));
                                // store IRD in detstm
                                Streams.streamInsert(line.getInfdetstm(), 0, "IRDELE" + TAB + MdaUtils.toString(irtcal.getIrdele().get(i).getRte()) + TAB + irtcal.getIrpcur() + TAB + MdaUtils.toString(irtcal.getIrdele().get(i).getBasamt()) + TAB + Formats.format(irtcal.getIrdele().get(i).getBegdat(), "DD/MM/YYYY") + TAB + Formats.format(irtcal.getIrdele().get(i).getEnddat(), "DD/MM/YYYY") + TAB + MdaUtils.toString(irtcal.getIrdele().get(i).getUni()) + TAB + MdaUtils.toString(irtcal.getIrdele().get(i).getAmt()) + TAB);
                                // some may print sums for same rate/amount without date details
                                pfx = "IRDSUM" + TAB + MdaUtils.toString(irtcal.getIrdele().get(i).getRte()) + TAB + irtcal.getIrpcur() + TAB + MdaUtils.toString(irtcal.getIrdele().get(i).getBasamt()) + TAB;
                                allin = Streams.streamSearch(line.getInfdetstm(), pfx);
                                if (allin == 0) {
                                    Streams.streamInsert(line.getInfdetstm(), 0, pfx + MdaUtils.toString(irtcal.getIrdele().get(i).getUni()) + TAB + MdaUtils.toString(irtcal.getIrdele().get(i).getAmt()) + TAB);
                                } else {
                                    Streams.streamClear(detlin);
                                    Utils.getTabValues(detlin, Strings.getLine(line.getInfdetstm(), allin));
                                    uni = Decimals.add(Strings.parseInt(Strings.getLine(detlin, 5)), irtcal.getIrdele().get(i).getUni());
                                    amt = Decimals.add(Strings.parseInt(Strings.getLine(detlin, 6)), irtcal.getIrdele().get(i).getAmt());
                                    Streams.streamSetLine(line.getInfdetstm(), allin, pfx + MdaUtils.toString(uni) + TAB + MdaUtils.toString(amt) + TAB);
                                }
                                // for following MIC-Codes units are IRTCAL-Units (days)
                                switch (irtcal.getIrt().getIrtmic()) {
                                    case "B60":
                                    case "B65":
                                    case "360":
                                    case "365":
                                    case "DFL":
                                    case "D65":
                                    case "F60":
                                    case "F65":
                                        line.setUnt(Decimals.add(line.getUnt(), irtcal.getIrdele().get(i).getUni()).intValue());
                                        break;
                                }
                                // if reference rate has been used, note it in DETSTM
                                if (MdaUtils.compareTo(irtcal.getIrdele().get(i).getRefrte(), 0) != 0) {
                                    if (Streams.streamSearch(line.getInfdetstm(), "REFRTE") == 0) {
                                        Streams.streamInsert(line.getInfdetstm(), 0, "REFRTE:" + irtcal.getIrt().getCod());
                                    }
                                }
                            }
                        }
                        line.setInftxt(line.getInftxt() + CR);
                        amt = new BigDecimal("0");
                        for (int i = 0; i < irtcal.getIrpele().size(); i++) {
                            if (MdaUtils.compareTo(irtcal.getIrpele().get(i).getAmt(), 0) != 0 && addit) {
                                line.setInftxt(line.getInftxt() + CR + Strings.getText("- $1-$2 $3 $4 ($5)", Formats.fmtDateShort(irtcal.getIrpele().get(i).getBegdat()), Formats.fmtDateShort(irtcal.getIrpele().get(i).getEnddat()), irtcal.getIrpele().get(i).getCur(), Utils.fmtAmount(irtcal.getIrpele().get(i).getAmt(), irtcal.getIrpele().get(i).getCur()), CommonContants.IRPELE_CALFLG_CODES.get(irtcal.getIrpele().get(i).getCalflg())));
                                Streams.streamInsert(line.getInfdetstm(), 0, "IRPELE" + TAB + Formats.format(irtcal.getIrpele().get(i).getBegdat(), "DD/MM/YYYY") + TAB + Formats.format(irtcal.getIrpele().get(i).getEnddat(), "DD/MM/YYYY") + TAB + irtcal.getIrpele().get(i).getCur() + TAB + MdaUtils.toString(irtcal.getIrpele().get(i).getAmt()) + TAB + irtcal.getIrpele().get(i).getCalflg() + TAB);
                                amt = Decimals.add(amt, irtcal.getIrpele().get(i).getAmt());
                                // each period is is a unit for other types
                                switch (irtcal.getIrt().getIrtmic()) {
                                    case "B60":
                                    case "B65":
                                    case "360":
                                    case "365":
                                    case "DFL":
                                    case "D65":
                                    case "F60":
                                    case "F65":
                                        break;
                                    default:
                                        line.setUnt(line.getUnt() + 1);
                                }
                                if (MdaUtils.isEmpty(line.getAllcalflg())) {
                                    line.setAllcalflg(irtcal.getIrpele().get(i).getCalflg());
                                } else {
                                    if (MdaUtils.compareTo(line.getAllcalflg(), irtcal.getIrpele().get(i).getCalflg()) != 0) {
                                        line.setAllcalflg("*");
                                    }
                                }
                                // check amount limit only if that settlement period is used
                                if (MdaUtils.compareTo(amt, maxnow) >= 0 && Dates.diff(irtcal.getIrpele().get(i).getEnddat(), Dates.today()) >= 0 && MdaUtils.compareTo(line.getModflg(), "d") != 0 && MdaUtils.compareTo(maxnow, 0) != 0) {
                                    if ((MdaUtils.compareTo(line.getWrkfec().getSetperflg(), "E") == 0 && line.getDat2dft() == Utils.getMaxDate()) || MdaUtils.compareTo(line.getWrkfec().getSetperflg(), "A") == 0 || MdaUtils.isEmpty(line.getWrkfec().getSetperflg())) {
                                        addit = false;
                                        line.setInftxt(line.getInftxt() + CR + "Commission limit for this settlement reached");
                                        line.setDat2(irtcal.getIrpele().get(i).getEnddat());
                                        fecamt = amt;
                                    }
                                }
                            }
                        }
                        fecamt = Decimals.add(fecamt, olddifamt);
                        // checking total fee minimum
                        if (MdaUtils.compareTo(mintot, Decimals.add(fecamt, oldamt)) > 0 && sgn > 0) {
                            // if not at end of daterange requested by transaction, try to settle another quarter
                            if (Dates.diff(line.getDat2(), line.getDat2dft()) < 0 && (!line.isModified("dat2"))) {
                                // as we use closed intervals, add one day for call of IRTCAL.GetNextPeriod
                                line.setDat2(irtcalService.getNextPeriod(line.getDat1(), Dates.add(line.getDat2(), 1), line.getWrkfec().getPertyp()));
                                if (Dates.diff(line.getDat2(), line.getDat2dft()) > 0) {
                                    line.setDat2(Dates.add(line.getDat2dft(), 1));
                                }
                                addqtr = addqtr + 1;
                                // goback one day as we use closed intervals
                                line.setDat2(Dates.add(line.getDat2(), -1));
                                enddat = line.getDat2();
                                continue;
                            }
                            // confirm usage of total minimum
                            Streams.streamInsert(line.getInfdetstm(), 0, "MINCON" + TAB + Formats.format(line.getDat1(), "DD/MM/YYYY") + TAB + Formats.format(line.getDat2(), "DD/MM/YYYY") + TAB + relcur + TAB + MdaUtils.toString(mintot));
                            fecamt = Decimals.sub(mintot, oldamt);
                            line.setInftxt(line.getInftxt() + CR + Strings.getText("Using fee minimum of $1 $2", feecur, Utils.fmtAmount(fecamt, feecur)));
                            line.setAllcalflg("T");
                        }
                    } catch (Exception e) {
                        line.setErrcod(CommonContants.GENERRCOD);
                        line.setErrtxt(e.getMessage());
                        line.setInftxt(line.getInftxt() + CR + Strings.getText("Interest on balance $2 cannot be calulated yet:\\n$1", line.getErrtxt()));
                    }
                    continue;
                }
            } else {
                line.setInftxt(line.getInftxt() + CR + Strings.getText("Interest not yet calulated. Dates not yet consistent", CommonContants.FEC_CALCBS_CODES.get(line.getWrkfec().getCalcbs()), line.getDat1(), line.getDat2()));
            }
        }
        if (addqtr > 0) {
            line.setInftxt(line.getInftxt() + CR + Strings.getText("$1 additional period(s) settled to reach total minimum of this fee", addqtr));
        }

        setInfdetstm(line, irtcal);
        return fecamt;
    }

    private void setInfdetstm(SetfelVo line, IrtcalVo irtcal) {
        Streams.streamInsert(line.getInfdetstm(), 1, "IRTMIC" + TAB + irtcal.getIrt().getIrtmic());
        Streams.streamInsert(line.getInfdetstm(), 1, "DETINF" + TAB + line.getWrkfec().getCalrul() + TAB + MdaUtils.toString(line.getWrkfec().getRatcal()) + TAB + MdaUtils.toString(line.getWrkfec().getUntamt()) + TAB + line.getWrkfec().getPertyp() + TAB + line.getAllcalflg() + TAB + MdaUtils.toString(line.getBasamt()) + TAB);
    }

    /**
     * source:fecism.0008.script
     */
    @Override
    public void clcFecAmt(SetfelVo line, SetmodVo setmod, Argument<String> relcur, BigDecimal relamt, String feecur) {
        // Calculate Feeamount
        IrtcalVo irtcal = new IrtcalVo();
        BigDecimal oldamt = new BigDecimal("0");
        line.setMinmaxflg(NULLSTR);
        BigDecimal fecamt = null;
        BigDecimal lowamt = null;
        BigDecimal minamt = null;
        String myDifFlg = line.getWrkfec().getClcdifflg();

        String relObjTyp = "";
        String relObjInr = "";
        String relconInf = "";

        if ("F".equals(line.getWrkfec().getCalrul())) {
            fecamt = new BigDecimal("0");
        } else {
            // get old amount for diffrence calculation. Possible for liability comissions only
            String liaallgPath = Utils.getPathofName(line.getObjmod());
            Date olddat = null;
/*TODO 涉及表外处理
            Date olddat = Datas.getContentDate(this, liaallgPath + "\\SETDATOLD");
            if (ctx.getErrorCode() > 0) {
                olddat = NULLDATE;
            } else {
                // Deactivate difference calculation if first settlement in period (e.g. in GITCOM)
                relcur.value = Datas.getContent(this, liaallgPath + "\\CUR");
                relamt = Datas.getContentNumeric(this, liaallgPath + "\\AMT");
                if (Dates.diff(olddat, line.getDat1()) < 0) {
                    myDifFlg = "";
                }
            }
*/
            //  If fee from pool, leave unchaged ( currency coversion is done by RecalcSetfel if necessary)
            // fees already advised to same party or if status of fee has not been set yet, don`t calulate fee again.
            if (MdaUtils.compareTo(line.getSrc(), "D") == 0 || MdaUtils.compareTo(line.getSrc(), "A") == 0 || MdaUtils.isEmpty(line.getSrc())) {
                return;
            }
            // clear register for relevant contract

            // if from pool recalulate and recreate detailled information
            if (MdaUtils.compareTo(line.getSrc(), "P") == 0 && !line.isModified("feetxtinf")) {
                line.setFeetxtinf(NULLSTR);
            }
            // Reset units for transaction fees not calulating units if units have not been modified (DO5SFS 500035)
            switch (line.getWrkfec().getCalrul()) {
                case "0":
                case "3":
                case "6":
                case "7":
                case "8":
                    if (!line.isModified("unt") && !MdaUtils.isEmpty(line.getFepinrlst()) && MdaUtils.compareTo(line.getSrc(), "T") == 0) {
                        FeeEntity feeEntity = setfegService.getFeeSet().getFeeEntityById(Strings.getLine(line.getFepinrlst(), 1));
                        if (feeEntity != null) {
                            line.setUnt(Strings.parseInt(feeEntity.getUnit()));
                        }
                    }
                    break;
            }
            String feccur = relcur.value;
            // determine rate to use
            String methreg = "365";
            BigDecimal ratuse = null;
            String sql = NULLSTR;
            if (!line.isModified("ratcal")) {
                ratuse = line.getRatcal();
                if (MdaUtils.compareTo(line.getWrkfec().getCalrul(), "2") == 0 && (!MdaUtils.isEmpty(line.getWrkfec().getRatirs()))) {
                    Irs wrkIrs = new Irs();
                    wrkIrs.setObjtyp("IRT");
                    irtcal.setIrs(wrkIrs);
                    Irt wrkIrt = irtMapper.selectByCodAndCur(line.getWrkfec().getRatirs(), relcur.value, SettleContext.getUserEtg().getExtkey());
                    if (wrkIrt != null) {
                        wrkIrs.setObjinr(wrkIrt.getInr());
                        wrkIrs.setCur(relcur.value);
                        methreg = wrkIrt.getIrtmic();
                        cbsRange.sdbRngRead(wrkIrs, Dates.today());
                        ratuse = Decimals.add(ratuse, wrkIrs.getRat());
                    } else {
                        throw new CommonServiceException("未查询到IRT的数据");
                    }
                }
                line.setRatcal(ratuse);
            } else {
                ratuse = line.getRatcal();
            }

            fecamt = new BigDecimal("0");
            BigDecimal clcamt = relamt;
            // take relevant amount from pool info, if avaliable
            int relamtlin = 0;
            IStream valstm = new StreamImpl();
            String fepinr = NULLSTR;
            List<Pte> pteStm = new ArrayList<>();
            BigDecimal tiramt = null;
            BigDecimal tirbca = null;
            Date refFecDat = null;
            switch (line.getSrc()) {
                case "D":
                case "A":
                case "O":
                case "P":
                    Streams.streamClear(line.getInfdetstm());
                    FeeEntity feeEntity = setfegService.getFeeSet().getFeeEntityById(Strings.getLine(line.getFepinrlst(), 1));
                    Streams.streamSet(line.getInfdetstm(), feeEntity.getInfdetStm());
                    relamtlin = Streams.streamSearch(line.getInfdetstm(), "RELAMT" + TAB);
                    if (relamtlin > 0) {
                        Streams.streamClear(valstm);
                        Utils.getTabValues(valstm, Strings.getLine(line.getInfdetstm(), relamtlin) + TAB);
                        if (Streams.streamCount(valstm) > 2) {
                            feccur = Strings.getLine(valstm, 2);
                            clcamt = new BigDecimal(Strings.getLine(valstm, 3));
                        }
                    }
                    // ... , and condition aswell
                    fepinr = Strings.getLine(line.getFepinrlst(), 1);
                    if (!MdaUtils.isEmpty(fepinr)) {
                        Fep wrkFep = fepMapper.selectByInr(fepinr);
                        relObjTyp = wrkFep.getRelobjtyp();
                        relObjInr = wrkFep.getRelobjinr();
                        // DO5LBW.00461 - if no PTE set,, but calculation for LIAAMT, find  PTE via Sql
                        if (!"PTE".equals(relObjTyp) && "LIAAMT".equals(line.getWrkfec().getCalcbs())) {
                            pteStm = pteMapper.selectForPte4Fec(relObjInr, relObjTyp, line.getWrkfec().getFeeinr());
                            if (pteStm == null || pteStm.size() > 1) {
                                clcFecAmtEnd(line);
                                return;
                            } else {
                                relObjTyp = "PTE";
                                relObjInr = pteStm.get(0).getInr();
                            }
                        }
                        if ("PTE".equals(relObjTyp) && !MdaUtils.isEmpty(relObjInr)) {
                            Pte wrkPte = pteMapper.selectByInr(relObjInr);
                            if (wrkPte != null) {
                                if (MdaUtils.compareTo(line.getRol(), wrkPte.getRolpay()) != 0) {
                                    Fee wrkFee = feeMapper.selectByCod(line.getFeecod());
                                    tiramt = setmodService.getRelAmount(wrkFee.getReltir(), line.getRol(), line, setmod);
                                    tirbca = xrtmodService.amtConvTypes(setmod.getDoccur(), tiramt, "M", xrtmodService.sysiso(), "M", Dates.today());
                                    FecVo wrkFec = fecmodService.getFec(setmod.getRec(), setmod.getFecrefdat(), tirbca, line.getRolptyinr(), wrkFee);
                                    line.setWrkfec(wrkFec);
                                    line.setObjmod("FECISM\\WRKPTE");
                                } else {
                                    Fec wrkFec = new Fec();
                                    wrkFec.setObjinr(wrkPte.getInr());
                                    wrkFec.setFeeinr(wrkPte.getFeeinr());
                                    wrkFec.setObjtyp("PTE");
                                    wrkFec.setFeepri("6");
                                    // if begin date of settlement is in the future, take that date as reference date of condition, as it is stored for that date
                                    if (Dates.diff(wrkPte.getBegdat(), Dates.today()) > 0) {
                                        refFecDat = wrkPte.getBegdat();
                                    } else {
                                        refFecDat = Dates.today();
                                    }
                                    cbsRange.sdbRngRead(wrkFec, refFecDat);
                                    line.setWrkfec(DtoMapper.INSTANCE.transfer(wrkFec));
                                    line.setObjmod("FECISM\\WRKPTE");
                                }
                            }
                        }
                    }
                    break;
            }
            // additional methods to retrieve relevant object
            // a) if OBJMOD is set, it is the relevant contract
//                IModule rejObjmod = null;
            int relconLin = 0;
            if (MdaUtils.isEmpty(relObjTyp)) {
/*TODO 涉及表外处理
                if (!MdaUtils.isEmpty(line.getObjmod())) {
                    rejObjmod = MdaDriver.getModule(this, line.getObjmod());
                    relObjtyp = Utils.recGetObj(rejObjmod);
                    relObjinr = Utils.recGetInr(rejObjmod);
                }
*/
                // b) if not, try to retrieve it form Pool/INFDETSTM (DO5WLB.001302)
                if (MdaUtils.isEmpty(relObjTyp)) {
                    relconLin = Streams.streamSearch(line.getInfdetstm(), "FECRELCON" + TAB);
                    if (relconLin > 0) {
                        Streams.streamClear(valstm);
                        relconInf = Strings.getLine(line.getInfdetstm(), relconLin);
                        Utils.getTabValues(valstm, relconInf + TAB);
                        relObjTyp = Strings.getLine(valstm, 2);
                        relObjInr = Strings.getLine(valstm, 3);
                    }
                }
                // c) if neither OBJMOD nor INFDETSTM holds the information, use MTABUT\\REC
                if (MdaUtils.isEmpty(relObjTyp)) {
                    relObjTyp = setmod.getRec().getObjtyp();
                    relObjInr = setmod.getRec().getObjinr();
                }
            }
            oldamt = new BigDecimal("0");

            String stmlin = NULLSTR;
            String pteinr = NULLSTR;
            BigDecimal oldRelamt = null;
            String oldCur = NULLSTR;
            Date nowdat2 = null;
            Date nowdat2dft = null;
            String nowDifflg = NULLSTR;
            switch (myDifFlg) {
                case "O":
                case "W":
                    switch (line.getSrc()) {
                        case "D":
                        case "A":
                        case "O":
                        case "P":
                            int lin = Streams.streamSearch(line.getInfdetstm(), "OLDCLCAMT" + TAB);
                            if (lin > 0) {
                                stmlin = Strings.getLine(line.getInfdetstm(), lin);
                                oldamt = new BigDecimal(Strings.mid(stmlin, Strings.pos(stmlin, TAB) + 1));
                            }
                            break;
                    }
                    if (MdaUtils.compareTo(oldamt, 0) == 0) {
                        // if OBJMOD is a PTEGRP (from LIAALLG) , take mounts from that LIAALLG
/*TODO 涉及表外处理
                        if (MdaUtils.compareTo(Strings.mid(line.getObjmod(), Strings.len(line.getObjmod()) - 6, 7), "\\PTEGRP") == 0) {
                            pteinr = Datas.getContent(this, line.getObjmod() + "\\REC\\INR");
                            oldRelamt = Datas.getContentNumeric(this, liaallgPath + "\\OLDAMT");
                            oldCur = Datas.getContent(this, liaallgPath + "\\CUR");
                            if (!MdaUtils.isEmpty(olddat) && !MdaUtils.isEmpty(oldRelamt) && !MdaUtils.isEmpty(oldCur)) {
                                nowdat2 = line.getDat2();
                                nowdat2dft = line.getDat2dft();
                                nowDifflg = line.getWrkfec().getClcdifflg();
                                line.setDat2(olddat);
                                line.setDat2dft(olddat);
                                line.getWrkfec().setClcdifflg("L");
                                Argument<String> oldCurBox = new Argument<String>("", oldCur);
                                clcFecAmt(line, setmod, oldCurBox, oldRelamt, feecur);
                                oldCur = oldCurBox.value;
                                oldamt = line.getAmt();
                                line.getWrkfec().setClcdifflg(nowDifflg);
                                line.setDat2(nowdat2);
                                line.setDat2dft(nowdat2dft);
                            }
                        }
*/
                    }
                    break;
            }
            // clear calculation Details
            MdaUtils.clear(line.getInfdetstm());
            // store relevant amount in DETSTM
            Streams.streamInsert(line.getInfdetstm(), 0, "RELAMT" + TAB + relcur.value + TAB + MdaUtils.toString(clcamt));
            // Flag $rndflg to be set if a caclulation is done which requires rounding of amount
            boolean rndflg = false;
            line.setInftxt(line.getInftxt() + CR + Strings.getText("Condition from $1 used.", codetableService.getTblLabel(line.getWrkfec().getFeepri(), "FEEPRI")));
            // common calculation info if not done seperately in rule
            switch (line.getWrkfec().getCalrul()) {
                case "C":
                case "P":
                    rndflg = true;
                    break;
                default:
                    line.setInftxt(line.getInftxt() + CR + Strings.getText("Calculation of $1 units of $3 ($2).", line.getUnt(), codetableService.getTblLabel(line.getWrkfec().getCalrul(), "CALRUL"), codetableService.getTblLabel(line.getFeecod(), "FEETXT")));
            }
            String pertyp = line.getWrkfec().getPertyp();
            //                                       Special calculation for "Permille with preperiods"
            if (MdaUtils.compareTo(line.getWrkfec().getCalrul(), "P") == 0) {
                fecamt = clcFec_P(line, relcur.value, clcamt, feecur);
                // ClcFec_P converts to target currency
                feccur = feecur;
            }
            //calculate fecamt of AMEFEE
            String trntyp = NULLSTR;
            if (MdaUtils.compareTo(line.getWrkfec().getCalrul(), "M") == 0) {
                rndflg = true;
                //在非进口信用证修改交易收取修改手续费时，不需要再走计算流程，防止费用重计算错误
                trntyp = SettleContext.getTransName();
                if (MdaUtils.compareTo(trntyp, "LITAME") == 0) {
                    fecamt = clcFec_M(line, setmod, relcur.value, clcamt, feecur);
                } else {
                    Fep wrkFep = fepMapper.selectByInr(Strings.getLine(line.getFepinrlst(), 1));
                    if (wrkFep != null) {
                        fecamt = wrkFep.getAmt();
                        oldCur = wrkFep.getCur();
                        if (MdaUtils.compareTo(oldCur, feecur) != 0) {
                            fecamt = xrtmodService.amtConvTypes(oldCur, fecamt, "M", feecur, "M", Dates.today());
                        }
                    } else {
                        fecamt = BigDecimal.ZERO;
                    }
                }
                // ClcFec_P converts to target currency
                feccur = feecur;
            }
            if (MdaUtils.compareTo(line.getWrkfec().getCalrul(), "0") == 0) {
                // calculate at least one unit if not modified
                if (line.getUnt() == 0 && !line.isModified("unt")) {
                    line.setUnt(1);
                }
                fecamt = Decimals.mul(line.getUntamt(), line.getUnt());
                line.setInftxt(line.getInftxt() + CR + Strings.getText("Flat Amount $1 $2 from conditions * $3 units = $1 $4", line.getWrkfec().getRatcur(), Utils.fmtAmount(line.getUntamt(), line.getWrkfec().getRatcur()), line.getUnt(), Utils.fmtAmount(fecamt, line.getWrkfec().getRatcur())));
                feccur = feecur;
                if (MdaUtils.compareTo(line.getWrkfec().getRatcur(), feecur) != 0) {
                    rndflg = true;
                    fecamt = fecIsmAmtConv(line, line.getWrkfec().getRatcur(), fecamt, feecur, Dates.today());
                }
            }
            // BA Discount
            if (MdaUtils.compareTo(line.getWrkfec().getCalrul(), "6") == 0) {
                rndflg = true;
                fecamt = Decimals.div(Decimals.mul(Decimals.mul(clcamt, ratuse), line.getUnt()), 36500);
                line.setInftxt(line.getInftxt() + CR + Strings.getText("Calculation: amount $1 $2 * rate $3 * $4 units = $1 $5", feecur, Utils.fmtAmount(clcamt, feecur), ratuse, line.getUnt(), Utils.fmtAmount(fecamt, feecur)));
            }
            // Basepoints of amount
            if (MdaUtils.compareTo(line.getWrkfec().getCalrul(), "B") == 0) {
                rndflg = true;
                if (!line.isModified("unt")) {
                    line.setUnt(1);
                }
                fecamt = Decimals.div(Decimals.mul(Decimals.mul(clcamt, ratuse), line.getUnt()), 10000);
                line.setInftxt(line.getInftxt() + CR + Strings.getText("Calculation: amount $1 $2 * rate $3 * $4 units = $1 $5", feecur, Utils.fmtAmount(clcamt, feecur), ratuse, line.getUnt(), Utils.fmtAmount(fecamt, feecur)));
            }
            // Permille fees
            int basis = 0;
            BigDecimal lftunt = null;
            int oldper = 0;
            BigDecimal tirunt = null;
            if (MdaUtils.compareTo(line.getWrkfec().getCalrul(), "1") == 0 || MdaUtils.compareTo(line.getWrkfec().getCalrul(), "4") == 0 || MdaUtils.compareTo(line.getWrkfec().getCalrul(), "%") == 0) {
                line.setInftxt(line.getInftxt() + CR + Strings.getText("Calculation based on $1 i.e. $2 $3", codetableService.getTblLabel(line.getWrkfec().getCalcbs(), "FECAMT"), relcur.value, Utils.fmtAmount(clcamt, relcur.value)));
                if (MdaUtils.compareTo(line.getModflg(), "u") != 0) {
                    if (!MdaUtils.isEmpty(line.getWrkfec().getPertyp())) {
                        if (!(MdaUtils.compareTo(line.getWrkfec().getPertyp(), "f") == 0)) {
                            pertyp = line.getWrkfec().getPertyp();
                            line.setUnt(getNrOfPeriodsPermille(line.getDat1(), line.getDat2(), pertyp));
                            line.setInftxt(line.getInftxt() + CR + Strings.getText("Units (re)calculated from $1 to $2 = $3 $4", Formats.fmtDateShort(line.getDat1()), Formats.fmtDateShort(line.getDat2()), line.getUnt(), codetableService.getTblLabel(pertyp, "FECPER")));
                        }
                        if (line.getUnt() < line.getWrkfec().getPermin()) {
                            pertyp = line.getWrkfec().getPertyp();
                            line.setUnt(line.getWrkfec().getPermin());
                            line.setInftxt(line.getInftxt() + CR + Strings.getText("Minimum units applied: $1 $2", line.getUnt(), codetableService.getTblLabel(pertyp, "FECPER")));
                        }
                    }
                }
                if (MdaUtils.compareTo(line.getWrkfec().getCalrul(), "4") == 0 && MdaUtils.compareTo(line.getWrkfec().getLowamt(), 0) != 0) {
                    // Tiered PerMille: only apply %0 to part inside tier,
                    // MINAMT should hold sum of lower tiers
                    if (MdaUtils.compareTo(feccur, xrtmodService.sysiso()) != 0) {
                        lowamt = fecIsmAmtConv(line, xrtmodService.sysiso(), line.getWrkfec().getLowamt(), feccur, Dates.today());
                    } else {
                        lowamt = line.getWrkfec().getLowamt();
                    }
                    if (MdaUtils.compareTo(feccur, line.getWrkfec().getMincur()) != 0) {
                        minamt = fecIsmAmtConv(line, line.getWrkfec().getMincur(), line.getWrkfec().getMinamt(), feccur, Dates.today());
                    } else {
                        minamt = line.getWrkfec().getMinamt();
                    }
                    rndflg = true;
                    fecamt = Decimals.add((Decimals.div(Decimals.mul(Decimals.mul((Decimals.sub(clcamt, lowamt)), ratuse), line.getUnt()), 1000)), minamt);
                    line.setInftxt(line.getInftxt() + CR + Strings.getText("Calculation: tier portion $1 $2 * per mille $3%o * units $4", feccur, Utils.fmtAmount((Decimals.sub(clcamt, lowamt)), feccur), ratuse, line.getUnt()));
                    line.setInftxt(line.getInftxt() + CR + Strings.getText("Plus per mille calculated from lower tiers $1 $2 = $1 $3", feccur, Utils.fmtAmount((minamt), feccur), Utils.fmtAmount((fecamt), feccur)));
                } else {
                    fecamt = new BigDecimal("0");
                    basis = 1000;
                    if (MdaUtils.compareTo(line.getWrkfec().getCalrul(), "%") == 0) {
                        basis = 100;
                    }
                    lftunt = new BigDecimal(line.getUnt());
//                    if (!MdaUtils.isEmpty(fecism.getBegcnfdat()) && Dates.diff(fecism.getBegcnfdat(), line.getDat1()) < 0) {
//                        oldper = getNrOfPeriodsPermille(fecism.getBegcnfdat(), line.getDat1(), pertyp);
//                    } else {
//                        oldper = 0;
//                    }
                    oldper = 0;
                    if (MdaUtils.compareTo(Decimals.add(lftunt, oldper), line.getWrkfec().getPerbegtr5()) >= 0 && line.getWrkfec().getPerbegtr5() != 0) {
                        tirunt = Decimals.add(Decimals.sub(Decimals.add(lftunt, oldper), line.getWrkfec().getPerbegtr5()), 1);
                        tirunt = getMinimum(tirunt, lftunt);
                        fecamt = Decimals.add(fecamt, (Decimals.div(Decimals.mul(Decimals.mul(clcamt, line.getWrkfec().getPerrattr5()), tirunt), basis)));
                        line.setInftxt(line.getInftxt() + CR + Strings.getText("Tiered by period: $1 periods at $2", tirunt, line.getWrkfec().getPerrattr5()));
                        lftunt = Decimals.sub(lftunt, tirunt);
                    }
                    if (MdaUtils.compareTo(Decimals.add(lftunt, oldper), line.getWrkfec().getPerbegtr4()) >= 0 && line.getWrkfec().getPerbegtr4() != 0) {
                        tirunt = Decimals.add(Decimals.sub(Decimals.add(lftunt, oldper), line.getWrkfec().getPerbegtr4()), 1);
                        tirunt = getMinimum(tirunt, lftunt);
                        fecamt = Decimals.add(fecamt, (Decimals.div(Decimals.mul(Decimals.mul(clcamt, line.getWrkfec().getPerrattr4()), tirunt), basis)));
                        line.setInftxt(line.getInftxt() + CR + Strings.getText("Tiered by period: $1 periods at $2", tirunt, line.getWrkfec().getPerrattr4()));
                        lftunt = Decimals.sub(lftunt, tirunt);
                    }
                    if (MdaUtils.compareTo(Decimals.add(lftunt, oldper), line.getWrkfec().getPerbegtr3()) >= 0 && line.getWrkfec().getPerbegtr3() != 0) {
                        tirunt = Decimals.add(Decimals.sub(Decimals.add(lftunt, oldper), line.getWrkfec().getPerbegtr3()), 1);
                        tirunt = getMinimum(tirunt, lftunt);
                        fecamt = Decimals.add(fecamt, (Decimals.div(Decimals.mul(Decimals.mul(clcamt, line.getWrkfec().getPerrattr3()), tirunt), basis)));
                        line.setInftxt(line.getInftxt() + CR + Strings.getText("Tiered by period: $1 periods at $2", tirunt, line.getWrkfec().getPerrattr3()));
                        lftunt = Decimals.sub(lftunt, tirunt);
                    }
                    if (MdaUtils.compareTo(Decimals.add(lftunt, oldper), line.getWrkfec().getPerbegtr2()) >= 0 && line.getWrkfec().getPerbegtr2() != 0) {
                        tirunt = Decimals.add(Decimals.sub(Decimals.add(lftunt, oldper), line.getWrkfec().getPerbegtr2()), 1);
                        tirunt = getMinimum(tirunt, lftunt);
                        fecamt = Decimals.add(fecamt, (Decimals.div(Decimals.mul(Decimals.mul(clcamt, line.getWrkfec().getPerrattr2()), tirunt), basis)));
                        line.setInftxt(line.getInftxt() + CR + Strings.getText("Tiered by period: $1 periods at $2", tirunt, line.getWrkfec().getPerrattr2()));
                        lftunt = Decimals.sub(lftunt, tirunt);
                        line.setInftxt(line.getInftxt() + CR + Strings.getText("Tiered by period: $1 periods at $2", lftunt, ratuse));
                    }
                    rndflg = true;
                    fecamt = Decimals.add(fecamt, (Decimals.div(Decimals.mul(Decimals.mul(clcamt, ratuse), lftunt), basis)));
                }
            }
            //                                       Interest p.a. 365
            BigDecimal minFeecurAmt = null;
            BigDecimal maxFeecurAmt = null;
            if (MdaUtils.compareTo(line.getWrkfec().getCalrul(), "2") == 0) {
                if (MdaUtils.isEmpty(line.getDat1()) || MdaUtils.isEmpty(line.getDat2())) {
                    line.setErrcod(0);
                    line.setErrtxt(NULLSTR);
                } else {
                    feccur = feecur;
                    setIrtcalBasics(line, irtcal, feccur, line.getDat1(), line.getDat1(), line.getDat2(), false);
                    irtcalService.modifyPrincipal(irtcal, line.getDat1(), clcamt, relcur.value);
                    minFeecurAmt = fecIsmAmtConv(line, line.getWrkfec().getMincur(), line.getWrkfec().getMinamt(), feecur, Dates.today());
                    maxFeecurAmt = fecIsmAmtConv(line, line.getWrkfec().getMaxcur(), line.getWrkfec().getMaxamt(), feecur, Dates.today());
                    try {
                        fecamt = irtcalService.calcAllCur(irtcal, feecur, minFeecurAmt, maxFeecurAmt);
                        feccur = feecur;
                        rndflg = true;
                        line.setErrcod(0);
                        line.setErrtxt(NULLSTR);
                        line.setUnt(0);
                        for (int i = 0; i < irtcal.getIrdele().size(); i++) {
                            line.setUnt(Decimals.add(line.getUnt(), irtcal.getIrdele().get(i).getUni()).intValue());
                        }
                    } catch (Exception e) {
                        line.setErrcod(CommonContants.GENERRCOD);
                        line.setErrtxt(e.getMessage());
                    }
                    line.setInftxt(line.getInftxt() + CR + Strings.getText("$1% Interest per annum on $2 for $3 periods (days)", ratuse, Utils.fmtAmount(clcamt, feccur), line.getUnt()));
                }
            } else {
                line.setErrcod(0);
                line.setErrtxt(NULLSTR);
            }
            //                                       Interest from CBS
            if (MdaUtils.compareTo(line.getWrkfec().getCalrul(), "C") == 0) {
                fecamt = clcFecIrtcal(line, irtcal, setmod, feccur, clcamt, feecur, setmod.getRefdat());
                rndflg = true;
                // ClcFecIrtcal converts to target currency
                feccur = feecur;
            }
            //                                       Monthly Interest
            //    You may divide by following numbers to reflect different types of calculation depending on needs of organization:
            //    100 : Interest rate is in % and also per month. This is used in standard
            //   1200 : Interest rate is in %, but a yearly rate
            //   1000 : Interest rate is in permille and also per month.
            //  10000 : Interest rate is in base points and also per month.
            //  12000 : Interest rate is in permille, but a yearly rate
            if (MdaUtils.compareTo(line.getWrkfec().getCalrul(), "3") == 0) {
                rndflg = true;
                fecamt = Decimals.div(Decimals.mul(Decimals.mul(clcamt, ratuse), line.getUnt()), 100);
                line.setInftxt(line.getInftxt() + CR + Strings.getText("$1% monthly interest on $2 for $3 months", ratuse, Utils.fmtAmount(clcamt, feccur), line.getUnt()));
            }
            //					Trimestral Prorrateado
            if (MdaUtils.compareTo(line.getWrkfec().getCalrul(), "7") == 0) {
                rndflg = true;
                fecamt = (Decimals.div(Decimals.mul(Decimals.mul(clcamt, ratuse), line.getUnt()), 3000));
                line.setInftxt(line.getInftxt() + CR + Strings.getText("$1% per quarter on $2 for $3 quarters", ratuse, Utils.fmtAmount(clcamt, feccur), line.getUnt()));
            }
            //					Trimestral
            if (MdaUtils.compareTo(line.getWrkfec().getCalrul(), "8") == 0) {
                rndflg = true;
                fecamt = (Decimals.div(Decimals.mul(Decimals.mul(clcamt, ratuse), line.getUnt()), 1000));
                line.setInftxt(line.getInftxt() + CR + Strings.getText("$1% on $2 for $3 quarters", ratuse, Utils.fmtAmount(clcamt, feccur), line.getUnt()));
            }
            // Insert installation specific fee calculation rules here
            // The amount has to be calulated in $fecamt
            // If fee is not in fee currency, convert into setfel fee curreny
            BigDecimal newamt = null;
            if (MdaUtils.compareTo(feccur, feecur) != 0) {
                rndflg = true;
                //费用牌价取中间价
                newamt = fecIsmAmtConv(line, feccur, fecamt, feecur, Dates.today());
                fecamt = newamt;
            }
            /**
             // Round result, if requiered, otherwise just move
             if $rndflg then
             line\\amt = AmtRndUnt( $fecamt, feecur )
             else
             line\\amt = $fecamt
             endif
             **/
            //由于优惠功能的需要，上面屏蔽的代码做如下修改
            // Round result, if requiered, otherwise just move
            if (rndflg) {
                line.setOgiamt(xrtmodService.amtRndUnt(fecamt, feecur));
                line.setAmt(xrtmodService.amtRndUnt(Decimals.sub(fecamt, line.getDctamt()), feecur));
            } else {
                line.setOgiamt(fecamt);
                line.setAmt(Decimals.sub(fecamt, line.getDctamt()));
            }
        }
        calculateFeeamount(line, setmod, minamt, oldamt, myDifFlg, relObjTyp, relObjInr);
        clcFecAmtEnd(line);
    }

    @Override
    public CalcFeeResponse clcFecAmt(CalcFeeRequest request) {
        IFeeCalculator feeCalculator = null;
        try {
            if (request.getCommCbsLines() != null && request.getCommCbsLines().size() > 0) {
                feeCalculator = SpringEnvContext.getBean("comm_FeeCalculator");
            } else {
                feeCalculator = SpringEnvContext.getBean(request.getCalrul() + "_FeeCalculator");
            }
        } catch (NoSuchBeanDefinitionException e) {
            log.warn("没有找到计算规则"+request.getCalrul()+"的实现类，系统使用老的计算费用方法");
        }
        if (feeCalculator != null) {
            return feeCalculator.calcFee(request);
        }
        return null;
    }

    private void calculateFeeamount(SetfelVo line, SetmodVo setmod, BigDecimal minamt, BigDecimal oldamt, String myDifFlg, String relObjTyp, String relObjInr) {
        // Minimum/Maximum cut ( done here to allow supporter to adapt this (eg. appliing MIN/MAX per unit or differently depending on calculation rule)
        int minmaxunits = 0;
        String mincur = NULLSTR;
        BigDecimal maxamt = null;

        // for CBS-Calculation MIN/MAX cut is done in irtcal. Add other methods handled in calulation directly to next line
        if ("C".equals(line.getWrkfec().getCalrul())) {
            // same for ClcFEC_P
        } else if ("P".equals(line.getWrkfec().getCalrul())) {
            // no min/max calculation for flat fees
        } else if ("0".equals(line.getWrkfec().getCalrul())) {
        } else {
            // To apply minium/maximum once per calculation, set $minmaxunits = 1 (this is standard now)
            // You may use $minmaxunits = line\\unt instead if minimum shall be applied for each unit, if necessary depending on line\\WRKFEC\\CALRUL
            // 2013.10 需求，有的费用需要乘以Unit
            if ("X".equals(line.getWrkfec().getAmtflg())) {
                minmaxunits = line.getUnt();
            } else {
                minmaxunits = 1;
            }
            // Minimum calculation
            line.setMinmaxflg(NULLSTR);
            mincur = line.getWrkfec().getMincur();
            if (!MdaUtils.isEmpty(mincur)) {
                if (MdaUtils.compareTo(line.getWrkfec().getMincur(), line.getCur()) != 0) {
                    minamt = xrtmodService.amtRndUnt(fecIsmAmtConv(line, line.getWrkfec().getMincur(), Decimals.mul(line.getWrkfec().getMinamt(), minmaxunits), line.getCur(), Dates.today()), line.getCur());
                    mincur = line.getCur();
                } else {
                    minamt = Decimals.mul(line.getWrkfec().getMinamt(), minmaxunits);
                }
                /**
                 if line\\amt < $minamt then
                 line\\amt = $minamt
                 **/
                //考虑优惠，上面代码作如下修改
                if (MdaUtils.compareTo(line.getOgiamt(), minamt) < 0) {
                    line.setAmt(minamt);
                    line.setOgiamt(minamt);
                    line.setMinmaxflg("<");
                    line.setInftxt(line.getInftxt() + CR + Strings.getText("Minimum $3 $4  used.", line.getCur(), Utils.fmtAmount(line.getAmt(), line.getCur()), line.getWrkfec().getMincur(), Utils.fmtAmount(line.getWrkfec().getMinamt(), line.getWrkfec().getMincur())));
                }
            }
            //                                       Maximum calculation
            if ((!MdaUtils.isEmpty(line.getWrkfec().getMaxcur())) && (!MdaUtils.isEmpty(line.getWrkfec().getMaxamt()))) {
                maxamt = new BigDecimal("0");
                if (MdaUtils.compareTo(line.getWrkfec().getMaxcur(), line.getCur()) != 0) {
                    maxamt = xrtmodService.amtRndUnt(fecIsmAmtConv(line, line.getWrkfec().getMaxcur(), Decimals.mul(line.getWrkfec().getMaxamt(), minmaxunits), line.getCur(), Dates.today()), line.getCur());
                } else {
                    maxamt = Decimals.mul(line.getWrkfec().getMaxamt(), minmaxunits);
                }
                /**
                 if line\\amt > $MAXAMT then
                 line\\amt = $MAXAMT
                 **/
                //考虑优惠，上面代码作如下修改
                if (MdaUtils.compareTo(line.getOgiamt(), maxamt) > 0) {
                    line.setAmt(maxamt);
                    line.setOgiamt(maxamt);
                    line.setMinmaxflg(">");
                    line.setInftxt(line.getInftxt() + CR + Strings.getText("Maximum $3 $4 used.", line.getCur(), Utils.fmtAmount(line.getAmt(), line.getCur()), line.getWrkfec().getMaxcur(), Utils.fmtAmount(line.getWrkfec().getMaxamt(), line.getWrkfec().getMaxcur())));
                }
            }
        }

        // compare with amount for old data in case of difference calulation
        // a) in case of minimum or maximum the pool is reference instead of calculation with old data
        String refcur = NULLSTR;
        switch (line.getAllcalflg()) {
            case "L":
                refcur = line.getWrkfec().getMincur();
                break;
            case "U":
                refcur = line.getWrkfec().getMaxcur();
                break;
            default:
                refcur = "";
        }
        List<Fep> retStream = null;
        String fepsql = NULLSTR;
        BigDecimal oldFepamt = Decimals.ZERO_SCALE2;
        int stmCnt = 0;
        String vline = NULLSTR;
        BigDecimal amt = Decimals.ZERO_SCALE2;
        String cur = NULLSTR;
        String modFlg = NULLSTR;
        String wrn = "";
        if (MdaUtils.compareTo(refcur, line.getCur()) == 0 && MdaUtils.compareTo(line.getAmt(), oldamt) != 0) {
            switch (myDifFlg) {
                case "W":
                case "O":
                    retStream = fepMapper.selectByCommonConditions(relObjTyp, relObjInr, line.getFeecod(), " ", line.getSrc(), Strings.getLine(line.getFepinrlst(), 1), line.getDat1(), line.getDat2());
            }
            stmCnt = retStream.size();
            for (int i = 1; i <= stmCnt; i++) {
                amt = retStream.get(i).getAmt();
                cur = retStream.get(i).getCur();
                modFlg = retStream.get(i).getModflg();
                if (MdaUtils.compareTo(cur, line.getCur()) == 0 && (MdaUtils.isEmpty(modFlg) || MdaUtils.compareTo(modFlg, "r") == 0)) {
                    oldFepamt = oldFepamt.add(amt);
                } else {
                    wrn = Strings.getText("Fee $1 have been amended or settled in other currencies in earlier transactions.\nPlease check recalculation of fee $1 carefully.", line.getFeecod());
                    line.setInftxt(line.getInftxt() + CR + CR + wrn);
                }
            }
            if (MdaUtils.isEmpty(wrn)) {
                oldamt = oldFepamt;
            }
        }

        // compare with old amount if difference recalculation is active,...
        String oldrol = NULLSTR;
        int feecnt = 0;
        int dbFepcnt = 0;
        Argument<Integer> _dbFepcntBox = Argument.box(dbFepcnt);
        if (MdaUtils.compareTo(oldamt, BigDecimal.ZERO) > 0) {
            if (MdaUtils.compareTo(line.getSrc(), "E") == 0 && MdaUtils.compareTo(Strings.mid(line.getObjmod(), 2, 6), "LIAALL") == 0) {
/*TODO 涉及表外处理
                    oldrol = Datas.getContent(this, "\\LIAALL\\WRKPTE\\ROLPAY");
*/
            } else {
                oldrol = line.getRol();
            }
            // ... , and role has not been changed
            if (MdaUtils.compareTo(oldrol, line.getRol()) == 0) {
                switch (line.getWrkfec().getClcdifflg()) {
                    case "W":
                    case "O":
                        int lin = Streams.streamSearch(line.getInfdetstm(), "OLDCLCAMT" + TAB);
                        if (lin == 0) {
                            Streams.streamInsert(line.getInfdetstm(), 0, "OLDCLCAMT" + TAB + MdaUtils.toString(oldamt));
                        } else {
                            Streams.streamSetLine(line.getInfdetstm(), lin, "OLDCLCAMT" + TAB + MdaUtils.toString(oldamt));
                        }
                        if (MdaUtils.compareTo(oldamt, line.getAmt()) > 0 && MdaUtils.compareTo(line.getWrkfec().getClcdifflg(), "O") == 0) {
                            line.setInftxt(line.getInftxt() + CR + Strings.getText("No additional charge as $1 $2 are calulated for old data.", line.getCur(), Utils.fmtAmount(oldamt, line.getCur())));
                            line.setAmt(new BigDecimal("0"));
                        } else {
                            if (MdaUtils.compareTo(line.getSrc(), "E") == 0 && MdaUtils.compareTo(Strings.mid(line.getObjmod(), 2, 6), "LIAALL") == 0) {
/*TODO 涉及表外处理
                                    oldrol = Datas.getContent(this, "\\LIAALL\\WRKPTE\\ROLPAY");
*/
                                if (MdaUtils.compareTo(oldrol, line.getRol()) == 0) {
                                    Streams.streamInsert(line.getInfdetstm(), lin, "OLDRED" + TAB + MdaUtils.toString(oldamt));
                                }
                            }
                            line.setAmt(Decimals.sub(line.getAmt(), oldamt));
                            if (MdaUtils.compareTo(line.getAmt(), 0) < 0) {
                                line.setInftxt(line.getInftxt() + CR + Strings.getText("reduced by result for old data : $1 $3\\nNow to be refunded: $1 $2", line.getCur(), Utils.fmtAmount(line.getAmt().negate(), line.getCur()), Utils.fmtAmount(oldamt, line.getCur())));
                                Streams.streamInsert(line.getInfdetstm(), lin, "OLDREP" + TAB + MdaUtils.toString(oldamt));
                            } else {
                                if (MdaUtils.compareTo(line.getAmt(), 0) > 0) {
                                    line.setInftxt(line.getInftxt() + CR + Strings.getText("reduced by result for old data : $1 $3\\nNow to be charged: $1 $2", line.getCur(), Utils.fmtAmount(line.getAmt(), line.getCur()), Utils.fmtAmount(oldamt, line.getCur())));
                                } else {
                                    line.setInftxt(line.getInftxt() + CR + "No additional charge as same result for old and new data");
                                }
                            }
                        }
                        if (MdaUtils.compareTo(line.getAmt(), 0) > 0) {
                            // Minimum calculation (wenn bisher keine Gebühr an die Rolle)
                            feecnt = 0;
                            for (int i = 0; i < setmod.getSetfeg().getSetfel().size(); i++) {
                                if (MdaUtils.compareTo(setmod.getSetfeg().getSetfel().get(i).getRol(), line.getRol()) == 0 && MdaUtils.compareTo(setmod.getSetfeg().getSetfel().get(i).getFeecod(), line.getFeecod()) == 0) {
                                    feecnt = feecnt + 1;
                                }
                            }
                            dbFepcnt = 0;
                            if (feecnt < 2) {
                                List<Fep> fepList = fepMapper.selectByCommonConditions(relObjTyp, relObjInr, line.getFeecod(), " ", line.getSrc(), Strings.getLine(line.getFepinrlst(), 1), line.getDat1(), line.getDat2());
                                feecnt = feecnt + fepList.size();
                            }
                            if (feecnt < 2) {
                                line.setMinmaxflg(NULLSTR);
                                mincur = line.getWrkfec().getMincur();
                                if (!MdaUtils.isEmpty(mincur)) {
                                    if (MdaUtils.compareTo(line.getWrkfec().getMincur(), line.getCur()) != 0) {
                                        minamt = xrtmodService.amtRndUnt(fecIsmAmtConv(line, line.getWrkfec().getMincur(), Decimals.mul(line.getWrkfec().getMinamt(), minmaxunits), line.getCur(), Dates.today()), line.getCur());
                                        mincur = line.getCur();
                                    } else {
                                        minamt = line.getWrkfec().getMinamt();
                                    }
                                    if (MdaUtils.compareTo(line.getAmt(), minamt) < 0) {
                                        line.setAmt(minamt);
                                        line.setMinmaxflg("<");
                                        line.setInftxt(line.getInftxt() + CR + Strings.getText("Minimum $3 $4  used.", line.getCur(), Utils.fmtAmount(line.getAmt(), line.getCur()), line.getWrkfec().getMincur(), Utils.fmtAmount(line.getWrkfec().getMinamt(), line.getWrkfec().getMincur())));
                                    }
                                }
                            }
                        }
                }
            }
        }
    }

    private void clcFecAmtEnd(SetfelVo line) {
        /*TODO
        Dynamic.sysWarningSet(SYSWRNTypeWarning, wrn, "ClcFeacmt" + MdaDriver.getModuleInfo(line, tdModuleInfoFullName));
        */
        // syndication comission is a payment to party
        if (Streams.streamSearch(line.getFepinrlst(), "TRNSYN") > 0) {
            line.setAmt(line.getAmt().negate());
        }
    }

    /**
     * source:fecism.0009.script
     */
    @Override
    @JsonIgnore
    public void setIrtcal(SetfelVo line, SetmodVo setmod, IrtcalVo irtcal, String relcur, Date argmindat, Date begdat, Date enddat, boolean wthChgFlg) {
        //! Set IRTCAL according to information in setfel line line incl. CBS-Amounts
        //! relcur   : relevant currency fo IRT access
        //! Argmindat: First startdate of commission in any settlent. if not set, begdat is assumed to be the start date (as no settlement has been executed before)
        // set basics
        setIrtcalBasics(line, irtcal, relcur, argmindat, begdat, enddat, wthChgFlg);
        // set CBS-Amounts
//        IModule mod = null;
        String cbc = NULLSTR;
        String extid = NULLSTR;
        String pstCmd = NULLSTR;
        IStream cbsAmtCon = new StreamImpl();
        int cbsAmtConCnt = 0;
        String lin = NULLSTR;
        int posTab = 0;
        String cacmod = NULLSTR;
        String cackey = NULLSTR;
//        IModule setcbsmod = null;
        Date modifydat = null;
        if (!MdaUtils.isEmpty(line.getRol())) {
            if (MdaUtils.compareTo(line.getBasamtin(), 0) != 0) {
                irtcalService.modifyPrincipal(irtcal, line.getDat1(), line.getBasamtin(), relcur);
            } else {
/*TODO 涉及表外处理
                mod = MdaDriver.getModule(this, line.getObjmod());
                if (ctx.getErrorCode() > 0) {
                    mod = ctx.absGet(Mtabut.class).getRec();
                }
                if (MdaUtils.compareTo(line.getWrkfec().getCalcbs(), "LIAAMT") == 0 || MdaUtils.compareTo(Utils.recGetObj(mod), "PTE") == 0) {
*/
                if (MdaUtils.compareTo(line.getWrkfec().getCalcbs(), "LIAAMT") == 0) {
                    cbc = "LIASUM";
                    extid = "AMT1";
                } else {
                    cbc = "OPN";
                    extid = "AMT1";
                }
/*TODO 涉及表外处理
                if (!Utils.recIsEmptyInr(mod)) {
                    pstCmd = "\\LIAALL.IrtCalSetPrincipal(\"" + line.getObjmod() + "\" , \"" + cbc + "\" , \"" + extid + "\" )";
                    try {
                        Modules.invokeExpress(this, pstCmd, -1);
                    } catch (Exception e) {
                        irtcal.setPrincipalCBB(cbc, extid, Utils.recGetObj(mod), Utils.recGetInr(mod));
                    }
                }
*/
                if (wthChgFlg) {
                    Streams.streamSet(cbsAmtCon, setmod.getSetfeg().getWrkcbsamtcon());
                    cbsAmtConCnt = Streams.streamCount(cbsAmtCon);
                    for (int i = 1; i <= cbsAmtConCnt; i++) {
                        lin = Strings.getLine(cbsAmtCon, i);
                        posTab = Strings.pos(lin, TAB);
                        if (posTab > 0) {
                            cacmod = Strings.mid(lin, 1, posTab - 1);
                            cackey = Strings.mid(lin, posTab + 1);
                            cackey = Strings.mid(cackey, 1, Strings.pos(cackey, CR) - 1);
/*TODO
                            Caches.cacheRead(setmod.getSetfeg().getSetcbs(), cacmod, cackey);
                            if (setmod.getSetfeg().getSetcbs() != null) {
                                setcbsmod = MdaDriver.getModule(this, setmodVo.getSetfegVo().getSetcbs().getObjmod());
                                if (setcbsmod != null) {
                                    if (MdaUtils.compareTo(Utils.recGetPath(setcbsmod), Utils.recGetPath(mod)) == 0 && MdaUtils.compareTo(extid, setmod.getSetfeg().getSetcbs().getExtid()) == 0 && MdaUtils.compareTo(cbc, setmod.getSetfeg().getSetcbs().getCbc()) == 0) {
                                        // use value date if set
                                        if (!MdaUtils.isEmpty(setmod.getSetfeg().getSetcbs().getBegdat())) {
                                            modifydat = setmod.getSetfeg().getSetcbs().getBegdat();
                                        } else {
                                            if (MdaUtils.isEmpty(setmod.getRefdat())) {
                                                modifydat = Dates.today();
                                            } else {
                                                modifydat = setmod.getRefdat();
                                            }
                                        }
                                        irtcalService.modifyPrincipal(irtcal, modifydat, setmod.getSetfeg().getSetcbs().getAmt(), setmod.getSetfeg().getSetcbs().getCur());
                                    }
                                }
                            }
*/
                        }
                    }
                }
            }
        }
        // use tiered rates only if rate was not modified
        BigDecimal amtbegtr = null;
        Date reldat = null;
        if (!(MdaUtils.compareTo(line.getModflg(), "x") == 0)) {
            if (MdaUtils.isEmpty(line.getWrkfec().getTirtyp())) {
                if (!MdaUtils.isEmpty(line.getWrkfec().getAmtbegtr4())) {
                    amtbegtr = xrtmodService.amtConvTypes(line.getWrkfec().getMincur(), line.getWrkfec().getAmtbegtr4(), "M", relcur, "M", Dates.today());
                    irtcalService.setTierMargin(irtcal, "4", amtbegtr, line.getWrkfec().getAmtrattr4());
                }
                if (!MdaUtils.isEmpty(line.getWrkfec().getAmtbegtr3())) {
                    amtbegtr = xrtmodService.amtConvTypes(line.getWrkfec().getMincur(), line.getWrkfec().getAmtbegtr3(), "M", relcur, "M", Dates.today());
                    irtcalService.setTierMargin(irtcal, "3", amtbegtr, line.getWrkfec().getAmtrattr3());
                }
                if (!MdaUtils.isEmpty(line.getWrkfec().getAmtbegtr2())) {
                    amtbegtr = xrtmodService.amtConvTypes(line.getWrkfec().getMincur(), line.getWrkfec().getAmtbegtr2(), "M", relcur, "M", Dates.today());
                    irtcalService.setTierMargin(irtcal, "2", amtbegtr, line.getWrkfec().getAmtrattr2());
                }
            } else {
                if (line.getWrkfec().getPerbegtr2() > 0) {
                    reldat = begdat;
                    for (int i = 1; i <= line.getWrkfec().getPerbegtr2(); i++) {
                        reldat = irtcalService.getNextPeriod(line.getDatrefbeg(), reldat, line.getWrkfec().getTirtyp());
                    }
                    irtcalService.modifyMargin(irtcal, reldat, line.getWrkfec().getPerrattr2());
                    if (line.getWrkfec().getPerbegtr3() > line.getWrkfec().getPerbegtr2()) {
                        for (int i = line.getWrkfec().getPerbegtr2() + 1; i <= line.getWrkfec().getPerbegtr3(); i++) {
                            reldat = irtcalService.getNextPeriod(line.getDatrefbeg(), reldat, line.getWrkfec().getTirtyp());
                        }
                        irtcalService.modifyMargin(irtcal, reldat, line.getWrkfec().getPerrattr3());
                    }
                }
            }
        }
    }

    /**
     * source:fecism.0010.script
     */
    @Override
    @JsonIgnore
    public void setIrtcalBasics(SetfelVo line, IrtcalVo irtcal, String relcur, Date argmindat, Date begdat, Date enddat, boolean wthChgFlg) {
        //! Set IRTCAL according to information in setfel line line without amounts
        //! relcur   : relevant currency fo IRT access
        //! Argmindat: First startdate of commission in any settlent. if not set, begdat is assumed to be the start date (as no settlement has been executed before)
        Date mindat = null;
        if (MdaUtils.isEmpty(argmindat)) {
            mindat = begdat;
        } else {
            mindat = argmindat;
        }
        //! BEGDAT : Start date of this calculation
        //! ENDDAT : end date of this calculation
        //! WthChgFlg : If true, amount changes in this transaction are used
        irtcalService.initialize(irtcal);
        // !! the following is only true if closed intervals are to be shown!!)
        // oherwise use IRTCAL.SetPeriod( begdat, DateAdd( enddat, 0 ) )
        irtcalService.setPeriod(irtcal, begdat, Dates.add(enddat, 0));
        String err = NULLSTR;
        if (MdaUtils.isEmpty(line.getWrkfec().getRatirs())) {
            irtcalService.setMCC(irtcal, "M30");
            irtcalService.setMIC(irtcal, "BPQ");
        } else {
            try {
                irtcalService.setIRT(irtcal, line.getWrkfec().getRatirs(), relcur);
                if (MdaUtils.isEmpty(irtcal.getIrt().getInr())) {
                    try {
                        irtcalService.setIRT(irtcal, line.getWrkfec().getRatirs(), xrtmodService.sysiso());
                    } catch (Exception ex) {
                        err = Strings.getText("$1 B.P. interest on balance $2 cannot be calulated: Reference rate $1 not found", line.getWrkfec().getRatirs());
                        line.setInftxt(line.getInftxt() + CR + err);
                        line.setErrcod(CommonContants.GENERRCOD);
                        line.setErrtxt(err);
                    }
                } else {
                    irtcalService.setRateIRS(irtcal, "IRT", irtcal.getIrt().getInr(), irtcal.getIrt().getCur());
                }
            } catch (Exception e) {
                try {
                    irtcalService.setIRT(irtcal, line.getWrkfec().getRatirs(), xrtmodService.sysiso());
                } catch (Exception ex) {
                    err = Strings.getText("$1 B.P. interest on balance $2 cannot be calulated: Reference rate $1 not found", line.getWrkfec().getRatirs());
                    line.setInftxt(line.getInftxt() + CR + err);
                    line.setErrcod(CommonContants.GENERRCOD);
                    line.setErrtxt(err);
                }
            }
        }
        // MDVIE 990163 For BPQ and BPY use max amount for IRD->IRP
        switch (irtcal.getIrt().getIrtmic()) {
            case "BPY":
            case "BPQ":
                irtcal.setFulperflg("M");
                break;
            default:
                irtcal.setFulperflg(NULLSTR);
        }
        if (Strings.len(line.getWrkfec().getPertyp()) == 6) {
            irtcalService.setPeriods(irtcal, mindat, line.getWrkfec().getPertyp());
        }
        irtcalService.setMargin(irtcal, line.getRatcal());
        // Min/Max-set done in IrtCalCur
    }

    /**
     * source:fecism.0011.script
     */
    public void getPertxtAndType(FecVo fecuse, IStream detstm, Argument<String> ratetype, Argument<String> pertyptxt) {
        //! gets description of periodtype (eq quarter) and rate type (e.g. "%") of calualation used in fec/detstm in language uil
        String vpertyptxt = CommonContants.PERMET_CODES.get(fecuse.getPertyp());
        int end = Strings.pos(vpertyptxt, " ");
        if (end > 0) {
            vpertyptxt = Strings.mid(vpertyptxt, 1, end - 1);
        }
        int irtmiclin = Streams.streamSearch(detstm, "IRTMIC");
        String type = "";
        int maridx = 0;
        String marlin = NULLSTR;
        String irtcod = NULLSTR;
        String irttxt = NULLSTR;
        if (irtmiclin > 0) {
            switch (Strings.mid(Strings.getLine(detstm, irtmiclin), 8)) {
                case "BPY":
                case "BPQ":
                    vpertyptxt = Strings.getText("$1 $2", vpertyptxt, "or fraction thereof");
                    break;
                case "B60":
                case "B65":
                    // do not show period, if calculation pro rata temporis (901082) with rate
                    if (MdaUtils.compareTo(fecuse.getRatcal(), 0) != 0) {
                        vpertyptxt = "";
                    }
                    // do not show period if pro rata temporis or discount calculation
                    break;
                case "360":
                case "365":
                case "DFL":
                case "D65":
                    vpertyptxt = "";
                    break;
                case "F60":
                case "F65":
                    vpertyptxt = "days";
                    break;
            }
            switch (Strings.mid(Strings.getLine(detstm, irtmiclin), 8, 1)) {
                case "B":
                    type = "bps";
                    break;
                default:
                    type = "% p.a.";
            }
            maridx = Streams.streamSearch(detstm, "REFRTE");
            if (maridx > 0) {
                marlin = Strings.getLine(detstm, maridx);
                irtcod = Strings.mid(marlin, Strings.pos(marlin, ":") + 1);
                irttxt = codetableService.getTblLabel(irtcod, "IRTTXT");
                if (!MdaUtils.isEmpty(irttxt)) {
                    type = Strings.getText("$1 above $2", type, irttxt);
                }
            }
        }
        pertyptxt.value = vpertyptxt;
        ratetype.value = type;
    }

    /**
     * source:fecism.0012.script
     */
    @Override
    public BigDecimal clcFec_P(SetfelVo line, String relcur, BigDecimal relamt, String feecur) {
        //! Permille calculation with preperiods (incl. Minium calculation)
        BigDecimal fecamt = new BigDecimal("0");
        BigDecimal relFeecurAmt = null;
        if (MdaUtils.compareTo(relcur, feecur) == 0) {
            relFeecurAmt = relamt;
        } else {
            relFeecurAmt = xrtmodService.amtConvTypes(relcur, relamt, "M", feecur, "M", Dates.today());
            line.setInftxt(line.getInftxt() + CR + Strings.getText("Relevant amount $1 $2 converted to $3 $4", relcur, Utils.fmtAmount(relamt, relcur), feecur, Utils.fmtAmount(relFeecurAmt, feecur)));
        }
        int perprv = 0;
        int peruse = 0;
        BigDecimal ratuse = null;
        BigDecimal minuse = null;
        BigDecimal maxUse = null;
        BigDecimal minamt = null;
        BigDecimal maxAmt = null;
        Date adddat = null;
        BigDecimal clcamt = null;
        BigDecimal addamt = null;
        if (MdaUtils.isEmpty(line.getDat1()) || MdaUtils.isEmpty(line.getDat2())) {
            line.setInftxt(line.getInftxt() + CR + Strings.getText("Fee not yet calculated. Please set both dates."));
        } else {
            if (Dates.diff(line.getDat1(), line.getDat2()) > 0) {
                line.setInftxt(line.getInftxt() + CR + Strings.getText("Fee not yet calculated. Start date cannot be later than end date"));
            } else {
                // get number of preperiods for selected validity
                perprv = getNrOfPeriodsPermille(line.getDat1(), line.getDat2(), line.getWrkfec().getPertypprv());
                // the preperiod to caclulate is the first entry for less or equal periods
                peruse = 0;
                ratuse = new BigDecimal("0");
                minuse = new BigDecimal("0");
                maxUse = new BigDecimal("0");
                if (line.getWrkfec().getPerbegtr2() >= perprv || line.getWrkfec().getPerbegtr3() == 0) {
                    peruse = line.getWrkfec().getPerbegtr2();
                    ratuse = line.getWrkfec().getPerrattr2();
                    minuse = line.getWrkfec().getPermintr2();
                    maxUse = line.getWrkfec().getPermaxtr2();
                } else {
                    if (line.getWrkfec().getPerbegtr3() >= perprv || line.getWrkfec().getPerbegtr4() == 0) {
                        peruse = line.getWrkfec().getPerbegtr3();
                        ratuse = line.getWrkfec().getPerrattr3();
                        minuse = line.getWrkfec().getPermintr3();
                        maxUse = line.getWrkfec().getPermaxtr3();
                    } else {
                        if (line.getWrkfec().getPerbegtr4() >= perprv || line.getWrkfec().getPerbegtr5() == 0) {
                            peruse = line.getWrkfec().getPerbegtr4();
                            ratuse = line.getWrkfec().getPerrattr4();
                            minuse = line.getWrkfec().getPermintr4();
                            maxUse = line.getWrkfec().getPermaxtr4();
                        } else {
                            if (line.getWrkfec().getPerbegtr5() >= perprv || line.getWrkfec().getPerbegtr6() == 0) {
                                peruse = line.getWrkfec().getPerbegtr5();
                                ratuse = line.getWrkfec().getPerrattr5();
                                minuse = line.getWrkfec().getPermintr5();
                                maxUse = line.getWrkfec().getPermaxtr5();
                            } else {
                                if (line.getWrkfec().getPerbegtr6() >= perprv || line.getWrkfec().getPerbegtr7() == 0) {
                                    peruse = line.getWrkfec().getPerbegtr6();
                                    ratuse = line.getWrkfec().getPerrattr6();
                                    minuse = line.getWrkfec().getPermintr6();
                                    maxUse = line.getWrkfec().getPermaxtr6();
                                } else {
                                    peruse = line.getWrkfec().getPerbegtr7();
                                    ratuse = line.getWrkfec().getPerrattr7();
                                    minuse = line.getWrkfec().getPermintr7();
                                    maxUse = line.getWrkfec().getPermaxtr7();
                                }
                            }
                        }
                    }
                }
                // calculate amount if preperiod selected
                if (peruse > 0) {
                    fecamt = Decimals.div(Decimals.mul(relFeecurAmt, ratuse), 1000);
                    line.setInftxt(line.getInftxt() + CR + Strings.getText("Calculation for first $4 periods:\\n   - amount $1 $2 * rate $3 = $1 $5", feecur, Utils.fmtAmount(relFeecurAmt, feecur), ratuse, peruse, Utils.fmtAmount(fecamt, feecur)));
                    // check minimum:
                    if (MdaUtils.compareTo(line.getWrkfec().getMincur(), line.getCur()) != 0) {
                        minamt = xrtmodService.amtConvTypes(line.getWrkfec().getMincur(), minuse, "M", line.getCur(), "M", Dates.today());
                    } else {
                        minamt = minuse;
                    }
                    if (MdaUtils.compareTo(fecamt, minamt) < 0) {
                        fecamt = minamt;
                        line.setInftxt(line.getInftxt() + CR + Strings.getText("- using minimum $1 $2 instead", feecur, Utils.fmtAmount(fecamt, feecur)));
                        line.setMinmaxflg("<");
                    }
                    // check maximum:
                    if (MdaUtils.compareTo(maxUse, 0) > 0) {
                        if (MdaUtils.compareTo(line.getWrkfec().getMaxcur(), line.getCur()) != 0) {
                            maxAmt = xrtmodService.amtConvTypes(line.getWrkfec().getMaxcur(), maxUse, "M", line.getCur(), "M", Dates.today());
                        } else {
                            maxAmt = maxUse;
                        }
                        if (MdaUtils.compareTo(fecamt, maxAmt) > 0) {
                            fecamt = maxAmt;
                            line.setInftxt(line.getInftxt() + CR + Strings.getText("- using maximum $1 $2 instead", feecur, Utils.fmtAmount(fecamt, feecur)));
                            line.setMinmaxflg(">");
                        }
                    }
                }
                // if more periods to be calculated, calculate and add the additional periods.
                if (perprv <= peruse) {
                    line.setUnt(1);
                    line.setRatcal(ratuse);
                } else {
                    // calulate date the calculation of the additional period has to start.

                    if (MdaUtils.compareTo(line.getWrkfec().getPertypprv(), "d") == 0) {
                        adddat = Dates.add(line.getDat1(), peruse);
                    } else if (MdaUtils.compareTo(line.getWrkfec().getPertypprv(), "m") == 0) {
                        adddat = line.getDat1();
                        for (int i = 1; i <= peruse; i++) {
                            adddat = irtcalService.getNextPeriod(line.getDat1(), adddat, "DURMON");
                        }
                    } else if (MdaUtils.compareTo(line.getWrkfec().getPertypprv(), "q") == 0) {
                        adddat = line.getDat1();
                        for (int i = 1; i <= peruse; i++) {
                            adddat = irtcalService.getNextPeriod(line.getDat1(), adddat, "DURQTR");
                        }
                    } else {
                        fecamt = new BigDecimal("0");
                        line.setInftxt(line.getInftxt() + CR + Strings.getText("- Sorry, repeated periods cannot be calculated.\\n   Please calculate and enter fee manually"));
                        return fecamt; //   ####   goto setit
                    }
                    if (MdaUtils.compareTo(line.getWrkfec().getPertyp(), "n") == 0) {
                        fecamt = new BigDecimal("0");
                        line.setInftxt(line.getInftxt() + CR + Strings.getText("- Sorry, condition only valid for previous periods.\\n   Please calculate and enter fee amount manually"));
                        return fecamt; //   ####   goto setit
                    }
                    if (Dates.diff(adddat, line.getDat2()) >= 0) {
                        line.setUnt(1);
                    } else {
                        line.setInftxt(line.getInftxt() + CR + Strings.getText("Calculation of repeated periods ($1-$2)", adddat, line.getDat2()));
                        // get number of additional periods
                        line.setUnt(getNrOfPeriodsPermille(adddat, line.getDat2(), line.getWrkfec().getPertyp()));
                        // check if maximum of periods covered by condition is reached
                        if (line.getUnt() > line.getWrkfec().getMaxpercov() && line.getWrkfec().getMaxpercov() > 0) {
                            line.setInftxt(line.getInftxt() + CR + Strings.getText("Condition only valid for $1 repeated periods. Please enter amount manually.", line.getWrkfec().getMaxpercov()));
                            fecamt = new BigDecimal("0");
                            return fecamt; //   ####   goto setit
                        }
                        // calculate permille amount for that periods.
                        clcamt = Decimals.div(Decimals.mul(Decimals.mul(relFeecurAmt, line.getRatcal()), line.getUnt()), 1000);
                        line.setInftxt(line.getInftxt() + CR + Strings.getText("- amount $1 $2 * rate $3 * $4 units = $1 $5", feecur, Utils.fmtAmount(relFeecurAmt, feecur), line.getRatcal(), line.getUnt(), Utils.fmtAmount(clcamt, feecur)));
                        // adding base amount (if any)
                        if (!MdaUtils.isEmpty(line.getWrkfec().getBasamt())) {
                            addamt = xrtmodService.amtConvTypes(line.getWrkfec().getMincur(), line.getWrkfec().getBasamt(), "M", line.getCur(), "M", Dates.today());
                            clcamt = Decimals.add(clcamt, Decimals.mul(addamt, line.getUnt()));
                            line.setInftxt(line.getInftxt() + CR + Strings.getText("adding base amount $1 $2 $3 times", feecur, Utils.fmtAmount(addamt, feecur), line.getUnt()));
                        }
                        // check maximum (if per period)
                        if (line.getWrkfec().getMaxpercnt() > 0 && MdaUtils.compareTo(line.getWrkfec().getMaxamt(), 0) > 0) {
                            if (MdaUtils.compareTo(line.getWrkfec().getMaxcur(), line.getCur()) != 0) {
                                maxAmt = xrtmodService.amtConvTypes(line.getWrkfec().getMaxcur(), line.getWrkfec().getMaxamt(), "M", line.getCur(), "M", Dates.today());
                            } else {
                                maxAmt = line.getWrkfec().getMaxamt();
                            }
                            maxAmt = Decimals.mul(maxAmt, Numerics.ceil(Decimals.div(line.getUnt(), line.getWrkfec().getMaxpercnt())));
                            if (MdaUtils.compareTo(clcamt, maxAmt) > 0) {
                                clcamt = maxAmt;
                                line.setInftxt(line.getInftxt() + CR + Strings.getText("- using maximum $1 $2 instead", feecur, Utils.fmtAmount(clcamt, feecur)));
                                line.setMinmaxflg(">");
                            }
                        }
                        // check minimum (if per period)
                        if (line.getWrkfec().getMinpercnt() > 0) {
                            if (MdaUtils.compareTo(line.getWrkfec().getMincur(), line.getCur()) != 0) {
                                minamt = xrtmodService.amtConvTypes(line.getWrkfec().getMincur(), line.getWrkfec().getMinamt(), "M", line.getCur(), "M", Dates.today());
                            } else {
                                minamt = line.getWrkfec().getMinamt();
                            }
                            minamt = Decimals.mul(minamt, Numerics.ceil(Decimals.div(line.getUnt(), line.getWrkfec().getMinpercnt())));
                            if (MdaUtils.compareTo(clcamt, minamt) < 0) {
                                clcamt = minamt;
                                line.setInftxt(line.getInftxt() + CR + Strings.getText("- using minimum $1 $2 instead", feecur, Utils.fmtAmount(clcamt, feecur)));
                                line.setMinmaxflg("<");
                            }
                        }
                        // add both amounts
                        fecamt = Decimals.add(fecamt, clcamt);
                    }
                }
                // check maximum (if for total amount)
                if (line.getWrkfec().getMaxpercnt() == 0 && MdaUtils.compareTo(line.getWrkfec().getMaxamt(), 0) > 0) {
                    if (MdaUtils.compareTo(line.getWrkfec().getMaxcur(), line.getCur()) != 0) {
                        maxAmt = xrtmodService.amtConvTypes(line.getWrkfec().getMaxcur(), line.getWrkfec().getMaxamt(), "M", line.getCur(), "M", Dates.today());
                    } else {
                        maxAmt = line.getWrkfec().getMaxamt();
                    }
                    if (MdaUtils.compareTo(fecamt, maxAmt) > 0) {
                        fecamt = maxAmt;
                        line.setInftxt(line.getInftxt() + CR + Strings.getText("- using maximum $1 $2 instead", feecur, Utils.fmtAmount(fecamt, feecur)));
                        line.setMinmaxflg(">");
                    }
                }
                // check minimum (if for total amount)
                if (MdaUtils.compareTo(line.getWrkfec().getMincur(), line.getCur()) != 0) {
                    minamt = xrtmodService.amtConvTypes(line.getWrkfec().getMincur(), line.getWrkfec().getMinamt(), "M", line.getCur(), "M", Dates.today());
                } else {
                    minamt = line.getWrkfec().getMinamt();
                }
                switch (line.getWrkfec().getMinpercnt()) {
                    case 0:
                        if (MdaUtils.compareTo(fecamt, minamt) < 0) {
                            fecamt = minamt;
                            line.setInftxt(line.getInftxt() + CR + Strings.getText("- using minimum $1 $2 instead", feecur, Utils.fmtAmount(fecamt, feecur)));
                            line.setMinmaxflg("<");
                        }
                        // check minimum (if minimum ist per month ( minpercount -3)
                        break;
                    case -3:
                        perprv = getNrOfPeriodsPermille(line.getDat1(), line.getDat2(), "m");
                        minamt = Decimals.mul(perprv, minamt);
                        if (MdaUtils.compareTo(fecamt, minamt) < 0) {
                            fecamt = minamt;
                            line.setInftxt(line.getInftxt() + CR + Strings.getText("$1 $2", feecur, Utils.fmtAmount(fecamt, feecur)));
                            line.setMinmaxflg("<");
                        }
                        break;
                }
                line.setInftxt(line.getInftxt() + CR + Strings.getText("Total result: $1 $2", feecur, Utils.fmtAmount(fecamt, feecur)));
                //    ####    label setit
            }
        }
        return fecamt;
    }

    /**
     * source:fecism.0013.script
     */
    @Override
    @JsonIgnore
    public void setDat2FromPerflg(String argPerflg, String argPerTyp, Date argdat1, Argument<Date> argdat2, Date argdat2dft, Date maxdat) {
        String setperflg = argPerflg;
        // if SETPERFLG is an number,
        int quarters = 0;
        try {
            quarters = Strings.parseInt(setperflg);
            // add quarters according to conditions
            argdat2.value = argdat1;
            for (int i = 1; i <= quarters; i++) {
                argdat2.value = irtcalService.getNextPeriod(argdat1, argdat2.value, "DURQ");
            }
        } catch (Exception e) {
            switch (setperflg) {
                // support of "settlement period" - as "S" is not in codetable in all installations, avoiding .is is intentional
                case "S":
                    argdat2.value = argdat1;
                    argdat2.value = irtcalService.getNextPeriod(argdat1, argdat2.value, argPerTyp);
                    // support of "months" - as "M" is not in codetable in all installations, avoiding .is is intentional
                    break;
                case "M":
                    argdat2.value = argdat1;
                    argdat2.value = irtcalService.getNextPeriod(argdat1, argdat2.value, "DURM");
                    break;
                default:
                    // set todat to today + about 5 years, if empty or defaulted to later date
                    Date trydat = Dates.add(argdat1, 1900);
                    if (MdaUtils.isEmpty(argdat2.value) || Dates.diff(argdat2.value, trydat) > 0) {
                        argdat2.value = trydat;
                    }
            }
        }
        // if the date is after to date set by transaction use that date instead
        if ((!MdaUtils.isEmpty(argdat2dft)) && (Dates.diff(argdat2dft, argdat2.value) <= 0 || MdaUtils.compareTo(argPerflg, "E") == 0) && Dates.diff(argdat2dft, maxdat) < 0) {
            argdat2.value = argdat2dft;
        } else {
            // as we use closed intervals, go one day back if not at end of interval
            argdat2.value = Dates.add(argdat2.value, -1);
        }
    }

    /**
     * source:fecism.0014.script
     */
    @Override
    public BigDecimal fecIsmAmtConv(SetfelVo line, String fromCur, BigDecimal amt, String toCur, Date value) {
        //! Betragskonvertierung für Gebührenberchnungen
        // Wird aus FECISM-und IRTCAL-Funktionen aufgerufen
        // Gibt die Rate in line\\Inftxt aus
        //
        // bei nderungen hier oder in  ClcratSetTypes ist zu prüfen, ob das auch im Gegenstück notwendig ist.
        //
        // set rate type for document conversion
        // this is done, if one of the following is true:
        // 1. The rate type isempty
        // 2. The rate type is not modified and
        //  2a) the selected type is not free rate or
        //  2b) the overall rate type is a crossrate or automatic (in this cases even free rates are resetted).
        BigDecimal res = null;
        String slgTyp = NULLSTR;
        String buyTyp = NULLSTR;
        String fromtyp = NULLSTR;
        String totyp = NULLSTR;
        BigDecimal rat = null;
        if (MdaUtils.compareTo(amt, 0) == 0) {
            res = new BigDecimal("0");
        } else {
            slgTyp = "S";
            buyTyp = "B";
            if (MdaUtils.compareTo(amt, 0) >= 0) {
                fromtyp = slgTyp;
                totyp = buyTyp;
            } else {
                fromtyp = buyTyp;
                totyp = slgTyp;
            }
            Argument<BigDecimal> ratBox = new Argument<BigDecimal>();
            res = xrtmodService.amtConvTypesWithCrossrateForFee(fromCur, amt, fromtyp, toCur, totyp, value, ratBox);

            rat = ratBox.value;

            line.setInftxt(line.getInftxt() + CR + Strings.getText("Amount $1 $2 converted to $3 $4 at rate $5", fromCur, Utils.fmtAmount(amt, fromCur), toCur, Utils.fmtAmount(res, toCur), Numerics.round(rat, 6)));
        }
        return res;
    }

    @Override
    public BigDecimal fecIsmAmtConv(Argument<String> inftxt, String fromCur, BigDecimal amt, String toCur, Date value) {
        //! Betragskonvertierung für Gebührenberchnungen
        // Wird aus FECISM-und IRTCAL-Funktionen aufgerufen
        // Gibt die Rate in line\\Inftxt aus
        //
        // bei nderungen hier oder in  ClcratSetTypes ist zu prüfen, ob das auch im Gegenstück notwendig ist.
        //
        // set rate type for document conversion
        // this is done, if one of the following is true:
        // 1. The rate type isempty
        // 2. The rate type is not modified and
        //  2a) the selected type is not free rate or
        //  2b) the overall rate type is a crossrate or automatic (in this cases even free rates are resetted).
        BigDecimal res = null;
        String slgTyp = NULLSTR;
        String buyTyp = NULLSTR;
        String fromtyp = NULLSTR;
        String totyp = NULLSTR;
        BigDecimal rat = null;
        if (MdaUtils.compareTo(amt, 0) == 0) {
            res = new BigDecimal("0");
        } else {
            slgTyp = "S";
            buyTyp = "B";
            if (MdaUtils.compareTo(amt, 0) >= 0) {
                fromtyp = slgTyp;
                totyp = buyTyp;
            } else {
                fromtyp = buyTyp;
                totyp = slgTyp;
            }
            Argument<BigDecimal> ratBox = new Argument<BigDecimal>();
            res = xrtmodService.amtConvTypesWithCrossrateForFee(fromCur, amt, fromtyp, toCur, totyp, value, ratBox);

            rat = ratBox.value;

            //inftxt.value = Strings.getText("Amount $1 $2 converted to $3 $4 at rate $5", fromCur, Utils.fmtAmount(amt, fromCur), toCur, Utils.fmtAmount(res, toCur), Numerics.round(rat, 6));
            //line.setInftxt(line.getInftxt() + CR + Strings.getText("Amount $1 $2 converted to $3 $4 at rate $5", fromCur, Utils.fmtAmount(amt, fromCur), toCur, Utils.fmtAmount(res, toCur), Numerics.round(rat, 6)));
            inftxt.value = Strings.getText("收取$1, 基于牌价$2, 实收$3 $4", toCur, Numerics.round(rat, 6), toCur, Utils.fmtAmount(res, toCur));
        }
        return res;
    }
    /**
     * source:fecism.0015.script
     */
    @Override
    public BigDecimal clcFec_M(SetfelVo line, SetmodVo setmod, String relcur, BigDecimal relamt, String feecur) {
        //! 计算信用证修改手续费费用金额
        //!================================================
        //!参数：  line    -  要计算的费用行
        //!        relcur  -  基准币种
        //!        relamt  -  基准金额
        //!        feecur  -  费用币种
        //!返回值：费用金额
        //!================================================
        //手续费分两部分：
        //1.增额金额按照当前期限算出增额部分；
        //2.展期情况下，要按信用证余额和展期后多出的期限计算
        BigDecimal fecamt = new BigDecimal("0");
        String addamtflg = "";
        BigDecimal oldopnamt = Decimals.sub(setmod.getOpnamt(), relamt);
        //1.信用证余额与增额
        BigDecimal relFeecurAmt = null;
        BigDecimal opnAmt = null;
        if (MdaUtils.compareTo(relcur, feecur) == 0) {
            relFeecurAmt = relamt;
            opnAmt = oldopnamt;
        } else {
            relFeecurAmt = xrtmodService.amtConvTypes(relcur, relamt, "M", feecur, "M", Dates.today());
            opnAmt = xrtmodService.amtConvTypes(relcur, oldopnamt, "M", feecur, "M", Dates.today());
            if (MdaUtils.compareTo(relFeecurAmt, 0) > 0) {
                line.setInftxt(line.getInftxt() + CR + Strings.getText("增额 $1 $2 converted to $3 $4", relcur, Utils.fmtAmount(relamt, relcur), feecur, Utils.fmtAmount(relFeecurAmt, feecur)));
            }
            if (MdaUtils.compareTo(opnAmt, 0) > 0) {
                line.setInftxt(line.getInftxt() + CR + Strings.getText("信用证余额 $1 $2 converted to $3 $4", relcur, Utils.fmtAmount(oldopnamt, relcur), feecur, Utils.fmtAmount(opnAmt, feecur)));
            }
        }
        //2.基础费率
        BigDecimal ratuse = line.getRatcal();
        String pertyp = line.getWrkfec().getPertyp();
        //3.展期增加区间与原计算区间
        int perprv = 0;
        int oldperprv = 0;
        int addprv = 0;
        BigDecimal fecamt1 = null;
        BigDecimal fecamt2 = null;
        int basis = 0;
        BigDecimal lftunt = null;
        int oldper = 0;
        BigDecimal tirunt = null;
        BigDecimal amtp5 = null;
        BigDecimal amtp4 = null;
        BigDecimal amtp3 = null;
        BigDecimal amtp2 = null;
        boolean rndflg = false;
        BigDecimal amtp1 = null;
        int addpernum = 0;
        BigDecimal amtq5 = null;
        BigDecimal amtq4 = null;
        BigDecimal amtq3 = null;
        BigDecimal amtq2 = null;
        BigDecimal amtq1 = null;
        BigDecimal minamt = null;
        BigDecimal maxAmt = null;
        if (MdaUtils.isEmpty(line.getDat1()) || MdaUtils.isEmpty(line.getDat2())) {
            line.setInftxt(line.getInftxt() + CR + Strings.getText("Fee not yet calculated. Please set both dates."));
        } else {
            if (Dates.diff(line.getDat1(), line.getDat2()) > 0) {
                line.setInftxt(line.getInftxt() + CR + Strings.getText("Fee not yet calculated. Start date cannot be later than end date"));
            } else {
                // get number of preperiods for selected validity
                perprv = getNrOfPeriodsPermille(line.getDat1(), line.getDat2(), pertyp);
                oldperprv = getNrOfPeriodsPermille(line.getDat1(), line.getOldmatdat(), pertyp);
                addprv = getNrOfPeriodsPermille(Dates.today(), line.getDat2(), pertyp);
                fecamt1 = new BigDecimal("0");
                fecamt2 = new BigDecimal("0");
                if (oldperprv < 1) {
                    //开证手续费最小期限为1
                    oldperprv = 1;
                }
                //计算增额部分费用
                if (MdaUtils.compareTo(relFeecurAmt, 0) != 0) {
                    addamtflg = "X";
                    line.setInftxt(line.getInftxt() + CR + Strings.getText("增额部分基于 $1 i.e. $2 $3 ($4 $5)", codetableService.getTblLabel(line.getWrkfec().getCalcbs(), "FECAMT"), relcur, Utils.fmtAmount(relamt, relcur), feecur, Utils.fmtAmount(relFeecurAmt, feecur)));
                    //line\\INFTXT = line\\INFTXT + CR + GetText( 'RG000232
                    if (MdaUtils.compareTo(line.getModflg(), "u") != 0) {
                        if (!MdaUtils.isEmpty(pertyp)) {
                            if (!(MdaUtils.compareTo(pertyp, "f") == 0)) {
                                line.setUnt(perprv);
                                line.setInftxt(line.getInftxt() + CR + Strings.getText("Units (re)calculated from $1 to $2 = $3 $4", Formats.fmtDateShort(Dates.today()), Formats.fmtDateShort(line.getDat2()), addprv, codetableService.getTblLabel(pertyp, "FECPER")));
                            }
                            if (addprv < line.getWrkfec().getPermin()) {
                                addprv = line.getWrkfec().getPermin();
                                line.setInftxt(line.getInftxt() + CR + Strings.getText("Minimum units applied: $1 $2", addprv, codetableService.getTblLabel(pertyp, "FECPER")));
                                ;
                            }
                        }
                    }
                    fecamt1 = new BigDecimal("0");
                    basis = 1000;
                    lftunt = new BigDecimal(addprv);
/*
                    if (!MdaUtils.isEmpty(fecism.getBegcnfdat()) && Dates.diff(fecism.getBegcnfdat(), line.getDat1()) < 0) {
                        oldper = getNrOfPeriodsPermille(fecism.getBegcnfdat(), line.getDat1(), pertyp);
                    } else {
                        oldper = 0;
                    }
*/
                    oldper = 0;
                    if (MdaUtils.compareTo(Decimals.add(lftunt, oldper), line.getWrkfec().getPerbegtr5()) >= 0 && line.getWrkfec().getPerbegtr5() != 0) {
                        tirunt = Decimals.add(Decimals.sub(Decimals.add(lftunt, oldper), line.getWrkfec().getPerbegtr5()), 1);
                        tirunt = getMinimum(tirunt, lftunt);
                        amtp5 = Decimals.div(Decimals.mul(Decimals.mul(relFeecurAmt, line.getWrkfec().getPerrattr5()), tirunt), basis);
                        fecamt1 = Decimals.add(fecamt1, amtp5);
                        line.setInftxt(line.getInftxt() + CR + Strings.getText("Tiered by period: $1 periods at $2,Calculated=$3$4", tirunt, line.getWrkfec().getPerrattr5(), feecur, Utils.fmtAmount(amtp5, feecur)));
                        lftunt = Decimals.sub(lftunt, tirunt);
                    }
                    if (MdaUtils.compareTo(Decimals.add(lftunt, oldper), line.getWrkfec().getPerbegtr4()) >= 0 && line.getWrkfec().getPerbegtr4() != 0) {
                        tirunt = Decimals.add(Decimals.sub(Decimals.add(lftunt, oldper), line.getWrkfec().getPerbegtr4()), 1);
                        tirunt = getMinimum(tirunt, lftunt);
                        amtp4 = Decimals.div(Decimals.mul(Decimals.mul(relFeecurAmt, line.getWrkfec().getPerrattr4()), tirunt), basis);
                        fecamt1 = Decimals.add(fecamt1, amtp4);
                        line.setInftxt(line.getInftxt() + CR + Strings.getText("Tiered by period: $1 periods at $2,Calculated=$3$4", tirunt, line.getWrkfec().getPerrattr4(), feecur, Utils.fmtAmount(amtp4, feecur)));
                        lftunt = Decimals.sub(lftunt, tirunt);
                    }
                    if (MdaUtils.compareTo(Decimals.add(lftunt, oldper), line.getWrkfec().getPerbegtr3()) >= 0 && line.getWrkfec().getPerbegtr3() != 0) {
                        tirunt = Decimals.add(Decimals.sub(Decimals.add(lftunt, oldper), line.getWrkfec().getPerbegtr3()), 1);
                        tirunt = getMinimum(tirunt, lftunt);
                        amtp3 = Decimals.div(Decimals.mul(Decimals.mul(relFeecurAmt, line.getWrkfec().getPerrattr3()), tirunt), basis);
                        fecamt1 = Decimals.add(fecamt1, amtp3);
                        line.setInftxt(line.getInftxt() + CR + Strings.getText("Tiered by period: $1 periods at $2,Calculated=$3$4", tirunt, line.getWrkfec().getPerrattr3(), feecur, Utils.fmtAmount(amtp3, feecur)));
                        lftunt = Decimals.sub(lftunt, tirunt);
                    }
                    if (MdaUtils.compareTo(Decimals.add(lftunt, oldper), line.getWrkfec().getPerbegtr2()) >= 0 && line.getWrkfec().getPerbegtr2() != 0) {
                        tirunt = Decimals.add(Decimals.sub(Decimals.add(lftunt, oldper), line.getWrkfec().getPerbegtr2()), 1);
                        tirunt = getMinimum(tirunt, lftunt);
                        amtp2 = Decimals.div(Decimals.mul(Decimals.mul(relFeecurAmt, line.getWrkfec().getPerrattr2()), tirunt), basis);
                        fecamt1 = Decimals.add(fecamt1, amtp2);
                        line.setInftxt(line.getInftxt() + CR + Strings.getText("Tiered by period: $1 periods at $2,Calculated=$3$4", tirunt, line.getWrkfec().getPerrattr2(), feecur, Utils.fmtAmount(amtp2, feecur)));
                        lftunt = Decimals.sub(lftunt, tirunt);
                    }
                    rndflg = true;
                    amtp1 = Decimals.div(Decimals.mul(Decimals.mul(relFeecurAmt, ratuse), lftunt), basis);
                    fecamt1 = Decimals.add(fecamt1, amtp1);
                    line.setInftxt(line.getInftxt() + CR + Strings.getText("Tiered by period: $1 periods at $2,Calculated=$3$4", lftunt, ratuse, feecur, Utils.fmtAmount(amtp1, feecur)));
                    line.setInftxt(line.getInftxt() + CR + Strings.getText("增额部分费用: $1$2 ", feecur, Utils.fmtAmount(fecamt1, feecur)));
                }
                //计算展期部分费用
                addpernum = perprv - oldperprv;
                if (addpernum > 0) {
                    line.setInftxt(line.getInftxt() + CR + Strings.getText("展期部分基于 OPNAMT $1 $2 ($3 $4)", relcur, Utils.fmtAmount(oldopnamt, relcur), feecur, Utils.fmtAmount(opnAmt, feecur)));
                    //line\\INFTXT = line\\INFTXT + CR + GetText( 'RG000233
                    if (MdaUtils.compareTo(line.getModflg(), "u") != 0) {
                        if (!MdaUtils.isEmpty(pertyp)) {
                            if (!(MdaUtils.compareTo(pertyp, "f") == 0)) {
                                line.setInftxt(line.getInftxt() + CR + Strings.getText("Units increased $3 $4(from $1 to $2)", Formats.fmtDateShort(line.getOldmatdat()), Formats.fmtDateShort(line.getDat2()), addpernum, codetableService.getTblLabel(pertyp, "FECPER")));
                            }
                        }
                    }
                    fecamt2 = new BigDecimal("0");
                    basis = 1000;
                    lftunt = new BigDecimal(perprv);
                    if (MdaUtils.compareTo(lftunt, line.getWrkfec().getPerbegtr5()) >= 0 && line.getWrkfec().getPerbegtr5() != 0) {
                        tirunt = Decimals.add(Decimals.sub(lftunt, line.getWrkfec().getPerbegtr5()), 1);
                        tirunt = getMinimum(tirunt, new BigDecimal(addpernum));
                        amtq5 = Decimals.div(Decimals.mul(Decimals.mul(opnAmt, line.getWrkfec().getPerrattr5()), tirunt), basis);
                        fecamt2 = Decimals.add(fecamt2, amtq5);
                        line.setInftxt(line.getInftxt() + CR + Strings.getText("Tiered by period: $1 periods at $2,Calculated=$3$4", tirunt, line.getWrkfec().getPerrattr5(), feecur, Utils.fmtAmount(amtq5, feecur)));
                        lftunt = Decimals.sub(lftunt, tirunt);
                    }
                    if (MdaUtils.compareTo(lftunt, line.getWrkfec().getPerbegtr4()) >= 0 && line.getWrkfec().getPerbegtr4() != 0 && MdaUtils.compareTo(lftunt, oldperprv) > 0) {
                        tirunt = Decimals.add(Decimals.sub(lftunt, line.getWrkfec().getPerbegtr4()), 1);
                        tirunt = getMinimum(tirunt, Decimals.sub(lftunt, oldperprv));
                        amtq4 = Decimals.div(Decimals.mul(Decimals.mul(opnAmt, line.getWrkfec().getPerrattr4()), tirunt), basis);
                        fecamt2 = Decimals.add(fecamt2, amtq4);
                        line.setInftxt(line.getInftxt() + CR + Strings.getText("Tiered by period: $1 periods at $2,Calculated=$3$4", tirunt, line.getWrkfec().getPerrattr4(), feecur, Utils.fmtAmount(amtq4, feecur)));
                        lftunt = Decimals.sub(lftunt, tirunt);
                    }
                    if (MdaUtils.compareTo(lftunt, line.getWrkfec().getPerbegtr3()) >= 0 && line.getWrkfec().getPerbegtr3() != 0 && MdaUtils.compareTo(lftunt, oldperprv) > 0) {
                        tirunt = Decimals.add(Decimals.sub(lftunt, line.getWrkfec().getPerbegtr3()), 1);
                        tirunt = getMinimum(tirunt, Decimals.sub(lftunt, oldperprv));
                        amtq3 = Decimals.div(Decimals.mul(Decimals.mul(opnAmt, line.getWrkfec().getPerrattr3()), tirunt), basis);
                        fecamt2 = Decimals.add(fecamt2, amtq3);
                        line.setInftxt(line.getInftxt() + CR + Strings.getText("Tiered by period: $1 periods at $2,Calculated=$3$4", tirunt, line.getWrkfec().getPerrattr3(), feecur, Utils.fmtAmount(amtq3, feecur)));
                        lftunt = Decimals.sub(lftunt, tirunt);
                    }
                    if (MdaUtils.compareTo(lftunt, line.getWrkfec().getPerbegtr2()) >= 0 && line.getWrkfec().getPerbegtr2() != 0 && MdaUtils.compareTo(lftunt, oldperprv) > 0) {
                        tirunt = Decimals.add(Decimals.sub(lftunt, line.getWrkfec().getPerbegtr2()), 1);
                        tirunt = getMinimum(tirunt, Decimals.sub(lftunt, oldperprv));
                        amtq2 = Decimals.div(Decimals.mul(Decimals.mul(opnAmt, line.getWrkfec().getPerrattr2()), tirunt), basis);
                        fecamt2 = Decimals.add(fecamt2, amtq2);
                        line.setInftxt(line.getInftxt() + CR + Strings.getText("Tiered by period: $1 periods at $2,Calculated=$3$4", tirunt, line.getWrkfec().getPerrattr2(), feecur, Utils.fmtAmount(amtq2, feecur)));
                        lftunt = Decimals.sub(lftunt, tirunt);
                    }
                    rndflg = true;
                    if (MdaUtils.compareTo(lftunt, oldperprv) > 0) {
                        amtq1 = Decimals.div(Decimals.mul(Decimals.mul(opnAmt, ratuse), lftunt), basis);
                        fecamt2 = Decimals.add(fecamt2, amtq1);
                        line.setInftxt(line.getInftxt() + CR + Strings.getText("Tiered by period: $1 periods at $2,Calculated=$3$4", lftunt, ratuse, feecur, Utils.fmtAmount(amtq1, feecur)));
                    }
                    line.setInftxt(line.getInftxt() + CR + Strings.getText("展期部分费用: $1$2 ", feecur, Utils.fmtAmount(fecamt2, feecur)));
                }
                fecamt = Decimals.add(fecamt1, fecamt2);
                line.setInftxt(line.getInftxt() + CR + Strings.getText("Total Amount: $1 $2 ", feecur, Utils.fmtAmount(fecamt, feecur)));
                // check minimum:
                if (MdaUtils.compareTo(line.getWrkfec().getMincur(), line.getCur()) != 0) {
                    minamt = xrtmodService.amtConvTypes(line.getWrkfec().getMincur(), line.getWrkfec().getMinamt(), "M", line.getCur(), "M", Dates.today());
                } else {
                    minamt = line.getWrkfec().getMinamt();
                }
                if (MdaUtils.compareTo(fecamt, minamt) < 0) {
                    fecamt = minamt;
                    line.setInftxt(line.getInftxt() + CR + Strings.getText("- using minimum $1 $2 instead", feecur, Utils.fmtAmount(fecamt, feecur)));
                    line.setMinmaxflg("<");
                }
                // check maximum:
                if (MdaUtils.compareTo(line.getWrkfec().getMaxamt(), 0) > 0) {
                    if (MdaUtils.compareTo(line.getWrkfec().getMaxcur(), line.getCur()) != 0) {
                        maxAmt = xrtmodService.amtConvTypes(line.getWrkfec().getMaxcur(), line.getWrkfec().getMaxamt(), "M", line.getCur(), "M", Dates.today());
                    } else {
                        maxAmt = line.getWrkfec().getMaxamt();
                    }
                    if (MdaUtils.compareTo(fecamt, maxAmt) > 0) {
                        fecamt = maxAmt;
                        line.setInftxt(line.getInftxt() + CR + Strings.getText("- using maximum $1 $2 instead", feecur, Utils.fmtAmount(fecamt, feecur)));
                        line.setMinmaxflg(">");
                    }
                }
            }
        }
        /**
         // if more periods to be calculated, calculate and add the additional periods.
         if $perprv <= $peruse then
         line\\unt = 1
         line\\RATCAL = $ratuse
         else
         // calulate date the calculation of the additional period has to start.
         select case TRUE
         case line\\wrkfec\\pertypprv.is ( "d" )
         $adddat = DateAdd( line\\DAT1, $peruse )
         case line\\wrkfec\\pertypprv.is ( "m" )
         $adddat = line\\DAT1
         for $i = 1 to $peruse
         $adddat = IRTCAL.GetNextPeriod ( line\\DAT1, $adddat, "DURMON" )
         next
         case line\\wrkfec\\pertypprv.is ( "q" )
         $adddat = line\\DAT1
         for $i = 1 to $peruse
         $adddat = IRTCAL.GetNextPeriod ( line\\DAT1, $adddat, "DURQTR" )
         next
         case else
         $fecamt = 0
         line\\INFTXT = line\\INFTXT + CR + GetText( 'LG000190' )
         goto setit
         end select
         if line\\WRKFEC\\PERTYP.is ( "n" ) then
         $fecamt = 0
         line\\INFTXT = line\\INFTXT + CR + GetText( 'LG000191' )
         goto setit
         endif
         if $adddat >= line\\dat2 then
         line\\unt = 1
         else
         line\\INFTXT = line\\INFTXT + CR + GetText( 'LG000192', $adddat, line\\dat2 )
         // get number of additional periods
         line\\unt = GetNrOfPeriodsPermille( $adddat, line\\dat2, line\\WRKFEC\\PERTYP )
         // check if maximum of periods covered by condition is reached
         if line\\unt > line\\wrkfec\\maxpercov and line\\wrkfec\\maxpercov > 0 then
         line\\INFTXT = line\\INFTXT + CR + GetText( 'LG000193', line\\wrkfec\\maxpercov )
         $fecamt = 0
         goto setit
         endif
         // calculate permille amount for that periods.
         $clcamt = $RelFeecurAmt * line\\RATCAL * line\\unt / 1000
         line\\INFTXT = line\\INFTXT + CR + GetText( 'LG000194', feecur, FmtAmount( $RelFeecurAmt, feecur ), line\\RATCAL, line\\unt, FmtAmount( $clcamt, feecur ) )
         // adding base amount (if any)
         if not IsEmpty( line\\wrkfec\\basamt ) then
         $addamt = AmtConvTypes( line\\WRKFEC\\MINCUR, line\\wrkfec\\basamt, "M", line\\CUR, "M", Today )
         $clcamt = $clcamt + $addamt * line\\unt
         line\\INFTXT = line\\INFTXT + CR + GetText( 'LG000195', feecur, FmtAmount( $addamt, feecur ), line\\unt )
         endif
         // check maximum (if per period)
         if line\\wrkfec\\maxpercnt > 0 and line\\WRKFEC\\MAXAMT > 0 then
         if line\\WRKFEC\\MaxCUR <> line\\CUR then
         $MaxAmt = AmtConvTypes( line\\WRKFEC\\MaxCUR, line\\WRKFEC\\MAXAMT, "M", line\\CUR, "M", Today )
         else
         $MaxAmt = line\\WRKFEC\\MAXAMT
         endif
         $MaxAmt = $MaxAmt * Ceil( line\\unt / line\\wrkfec\\maxpercnt )
         if $clcamt > $MaxAmt then
         $clcamt = $MaxAmt
         line\\INFTXT = line\\INFTXT + CR + GetText( 'LG000196', feecur, FmtAmount( $clcamt, feecur ) )
         line\\minmaxflg = ">"
         endif
         endif
         // check minimum (if per period)
         if line\\wrkfec\\minpercnt > 0 then
         if line\\WRKFEC\\MINCUR <> line\\CUR then
         $minamt = AmtConvTypes( line\\WRKFEC\\MINCUR, line\\WRKFEC\\MINAMT, "M", line\\CUR, "M", Today )
         else
         $minamt = line\\WRKFEC\\MINAMT
         endif
         $minamt = $minamt * Ceil( line\\unt / line\\wrkfec\\minpercnt )
         if $clcamt < $minamt then
         $clcamt = $minamt
         line\\INFTXT = line\\INFTXT + CR + GetText( 'LG000197', feecur, FmtAmount( $clcamt, feecur ) )
         line\\minmaxflg = "<"
         endif
         endif
         // add both amounts
         $fecamt = $fecamt + $clcamt
         endif
         endif
         // check maximum (if for total amount)
         if line\\wrkfec\\maxpercnt == 0 and line\\WRKFEC\\MAXAMT > 0 then
         if line\\WRKFEC\\MaxCUR <> line\\CUR then
         $MaxAmt = AmtConvTypes( line\\WRKFEC\\MaxCUR, line\\WRKFEC\\MAXAMT, "M", line\\CUR, "M", Today )
         else
         $MaxAmt = line\\WRKFEC\\MAXAMT
         endif
         if $fecamt > $MaxAmt then
         $fecamt = $MaxAmt
         line\\INFTXT = line\\INFTXT + CR + GetText( 'LG000198', feecur, FmtAmount( $fecamt, feecur ) )
         line\\minmaxflg = ">"
         endif
         endif
         // check minimum (if for total amount)
         if line\\WRKFEC\\MINCUR <> line\\CUR then
         $minamt = AmtConvTypes( line\\WRKFEC\\MINCUR, line\\WRKFEC\\MINAMT, "M", line\\CUR, "M", Today )
         else
         $minamt = line\\WRKFEC\\MINAMT
         endif
         select case line\\wrkfec\\minpercnt
         case 0
         if $fecamt < $minamt then
         $fecamt = $minamt
         line\\INFTXT = line\\INFTXT + CR + GetText( 'LG000199', feecur, FmtAmount( $fecamt, feecur ) )
         line\\minmaxflg = "<"
         endif
         // check minimum (if minimum ist per month ( minpercount -3)
         case - 3
         $perprv = GetNrOfPeriodsPermille( line\\DAT1, line\\dat2, "m" )
         $minamt = $perprv * $minamt
         if $fecamt < $minamt then
         $fecamt = $minamt
         line\\INFTXT = line\\INFTXT + CR + GetText( 'LG000200', feecur, FmtAmount( $fecamt, feecur ) )
         line\\minmaxflg = "<"
         endif
         end select
         line\\INFTXT = line\\INFTXT + CR + GetText( 'LG000201', feecur, FmtAmount( $fecamt, feecur ) )
         label setit
         endif
         **/
        return fecamt;
    }

    /**
     * source:fecism.0000.script
     */
    @JsonIgnore
    public String getfeetxtinf(String feecod, String allcalflg, FecVo fecuse, String feetxt, BigDecimal rat, int unt, Date dat1, Date dat2, IStream detstm) {
        // store execution language
        boolean uilChg = false;
/*TODO
        if (!MdaUtils.isEmpty(uil)) {
            Systems.setLang(uil);
            if (ctx.getErrorCode() == 0) {
                uilChg = true;
            } else {
                Systems.setLang(oldlanguage);
            }
        }
*/
        String vrat = NULLSTR;
        String txt = NULLSTR;
        String feegrp = NULLSTR;
        String type = NULLSTR;
        String pertyptxt = NULLSTR;
        String unttxt = NULLSTR;
        if (MdaUtils.isEmpty(feecod)) {
            return "";
        } else {
            //IF FEECOD="BFINT" then
            if (MdaUtils.compareTo(Numerics.toInt(rat), rat) == 0) {
                vrat = MdaUtils.toString(Numerics.toInt(rat));
            } else {
                vrat = MdaUtils.toString(rat);
                while (Strings.len(vrat) > 1 && MdaUtils.compareTo(Strings.mid(vrat, Strings.len(vrat), 1), "0") == 0) {
                    vrat = Strings.mid(vrat, 1, Strings.len(vrat) - 1);
                }
            }
            // as ismodified does not work for parameters comprae with default value
            if (StringUtils.isNotEmpty(feetxt) && MdaUtils.compareTo(feetxt, codetableService.getTblLabel(feecod.toUpperCase(), "FEETXT")) != 0) {
                txt = feetxt;
            } else {
                txt = codetableService.getTblLabel(feecod.toUpperCase(), "FEETXT");
            }
            if (Streams.streamSearch(detstm, "OLDRED" + TAB) > 0) {
                if (Streams.streamSearch(detstm, "OLDREP" + TAB) > 0) {
                    return Strings.getText("Refund $1", txt);
                } else {
                    return Strings.getText("Additional claim $1", txt);
                }
            } else {
                switch (fecuse.getCalrul()) {
                    case "1":
                    case "P":
                        if (MdaUtils.compareTo(rat, 0) == 0) {
                            if (MdaUtils.compareTo(fecuse.getCalrul(), "1") == 0) {
                                return Strings.getText("$1", txt);
                            } else {
                                return txt;
                            }
                        } else {
                            switch (allcalflg) {
                                case "<":
                                    return Strings.getText("$1", txt);
                                //              = GetText( 'R0000177
                                // break;
                                case ">":
                                    return Strings.getText("$1", txt);
                                //              = GetText( 'R0000179
                                // break;
                                default:
                                    return Strings.getText("$1", txt);
                                //              = GetText( 'R0000175
                            }
                        }
                        // break;
                    case "2":
                        return Strings.getText("$1", txt, rat, unt);
                    case "3":
                        return Strings.getText("$1 for $3 months", txt, rat, unt);
                    case "C":
                        Argument<String> typeBox = new Argument<String>();
                        Argument<String> pertyptxtBox = new Argument<String>();
                        getPertxtAndType(fecuse, detstm, typeBox, pertyptxtBox);

                        type = typeBox.value;
                        pertyptxt = pertyptxtBox.value;

                        if (MdaUtils.isEmpty(type)) {
                            return txt;
                        } else {
                            if (MdaUtils.isEmpty(pertyptxt)) {
                                unttxt = "";
                            } else {
                                unttxt = MdaUtils.toString(unt);
                            }
                            switch (allcalflg) {
                                case "L":
                                case "T":
                                    // if rate is zero minimum is the flat fee
                                    if (MdaUtils.compareTo(rat, 0) == 0) {
                                        return Strings.getText("$1 $3 $4", txt, vrat, unttxt, pertyptxt, type);
                                    } else {
                                        return Strings.getText("$2 $5 $1 for $3 $4 min", txt, vrat, unttxt, pertyptxt, type);
                                    }
                                    // break;
                                case "C":
                                case "*":
                                    return Strings.getText("$2 $5 $1 for $3 $4", txt, vrat, unttxt, pertyptxt, type);
                                default:
                                    return txt;
                            }
                        }
                        // break;
                    default:
                        switch (allcalflg) {
                            case "<":
                                return Strings.getText("$1", txt);
                            case ">":
                                return Strings.getText("$1 max.", txt);
                            default:
                                return txt;
                        }
                }
            }
        }
        // reset execution language
        // if( uilChg )
        // {
        //   Platform.setLang( oldlanguage );
        // }
    }

    /**
     * source:fecism.0001.script
     */
    @JsonIgnore
    public static int getNrOfPeriodsLCComm(Date from, Date till) {
        int per = 0;
        if (!(Dates.diff(from, till) > 0)) {
            per = (Dates.getYear(till) - Dates.getYear(from)) * 12;
            per = per + Dates.getMonth(till) - Dates.getMonth(from);
            if (Dates.getDay(till) >= Dates.getDay(from)) {
                per = per + 1;
            }
            return per;
        } else {
            return 0;
        }
    }

    /**
     * source:fecism.0002.script
     */
    @JsonIgnore
    public static int getNrOfPeriodsPermille(Date from, Date till, String pertyp) {
        // default result is 0 periods
        int per = 0;
        if (MdaUtils.compareTo(pertyp, "d") == 0) {
            per = Dates.diff(till, from);
        } else {
            if (!(Dates.diff(from, till) > 0)) {
                /**
                 $per = ( Year( till ) - Year( from ) ) * 12
                 $per = $per + Month( till ) - Month( from )
                 if Day( till ) >= Day( from ) then
                 $per = $per + 1
                 endif
                 select case pertyp
                 case "q"
                 $per = Ceil( $per / 3 )
                 case "s"
                 $per = Ceil( $per / 6 )
                 case "y"
                 $per = Ceil( $per / 12 )
                 end select
                 **/
                // 按月计费费用种类包括：汇出汇款手续费、国内信用证承付手续费、进口信用证承兑手续费，费用计算规则调整为：期间天数/30取整数、如有余数+1=月数
                //按季计费费用种类包括：进口信用证承兑费、进口开证手续费、进口开证修改手续费、出口信用证保兑费、保函开立手续费、保函开立修改手续费；费用计算规则调整为：
                //期间天数 / 90 取整数、如有余数 + 1 = 季数。（注意：有的费用种类有多个费用代号，均需调整。）
                per = Dates.diff(till, from);
                if (per == 0) {
                    per = per + 1;
                }
                switch (pertyp) {
                    case "m":
                        per = Numerics.ceil(Decimals.div(per, 30));
                        break;
                    case "q":
                        per = Numerics.ceil(Decimals.div(per, 90));
                        break;
                    case "s":
                        per = Numerics.ceil(Decimals.div(per, 180));
                        break;
                    case "y":
                        per = Numerics.ceil(Decimals.div(per, 365));
                        break;
                }
            } else {
                per = 0;
            }
        }
        if (per < 0) {
            per = 0;
        }
        return per;
    }

}
