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

import cn.hutool.core.codec.Base64;
import cn.hutool.core.lang.Assert;
import com.brilliance.mda.runtime.mda.CodetableItem;
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.atcmod.service.AtcmodService;
import com.brilliance.isc.common.cache.atpbch.AtpBchCacheService;
import com.brilliance.isc.common.codetable.service.CodetableService;
import com.brilliance.isc.common.component.PtsptaToolComponent;
import com.brilliance.isc.common.context.AppEnv;
import com.brilliance.isc.common.context.SettleContext;
import com.brilliance.isc.common.context.SpringEnvContext;
import com.brilliance.isc.common.counter.service.CounterService;
import com.brilliance.isc.common.doc.ITransactionDocSet;
import com.brilliance.isc.common.doc.TextResultVo;
import com.brilliance.isc.common.docimm.service.DocimmService;
import com.brilliance.isc.common.exception.CommonServiceException;
import com.brilliance.isc.common.register.document.IDocumentRegister;
import com.brilliance.isc.common.sysmod.SysmodService;
import com.brilliance.isc.common.transaction.help.TransactionHelpService;
import com.brilliance.isc.common.transaction.vo.BaseTransactionVo;
import com.brilliance.isc.common.trndoc.service.DoceotService;
import com.brilliance.isc.common.trndoc.service.TrndocService;
import com.brilliance.isc.common.trndoc.vo.DoceotVo;
import com.brilliance.isc.common.trndoc.vo.TrndocVo;
import com.brilliance.isc.common.trnmod.vo.TrnmodVo;
import com.brilliance.isc.common.util.DocUtils;
import com.brilliance.isc.common.util.SettleExceptionUtils;
import com.brilliance.isc.common.util.StringUtils;
import com.brilliance.isc.common.util.Utils;
import com.brilliance.isc.common.vo.AbstractCommonVo;
import com.brilliance.isc.common.vo.BizInfoVo;
import com.brilliance.isc.common.vo.FremsgVo;
import com.brilliance.isc.common.vo.PtsptaVo;
import com.brilliance.isc.mda.dao.*;
import com.brilliance.isc.utils.DateUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.poi.util.IOUtils;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
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.io.*;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

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

@Service
public class TrndocServiceImpl implements TrndocService {

    private static final List<String> DOCNAMSEA = Arrays.asList("SS", "SR", "OS", "XS", "LP",
            "TS", "ES", "ER", "AS", "AR", "RR", "BS", "JR", "ZS", "ZR", "QS", "QR",
            "FS", "FR", "MS", "MR", "CS", "CR");
    private static Logger log = LoggerFactory.getLogger(TrndocServiceImpl.class);
    private static ThreadLocal<List<String>> corTypeListLocal = ThreadLocal.withInitial(() -> new ArrayList<>());


    @Autowired
    private PtsptaToolComponent ptsptaToolComponent;

    @Autowired
    private SysmodService sysmodService;

    @Autowired
    private DoceotService doceotService;

    @Autowired
    private AtcmodService atcmodService;

    @Autowired
    private TransactionHelpService transactionHelpService;

    @Autowired
    private DocimmService docimmService;

    @Autowired
    private CounterService counterService;

    @Autowired
    private CodetableService codetableService;

    @Autowired
    private SmhMapper smhMapper;

    @Autowired
    private ErsMapper ersMapper;

    @Autowired
    private TrnMapper trnMapper;

    @Autowired
    private AppEnv appEnv;

    @Autowired
    private EtaMapper etaMapper;

    @Autowired
    private EtauilMapper etauilMapper;

    @Autowired
    private AtpBchCacheService atpBchCacheService;

    public void clearCorTypeList() {
        corTypeListLocal.remove();
    }

    public List<String> getCortypeList() {
        return corTypeListLocal.get();
    }

    public void registerCorType(String... cortyps) {
        for (String cortyp : cortyps) {
            corTypeListLocal.get().add(cortyp);
        }
    }

    /**
     * source:trndoc.0022.script
     */
    public void assertDOCS(TrndocVo trndoc) {
        // assert, that DOCEOT Grid is filled correctly
        defDocEotBop(trndoc);
        //按照交易码，获取注册Spring的交易Bean
        // post all registerdocument methods
        String transName = SettleContext.getTransName();
        try {
            IDocumentRegister documentRegister = SpringEnvContext.getBean(atpBchCacheService.getSdTransNameForRegister().toLowerCase() + ".documentRegister");
            documentRegister.registerDocument(trndoc);
        } catch (NoSuchBeanDefinitionException e) {
            throw new CommonServiceException("没有找到交易注册类");
        }
        defDocEotEop(trndoc);
    }

    /**
     * source:trndoc.0014.script
     */
    public void defDocEotBop(TrndocVo trndoc) {
        int gridcnt = 0;
        if (trndoc.getDoceot() != null) {
            gridcnt = trndoc.getDoceot().size();
        }
        if (gridcnt > 0) {
            for (int i = 0; i < gridcnt; i++) {
                DoceotVo doceot = trndoc.getDoceot().get(i);
                if (MdaUtils.compareTo(doceot.getLev(), "4") != 0
                        && (MdaUtils.compareTo(doceot.getLev(), "5") != 0 || MdaUtils.compareTo(doceot.getAtttyp(), "INT") == 0)) {
                    doceot.setUsdflg(-1);
                }
            }
        }
    }

    /**
     * source:trndoc.0016.script
     */
    public void defDocEotEop(TrndocVo trndoc) {
        // execute customer specific assertions
        defDocEotEopCUS(trndoc);
        IStream docIdsforATC = new StreamImpl();
        Streams.streamClear(docIdsforATC);
        IStream docIdsfromATC = new StreamImpl();
        Streams.streamClear(docIdsfromATC);
        // cleanup of DOCEOT grid i.e. delete empty rows
        int count = 0;
        if (trndoc.getDoceot() != null) {
            count = trndoc.getDoceot().size();
        }
        BigDecimal remCnt = null;
        String docID = NULLSTR;
        int aTCidx = 0;
        String aTCLine = NULLSTR;
        int pos = 0;
        String rolgrp = NULLSTR;
        String rolptainr = NULLSTR;
        int refidx = 0;
        int reDoMLIidx = 0;
        String refid = NULLSTR;
        int attidx = 0;
        String attid = NULLSTR;
        int delcount = 0;
        Set<Integer> removeIds = new HashSet<>();
        if (count > 0) {
            // delete unused primary entries, add maling instructions for others
            for (int i = 0; i < count; i++) {
                DoceotVo doceot = trndoc.getDoceot().get(i);
                if (doceot.getUsdflg() == -1) {
                    if (MdaUtils.compareTo(doceot.getLev(), "2") != 0) {
                        /*MdaUtils.clear(this.getDoceot().getElement(i));
                        // the warnings of the address has to be deleted as well
                        remCnt = Dynamic.oitSysWrnGrpRemove( Platform.recGetPath(this.getDoceot().getElement(i).getRcvp().getPtspta()) + "\\" );
                        Dynamic.sysWarningGroupCompleted();*/
                        removeIds.add(i);
                    }
                } else {
                    // Add entries from mailing instructions, if triggering party has changed
                    if (MdaUtils.compareTo(doceot.getLev(), "1") == 0) {
                        if (MdaUtils.compareTo(doceot.getOldptyinr(), doceot.getPtaptyinr()) != 0 || MdaUtils.compareTo(doceot.getOldapfcod(), Strings.mid(doceot.getApf(), 1, 3)) != 0) {
                            doceot.setOldptyinr(doceot.getPtaptyinr());
                            doceot.setOldapfcod(Strings.mid(doceot.getApf(), 1, 3));
                            if (!MdaUtils.isEmpty(doceot.getPtaptyinr())) {
                                //TODO setMLI( doceot.getId(), doceot.getApf(), doceot.getPtaptyinr() );
                            } else {
                                doceot.setApfcpymli(-1);
                            }
                        }
                    }
                }
                /**
                 // defaulting for ADDTXT/ADDSTR from RegisterAddTxt
                 //   DO5DOS.008383 - Moved from DefDocEot to DefDocEotEop to call RegisterAddTxt methods also for
                 // messages via [Send to]/ [Copy to].
                 $DocID = DOCEOT( $i )\\id
                 if not IsEmpty( $DocID ) then
                 StreamInsert( $DocIdsforATC, 0, $DocID )
                 if not IsModified( DOCEOT( $i )\\ADDTXT ) and not IsModified( DOCEOT( $i )\\ADDSTR ) then
                 Clear( ADDTXT )
                 $Cnt = PostAllFittingMethods( \\, "REGISTERADDTXT", GetModuleInfo( DOCEOT( $i ), tdModuleInfoFullName ), TRUE, -1 )
                 DOCEOT( $i )\\ADDTXT = ADDTXT
                 DOCEOT( $i )\\ADDSTR = ADDTXT
                 endif
                 endif
                 **/
                docID = doceot.getId();
                if (!MdaUtils.isEmpty(docID)) {
                    Streams.streamInsert(docIdsforATC, 0, docID);
                }
            }
            // overwriting of mandatory flag by entries from table ATC
            atcmodService.setMandatoryForDocs(docIdsforATC, docIdsfromATC);
            if (Streams.streamCount(docIdsfromATC) > 0) {
                for (int i = 0; i < count; i++) {
                    DoceotVo doceot = trndoc.getDoceot().get(i);
                    docID = doceot.getId();
                    if (!MdaUtils.isEmpty(docID)) {
                        aTCidx = Streams.streamSearch(docIdsfromATC, Strings.trim(docID) + TAB);
                        if (aTCidx > 0) {
                            aTCLine = Strings.getLine(docIdsfromATC, aTCidx);
                            pos = Strings.pos(aTCLine, TAB);
                            switch (Strings.mid(aTCLine, pos + 1, 1)) {
                                case "0":
                                    doceot.setMdtflg(0);
                                    break;
                                case "1":
                                    doceot.setMdtflg(1);
                                    break;
                                case "2":
                                    doceot.setMdtflg(2);
                                    if (!doceot.isModified("pandsc")) {
                                        doceot.setDelflg("D");
                                    }
                                    break;
                                case "R":
                                    //MdaUtils.clear(this.getDoceot().getElement(i)); // remove entry
                                    removeIds.add(i);
                                    break;
                            }
                        }
                    }
                }
            }
            // check whether primary entry for secondary entries is still there
            // and triggering party has not changed
            for (int outidx = 0; outidx < count; outidx++) {
                DoceotVo doceot = trndoc.getDoceot().get(outidx);
                if (MdaUtils.compareTo(doceot.getLev(), "2") == 0) {
                    // DO53BE.000980
                    if (!MdaUtils.isEmpty(doceot.getRole())) {
                        PtsptaVo ptspta = ptsptaToolComponent.getPtsFromListByRol(trndoc.getPtsptas(), doceot.getRole());
                        if (ptspta != null) {
                            rolptainr = ptspta.getPtainr();
                            if (MdaUtils.compareTo(doceot.getPtainr(), rolptainr) != 0 && MdaUtils.compareTo(doceot.getRole(), doceot.getRoladr()) == 0) {
                                doceot.setPtainr(rolptainr);
                                doceotService.setAddressingInfo(doceot, trndoc.getPtsptas());
                                doceotService.setPanDsc(doceot);
                            }
                        }
                    }
                    refidx = -1;
                    reDoMLIidx = -1;
                    refid = doceot.getIdref();
                    for (int i = 0; i < count; i++) {
                        if (MdaUtils.compareTo(refid, trndoc.getDoceot().get(i).getId()) == 0) {
                            refidx = i;
                            reDoMLIidx = refidx;
                            if (!MdaUtils.isEmpty(doceot.getOldptyinr()) && MdaUtils.compareTo(doceot.getOldptyinr(), trndoc.getDoceot().get(i).getPtaptyinr()) != 0) {
                                refidx = -1;
                                //           following line should be enabled during save event only
                                //           PromptAfterSaveComplete( `Addressing instruction remove/replaced due to change of party` )
                            }
                            if (!MdaUtils.isEmpty(doceot.getOldapfcod()) && MdaUtils.compareTo(doceot.getOldapfcod(), Strings.mid(trndoc.getDoceot().get(i).getApf(), 1, 3)) != 0) {
                                refidx = -1;
                                //           following line should be enabled during save event only
                                //           PromptAfterSaveComplete( `Addressing instruction remove/replaced due to change of form` )
                            }
                            // suppress `copy to` and `send to` when original is suppressed
                            if (MdaUtils.compareTo(trndoc.getDoceot().get(i).getDelflg(), "D") == 0) {
                                doceot.setDelflg("D");
                            }
                            // set role and SRTKEY according to original
                            doceot.setRole(trndoc.getDoceot().get(i).getRole());
                            doceotService.setSRTKEY(doceot);
                            i = count;
                        }
                    }
                    if (refidx < 0) {
                        //MdaUtils.clear(this.getDoceot().getElement(outidx));
                        removeIds.add(outidx);
                        if (reDoMLIidx >= 0) {
                            if (!MdaUtils.isEmpty(trndoc.getDoceot().get(reDoMLIidx).getPtaptyinr())) {
                                //TODO setMLI( trndoc.getDoceot().get(reDoMLIidx).getId(), trndoc.getDoceot().get(reDoMLIidx).getApf(), trndoc.getDoceot().get(reDoMLIidx).getPtaptyinr() );
                            }
                        }
                    }
                }
                if (MdaUtils.compareTo(doceot.getLev(), "5") == 0) { // Attachments
                    refid = doceot.getIdref();
                    // assert existence of optional idref (i.e. created document attached to other document)
                    if (MdaUtils.isEmpty(refid)) {
                        refidx = outidx;
                    } else {
                        refidx = -1;
                    }
                    attidx = -1;
                    attid = doceot.getIdatt();
                    for (int i = 0; i < count; i++) {
                        if (MdaUtils.compareTo(refid, trndoc.getDoceot().get(i).getId()) == 0) {
                            refidx = i;
                            // DO5DOS.007273 suppress attachment when referenced document is suppressed
                            if (MdaUtils.compareTo(trndoc.getDoceot().get(refidx).getDelflg(), "D") == 0) {
                                doceot.setDelflg("D");
                            }
                            if (attidx > 0) {
                                // stop searching when both indexes are found
                                i = count;
                                continue;
                            }
                        }
                        if (MdaUtils.compareTo(attid, trndoc.getDoceot().get(i).getId()) == 0) {
                            attidx = i;
                            // suppress attachment when parent is suppressed
                            if (MdaUtils.compareTo(trndoc.getDoceot().get(i).getDelflg(), "D") == 0) {
                                trndoc.getDoceot().get(outidx).setDelflg("D");
                            } else {
                                // # of originals=0 for attachments of technical messages
                                if (doceotService.isCORTYPTechnical(trndoc.getDoceot().get(i).getCortyp())) {
                                    doceot.setApfcpy1(0);
                                    //Dynamic.disableAndClearField( MdaDriver.getDatafield(this.getDoceot().getElement(outidx),"apfcpy1") );
                                }
                            }
                            // set role and SRTKEY according to original
                            doceot.setRole(trndoc.getDoceot().get(i).getRole());
                            doceot.setRoladr(trndoc.getDoceot().get(i).getRoladr());
                            doceotService.setSRTKEY(doceot);
                            if (refidx > 0) {
                                // stop searching when both indexes are found
                                i = count;
                            }
                        }
                    }
                    if (attidx < 0) {
                        removeIds.add(outidx);
                        //MdaUtils.clear(this.getDoceot().getElement(outidx));
                    }
                    if (refidx < 0) {
                        removeIds.add(outidx);
                        //MdaUtils.clear(this.getDoceot().getElement(outidx));
                    }
                }
            }
            // compress list
            // the empty rows must be deleted in place ,as copying via SetModuleContent loses valuelists and enabled/disabled status
            //  $nxt = 1
            for (int i = 0; i < count; i++) {
                if (MdaUtils.isEmpty(trndoc.getDoceot().get(i).getId())) {
                    removeIds.add(i);
                }
            }
        }
        if (removeIds.size() > 0) {
            List<Integer> removeIdList = removeIds.stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList());
            for (int i = trndoc.getDoceot().size() - 1; i >= 0; i--) {
                if (removeIdList.contains(i)) {
                    trndoc.getDoceot().remove(i);
                }
            }
        }
        // mark modified entries visibly for control & release (by marking colum DOCNAM)
        /*for(int  i = 0;i < count;i++) {
            if( !MdaUtils.isEmpty(trndoc.getDoceot().get(i).getId()) ) {
                if( Systems.isModified(this,"doceot[i]") ) {
                    Systems.setModified(this.getDoceot().getElement(i),"pandsc");
                }
            }
        }*/
        // create stable order of grid rows
        //Grids.gridSort(this.getDoceot(),"SRTKEY","A");
        List<DoceotVo> doceots = trndoc.getDoceot().stream().sorted(Comparator.comparing(doceot -> doceot.getSrtkey())).collect(Collectors.toList());
        trndoc.setDoceot(doceots);
    }

    /**
     * source:trndoc.0115.script
     */
    public void defDocEotEopCUS(TrndocVo trndoc) {
        // generate TCA attachment entries for TradeConnect
        // cleanup of DOCEOT grid i.e. delete empty rows
        int count = 0;
        if (trndoc.getDoceot() != null) {
            count = trndoc.getDoceot().size();
        }
        boolean tCAFlag = false;
        String docnam = NULLSTR;
        int refidx = 0;
        String id = NULLSTR;
        boolean delFlg = false;
        if (count > 0) {
            // add TCA entries
            for (int i = 0; i < count; i++) {
                DoceotVo doceot = trndoc.getDoceot().get(i);
                if (MdaUtils.compareTo(doceot.getCortyp(), "TCO") == 0) {
                    if (MdaUtils.compareTo(doceot.getLev(), "1") == 0) {
                        docnam = doceot.getDocnam();
                        String rulename = doceotService.getDocRul(docnam, "T", "S");
                        // TCO message is created by static function defined for TC
                        String lETrulename = doceotService.getDocRul(docnam, "L", "P");
                        // corresponding letter exists
                        tCAFlag = true;       // create letter as attachment
                        // search for existing TCA entry with same document
                        refidx = -1;
                        for (int seaIdx = 0; seaIdx < count; seaIdx++) {
                            if (MdaUtils.compareTo(docnam, trndoc.getDoceot().get(seaIdx).getDocnam()) == 0) {
                                if (MdaUtils.compareTo(trndoc.getDoceot().get(seaIdx).getCortyp(), "TCA") == 0) {
                                    refidx = seaIdx;
                                }
                            }
                        }
                        if (refidx < 0) {
                            if (MdaUtils.compareTo(tCAFlag, true) == 0) {
                                // TCA entry does not yet exist, create it
                                id = doceot.getId();
                                // letter before last is removed from DOCEOT\\DOCNAM
                                docnam = Strings.mid(docnam, 1, Strings.len(docnam) - 1) + "L" + Strings.mid(docnam, Strings.len(docnam), 1);
                                defDocEot(trndoc, Strings.mid(id, 1, 3), Strings.mid(id, 4, 8) + "z", docnam, doceot.getOldapf(), 0, doceot.getArgptainr(), doceot.getObjmod(), doceot.getPandsc(), doceot.getRole(), doceot.getAdrblk());
                                int a = docEOTGetRow(trndoc.getDoceot(), Strings.mid(id, 1, 3) + Strings.mid(id, 4, 8) + "z");
                                trndoc.getDoceot().get(a).setCortyp("LET");
                                doceotService.setApfFlag(trndoc, trndoc.getDoceot().get(a), false);
                                trndoc.getDoceot().get(a).setCortyp("TCA");
                                trndoc.getDoceot().get(a).setValcortyp("TCA");
                                trndoc.getDoceot().get(a).setUsdflg(0);
                                doceotService.setLang(trndoc.getDoceot().get(a));
                                trndoc.getDoceot().get(a).setIdatt(id);
                                trndoc.getDoceot().get(a).setLev("5");
                                doceotService.setCORTYP(trndoc.getDoceot().get(a));
                            }
                        } else {
                            // otheerwise mark entry as used
                            if (MdaUtils.compareTo(tCAFlag, true) == 0) {
                                trndoc.getDoceot().get(refidx).setUsdflg(0);
                                doceotService.setLang(trndoc.getDoceot().get(refidx));
                            } else {
                                trndoc.getDoceot().get(refidx).setUsdflg(-1);
                            }
                        }
                    }
                }
            }
            // remove automatic TCA entries when carrier is no longer TCO
            for (int i = 0; i < count; i++) {
                if (MdaUtils.compareTo(trndoc.getDoceot().get(i).getCortyp(), "TCA") == 0) {
                    if (MdaUtils.isEmpty(trndoc.getDoceot().get(i).getAtttyp())) {
                        // automatically created attachement
                        delFlg = true;
                        for (int j = 0; j < count; j++) {
                            if (MdaUtils.compareTo(trndoc.getDoceot().get(j).getId(), trndoc.getDoceot().get(i).getIdatt()) == 0) {
                                if (MdaUtils.compareTo(trndoc.getDoceot().get(j).getCortyp(), "TCO") == 0) {
                                    delFlg = false;
                                    j = count;
                                }
                            }
                        }
                        if (MdaUtils.compareTo(delFlg, true) == 0) {
                            trndoc.getDoceot().get(i).setUsdflg(-1);
                        }
                    }
                }
            }
        }
    }

    /**
     * sub to register document for list of documents
     * <p>
     * arguments:
     * trigger
     * which part of the software triggered this document, e.g. "TRN" or "SET"
     * (to avoid naming conflicts)
     * subid
     * id of the registration
     * <p>
     * trigger+subid identify a line in the list of documents
     * <p>
     * argdocpannam
     * rule / panelname to generate document
     * APFCOD (optional)
     * application from set
     * 3 digits (to select group) or 6 digits can be specified
     * mandatoryFlag
     * = 0 not mandatory
     * = 1 mandatory
     * = 2 not mandatory, initially suppressed
     * ptainr (optional)
     * PTA of addressee (if role is a valid PTSPTA in objmod, this item is derived from PTSPTA and this parameter is ignored)
     * objmod
     * object for which this document is generated (Path)
     * (e.g. to determine proper OWNREF field)
     * pandsc
     * description of line
     * role
     * role of addressee (mandatory)
     * adrblk (optional)
     * address of addressee (do not pass if it should be derived from PTSPTA in objmod, if set it is used as address of the message to be able to pass a letter to a different address (e.g. attatchment to beneficiary sent to advising bank)
     * id for primary message has maximum 12 digits (to leave 3 additional digits for secondary entries
     */
    public void defDocEot(TrndocVo trndoc, String trigger, String subId, String argdocpannam, String apfcod, int mandatoryFlag, String argptainr, String objmod, String pandsc, String role, String argadrblk) {

        String id = Strings.mid(trigger, 1, 3) + Strings.mid(subId, 1, 9);

        PtsptaVo ptspta = ptsptaToolComponent.getPtsFromListByRol(trndoc.getPtsptas(), role);
        String ptainr = NULLSTR;
        String adrblk = NULLSTR;
        if (ptspta == null || MdaUtils.isEmpty(ptspta.getAdrblk())) {
            ptainr = argptainr;
            adrblk = argadrblk;
        } else {
            if (MdaUtils.isEmpty(argadrblk)) {
                adrblk = ptspta.getAdrblk();
            } else {
                adrblk = argadrblk;
            }
            if (MdaUtils.isEmpty(argptainr)) {
                ptainr = ptspta.getPtainr();
            } else {
                ptainr = argptainr;
            }
        }
        argdocpannam = Strings.mid(argdocpannam, 1, Strings.len(argdocpannam) - 2) + Strings.mid(argdocpannam, Strings.len(argdocpannam), 1);
        //去掉多余的斜线
        if (argdocpannam.startsWith("\\")) {
            argdocpannam = argdocpannam.substring(1);
        }
        //  check if message is already in list
        int gridCnt = 0;
        if (trndoc.getDoceot() != null) {
            gridCnt = trndoc.getDoceot().size();
        }
        int i = 0;
        while (true) {
            if (gridCnt > 0) {
                for (i = 0; i < gridCnt; i++) {
                    DoceotVo doceotVo = trndoc.getDoceot().get(i);
                    if (MdaUtils.compareTo(id, doceotVo.getId()) == 0) {
                        if (StringUtils.isNotEmpty(argdocpannam)) {
                            doceotVo.setDocnam(argdocpannam);
                        }
                        //      if DOCEOT( $i )\\docnam <> $docnam then
                        //        SetErrorCode( 999, FormatText( "attempt to change document prototype for $1 from $2 to $3", $id, DOCEOT( $i )\\docnam, $docnam ) )
                        //        exit
                        //      endif
                        // if PTA has been change, reinitialize entry
                        if (MdaUtils.compareTo(doceotVo.getArgptainr(), ptainr) != 0) {
                            //MdaUtils.clear(this.getDoceot().getElement(i));
                            trndoc.getDoceot().remove(i);
                            gridCnt = trndoc.getDoceot().size();
                            break; // #######TODO 改写 LABEL与GOTO  ####   goto setit
                        }
                        doceotVo.setPandsc(pandsc);
                        doceotVo.setRole(role);
                        if (!doceotVo.isModified("roladr")) {
                            doceotVo.setRoladr(role);
                        }
                        if (!doceotVo.isModified("adrblk")) {
                            doceotVo.setAdrblk(adrblk);
                        }
                        if (MdaUtils.compareTo(mandatoryFlag, 1) == 0) {
                            doceotVo.setMdtflg(1);
                            doceotVo.setDelflg(" ");
                        } else {
                            doceotVo.setMdtflg(0);
                            // no default may possible, if document is enabled/disabled manually by user
                            // [event BUTDEL in DOCEOT holds SetModified]
                            // do5cob.701827
                            if (!doceotVo.isModified("mdtflg")) {
                                if (MdaUtils.compareTo(mandatoryFlag, 2) == 0) {
                                    doceotVo.setDelflg("D");
                                } else {
                                    doceotVo.setDelflg(" ");
                                }
                            }
                        }
                        doceotVo.setOldapf(apfcod);       // remember original apf
                        if (!doceotVo.isModified("apf")) {
                            doceotVo.setApf(apfcod);
                        }
                        doceotVo.setUsdflg(0);
                        doceotService.setSRTKEY(doceotVo);
                        doceotService.setAddressingInfo(doceotVo, trndoc.getPtsptas());
                        doceotVo.setDocnam(argdocpannam);
                        /*if( MdaUtils.compareTo(doceotVo.getDocnam(),docnam) != 0 ) {
                            doceotVo.setDocnam(docnam);
                            doceotVo.setDocstrsea(getStructures(trndoc, docnam));
                        }
                        //do5dos.008782
                        //always execute setDOCCORSEA, as previously allowed CORTYPES may have been deactivated in the meantime
                        doceotService.setDOCCORSEA(doceotVo);*/
                        doceotService.setCORTYP(doceotVo);
                        doceotService.setLang(doceotVo);
                        doceotService.setApfFlag(trndoc, doceotVo, false);
                        if (!doceotVo.isModified("docdat")) {
                            doceotVo.setDocdat(Dates.today());
                            /*this.getDoceot().getElement(i).setDocdat( Datas.getContentDate(this,"\\MTABUT\\CONINF\\CONEXEDAT"));
                            if( ctx.getErrorCode() != 0 )
                            {
                                this.getDoceot().getElement(i).setDocdat(null);
                            }
                            if( MdaUtils.isEmpty(this.getDoceot().getElement(i).getDocdat()) )
                            {
                                this.getDoceot().getElement(i).setDocdat( Dates.today());
                            }*/
                        }
                        doceotService.setDocSendFormat(doceotVo);
                        return;
                    }
                }
            }
            //新增一条记录
            DoceotVo doceot = new DoceotVo();
            trndoc.getDoceot().add(doceot);
            i = trndoc.getDoceot().size() - 1;
            break;
        }
        // set line in DOCEOT
        if (i >= 0) {
            DoceotVo doceot = trndoc.getDoceot().get(i);
            doceot.setId(id);
            doceot.setLev("1");
            doceot.setIdref("");
            doceot.setApfcpymli(-1);
            doceot.setDocnam(argdocpannam);
            //doceot.setDocnam( docnam);
            doceot.setPtainr(ptainr);
            doceot.setArgptainr(ptainr);
            doceot.setObjmod(objmod);
            doceot.setPandsc(pandsc);
            doceot.setRole(role);
            doceot.setRoladr(role);
            doceot.setAdrblk(adrblk);
            if (MdaUtils.compareTo(mandatoryFlag, 1) == 0) {
                doceot.setMdtflg(1);
            } else {
                doceot.setMdtflg(0);
                if (MdaUtils.compareTo(mandatoryFlag, 2) == 0) {
                    doceot.setDelflg("D");
                }
            }
            if (StringUtils.isNotEmpty(argdocpannam)) {
                doceot.setDocnam(argdocpannam);
            }
            doceot.setMdtflg(mandatoryFlag);
            doceot.setApf(apfcod);
            doceot.setOldapf(apfcod);     // remember original apf
            doceot.setUsdflg(0);
            doceotService.setSRTKEY(doceot);
            doceotService.setAddressingInfo(doceot, trndoc.getPtsptas());
            /*doceot.setDocstrsea(getStructures(trndoc, docnam));
            doceotService.setDOCCORSEA(doceot);*/
            doceotService.setCORTYP(doceot);
            doceotService.setLang(doceot);
            doceotService.setApfFlag(trndoc, doceot, false);
            //Systems.setEnabled(this.getDoceot().getElement(i),"butadd");
            if (!doceot.isModified("docdat")) {
                doceot.setDocdat(Dates.today());
            }
            doceotService.setDocSendFormat(doceot);
        }
        // delete message with deactivated channels
        for (i = trndoc.getDoceot().size() - 1; i >= 0; i--) {
            String cortyp = trndoc.getDoceot().get(i).getCortyp();
            if (!isCortypActive(cortyp) || MdaUtils.isEmpty(cortyp)) {
                trndoc.getDoceot().remove(i);
            }
        }
        // defaulting for ADDTXT/ADDSTR from RegisterAddTxt
        // #######TODO 改写 LABEL与GOTO   ####    label DoRegisterAddTxt
    }

    /**
     * source:mtabut.0050.script
     */
    @Override
    public boolean isConInPdxChanged(TrndocVo trndoc) {
        //! return TRUE if any values in PDX-Run changed that are defined as fixed
        boolean res = false;
        Streams.streamClear(trndoc.getOldpdxfixstm());
        Streams.streamInsert(trndoc.getOldpdxfixstm(), 0, trndoc.getPdxfixstm());
        Streams.streamClear(trndoc.getPdxfixstm());
        // post SetPdxFixStm-Methoden always to fill PDXFIXSTM
        //int cnt = Platform.postAllFittingMethods(ctx.getRoot(),"SETPDXFIXSTM","",true,-1);
        setPdxFixStm(trndoc);
        // compare both streams only in PDX recalculation
        if (sysmodService.sptIsExeLoaded(trndoc.getSpt().getSta()) && MdaUtils.compareTo("", "B") == 0) {
            Streams.streamSort(trndoc.getOldpdxfixstm());
            Streams.streamSort(trndoc.getPdxfixstm());
            if (MdaUtils.compareTo(MdaUtils.toString(trndoc.getOldpdxfixstm()), MdaUtils.toString(trndoc.getPdxfixstm())) != 0) {
                res = true;
            }
        }
        return res;
    }

    @Override
    public void trnDOCFillDOCTree() {

    }

    /**
     * source:trndoc.0129.script
     */
    public void setPdxFixStm(TrndocVo trndoc) {
        //! documets may not be changed by recalculation
        //! set PTAINR, CORTYP and name of documents in MTABUT\\PDXFIXSTM that may not be changed
        int cnt = trndoc.getDoceot().size();
        for (int i = 0; i < cnt; i++) {
            DoceotVo doceot = trndoc.getDoceot().get(i);
            if (!MdaUtils.isEmpty(doceot.getPtainr())) {
                Streams.streamInsert(trndoc.getPdxfixstm(), 0, doceot.getPtainr() + TAB + doceot.getCortyp() + doceot.getDocnam());
            }
        }
    }

    /**
     * source:cormod.0001.script
     */
    public boolean isCortypActive(String argCortyp) {
        // Check if cortyp is active
        // Cortyp can be activated/deactivated in transaction dbisys
        boolean active = true;
        String flag = NULLSTR;
        IStream corStm = new StreamImpl();
        Argument<String> _flagBox = Argument.box(flag);
        String corBlk = Inis.getIniBlock(AppIniFile, "CORTYP", "Cortyp");
        if (MdaUtils.isEmpty(corBlk)) {
            //Section [CORTYP] is empty or does not exist (DBISYS has not been executed), thus all CORTYPes are allowed
            flag = "X";
        } else {
            Streams.streamSet(corStm, corBlk);
            int idx = Streams.streamSearch(corStm, argCortyp);
            if (idx > 0) {
                String line = Strings.getLine(corStm, idx);
                String type = Strings.mid(line, 1, 3);
                flag = Strings.mid(line, 5, 1);
                if (MdaUtils.isEmpty(flag)) {
                    active = false;
                }
            }
        }
        return active;
    }

    /**
     * source:trndoc.0017.script
     */
    @Override
    public int docEOTGetRow(List<DoceotVo> doceotList, String refid) {
        int gridcnt = 0;
        if (doceotList != null) {
            gridcnt = doceotList.size();
        }
        for (int i = 0; i < gridcnt; i++) {
            Assert.notNull(doceotList);
            if (MdaUtils.compareTo(doceotList.get(i).getId(), refid) == 0) {
                return i;
            }
        }
        return -1;

    }

    /**
     * source:trndoc.0107.script
     */
    public String getStructures(TrndocVo trndoc, String argDOCNAM) {
        // GetTemplates returns for every document name (with structure byte removed)
        // a string containing a list of available structures
        // the consists of
        // (i.e. structures for which an implementing rule exists)
        // the string returned is a list of two byte entries separated by space
        // (before+after every entry)
        // 1st byte = structure
        //   L Letter
        //   S SWIFT
        //   X Telex
        //   T TradeConnect
        //   (list might be extended by customisation)
        // 2nd byte = `type of execution`
        //   P Panel
        //   R Rule (Method)
        //   S static function
        // DOCNAMSTM caches these entries
        int idx = Streams.streamSearch(trndoc.getDocnamstm(), argDOCNAM);
        String line = NULLSTR;
        String list = NULLSTR;
        String structure = NULLSTR;
        String execution = NULLSTR;
        String rulename = NULLSTR;
        if (idx > 0) {
            line = Strings.getLine(trndoc.getDocnamstm(), idx);
            return Strings.mid(line, Strings.pos(line, " ") + 1);
        } else {
            // determine available implementations
            list = " ";
            for (int i = 0; i < DOCNAMSEA.size(); i++) {
                structure = DOCNAMSEA.get(i).substring(0, 1);
                if (Strings.pos(list, " " + structure) == 0) {
                    // attempt only if structure is not yet covered
                    execution = DOCNAMSEA.get(i).substring(1);
                    rulename = doceotService.getDocRul(argDOCNAM, structure, execution);
                    switch (execution) {
                        case "P":
                        case "C":
                        case "S":
                        case "Q":
                        case "R":
                           /*if( Systems.checkStatement(this,"$a=" + rulename) )
                           {*/
                            // document panel
                            list = list + structure + execution + " ";
                            break;
                        /*}*/
                        /*}*/
                        /*}*/
                        default:
                            throw new CommonServiceException(Strings.formatText("Illegal Execution Flag $1", execution));
                    }
                }
            }
            Streams.streamInsert(trndoc.getDocnamstm(), 0, argDOCNAM + " " + list);
            return list;
        }

    }

    /**
     * source:trndoc.0032.script
     */
    public void setCORTYPList(TrndocVo trndocVo, String trigger, String subId, String corTYPList) {
        //! set allowed CORTYP values for the specified document
        //! if this function has been called onyl CORTYP matching with this list (one CORTYP per line) are allowed
        //! if this list contains just one entry, field CORTYP is automatically disabled
        // Should be called (e.g. in Registerdocument )immediately following  defdoceot
        String id = Strings.mid(trigger, 1, 3) + subId;
        int gridcnt = 0;
        if (trndocVo.getDoceot() != null) {
            gridcnt = trndocVo.getDoceot().size();
        }
        for (int i = 0; i < gridcnt; i++) {
            if (MdaUtils.compareTo(id, trndocVo.getDoceot().get(i).getId()) == 0) {
                trndocVo.getDoceot().get(i).setValcortyp(corTYPList);
                doceotService.setCORTYP(trndocVo.getDoceot().get(i));
                doceotService.setApfFlag(trndocVo, trndocVo.getDoceot().get(i), false);
            }
        }
    }

    /**
     * source:trndoc.0018.script
     */
    public String docEOTGetORISMHINR(List<DoceotVo> doceots, String refid) {
        for (int i = 0; i < doceots.size(); i++) {
            if (MdaUtils.compareTo(doceots.get(i).getId(), refid) == 0) {
                String smhinr = doceots.get(i).getSmhinr();
                if (MdaUtils.isEmpty(smhinr)) {
                    log.warn("SMHINR of referenced document does not yet exist in DOCEOTGetORISMHINR");
                    return "";
                } else {
                    return smhinr;
                }
            }
        }
        log.warn(Strings.formatText("Id $1 of referenced document not found", refid));
        return "";

    }

    /**
     * source:trndoc.0000.script
     */
    public void saveDocuments(TrndocVo trndoc) {
        // remove old documents of transaction if in recalculation
        String trninr = transactionHelpService.getTrninr();
        if (sysmodService.sptIsExeLoaded(trndoc.getSpt().getSta())) {
            Smh smh = smhMapper.getSmhByTrntypAndTrninrAndDir("TRN", trninr, ">");
            if (smh != null) {
                //删掉之前的文件和smh数据
                String fdr = docimmService.getDocumentFdr(smh);
                Files.fileDelete(fdr);
                smhMapper.deleteByPrimaryKey(smh);
            }
        }
        // Set ORIFLG in LEV=1 entries (to Original)
        int gridcnt = 0;
        if (trndoc.getDoceot() != null) {
            gridcnt = trndoc.getDoceot().size();
        }
        // Set ORIFLG in LEV=1 entries (to `No Original` if `Send to` entry exists)
        // Set GRPSEQ for attachments
        int groupLeaderIdx = -1;
        String refid = NULLSTR;
        for (int outidx = 0; outidx < gridcnt; outidx++) {
            DoceotVo doceotVo = trndoc.getDoceot().get(outidx);
            if ("1".equals(doceotVo.getLev())) {
                doceotVo.setOriflg("O");
            }
            doceotVo.setGrpseq(0);
            doceotVo.setSmhinr(NULLSTR);
            switch (doceotVo.getLev()) {
                case "2":
                    if (MdaUtils.compareTo(doceotVo.getOriflg(), "S") == 0) {
                        if (MdaUtils.compareTo(doceotVo.getDelflg(), " ") == 0) { // only if `Send To` entry is not suppressed
                            refid = doceotVo.getIdref();
                            for (int i = 0; i < gridcnt; i++) {
                                if (MdaUtils.compareTo(refid, doceotVo.getId()) == 0) {
                                    doceotVo.setOriflg("N");
                                    break;
                                }
                            }
                        }
                    }
                    groupLeaderIdx = outidx;
                    break;
                case "5":
                    // as attachments are sorted behind main document, $GroupLeaderIdx is always set when attachment is handled here
                    if (groupLeaderIdx > -1) {
                        trndoc.getDoceot().get(groupLeaderIdx).setGrpseq(trndoc.getDoceot().get(groupLeaderIdx).getGrpseq() + 1);
                        doceotVo.setGrpseq(trndoc.getDoceot().get(groupLeaderIdx).getGrpseq());
                    }
                    break;
                default:
                    groupLeaderIdx = outidx;
            }
        }
        IStream stmsmhinr = new StreamImpl();
        Streams.streamClear(stmsmhinr);
        //EOT will be saved after
        // save documents
        String msgtoo = NULLSTR;
        String docnam = NULLSTR;
        Map<String, String> smhMap = new HashMap<>();
        if (gridcnt > 0) {
            for (int idx = 0; idx < gridcnt; idx++) {
                //    if not IsEmpty( DOCEOT( $idx )\\id ) then
                DoceotVo doceotVo = trndoc.getDoceot().get(idx);
                if (!MdaUtils.isEmpty(doceotVo.getId())
                        && MdaUtils.compareTo(doceotVo.getAtttyp(), "EOT") != 0) {
                    Smh smh = doceotService.savdoc(doceotVo, trndoc);
                    if (smh == null) {
                        continue;
                    }
                    if ("ERC".equals(smh.getCortyp())) {
                        savErsdoc(smh);
                    }
                    if ("ERX".equals(smh.getCortyp())) {
                        savErxDoc(smh, doceotVo.getMsgtoo());
                    }
                    if ("SWT".equals(smh.getCortyp()) &&
                            ("103".equals(smh.getMsgtyp()) || "202".equals(smh.getMsgtyp()) || "200".equals(smh.getMsgtyp()))) {
                        savSMHOUT(smh, doceotVo.getDocnam());
                    }
                    smhMap.put(doceotVo.getId(), smh.getInr());
                }
            }
        }
        //Set EOT
        if (gridcnt > 0) {
            for (int idx = 0; idx < gridcnt; idx++) {
                DoceotVo doceotVo = trndoc.getDoceot().get(idx);
                if (!MdaUtils.isEmpty(doceotVo.getId())
                        && MdaUtils.compareTo(doceotVo.getAtttyp(), "EOT") == 0) {
                    String attinr = doceotVo.getAttinr();
                    if (smhMap.containsKey(attinr)) {
                        doceotVo.setAttinr(smhMap.get(attinr));
                    }
                    doceotVo.setAtttyp("SMH");
                    doceotService.savdoc(doceotVo, trndoc);
                    //Set back for repair
                    doceotVo.setAtttyp("EOT");
                    doceotVo.setAttinr(attinr);
                }
            }
        }
        // update missing ORISMHINR values
        if (gridcnt > 0) {
            for (int idx = 0; idx < gridcnt; idx++) {
                DoceotVo doceotVo = trndoc.getDoceot().get(idx);
                if ("INT".equals(doceotVo.getAtttyp())) {
                    if (MdaUtils.isEmpty(doceotVo.getAttinr())) {
                        Smh smh = new Smh();
                        smh.setInr(doceotVo.getSmhinr());
                        smh = smhMapper.selectByPrimaryKey(smh);
                        if (smh != null) {
                            smh.setOrismhinr(docEOTGetORISMHINR(trndoc.getDoceot(), doceotVo.getIdref()));
                            smhMapper.updateByPrimaryKeySelective(smh);
                        }
                    }
                }
            }
        }
    }

    /**
     * source:trndoc.0177.script
     */
    public void savErsdoc(Smh smh) {
        TrnmodVo trnmodVo = transactionHelpService.getTransactionStoreSet().getTrnmodVo();
        //! save document for this DOCEOT
        Ers ers = new Ers();
        ers.setCredat(Dates.today());
        ers.setSmhinr(smh.getInr());
        ers.setTrninr(smh.getTrninr());
        ers.setCortyp("ERC");
        ers.setMsgtyp(smh.getMsgtyp());
        ers.setDir(">");
        ers.setObjtyp(smh.getObjtyp());
        ers.setObjinr(smh.getObjinr());
        ers.setIniusr(smh.getCreusr());
        ers.setInifrm(SettleContext.getTransName());
        ers.setDocpth(smh.getDocpth());
        ers.setDocfil(smh.getDocfil() + "." + smh.getDocfxt());
        if (MdaUtils.compareTo(Strings.mid(ers.getMsgtyp(), 1, 2), "BH") == 0) {
            ers.setMsgfrm("270");
            ers.setMsgtoo("321");
        } else {
            ers.setMsgfrm("Z00001");
            ers.setMsgtoo("Z00074");
        }
        ers.setSta("C");
        ers.setBchcod(SettleContext.getUserSession().getWrkbch().getBranch());
        ers.setUsrbchcod(SettleContext.getUserSession().getBch().getBranch());
        ers.setOwnref(trnmodVo.getTrn().getOwnref());
        ers.setAmt(trnmodVo.getTrn().getRelamt());
        ers.setCur(trnmodVo.getTrn().getRelcur());
        ers.setInr(counterService.dbCounterFormat("ERS"));
        ersMapper.insert(ers);
    }

    /**
     * source:trndoc.0176.script
     */
    public void savErxDoc(Smh smh, String argMsgToo) {
        //!################################################
        //!功能：ERX报文保存ERS记录
        //! 输入：argmsgtoo发报系统编号,是否生成根据控制Codetable ERSSYS
        //!################################################
        TrnmodVo trnmodVo = transactionHelpService.getTransactionStoreSet().getTrnmodVo();
        List<CodetableItem> codetableItems = codetableService.selectList("ERSSYS", "CN");
        boolean value = false;
        if (codetableItems != null && codetableItems.size() > 0) {
            for (CodetableItem item : codetableItems) {
                if (item.getValue().equals(argMsgToo)) {
                    value = true;
                    break;
                }
            }
        }
        if (value) {
            //---主动发起云平台保函后续交易，读取开立时候第三方编号---
            /*if( ! MdaUtils.isEmpty(this.getErsmod().getWrkers().getInr()) ) {
                resnum = "";
                status = "03";
                ctx.getDaoSession().dbRead(this.getErs(),this.getErsmod().getWrkers().getInr(),"INR");
                err = ctx.getErrorCode();
                this.getErs().setFtynum( "");
                this.getErs().setResnum( "");
            }*/
            Ers ers = new Ers();
            ers.setCredat(Dates.today());
            ers.setSmhinr(smh.getInr());
            ers.setTrninr(smh.getTrninr());
            ers.setCortyp(smh.getCortyp());
            ers.setMsgtyp(smh.getMsgtyp());
            ers.setDir(">");
            ers.setObjtyp(smh.getObjtyp());
            ers.setObjinr(smh.getObjinr());
            ers.setIniusr(smh.getCreusr());
            ers.setInifrm(SettleContext.getTransName());
            ers.setDocpth(smh.getDocpth());
            ers.setDocfil(smh.getDocfil() + "." + smh.getDocfxt());
            if (MdaUtils.compareTo(Strings.mid(ers.getMsgtyp(), 1, 2), "BH") == 0) {
                ers.setMsgfrm("270");
                ers.setMsgtoo("321");
            } else {
                ers.setMsgfrm("Z00001");
                ers.setMsgtoo(argMsgToo);
            }
            ers.setSta("C");
            ers.setOwnextkey(SettleContext.getUserSession().getWrkbch().getBranch());
            ers.setUsrbchcod(SettleContext.getUserSession().getBch().getBranch());
            ers.setOwnref(trnmodVo.getTrn().getOwnref());
            ers.setInr(counterService.dbCounterFormat("ERS"));
            ersMapper.insert(ers);
        }
    }

    /**
     * source:trndoc.0178.script
     */
    public void savSMHOUT(Smh smh, String docnamtext) {

    }

    /**
     * source:trndoc.0025.script
     */
    public String docEOTGetCORTYP(TrndocVo trndoc, String trigger, String subId) {
        // retrieves CORTYP for document
        // should be used only after final call of assertDOCS
        // if digit 10-12 of subid are filled, secondary messages can be addressed
        String id = Strings.mid(trigger, 1, 3) + Strings.mid(subId, 1, 12);
        int gridcnt = trndoc.getDoceot().size();
        if (gridcnt > 0) {
            for (int i = 0; i < gridcnt; i++) {
                if (MdaUtils.compareTo(id, trndoc.getDoceot().get(i).getId()) == 0) {
                    return trndoc.getDoceot().get(i).getCortyp();
                }
            }
        }
        return "";
    }

    /**
     * source:gitdoc.0000.script
     */
    public void documentToAllApplicants(TrndocVo trndocVo, String subId, String argdocpannam, String apfcod, BigDecimal mandatoryFlag, String pandsc, String excluderole) {
        //! registers the document identified by panel argdocpannam for all applicants.
        //! the passed arguments are the same as for defdoceot.
        //! the subid must not be longer than 9 digits as the role is added for the real subids.
        //! With excluderole a specified role may be excluded, e.g. if the applicant gets another message with the undertaking
        // set document for applicant
        if (MdaUtils.compareTo(excluderole, "APL") != 0) {
            PtsptaVo aplPtspta = ptsptaToolComponent.getPtsFromListByRol(trndocVo.getPtsptas(), "APL");
            defDocEot(trndocVo, "TRN", subId + "APL", argdocpannam, apfcod, mandatoryFlag.intValue(), aplPtspta == null ? "" : aplPtspta.getPtainr(), "\\GIDGRP\\REC", pandsc, "APL", "");
        }
        // set document for applicants 2 to 9 (if set)
        for (int i = 2; i <= 9; i++) {
            if (MdaUtils.compareTo(excluderole, "AP" + MdaUtils.toString(i, -1)) != 0) {
                PtsptaVo apiPtspta = ptsptaToolComponent.getPtsFromListByRol(trndocVo.getPtsptas(), "AP" + MdaUtils.toString(i, -1));
                if (apiPtspta != null && StringUtils.isNotEmpty(apiPtspta.getPts().getInr())) {
                    defDocEot(trndocVo, "TRN", subId + "AP" + MdaUtils.toString(i, -1), argdocpannam, apfcod, mandatoryFlag.intValue(), "", "\\GIDGRP\\REC", pandsc, "AP" + MdaUtils.toString(i, -1), "");
                }
            }
        }
    }

    public void setMdtFlg(TrndocVo trndocVo, String subID, Argument<BigDecimal> mdt) {
        //! Manages how a document is shown on message panel.
        //! It is used in RegisterDocument (for defdoceot) only.
        //! Has to be called after the connected but before the controlled document.
        // If SubID (of the connected document) is empty "0" (= not mandatory) is returned.
        // Cover Letter should be shown "suppressed" (= 2) on messages panel, if the
        //   connected document, i.e. Guarantee, Guarantee Draft, will be sent via Swift or Telex,
        //   otherwise will be returned "not mandatory" (= 0).
        // (DO5DOS 001809)
        mdt.value = new BigDecimal("0");
        String corTyp = NULLSTR;
        if (!MdaUtils.isEmpty(subID)) {
            corTyp = docEOTGetCORTYP(trndocVo, "TRN", subID);
            // suppress for Swift and Telex
            if (MdaUtils.compareTo(corTyp, "SWT") == 0 || MdaUtils.compareTo(corTyp, "TLX") == 0) {
                mdt.value = new BigDecimal("2");
            }
        }

    }

    /**
     * source:trndoc.0093.script
     */
    public String streamGet2SWIFT(IStream argStm, int argMaxLin, int argMaxLen, String argCharSet) {
        //取得上下文
//        IContext ctx = MdaEnv.getContext();
//        ctx.setErrorCode(NO_ERROR);
        //! Get raw character data from ArgStm
        //! Set errorcode -1 if 1st digit in lines are - or :
        //DNGDEV.001768: Added ArgCharset
        String gidTxt = Streams.streamGetAdditional(argStm, "CHARDATA", "");
        int convOption = 1;
        if (sysmodService.isSwtRelNov2020Active()) {
            convOption = 6;
        }
        String block = Strings.convertBlock(gidTxt, convOption, argMaxLin, argMaxLen); // DNGDEV.000691.GID 2019? (called in  GIMOPN, GITOPN, GITAME and GITASG)
        IStream wrkStm = new StreamImpl();
        Streams.streamSet(wrkStm, gidTxt);
        int pos1 = Streams.streamSearch(wrkStm, "-");
        int pos2 = Streams.streamSearch(wrkStm, ":");
        if (pos1 > 0 || pos2 > 0) {
            SettleExceptionUtils.sysDump("");
        }
        return block;

    }

    /**
     * source:trndoc.0008.script
     */
    public boolean docEOTIsCORTYPUsed(String argCorTyp, TrndocVo trndoc) {
        //取得上下文
//        IContext ctx = MdaEnv.getContext();
//        ctx.setErrorCode(NO_ERROR);
        // Returns TRUE if at least one message in DOCEOT is send via ArgCorTyp
        boolean isCorTypused = false;
        int gridcnt = trndoc.getDoceot().size();
        if (gridcnt > 0) {
            for (int i = 0; i < gridcnt; i++) {
                if (MdaUtils.compareTo(trndoc.getDoceot().get(i).getCortyp(), argCorTyp) == 0) {
                    isCorTypused = true;
                    break;
                }
            }
        }
        return isCorTypused;

    }

    /**
     * source:trndoc.0081.script
     */
    public void defDocAtt(TrndocVo trndocVO, String trigger, String argID, String argSUBID, String argDsc, String argATTTYP, String argATTINR, String argIDREF) {
        //! sub to register attachment
        //! `trigger`+`argID` internal ID of parent document
        //! `argSubID` subId under parent
        //! `argATTTYP` record type of attachement (`SMH`, `FIL`, `INT`
        //! `argIDREF` id of attachend internal document if argATTTYP is `INT`
        //! function returns new ID
        String idref = NULLSTR;
        if (MdaUtils.compareTo(argATTTYP, "INT") == 0) {
            idref = argIDREF;
        } else {
            idref = "";
        }
        // determine full for attachment
        String parentID = trigger + argID;
        int len = Strings.len(argSUBID);
        String id = Strings.mid(parentID, 1, 15 - 1 - len);
        id = Strings.trim(id) + "A" + argSUBID;
        int doceotIdx = docEOTGetRow(trndocVO.getDoceot(), parentID);
        String newCORTYP = getCORTYPofAttachment(trndocVO.getDoceot().get(doceotIdx).getCortyp());
        //新增一条附件 doceot
        DoceotVo doceotVo = new DoceotVo();
        doceotVo.setId(id);
        doceotVo.setLev("5");
        doceotVo.setIdref(idref);
        doceotVo.setApfcpymli(-1);
        doceotVo.setDocnam("");
        doceotVo.setPtainr(trndocVO.getDoceot().get(doceotIdx).getPtainr());
        doceotVo.setArgptainr(trndocVO.getDoceot().get(doceotIdx).getPtainr());
        doceotVo.setObjmod(trndocVO.getDoceot().get(doceotIdx).getObjmod());
        doceotVo.setPandsc(argDsc);
        doceotVo.setRole(trndocVO.getDoceot().get(doceotIdx).getRole());
        doceotVo.setAdrblk("");
        doceotVo.setMdtflg(0);
        doceotVo.setApf(trndocVO.getDoceot().get(doceotIdx).getApf());
        doceotVo.setOldapf(doceotVo.getApf());     // remember original apf
        doceotVo.setUsdflg(0);
        doceotService.setAddressingInfo(doceotVo, trndocVO.getPtsptas());
        doceotVo.setDocstrsea("");
        doceotVo.setDoccorsea(newCORTYP + " ");
        doceotVo.setCortyp(newCORTYP);
        doceotVo.setValcortyp(newCORTYP);
        doceotVo.setIdatt(parentID);
        doceotVo.setAtttyp(argATTTYP);
        doceotVo.setAttinr(argATTINR);
        doceotService.setLang(doceotVo);
        doceotService.setApfFlag(trndocVO, doceotVo, false);
        doceotService.setSRTKEY(doceotVo);
        trndocVO.getDoceot().add(doceotVo);
    }

    /**
     * source:trndoc.0112.script
     */
    public String getCORTYPofAttachment(String argCORTYP) {
        //! returns CORTYP of attachment for argCORTYP
        switch (argCORTYP) {
            case "BOL":
                return "BOA";
            case "EML":
                return "EMA";
            case "TCO":
                return "TCA";
            case "LET":
                return "LET";
            case "SWT":
                return "SWA";
            default:
                log.warn(Strings.formatText("Messages of type \'$1\' cannot carry attachments!", codetableService.getTblLabel(argCORTYP, "CORTYP")));
                return "";
        }

    }

    public void setDOCUIL(TrndocVo trndocVo, String trigger, String subId, String docUil) {
        List<DoceotVo> doceotList = trndocVo.getDoceot();
        if (!CollectionUtils.isEmpty(doceotList)) {
            String id = trigger.substring(0, 3) + subId;
            //! set allowed DOCUIL values for the specified document
            //! if this function has been called only DOCUIL matching with one entry are allowed
            // Should be called (e.g. in Registerdocument )immediately following  defdoceot
            for (int i = 1; i <= doceotList.size(); i++) {
                if (MdaUtils.compareTo(id, doceotList.get(i).getId()) == 0) {
                    doceotList.get(i).setValdocuil(docUil);
                    //doceotList.get(i).setLang();
                    return;
                }
            }
        }

    }

    /**
     * source:trndoc.0042.script
     */
    public String docEOTGetField(TrndocVo trndocVo, String refid, String fieldName) {
        if (!MdaUtils.isEmpty(trndocVo.getDoceot())) {
            Optional<DoceotVo> doceotOption = trndocVo.getDoceot().stream().filter(doceot -> refid.equals(doceot.getId().trim())).findFirst();
            if (doceotOption.isPresent()) {
                DoceotVo doceotVo = doceotOption.get();
                return Utils.getFieldValue(doceotVo, fieldName);
            }
        }
        return "";
    }

    @Override
    public String showDOCS(Map<String, Object> paramMap) throws IOException {
        return "";
//        JSONArray doceot = (JSONArray) paramMap.get("doceot");
//        String templateName = "";
//        if (doceot != null && doceot.size() > 0) {
//            Map<String, Object> doceotVo = (Map<String, Object>) doceot.get(0);
//            templateName = (String) doceotVo.get("docnam");
//        }
////            String templateName = (String) paramMap.get("docnam");
//        if (StringUtils.isEmpty(templateName)) {
//            return "";
//        }
////            String fileName = (String) paramMap.get("fileName");
//        File dir = new File(appEnv.getRootPath() + "/file");
//        if (!dir.exists()) {
//            dir.mkdirs();
//        }
//        String filePath = appEnv.getRootPath() + "/file/" + templateName;
////            String templatePath = appEnv.getRootPath() + "/template/"+templateName;
//        InputStream resourceAsStream = TrndocServiceImpl.class.getResourceAsStream("/template/" + templateName);
//        if (resourceAsStream == null) {
//            return null;
//        }
//        XWPFTemplate template = XWPFTemplate.compile(resourceAsStream).render(paramMap);
//        // 将模板参数写入路径
//        template.writeToFile(filePath);
//        template.close();
//        //读取文件返回给前端
//        File file = new File(filePath);
//        FileInputStream fis = null;
//        ByteArrayOutputStream bos = new ByteArrayOutputStream(1000);
//        try {
//            fis = new FileInputStream(file);
//            byte[] b = new byte[1000];
//            int n;
//            while ((n = fis.read(b)) != -1) {
//                bos.write(b, 0, n);
//            }
//            byte[] data = bos.toByteArray();
//            String base64String = Base64.encode(data);
//            return base64String;
//        } catch (IOException e) {
//            throw e;
//        } finally {
//            if (fis != null) {
//                fis.close();
//            }
//            if (bos != null) {
//                bos.close();
//            }
//
//        }
    }

    @Override
    public void wordToPdf(String filePath, String targetPath) throws IOException {
        //将文件转成pdf
//        Document doc = new Document();
//        // Load a sample Word document
//        doc.loadFromFile(filePath);
//        // Create a ToPdfParameterList instance
//        ToPdfParameterList ppl = new ToPdfParameterList();
//        // Embed all fonts in the PDF document
//        ppl.isEmbeddedAllFonts(true);
//        // Remove the hyperlinks and keep the character formats
//        ppl.setDisableLink(true);
//        // Set the output image quality as 40% of the original image. 80% is the default setting.
//        doc.setJPEGQuality(40);
//        // Save the document as PDF
//        doc.saveToFile(targetPath, ppl);
    }

    public void removeCORTYPFromList(TrndocVo trndocVo, String trigger, String subId, String cortyp) {
        //! Removes the CORTYP from the list of possible CORTYP values for the specified document
        //! must be called after DEFDOCEOT call for that document
        //! if this list contains just one entry, field CORTYP is automatically disabled
        // Should be called (e.g. in Registerdocument )immediately following  defdoceot
        IStream cORTYPStm = new StreamImpl();
        Streams.streamClear(cORTYPStm);
        Streams.streamSet(cORTYPStm, cortyp);
        int cORTYPCnt = Streams.streamCount(cORTYPStm);
        String id = Strings.mid(trigger, 1, 3) + subId;
        int gridcnt = null == trndocVo.getDoceot() ? 0 : trndocVo.getDoceot().size();
        String doccorsea = NULLSTR;
        for (int i = 0; i < gridcnt; i++) {
            if (id.equals(trndocVo.getDoceot().get(i).getId())) {
                doccorsea = trndocVo.getDoceot().get(i).getDoccorsea();
            }
        }
        IStream allowedCORTYPES = new StreamImpl();
        for (int idx = 1; idx <= Strings.len(doccorsea); idx += 4) {
            Streams.streamInsert(allowedCORTYPES, 0, Strings.mid(doccorsea, idx, 3));
        }
        String vcortyp = NULLSTR;
        int delidx = 0;
        for (int i = 1; i <= cORTYPCnt; i++) {
            vcortyp = Strings.getLine(cORTYPStm, i);
            delidx = Streams.streamSearch(allowedCORTYPES, vcortyp);
            if (delidx > 0) {
                Streams.streamDelete(allowedCORTYPES, delidx);
            }
        }
        setCORTYPList(trndocVo, trigger, subId, MdaUtils.toString(allowedCORTYPES));

    }

    @Override
    public String convertDocx2Pdf(String wordPath, String pdfOutPath) throws Exception {
        long startTime = System.currentTimeMillis();
        OutputStream os = null;
        InputStream is = null;
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            fis = new FileInputStream(wordPath);
            XWPFDocument document = new XWPFDocument(fis);
            List<XWPFParagraph> paragraphs = document.getParagraphs();
            for (XWPFParagraph paragraph : paragraphs) {
                List<XWPFRun> runs = paragraph.getRuns();
                for (XWPFRun run : runs) {
                    run.setFontFamily("宋体");
                }
            }
            fos = new FileOutputStream(wordPath);
            document.write(fos);
        } finally {
            IOUtils.closeQuietly(fis);
            Assert.notNull(fos);
            fos.flush();
            IOUtils.closeQuietly(fos);
        }
        try {
            is = new FileInputStream(wordPath);
            /*WordprocessingMLPackage mlPackage = WordprocessingMLPackage.load(is);
            Mapper fontMapper = new IdentityPlusMapper();
            fontMapper.put("隶书", PhysicalFonts.get("LiSu"));
            fontMapper.put("宋体", PhysicalFonts.get("SimSun"));
            fontMapper.put("微软雅黑", PhysicalFonts.get("Microsoft Yahei"));
            fontMapper.put("黑体", PhysicalFonts.get("SimHei"));
            fontMapper.put("楷体", PhysicalFonts.get("KaiTi"));
            fontMapper.put("新宋体", PhysicalFonts.get("NSimSun"));
            fontMapper.put("华文行楷", PhysicalFonts.get("STXingkai"));
            fontMapper.put("华文仿宋", PhysicalFonts.get("STFangsong"));
            fontMapper.put("宋体扩展", PhysicalFonts.get("simsun-extB"));
            fontMapper.put("仿宋", PhysicalFonts.get("FangSong"));
            fontMapper.put("仿宋_GB2312", PhysicalFonts.get("FangSong_GB2312"));
            fontMapper.put("幼圆", PhysicalFonts.get("YouYuan"));
            fontMapper.put("华文宋体", PhysicalFonts.get("STSong"));
            fontMapper.put("华文中宋", PhysicalFonts.get("STZhongsong"));
            mlPackage.setFontMapper(fontMapper);
            os = new FileOutputStream(pdfOutPath);
            FOSettings foSettings = Docx4J.createFOSettings();
            foSettings.setWmlPackage(mlPackage);
            Docx4J.toFO(foSettings, os, Docx4J.FLAG_EXPORT_PREFER_XSL);
            System.out.println("转换完成" + (System.currentTimeMillis() - startTime) + " ms.");*/
        } finally {
            IOUtils.closeQuietly(is);
            IOUtils.closeQuietly(os);
//            File file = new File(wordPath);
//            if (file != null && file.isFile() && file.exists()) {
//                file.delete();
//            }
        }
        return getBase64ByFile(pdfOutPath);
    }

    public String getBase64ByFile(String filePath) throws IOException {
        //读取文件返回给前端
        File file = new File(filePath);
        FileInputStream fis = null;
        ByteArrayOutputStream bos = new ByteArrayOutputStream(1000);
        try {
            fis = new FileInputStream(file);
            byte[] b = new byte[1000];
            int n;
            while ((n = fis.read(b)) != -1) {
                bos.write(b, 0, n);
            }
            byte[] data = bos.toByteArray();
            String base64String = Base64.encode(data);
            return base64String;
        } catch (IOException e) {
            throw e;
        } finally {
            IOUtils.closeQuietly(fis);
            IOUtils.closeQuietly(bos);
        }
    }

    /**
     * 设置对应的doceot条目的 79z的cortyp
     *
     * @param trndoc
     * @param trigger
     * @param subId
     * @param argCortypLst
     */
    @Override
    public void setUseTag79Z(TrndocVo trndoc, String trigger, String subId, String argCortypLst) {
        //! set list of cortypes that use tag :79Z:
        //! in trigger and SubId:ID and sub id of message as used in defdoceot
        //! ArgCortypLst : CR seperated list of Cortypes supporting Tag 79Z
        String id = Strings.mid(trigger, 1, 3) + subId;
        int gridCnt = trndoc.getDoceot().size();
        for (int i = 0; i < gridCnt; i++) {
            if (MdaUtils.compareTo(id, trndoc.getDoceot().get(i).getId()) == 0) {
                Streams.streamSet(trndoc.getDoceot().get(i).getUsetag79z(), argCortypLst);
                return;
            }
        }
    }

    public void printheaderen(Map<String, Object> map) {
        String branch = "";
        String address = "";
        String swiftbic = "";
        String tel = "";
        String fax = "";
        if (SettleContext.getUserSession().getWrkbch() != null) {
            branch = SettleContext.getUserSession().getWrkbch().getNamen();
            address = SettleContext.getUserSession().getWrkbch().getAdren() + " " + SettleContext.getUserSession().getWrkbch().getAdren2();
            if (Strings.pos(branch, "CHINA EVERBRIGHT BANK") != 0) {
                branch = Strings.mid(branch, 23);
            }
            swiftbic = SettleContext.getUserSession().getWrkbch().getSwfcod();
            tel = SettleContext.getUserSession().getWrkbch().getTel();
            fax = SettleContext.getUserSession().getWrkbch().getFax();
        }
        map.put("branch", branch);
        map.put("address", address);
        map.put("swiftbic", swiftbic);
        map.put("tel", tel);
        map.put("fax", fax);
    }

    public void printfooteren(Map<String, Object> map) {
        //修改面函机构，xxf改<------------------------------------------------------
        //----英文取分行的名称--------------------
        String xml14 = "";
        String swift = "";
        if (SettleContext.getUserSession().getWrkbch() != null) {
            xml14 = Strings.trim(SettleContext.getUserSession().getWrkbch().getNamen())
                    + " " + SettleContext.getUserSession().getWrkbch().getAdren()
                    + " " + SettleContext.getUserSession().getWrkbch().getAdren2();
            swift = "SWIFT:" + Strings.trim(SettleContext.getUserSession().getWrkbch().getSwfcod());
        }else if(SettleContext.getUserSession().getBch() != null) {
            //当前选择的所属机构号
            Bch bch = SettleContext.getUserSession().getBch();
            xml14 = new StringBuffer().append(Strings.trim(bch.getNamen())).append(" ").append(bch.getAdren()).append(" ").append(bch.getAdren2()).toString();
            swift = "SWIFT:" + Strings.trim(bch.getSwfcod());
        }
        map.put("XML14", xml14);
        map.put("SWIFT", swift);
    }


    /**
     * source:trndoc.0219.script
     */
    public void xmlPrtAdrOfReceiver(Map<String, List<String[]>> dataMap, DoceotVo doceotVo) {
        //! Receiver address is printed with four empty lines.
        // print Dispatch Type
        doceotService.xmlPrintClass_TRX(dataMap, doceotVo);
//        xmlPrtEmptyLine(dataMap, 1);
        doceotService.printAddressBlock(dataMap, doceotVo);
        /*
         $trnnam = GetModuleInfo( \\, tdModuleInfoClass )
         $prdtyp = Mid( $trnnam, 1, 2 )
         $recgrp = "\\\\" + $prdtyp + "DGRP\\\\REC"
         */
        /*
         // get current xxxGRP (also OLDxxxGRP, etc.)
         $mod = GetModule( \\TRNMOD\\PTSMOD\\PTSPTAMODNAM )
         // get current PTSPTA
         PtsmodGetPtsptaFromRol( DOCCUR\\ROLE, $mod, \\TRNMOD\\PTSMOD\\PTSPTA )
         // print address
         PtsModXMLPrtAdr( \\TRNMOD\\PTSMOD\\PTSPTA )
         */
//        xmlPrtEmptyLine(dataMap, 4);
    }

    /**
     * source:trndoc.0222.script
     */
    public void xmlPrtEmptyLine(Map<String, List<String[]>> dataMap, int argNumber) {
        //! prints a number of empty lines
        //! with following properties for Page-Break:
        //! - widows = 3
        //! - keep with previous
        // Please note: If properties of following XMLs are changed,
        //              the documentation of messages (gnMsgRul.htm)
        //              also has to be corrected!
        for (int i = 1; i <= argNumber; i++) {
            DocUtils.putData(dataMap, "trndoc.emptyline");
        }
        // The following call has the same result!
        // Really? How works "keep with previous"?
        /*
         print vertical ( 1, tdUnitem, tdPosCurrent
        */
    }

    /**
     * source:trndoc.0108.script
     */
    public int getCurrentCORLineLength(DoceotVo doceotVo) {
        //! returns the maximum linelength for the medium for which the current document/message is created
        //! -1 is returned for Letter
        //! -2 is returned for `ASCII only` messages without limitation or line length
        // the value returned by this function has to be respected if
        // e.g. a letter will generate different parts (e.g. header or settlement)
        // if the eventual medium will be telex or fax
        switch (doceotVo.getCortyp()) {
            case "TLX":
            case "EML":
            case "FAX":
                return 69;
            case "BOL":
                return 79;
            case "SWT":
            case "RTG":
            case "TAR":
            case "TCO":
                return -2;
            default:
                return -1;
        }
    }

    /**
     * source:trndoc.0236.script
     */
    public String xmlGetTxtSepTxt(String argTxt1, String argSep, String argTxt2) {
        //! Bind two arguments with a passed seperator.
        //! Insert seperator between argTxt1 and argTxt2.
        //! argSep = seperator, e.g. (" "), (", "), ("\\b"), (CR), (CR + CR)
        String res = "";
        if (!MdaUtils.isEmpty(argTxt1)) {
            res = argTxt1;
        }
        if (!MdaUtils.isEmpty(argTxt2)) {
            if (MdaUtils.isEmpty(res)) {
                res = argTxt2;
            } else {
                res = res + argSep + argTxt2;
            }
        }
        return res;
    }

    /**
     * source:trndoc.0211.script
     */
    public void xmlPrintTitleBold(Map<String, List<String[]>> dataMap, DoceotVo doceotVo, String argTitle) {
        //! prints a bold text with an empty line
        //! most used as titel of letters
        if (!MdaUtils.isEmpty(argTitle)) {
            switch (getCurrentCORLineLength(doceotVo)) {
                case -1:  // for Letter
                    // the text is bold with font-size = default
                    DocUtils.putData(dataMap, "trndoc.LineBold", argTitle);
//                    xmlPrtEmptyLine(dataMap, 1);
                    break;
                case 69:   // for Telex, Fax and eMail
//                    Systems.printTemp(argTitle);
                    //                printTemp2Segment(buf,o.toString());
                    DocUtils.putData(dataMap, "trndoc.LineBold", argTitle);
                    break;
                default: // for Swift
                    // no output of title
            }
        }

    }

    /**
     * source:trndoc.0233.script
     */
    public void xmlPrtRefLine(Map<String, List<String[]>> dataMap, DoceotVo doceotVo, String argText, String argValue) {
        //! Get standard line of reference.
        //! If no argValue is passed, no line of reference will be returned.
        if (!MdaUtils.isEmpty(argValue)) {
            switch (getCurrentCORLineLength(doceotVo)) {
                case -1:  // for Letter
                    DocUtils.putData(dataMap, "refLine", argText, argValue);
                    break;
                case 69:   // for Telex, Fax and eMail
                    DocUtils.putData(dataMap, "refLine", Strings.mid(argText + ":", 1, 25) + argValue + "\r\n");
                    break;
                default: // for Swift
                    // no output of reference lines
            }
        }
    }

    /**
     * source:trndoc.0220.script
     */
    public void xmlPrtSalutation(Map<String, List<String[]>> dataMap, DoceotVo doceotVo) {
        //! is used at ordinary letters over all transactions
        // print salutation only for LETTER and TELEX
        if (getCurrentCORLineLength(doceotVo) == -1 || MdaUtils.compareTo(doceotVo.getCortyp(), "TLX") == 0) {
            // salutation
            DocUtils.putData(dataMap, "salutation", "Dear Sirs,");
//            xmlPrtEmptyLine(dataMap, 1);
        }

    }

    /**
     * source:trndoc.0237.script
     */
    public <T extends BaseTransactionVo> void xmlPrtRefLineYourRef(Map<String, List<String[]>> dataMap, T transactionVo, DoceotVo doceotVo) {
        //! prints a common reference block with four empty lines
        //! is used at ordinary letters over all transactions
        // print reference block only for LETTER and TELEX
        if (getCurrentCORLineLength(doceotVo) == -1 || MdaUtils.compareTo(doceotVo.getCortyp(), "TLX") == 0) {
            xmlPrtRefLine(dataMap, doceotVo, "Your Reference", getYourRef(transactionVo, doceotVo));
        }
    }

    public <T extends BaseTransactionVo> void xmlPrtRefLineOwnRef(Map<String, List<String[]>> dataMap, T transactionVo, DoceotVo doceotVo) {
        //! prints a common reference block with four empty lines
        //! is used at ordinary letters over all transactions
        // print reference block only for LETTER and TELEX
        if (getCurrentCORLineLength(doceotVo) == -1 || MdaUtils.compareTo(doceotVo.getCortyp(), "TLX") == 0) {
            xmlPrtRefLine(dataMap, doceotVo, "Our Reference", getYourRef(transactionVo, doceotVo));
        }
    }
    /**
     * source:trndoc.0217.script
     */
    private <T extends BaseTransactionVo> String getYourRef(T transactionVo, DoceotVo doceotVo) {
        //! get reference for role of current document
        String yourref = "";
//        String grpMod = Dynamic.ptsModGetConGrpPathOfRol( role );
        String role = doceotVo.getRole();
        List<PtsptaVo> ptsList = transactionHelpService.getTransactionStoreSet().getPtsList();
        PtsptaVo pts = ptsptaToolComponent.getPtsFromListByRol(ptsList, role);

        if (MdaUtils.isEmpty(pts)) {
            // For "OTH" it may be available in \\FREMSG\\OTHMSG
            if ("OTH".equals(role)) {
                FremsgVo fremsg = Utils.getFieldValue(transactionVo, "fremsg");
                if (fremsg == null) {
                    yourref = NULLSTR;
                } else {
                    String msgrol = fremsg.getMsgrol();
                    String ccrol = fremsg.getCcrol();
                    if (StringUtils.isEmpty(msgrol) || StringUtils.isEmpty(ccrol)) {
                        yourref = NULLSTR;
                    } else {
                        // MSGROL or/and CCROL could be OTH
                        if (MdaUtils.compareTo(msgrol, role) == 0 || MdaUtils.compareTo(ccrol, role) == 0) {
                            String othmsgExtkey = fremsg.getOthmsg().getPts().getExtkey();
                            if (MdaUtils.compareTo(othmsgExtkey, doceotVo.getRcv().getPts().getExtkey()) == 0) {
                                yourref = fremsg.getOthmsg().getPts().getRef();
                            } else {
                                String othccExtkey = fremsg.getOthcc().getPts().getExtkey();
                                if (MdaUtils.compareTo(othccExtkey, doceotVo.getRcv().getPts().getExtkey()) == 0) {
                                    yourref = fremsg.getOthcc().getPts().getRef();
                                } else {
                                    yourref = NULLSTR;
                                }
                            }
                        } else {
                            yourref = NULLSTR;
                        }
                    }
                }
            }
        } else {
            yourref = pts.getPts().getRef();
//            if (ctx.getErrorCode() != 0) {
//                yourref = NULLSTR;
//                ctx.setMessage(MdaUtils.getI18NString("trndoc", "CT000955"), ctx.getErrorCode());
//            }
        }
        // (DO5DOS.006144)
        // for cover letters (via "Copy to"/ "Send to") take reference from ADRREF instead of ROLE,
        //   because in this case ROLADR can be different from ROLE
        if (MdaUtils.compareTo(doceotVo.getLev(), "2") == 0) {
            // do not print literal "NONREF"
            if (MdaUtils.compareTo(doceotVo.getAdrref(), "NONREF") != 0) {
                yourref = doceotVo.getAdrref();
            }
        }
        return yourref;
    }

    /**
     * source:trndoc.0230.script
     */
    public String xmlFmtCurAmt(DoceotVo doceotVo, String argCUR, BigDecimal argAMT) {
        //! connect currency and amount at current language of document
        //! with a non-breaking (protected) space
        return argCUR + " " + Formats.fmtAmount(argAMT, argCUR, doceotVo.getDocuil());
    }

    /**
     * source:trndoc.0231.script
     */
    public String xmlFmtAmount(DoceotVo doceotVo, BigDecimal argAMT, String argCUR) {
        //! get amount at current language of document
        return Formats.fmtAmount(argAMT, argCUR, doceotVo.getDocuil());

    }

    /**
     * source:trndoc.0229.script
     */
    public String xmlFmtDate(DoceotVo doceotVo, String argDatTyp, Date argDate) {
        //! get date at current language of document
        //! possible values of argDatTyp are:
        //!   "L" = long
        //!   "S" = short
        //!   "N" = normal
        String docUil = doceotVo.getDocuil();
        String typ = Strings.toUpper(argDatTyp);
        switch (typ) {
            case "L":
                return Formats.fmtDateLong(argDate, docUil);
            case "S":
                return Formats.fmtDateShort(argDate, docUil);
            case "N":
                return Formats.format(argDate, docUil);
        }
        return NULLSTR;
    }

    /**
     * source:trndoc.0232.script
     */
    public void xmlPrtRefLinePty(Map<String, List<String[]>> dataMap, DoceotVo doceotVo, PtsptaVo argGrpRol) {
        //! Get complete name of party with his description of role
        //! for standard line of reference block.
        String desc = codetableService.getTblLabel(argGrpRol.getRol(), "rolall");
        String pty = xmlGetPtyNam(argGrpRol);
        xmlPrtRefLine(dataMap, doceotVo, desc, pty);
    }

    /**
     * source:trndoc.0234.script
     */
    private String xmlGetPtyNam(PtsptaVo argGrpRol) {
        //! Get complete name of party.
        StringBuilder nam = new StringBuilder();
        Adr adr = argGrpRol.getAdr();
        if (adr != null) {
            String nam1 = adr.getNam1();
            String nam2 = adr.getNam2();
            String nam3 = adr.getNam3();
            String town = adr.getLoctxt();
            nam.append(xmlGetTxtSepTxt(nam1, " ", nam2));
            nam.append(xmlGetTxtSepTxt(nam.toString(), " ", nam3));
            nam.append(xmlGetTxtSepTxt(nam.toString(), ", ", town));
        } else {
            //! returns the Addressline of address ARGPTSPTA delimited by ","
            String dlm = "";
            IStream adrstm = new StreamImpl();
            Streams.streamSet(adrstm, argGrpRol.getPts().getAdrblk());
            int cnt = Streams.streamCount(adrstm);
            String lin;
            for (int i = 1; i <= cnt; i++) {
                lin = Strings.getLine(adrstm, i);
                if (!MdaUtils.isEmpty(lin)) {
                    nam.append(dlm).append(lin);
                    dlm = ", ";
                }
            }
        }

        return nam.toString();
    }

    /**
     * source:trndoc.0247.script
     */
    public void xmlPrtReplyStart(Map<String, List<String[]>> dataMap, DoceotVo doceotVo) {
        //! print end of reply message
        //这个方法会读取Etauil，数据库中不存在此表，暂时注释
        Etauil etauil = getETAUIL(doceotVo);
        DocUtils.putData(dataMap, "replyReturnTo", "Please return to:");
        xmlPrtEmptyLine(dataMap, 1);
        String own = "";
        if (etauil != null) {
            own = xmlGetTxtSepTxt(own, "", etauil.getLetnam());
            own = xmlGetTxtSepTxt(own, CR, etauil.getLetstr());
            own = xmlGetTxtSepTxt(own, CR, etauil.getLetzip());
            own = xmlGetTxtSepTxt(own, " ", etauil.getLetloc());
        }
        String usr = xmlGetUser();
        DocUtils.putData(dataMap, "replyReceiver", own, usr);
        xmlPrtEmptyLine(dataMap, 4);
    }

    /**
     * source:trndoc.0202.script
     */
    private Etauil getETAUIL(DoceotVo doceotVo) {
        String etaextkey = SettleContext.getUserSession().getUsr().getEtaextkey();
        Eta eta = etaMapper.getEtaByExtkey(etaextkey);
        if (eta != null && StringUtils.isNotEmpty(doceotVo.getDocuil())) {
            return etauilMapper.selectByEtainrAndUil(eta.getInr(), doceotVo.getDocuil());
        }
        return new Etauil();
    }

    /**
     * source:trndoc.0225.script
     */
    private String xmlGetUser() {
        Usr usr = SettleContext.getUserSession().getUsr();
        //! get name, phone, fax and e-mail of current user for documents
        String usrInfo = "Clerk" + ": " + usr.getNam();
        if (!MdaUtils.isEmpty(usr.getTel())) {
            usrInfo = usrInfo + CR + "Phone" + ": " + usr.getTel();
        }
        if (!MdaUtils.isEmpty(usr.getFax())) {
            usrInfo = usrInfo + CR + "Fax" + ": " + usr.getFax();
        }
        if (!MdaUtils.isEmpty(usr.getEml())) {
            usrInfo = usrInfo + CR + "e-mail" + ": " + usr.getEml();
        }
        return usrInfo;
    }

    /**
     * source:trndoc.0212.script
     */
    public void xmlPrintTitleUnderlineFS11(Map<String, List<String[]>> dataMap, DoceotVo doceotVo, String txt) {
        //! prints an underline text with an empty line
        //! most used as titel of letters
        // for Letter
        if (getCurrentCORLineLength(doceotVo) == -1) {
            // the text is underline with font-size: 11
            DocUtils.putData(dataMap, "trndoc.LineUnderlineFS11", txt);
//            DocUtils.putData(dataMap, "trndoc.emptyline");
        } else {
            // for Telex and Swift
            // font-size is 8
            DocUtils.putData(dataMap, "trndoc.LineFS8", "Title: " + txt);
        }
    }

    /**
     * source:trndoc.0210.script
     */
    public void xmlPrtTxtBlk(Map<String, List<String[]>> dataMap, String argNam, String argText) {
        //! prints a textblock with an empty line
        if (!MdaUtils.isEmpty(argText)) {
            DocUtils.putData(dataMap, argNam, argText);
//            xmlPrtEmptyLine(dataMap, 1);
        }
    }

    /**
     * source:trndoc.0089.script
     */
    public <T extends BaseTransactionVo> void xmlPrtAmeFields(Map<String, List<String[]>> dataMap, T transactionVo, List<String> excludeList) {
        //! output of fields
        // output all changed fields of LIDGRP
        List<String> allFldAme = xmlGetAllFldAme(transactionVo);
        // remove exclude fields from modified fields stream
        allFldAme.removeAll(excludeList);
        // 删除了原先的排序逻辑
        if (!allFldAme.isEmpty()) {
            xmlPrtDifIntro(dataMap);
            xmlPrtDifHdr(dataMap);
            xmlPrtFldDifFromStream(dataMap, transactionVo, allFldAme);
        }
    }

    /**
     * source:trndoc.0044.script
     */
    private <T extends BaseTransactionVo> List<String> xmlGetAllFldAme(T transactionVo) {
        // TODO: 2023/6/19
        //  需要根据前端传入的modifySet调整
        Set<String> modifySet = transactionVo.getModifySet();
        // set fields to stream, that have to be removed generally
        // 指定不需要比较的字段
        List<String> excludedFields = Arrays.asList("REF", "PTAINR", "PTYINR", "NAM", "VER", "INR", "FLDMODBLK", "AMEAMT",
                "AMECUR", "AMEBLK", "AMEDAT", "AMENBR", "NARTXT", "NARTXTFLG", "ACTAMENBR", "CONTAG72", "CONTAG79");
        // remove fields listed in excludestream from list of modified fields
        // and replace any modified ADRBLK by its EXTKEY
        List<String> modifyList = new ArrayList<>();
        for (String modify : modifySet) {
            String fieldName = Strings.trim(modify);
            if (!excludedFields.contains(Strings.mid(fieldName, 3))) {
                if (MdaUtils.compareTo(Strings.mid(fieldName, 3), "ADRBLK") == 0) {
                    fieldName = Strings.mid(fieldName, 1, Strings.len(fieldName) - 6) + "EXTKEY";
                }
                modifyList.add(fieldName);
            }
        }
        return modifyList;
    }

    /**
     * source:trndoc.0240.script
     */
    private void xmlPrtDifIntro(Map<String, List<String[]>> dataMap) {
        //! Print sentence of introduction for changed conditions.
        //! Most used by xxTAME + xxTRAM.
        DocUtils.putData(dataMap, "fieldDiff.intro", "Following terms and conditions have been changed:");
        xmlPrtEmptyLine(dataMap, 1);
    }

    /**
     * source:trndoc.0241.script
     */
    private void xmlPrtDifHdr(Map<String, List<String[]>> dataMap) {
        //! Print header for differences (XMLPrtFldDifFromStream).
        DocUtils.putData(dataMap, "fieldDiff.hdr", "", "Old content", "New content");
        xmlPrtEmptyLine(dataMap, 1);
    }

    /**
     * source:trndoc.0045.script
     */
    private <T extends BaseTransactionVo> void xmlPrtFldDifFromStream(Map<String, List<String[]>> dataMap, T transactionVo, List<String> ameFldList) {
        Class<? extends BaseTransactionVo> clazz = transactionVo.getClass();
        for (String property : ameFldList) {
            String[] fieldNames = property.split("\\.");
            Field field;
            try {
                field = clazz.getDeclaredField(fieldNames[0]);
                field.setAccessible(true);
                Object fieldValue = field.get(transactionVo);

                for (int i = 1; i < fieldNames.length; i++) {
                    String fieldName = fieldNames[i];
                    field = fieldValue.getClass().getDeclaredField(fieldName);
                    field.setAccessible(true);
                    fieldValue = field.get(fieldValue);
                }
                // TODO: 2023/6/20
                //  描述x 旧值x 新值√ 对PTS\EXTKEY特殊处理x
                DocUtils.putData(dataMap, "fieldDiff.list", "<empty>", "<empty>", (String) fieldValue);
            } catch (NoSuchFieldException | IllegalAccessException e) {
                DocUtils.putData(dataMap, "fieldDiff.list", "<empty>", "<empty>", "<empty>");
            }
            xmlPrtEmptyLine(dataMap, 1);
        }
    }

    /**
     * source:trndoc.0214.script
     */
    public void xmlDocEnd(Map<String, List<String[]>> dataMap, DoceotVo doceotVo, String argEncInf) {
        //! is used at ordinary letters over all transactions
        // Please note: If properties of following XMLs are changed,
        //              the documentation of messages (gnMsgRul.htm)
        //              also has to be correct!
        // print end of letter only for LETTER and TELEX
        if (getCurrentCORLineLength(doceotVo) == -1 || MdaUtils.compareTo(doceotVo.getCortyp(), "TLX") == 0) {
            // properties for Page-Break of XML "DocEnc" are:
            // - widows = 5
            // - keep with previous
            DocUtils.putData(dataMap, "docEnd", getETAUIL(doceotVo).getLetnam());
            // meaning of EncInf is ...
            //   is empty  = print nothing
            //   "X"       = `Enclosure(s)`
            //   `literal` = `(literal)`
            String endInfo = "";
            if (MdaUtils.compareTo(Strings.toUpper(argEncInf), "X") == 0) {
                endInfo = "Enclosure(s)";
            } else {
                endInfo = argEncInf;
            }
            if (!MdaUtils.isEmpty(endInfo)) {
//                xmlPrtEmptyLine(dataMap, 4);
                // properties for Page-Break of XML "EncInf" are:
                // - widows = 10
                // - keep with previous
                DocUtils.putData(dataMap, "encInf", endInfo);
            }
        }
    }

    /**
     * source:trndoc.0048.script
     */
    public void xmlPrtTxtAboveBlk(Map<String, List<String[]>> dataMap, String argText, String argValue) {
        //! Print any (calling) text (=argText) at 1st line.
        //! Print connected block (=argValue) to next line.
        //! If no argValue is passed, nothing will be returned.
        if (!MdaUtils.isEmpty(argValue)) {
            // Do not output empty argText to avoid an empty line.
            if (!MdaUtils.isEmpty(argText)) {
                // If last sign of argText is an ".", ",", ":", "!" or "?"
                //   do nothing (it seems to be a sentence).
                //   Otherwise add ":".
                String lit = argText;
                String lastSign = Strings.mid(lit, Strings.len(lit), 1);
                switch (lastSign) {
                    case ".":
                    case ",":
                    case ":":
                    case "!":
                    case "?":
                        // passed argText is right
                        break;
                    default:
                        lit = lit + ":";
                }
                DocUtils.putData(dataMap, "lit", lit);
            }
            xmlPrtTxtBlk(dataMap, "aboveTxtBlk", argValue);
        }
    }

    /**
     * source:trndoc.0208.script
     */
    public void printTwoAddresses(Map<String, List<String[]>> dataMap, DoceotVo doceotVo, PtsptaVo ptsptaVo1, PtsptaVo ptsptaVo2) {
        String bicAut1 = doceotService.getPtmField(ptsptaVo1.getPta().getInr(), "SWT", "AUTFLG");
        String bic1;
        if (MdaUtils.compareTo(bicAut1, "Z") != 0) {
            bic1 = ptsptaVo1.getAdr().getBic();
        } else {
            bic1 = "";
        }
        String bicAut2 = doceotService.getPtmField(ptsptaVo2.getPta().getInr(), "SWT", "AUTFLG");
        String bic2;
        if (MdaUtils.compareTo(bicAut2, "Z") != 0) {
            bic2 = ptsptaVo2.getAdr().getBic();
        } else {
            bic2 = "";
        }
        String txt1;
        if (MdaUtils.isEmpty(ptsptaVo1.getPts().getAdrblk())) {
            txt1 = "";
        } else {
            txt1 = codetableService.getTblLabel(ptsptaVo1.getRol(), "rol");
            txt1 = txt1 + ":";
            if (!MdaUtils.isEmpty(bic1)) {
                txt1 = txt1 + " (" + bic1 + ")";
            }
            txt1 = txt1 + CR + ptsptaToolComponent.ptsGetCompressedAddress(ptsptaVo1);
        }
        String txt2;
        if (MdaUtils.isEmpty(ptsptaVo2.getPts().getAdrblk())) {
            txt2 = "";
        } else {
            txt2 = codetableService.getTblLabel(ptsptaVo2.getRol(), "rol");
            txt2 = txt2 + ":";
            if (!MdaUtils.isEmpty(bic2)) {
                txt2 = txt2 + " (" + bic2 + ")";
            }
            txt2 = txt2 + CR + ptsptaToolComponent.ptsGetCompressedAddress(ptsptaVo2);
        }
        IStream stm1 = new StreamImpl();
        Streams.streamSet(stm1, txt1);
        IStream stm2 = new StreamImpl();
        Streams.streamSet(stm2, txt2);
        int stm1cnt = Streams.streamCount(stm1);
        int stm2cnt = Streams.streamCount(stm2);
        int maxcnt;
        String lin1;
        String lin2;
        String lastlin;
        String lin;
        if (getCurrentCORLineLength(doceotVo) == -1) {
            maxcnt = Math.max(stm1cnt, stm2cnt);
            DocUtils.putData(dataMap, "trndoc.OneAdr", "");
            for (int i = 1; i <= maxcnt; i++) {
                if (i > stm1cnt) {
                    lin1 = "";
                } else {
                    lin1 = Strings.getLine(stm1, i);
                }
                if (i > stm2cnt) {
                    lin2 = "";
                } else {
                    lin2 = Strings.getLine(stm2, i);
                }
                if ((!MdaUtils.isEmpty(lin1)) || (!MdaUtils.isEmpty(lin2))) {
                    if (MdaUtils.isEmpty(txt2)) {
                        DocUtils.putData(dataMap, "trndoc.OneAdr", lin1);
                    } else {
                        DocUtils.putData(dataMap, "trndoc.TwoAdr", lin1, lin2);
                    }
                }
            }
        } else {
            // print for Swift/Telex/Fax/email
//            Systems.printTemp();
            DocUtils.putData(dataMap, "", "\r\n");
            lastlin = "";
            for (int i = 1; i <= stm1cnt; i++) {
                lin = Strings.getLine(stm1, i);
                if (!MdaUtils.isEmpty(lin)) {
                    DocUtils.putData(dataMap, "", lin + "\r\n");
                    lastlin = lin;
                }
            }
            //separating empty line
            if (!MdaUtils.isEmpty(lastlin)) {
                DocUtils.putData(dataMap, "", "\r\n");
            }
            for (int i = 1; i <= stm2cnt; i++) {
                lin = Strings.getLine(stm2, i);
                if (!MdaUtils.isEmpty(lin)) {
                    // avoid two consecutive empty lines
                    DocUtils.putData(dataMap, "", lin + "\r\n");
                }
            }
        }
    }

    /**
     * source:trndoc.0239.script
     */
    public String xmlGetTblText(DoceotVo doceotVo, String argKey, String argTable) {
        //! Get text of table at current language of document.
        return codetableService.getTblLabel(argKey, argTable, doceotVo.getDocuil());
    }

    /**
     * source:trndoc.0136.script
     */
    public <T extends AbstractCommonVo> boolean isRmbCancel(T grp, T oldGrp) {
        Object rec = Utils.getFieldValue(grp, "rec");
        String rmbflg = Utils.getFieldValue(rec, "rmbflg");
        PtsptaVo rmb = Utils.getFieldValue(rec, "rmb");
        String ptaInr = rmb.getPtainr();

        Object oldRec = Utils.getFieldValue(oldGrp, "rec");
        String oldRmbflg = Utils.getFieldValue(oldGrp, "rmbflg");
        PtsptaVo oldRmb = Utils.getFieldValue(oldRec, "rmb");
        String oldPtaInr = oldRmb.getPtainr();
        boolean status;
        // Reimbursement authorization is canceled, so no output of Tags 31E,32B,33B,34B,39 and 39c
        status = (!MdaUtils.isEmpty(oldRmbflg) && MdaUtils.isEmpty(rmbflg)) || ((!MdaUtils.isEmpty(oldRmbflg) && !MdaUtils.isEmpty(rmbflg)) && MdaUtils.compareTo(oldPtaInr, ptaInr) != 0);
        return status;
    }

    /**
     * source:trndoc.0248.script
     */
    public void xmlPrtAPPRULRMB(Map<String, List<String[]>> dataMap, String argAPPRULRMB) {
        // prints information about the applicable rules of reimbursement
        switch (Strings.toUpper(argAPPRULRMB)) {
            // according to applicable rule of Reimbursement
            case "URR LATEST VERSION":
                DocUtils.putData(dataMap, "trndoc.RMBURR");
//                xmlPrtEmptyLine(dataMap, 1);
                break;
            case "NOTURR":
                DocUtils.putData(dataMap, "trndoc.NOTURR");
//                xmlPrtEmptyLine(dataMap, 1);
                break;
            default:
                //no other values available
        }
    }

    /**
     * 后端生成面函报文文件的统一入口
     *
     * @param doceotVo
     * @return
     */
    public TextResultVo outputMessage(DoceotVo doceotVo) {
        String transName = SettleContext.getTransName();
        BaseTransactionVo bo = transactionHelpService.getTransactionStoreSet().getTransactionVo();
        ITransactionDocSet transactionDocSet = SpringEnvContext.getBean(atpBchCacheService.getSdTransNameForRegister().toLowerCase() + ".docset");
        log.info("面函信息role:{},docNam:{},corTyp:{}", doceotVo.getRole(), doceotVo.getDocnam(), doceotVo.getCortyp());
        TextResultVo textResultVo = transactionDocSet.outputMessage(bo, doceotVo);
        return textResultVo;
    }

    /**
     * source:trndoc.0005.script
     */
    public String getScore23X(DoceotVo doceotVo) {
        BaseTransactionVo bo = transactionHelpService.getTransactionStoreSet().getTransactionVo();
        List<DoceotVo> doceots = bo.getDoceot();
        for (DoceotVo doceot : doceots) {
            if (doceotVo.getId().equals(doceot.getIdatt())) {
                return "FACT/SCORE" + doceotVo.getSmhinr() + ".zip";
            }
        }
        return null;
    }

    /**
     * source:trndoc.0114.script
     */
    public void fillCONDOCSTM(TrndocVo trndocVo) {
        //! CONDOCSTM with connected documents
        List<Map<String, String>> shwinc = new ArrayList<>();
        List<Map<String, String>> shwout = new ArrayList<>();
        Spt spt = trndocVo.getSpt();
        // Incoming message(s)
        if (StringUtils.isNotEmpty(spt.getSmhinr())) {
            List<Smh> smhList = smhMapper.selectByGrpinr(spt.getSmhinr());
            for (Smh smh : smhList) {
                if ("<".equals(smh.getDir())) {
                    fillCONDOCSTMfromSMH(shwinc, smh);
                }
                if (">".equals(smh.getDir())) {
                    fillCONDOCSTMfromSMH(shwout, smh);
                }
            }
        } else {
            // other incoming messages, read for all contracts in TRNMOD\\TRORESCON
            BizInfoVo rec = trndocVo.getRec();
            String objtyp = rec.getObjtyp();
            String objinr = rec.getObjinr();
            if (!"PTE".equals(objtyp)) {
                if (StringUtils.isNotEmpty(objinr)) {
                    List<Smh> incSmhList = smhMapper.selectByObjtypAndObjinrAndDir(objtyp, objinr, "<");
                    List<Smh> outSmhList = smhMapper.selectByObjtypAndObjinrAndDir(objtyp, objinr, ">");
                    incSmhList.forEach(smh -> {
                        fillCONDOCSTMfromSMHRest(shwinc, smh);
                    });
                    outSmhList.forEach(smh -> {
                        fillCONDOCSTMfromSMHRest(shwout, smh);
                    });
                }
            }
        }
        trndocVo.setShwinc(shwinc);
        trndocVo.setShwout(shwout);
    }




    /**
     * source:trndoc.0066.script
     */
    private void fillCONDOCSTMfromSMH(List<Map<String, String>> mapList, Smh smh) {
        if (!"RIM".equals(smh.getCortyp())) {
            Map<String, String> map = new HashMap<>();
            //! Add SMHs selected by SQL to list of connected documents
            Trn trn = Optional.ofNullable(trnMapper.selectByTrninr(smh.getTrninr())).orElseGet(Trn::new);
            map.put("CORTYP", smh.getCortyp());
            map.put("INIFRM", trn.getInifrm());
            map.put("NAM", smh.getNam());
            map.put("CREDATTIM", DateUtils.dateTime(smh.getCredattim()));
            mapList.add(map);
        }
    }

    /**
     * source:trndoc.0076.script
     */
    private void fillCONDOCSTMfromSMHRest(List<Map<String, String>> mapList, Smh smh) {
        Trn trn = trnMapper.selectByTrninr(smh.getTrninr());
        String relflg = trn.getRelflg();
        //! Add SMHs selected by SQL but wihtou incomin SMH for this TRN and for rejected TRN
        if (!(relflg.equals("P") || relflg.equals("N") || relflg.equals("C") || relflg.equals("M"))) {
            fillCONDOCSTMfromSMH(mapList, smh);
        }
    }

    @Override
    public <T> String getFretxt(DoceotVo doceotVo, T transactionVo) {
        String freTxt = "";
        String OriAddStrTxt = doceotVo.getAddstr();
        FremsgVo fremsg = (FremsgVo)Utils.getFieldValue(transactionVo,"fremsg");
        Spt spt = (Spt)Utils.getFieldValue(transactionVo,"spt");
        String txt = fremsg.getDet();
        String cr = "";
        switch (fremsg.getSndmt()){
            case "92":
                freTxt = Strings.getText("We request you to consider cancellation of SWIFT Message MT $1 from $2.\n\n$3",fremsg.getRelmt(),DateUtils.parseDateToStr("dd.MM.yyyy",fremsg.getReldat()),fremsg.getStrinf(),doceotVo.getDocuil());
                break;
            case "95":
                if (!MdaUtils.isEmpty(fremsg.getTag77a())){
                    txt = xmlGetTxtSepTxt(txt,CR + CR,Strings.getText("Narrative:\n$1",fremsg.getTag77a()));
                 }
               if(!MdaUtils.isEmpty(fremsg.getStrinf())){
                   txt = xmlGetTxtSepTxt(txt,CR + CR,Strings.getText("Further Information:\n$1",fremsg.getStrinf()));
               }
                if(MdaUtils.isEmpty(fremsg.getReldat())){
                    freTxt = Strings.getText("Query to message MT $1.\n\n$2",fremsg.getRelmt(),txt,doceotVo.getDocuil());
                }else{
                    freTxt = Strings.getText("Query to message MT $1 dated $2.\n\n$3",fremsg.getRelmt(),DateUtils.parseDateToStr("dd.MM.yyyy",fremsg.getReldat()),txt,doceotVo.getDocuil());
                }
                break;
            case "96":
                if (!MdaUtils.isEmpty(fremsg.getTag77a())){
                    txt = xmlGetTxtSepTxt(txt,CR + CR,Strings.getText("Narrative:\n$1",fremsg.getTag77a()));
                }
                if(!MdaUtils.isEmpty(fremsg.getStrinf())){
                    txt = xmlGetTxtSepTxt(txt,CR + CR,Strings.getText("Further Information:\n$1",fremsg.getStrinf()));
                }
                if(MdaUtils.isEmpty(fremsg.getReldat())){
                    freTxt = Strings.getText("Answer to Message MT $1.\n\n$2",fremsg.getRelmt(),txt,doceotVo.getDocuil());
                }else{
                    freTxt = Strings.getText("Answer to Message MT $1 dated $2.\n\n$3",fremsg.getRelmt(),DateUtils.parseDateToStr("dd.MM.yyyy",fremsg.getReldat()),txt,doceotVo.getDocuil());
                }
                break;
            case "30":
                freTxt = "";
                freTxt = OriAddStrTxt;
                break;
            case "59":
              freTxt = "";
              if(!Objects.isNull(spt) && sysmodService.sptIsIncLoaded(spt.getSta())){
                  txt = Strings.getText("We herewith provide information in connection with our abovementioned contract:",doceotVo.getDocuil());
              }else{
                  txt = Strings.getText("We herewith request information in connection with our abovementioned contract:",doceotVo.getDocuil());
              }
                if(!MdaUtils.isEmpty(txt)){
                    txt += CR;
                    cr += CR;
                }
                if(!MdaUtils.isEmpty(fremsg.getTag22d())){
                    txt = txt + cr + "Form of Undertaking (:22D:): " + codetableService.getTblLabel(fremsg.getTag22d(),"legfrm",doceotVo.getDocuil());
                    cr += CR;
                }
                if(!MdaUtils.isEmpty(fremsg.getTag23())){
                    txt = txt + cr + "Number of Undertaking (:23:): " + fremsg.getTag23();
                    cr += CR;
                }
                if(!MdaUtils.isEmpty(fremsg.getIssrol())){
                    txt = txt + cr + "Issuer (:52A:): " + CR + fremsg.getIss().getPts().getAdrblk();
                    cr += CR;
                }
                String _23h = codetableService.getTblLabel(fremsg.getTag23h(),"tag23h",doceotVo.getDocuil());
                if(!MdaUtils.isEmpty(_23h)){
                    txt = txt + cr + "Function (:23H:): " + _23h;
                    cr += CR;
                }
               if(!MdaUtils.isEmpty(fremsg.getTag45d())){
                   txt = txt + cr + "Narrative (:45D:): " + fremsg.getTag45d();
                   cr += CR;
               }
               freTxt = txt;
               break;
            default:
                freTxt = "";
                cr = "";
                if(!MdaUtils.isEmpty(fremsg.getStrinf())){
                    freTxt = freTxt + cr + fremsg.getStrinf();
                    cr = CR;
                }
                break;
        }

        return freTxt;
    }

    @Override
    public String getFretxt(DoceotVo doceotVo, FremsgVo fremsg,Spt spt) {
        String freTxt = "";
        String OriAddStrTxt = doceotVo.getAddstr();
//        FremsgVo fremsg = (FremsgVo)Utils.getFieldValue(transactionVo,"fremsg");
//        Spt spt = (Spt)Utils.getFieldValue(transactionVo,"spt");
        String txt = fremsg.getDet();
        String cr = "";
        switch (fremsg.getSndmt()){
            case "92":
                freTxt = Strings.getText("We request you to consider cancellation of SWIFT Message MT $1 from $2.\n\n$3",fremsg.getRelmt(),DateUtils.parseDateToStr("dd.MM.yyyy",fremsg.getReldat()),fremsg.getStrinf(),doceotVo.getDocuil());
                break;
            case "95":
                if (!MdaUtils.isEmpty(fremsg.getTag77a())){
                    txt = xmlGetTxtSepTxt(txt,CR + CR,Strings.getText("Narrative:\n$1",fremsg.getTag77a()));
                }
                if(!MdaUtils.isEmpty(fremsg.getStrinf())){
                    txt = xmlGetTxtSepTxt(txt,CR + CR,Strings.getText("Further Information:\n$1",fremsg.getStrinf()));
                }
                if(MdaUtils.isEmpty(fremsg.getReldat())){
                    freTxt = Strings.getText("Query to message MT $1.\n\n$2",fremsg.getRelmt(),txt,doceotVo.getDocuil());
                }else{
                    freTxt = Strings.getText("Query to message MT $1 dated $2.\n\n$3",fremsg.getRelmt(),DateUtils.parseDateToStr("dd.MM.yyyy",fremsg.getReldat()),txt,doceotVo.getDocuil());
                }
                break;
            case "96":
                if (!MdaUtils.isEmpty(fremsg.getTag77a())){
                    txt = xmlGetTxtSepTxt(txt,CR + CR,Strings.getText("Narrative:\n$1",fremsg.getTag77a()));
                }
                if(!MdaUtils.isEmpty(fremsg.getStrinf())){
                    txt = xmlGetTxtSepTxt(txt,CR + CR,Strings.getText("Further Information:\n$1",fremsg.getStrinf()));
                }
                if(MdaUtils.isEmpty(fremsg.getReldat())){
                    freTxt = Strings.getText("Answer to Message MT $1.\n\n$2",fremsg.getRelmt(),txt,doceotVo.getDocuil());
                }else{
                    freTxt = Strings.getText("Answer to Message MT $1 dated $2.\n\n$3",fremsg.getRelmt(),DateUtils.parseDateToStr("dd.MM.yyyy",fremsg.getReldat()),txt,doceotVo.getDocuil());
                }
                break;
            case "30":
                freTxt = "";
                freTxt = OriAddStrTxt;
                break;
            case "59":
                freTxt = "";
                if(!Objects.isNull(spt) && sysmodService.sptIsIncLoaded(spt.getSta())){
                    txt = Strings.getText("We herewith provide information in connection with our abovementioned contract:",doceotVo.getDocuil());
                }else{
                    txt = Strings.getText("We herewith request information in connection with our abovementioned contract:",doceotVo.getDocuil());
                }
                if(!MdaUtils.isEmpty(txt)){
                    txt += CR;
                    cr += CR;
                }
                if(!MdaUtils.isEmpty(fremsg.getTag22d())){
                    txt = txt + cr + "Form of Undertaking (:22D:): " + codetableService.getTblLabel(fremsg.getTag22d(),"legfrm",doceotVo.getDocuil());
                    cr += CR;
                }
                if(!MdaUtils.isEmpty(fremsg.getTag23())){
                    txt = txt + cr + "Number of Undertaking (:23:): " + fremsg.getTag23();
                    cr += CR;
                }
                if(!MdaUtils.isEmpty(fremsg.getIssrol())){
                    txt = txt + cr + "Issuer (:52A:): " + CR + fremsg.getIss().getPts().getAdrblk();
                    cr += CR;
                }
                String _23h = codetableService.getTblLabel(fremsg.getTag23h(),"tag23h",doceotVo.getDocuil());
                if(!MdaUtils.isEmpty(_23h)){
                    txt = txt + cr + "Function (:23H:): " + _23h;
                    cr += CR;
                }
                if(!MdaUtils.isEmpty(fremsg.getTag45d())){
                    txt = txt + cr + "Narrative (:45D:): " + fremsg.getTag45d();
                    cr += CR;
                }
                freTxt = txt;
                break;
            default:
                freTxt = "";
                cr = "";
                if(!MdaUtils.isEmpty(fremsg.getStrinf())){
                    freTxt = freTxt + cr + fremsg.getStrinf();
                    cr = CR;
                }
                break;
        }

        return freTxt;
    }



    @Override
    public void setDocSendFormat(TrndocVo trndocVo, String id, String cortyp, String docsnf) {
        trndocVo.getDoceot().forEach(doceotVo -> {
            if (cortyp.equals(doceotVo.getCortyp())
                    && StringUtils.isNotEmpty(doceotVo.getId())
                    && doceotVo.getId().trim().equals(id)
            && StringUtils.isEmpty(doceotVo.getDocsnf())) {
                doceotVo.setDocsnf(docsnf);
            }
        });
    }

    @Override
    public void setMt(TrndocVo trndocVo, String id, String cortyp, String mt) {
        trndocVo.getDoceot().forEach(doceotVo -> {
            if (cortyp.equals(doceotVo.getCortyp())
                    && StringUtils.isNotEmpty(doceotVo.getId())
                    && doceotVo.getId().trim().equals(id)) {
                doceotVo.setMt(mt);
            }
        });
    }

    @Override
    public boolean existDocidAndCortyp(List<DoceotVo> doceots, String id, String cortyp) {
        boolean flag = false;
        for (DoceotVo doceotVo : doceots) {
            if (StringUtils.isNotEmpty(doceotVo.getId()) && doceotVo.getId().trim().equals(id)
                    && cortyp.equals(doceotVo.getCortyp())) {
                flag = true;
                break;
            }
        }
        return flag;
    }
}
