import com.gridnine.xtrip.common.model.EntityContainer
import com.gridnine.xtrip.common.model.EntityReference
import com.gridnine.xtrip.common.model.booking.BlankType
import com.gridnine.xtrip.common.model.booking.ProductStatus
import com.gridnine.xtrip.common.model.booking.TicketType
import com.gridnine.xtrip.common.model.booking.TransportationType
import com.gridnine.xtrip.common.model.booking.agencymemo.AgencyMemoProductEntryType
import com.gridnine.xtrip.common.model.booking.agencymemo.AgencyMemoProductItemGroup
import com.gridnine.xtrip.common.model.booking.agencymemo.AgencyMemoProductType
import com.gridnine.xtrip.common.model.booking.air.*
import com.gridnine.xtrip.common.model.dict.*
import com.gridnine.xtrip.common.model.entity.EntityStorage
import com.gridnine.xtrip.common.model.helpers.AirProductHelper
import com.gridnine.xtrip.common.model.helpers.DictHelper
import com.gridnine.xtrip.common.model.profile.*
import com.gridnine.xtrip.common.model.system.CardVendor
import com.gridnine.xtrip.common.model.system.Metadata
import com.gridnine.xtrip.common.model.system.PaymentType
import com.gridnine.xtrip.common.reports.model.AirTicketsTemplateReportSegment
import com.gridnine.xtrip.common.reports.model.AirTicketsTemplateReportSegmentTariff
import com.gridnine.xtrip.common.reports.model.AirTicketsTemplateReportTicket
import com.gridnine.xtrip.common.reports.render.salesreports.SalesReportHelper
import com.gridnine.xtrip.common.reports.render.salesreports.su.base.SuReportConstants
import com.gridnine.xtrip.common.search.SearchCriterion
import com.gridnine.xtrip.common.search.SearchQuery
import com.gridnine.xtrip.common.util.TextUtil

import java.text.DecimalFormat
import java.text.DecimalFormatSymbols
import java.text.SimpleDateFormat
import java.util.stream.Collectors

// Properties
Date periodBeginParameter = parameters['key-report-params']?.periodBegin
Date periodEndParameter = parameters['key-report-params']?.periodEnd
EntityReference<Organization> agencyParameter = parameters['key-report-params']?.agency

final String U6_AGENCY_CODE = 'KEY_U6_AGENCY_CODE'

final Map<MCOCategory, String> mcoCategory2emscCode = new HashMap<MCOCategory, String>() {
    {
        put(MCOCategory.PENALTY, "995")
        put(MCOCategory.RETURN_TICKET, "98D")
        put(MCOCategory.REBOOKING, "98F")
        put(MCOCategory.ADDITIONAL_TARIFF, "98K")
        put(MCOCategory.SEAT_RESERVATION, "0B5")
        put(MCOCategory.SPECIAL_FOOD, "0B3")
        put(MCOCategory.EXCESS_LUGGAGE, "0DG")
        put(MCOCategory.UNESCORTED_MINOR, "0BH")
        put(MCOCategory.STICKER, "98F")
    }
}

final Map<MCOCategory, String> mcoCategory2usageCode = new HashMap<MCOCategory, String>() {
    {
        put(MCOCategory.PTA, "PTA")
        put(MCOCategory.RETURN_FARE_DIFFERENCE, "Справка")
        put(MCOCategory.GROUP_PREPAYMENT, "Прочее")
        put(MCOCategory.PENALTY, "Справка")
        put(MCOCategory.REBOOKING, "Справка")
        put(MCOCategory.STICKER, "Справка")
        put(MCOCategory.ADDITIONAL_TARIFF, "Справка")
        put(MCOCategory.DUPLICATE_TICKET, "Справка")
        put(MCOCategory.INFORMATION, "Справка")
        put(MCOCategory.GROUP_PENALTY, "Прочее")
        put(MCOCategory.SEAT_RESERVATION, "Прочее")
        put(MCOCategory.SPECIAL_FOOD, "Прочее")
        put(MCOCategory.UNESCORTED_MINOR, "Прочее")
        put(MCOCategory.COMFORT_PLUS, "Прочее")
        put(MCOCategory.PAPER_SURCHARGE, "Прочее")
        put(MCOCategory.NOT_SOLD_SEATS, "Штраф за непроданные б/места")
    }
}

def agencyCode = { EntityReference<Organization> reference ->
    Organization organization = EntityStorage.get().resolve(reference)?.entity
    if (organization == null) {
        return null
    }

    for (Metadata metadata : organization.metadata) {
        if ((metadata.getKey() != null) && metadata.key.code
                .equals(U6_AGENCY_CODE)) {
            return (String)metadata.value
        }
    }

    return null
}

final Map<MCOCategory, String> mcoCategory2otherKind = new HashMap<MCOCategory, String>() {
    {
        put(MCOCategory.GROUP_PREPAYMENT, "Оплата групп")
        put(MCOCategory.GROUP_PENALTY, "Штраф за группу")
        put(MCOCategory.UNESCORTED_MINOR, "Дети без сопров")
        put(MCOCategory.COMFORT_PLUS, "Комфорт+")
        put(MCOCategory.PAPER_SURCHARGE, "Сервисный сбор")
    }
}

def sdf(Date d) {
    def format = new SimpleDateFormat('yyyy-MM-dd', Locale.US)
    return String.format(d != null ? format.format(d) : null)
}
String bdf(BigDecimal b) {
    DecimalFormat decimalFormat = new DecimalFormat("#.##", new DecimalFormatSymbols(Locale.ENGLISH))
    return decimalFormat.format(b?: BigDecimal.ZERO)
}

String getOtherKind(AirTicketsTemplateReportTicket ticket,
                    Map<MCOCategory, String> mcoCategory2otherKind) {
    if (ticket.mcoCategory == null) {
        return null
    }
    return mcoCategory2otherKind.get(ticket.mcoCategory)
}

Map<String, List<List<AirTicketsTemplateReportTicket>>> getBatchMap(List<AirTicketsTemplateReportTicket> tickets) {
    Map<String, List<List<AirTicketsTemplateReportTicket>>> batchMap = new LinkedHashMap<>()
    List<AirTicketsTemplateReportTicket> CANCEL = new ArrayList<>()
    List<AirTicketsTemplateReportTicket> REFUND = new ArrayList<>()
    List<AirTicketsTemplateReportTicket> EXC = new ArrayList<>()
    List<AirTicketsTemplateReportTicket> SALE = new ArrayList<>()
    List<AirTicketsTemplateReportTicket> MEMO = new ArrayList<>()

    tickets.forEach() { AirTicketsTemplateReportTicket ticket ->
        if (ProductStatus.SELL == ticket.status) {
            SALE.add(ticket)
        } else if (ProductStatus.EXCHANGE == ticket.status) {
            EXC.add(ticket)
        } else if (ProductStatus.REFUND == ticket.status) {
            REFUND.add(ticket)
        } else if (ProductStatus.VOID == ticket.status) {
            CANCEL.add(ticket)
        } else {
            MEMO.add(ticket)
        }
    }
    batchMap.put(Operation.CANCEL.name(), getSortedByAmountList(CANCEL))
    batchMap.put(Operation.REFUND.name(), getSortedByAmountList(REFUND))
    batchMap.put(Operation.EXC.name(), getSortedByAmountList(EXC))
    batchMap.put(Operation.SALE.name(), getSortedByAmountList(SALE))
    batchMap.put(Operation.MEMO.name(), getSortedByAmountList(MEMO))

    return batchMap
}

List<List<AirTicketsTemplateReportTicket>> getSortedByAmountList(List<AirTicketsTemplateReportTicket> tickets) {
    Map<Integer, List<AirTicketsTemplateReportTicket>> batchTicketList = new HashMap<>()
    final int LIMIT = 50
    int ticketCount = 1
    int batchCount = 0

    List<AirTicketsTemplateReportTicket> ticketList
    tickets.forEach() { AirTicketsTemplateReportTicket ticket ->
        if (ticketCount > LIMIT) {
            ticketCount = 1
            ++batchCount
        }
        ticketList = batchTicketList.get(batchCount)
        if (!ticketList){
            ticketList = new ArrayList<>()
            batchTicketList.put(batchCount, ticketList)
        }
        ticketList.add(ticket)
        ticketCount++
    }
    return batchTicketList.values().stream().collect(Collectors.toList())
}

boolean isVatIncluded(AirTicketsTemplateReportTicket ticket) {
    if ((TransportationType.DOMESTIC == ticket.transportationType)
            || ((ProductCategory.MCO == ticket.productCategory)
            && (MCOCategory.PENALTY == ticket.mcoCategory))
            || (ProductStatus.VOID == ticket.status)) {
        return true
    }
    return false
}

String getTicketElementName (AirTicketsTemplateReportTicket ticket) {
    if(ProductStatus.VOID == ticket.status){
        return "CANCEL"
    }
    switch (ticket.productCategory) {

        case ProductCategory.AIR:
            return "TCT"
        case ProductCategory.MCO:
            if (MCOCategory.RETURN_TICKET == ticket.mcoCategory) {
                return "TCT"
            }
            if (ticket.eticket) {
                return "MCO_EMD"
            }
            return "MCO"
        case ProductCategory.EXCESS_BAGAGE:
            if (ticket.eticket) {
                return "EBT_EMD"
            }
            return "EBT"
        default:
            break
    }
    return null
}
String getTransactionType (AirTicketsTemplateReportTicket ticket, String type) {
    if (Operation.CANCEL.toString() == type){
        return "CANCEL"
    } else if (Operation.REFUND.toString() == type) {
        if ((ProductCategory.MCO == ticket.productCategory)
                && (ProductStatus.SELL == ticket.status)) {
            return "SALE"
        }
        return ProductStatus.REFUND.name()
    } else if (Operation.EXC.toString() == type) {
        if (ProductStatus.EXCHANGE == ticket.status) {
            ticket.status = ProductStatus.SELL
            return "SALE"
        }
        return ProductStatus.REFUND.name()
    } else if (Operation.SALE.toString() == type) {
        return isSticker(ticket) ? "STICKER" : "SALE"
    } else if (Operation.MEMO.toString() == type) {
        return "SALE"
    }
    return null
}

boolean isSticker (AirTicketsTemplateReportTicket ticket) {
    if ((ProductCategory.MCO == ticket.productCategory)
            && ((MCOCategory.REBOOKING == ticket.mcoCategory)
            || (MCOCategory.STICKER == ticket.mcoCategory))
            && ticket.hasRelatedProducts) {
        if ((MCOCategory.STICKER == ticket.mcoCategory)
                && (ProductStatus.SELL == ticket.relatedFirstProductStatus)) {
            return true
        }
    }
    return false
}

String getTDNR (AirTicketsTemplateReportTicket ticket) {
    if (TextUtil.isBlank(ticket.ticketNumber)) {
        if (!ticket.scns.isEmpty()) {
            if (!TextUtil.isBlank(ticket.scns.get(0))) {
                String scn = ticket.scns.first()
                if (scn && scn.length() > 2) {
                    return ticket.blankOwnerNumber + " 05" + scn.substring(2)
                }
            }
        }
    } else {
        return ticket.blankOwnerNumber + " " + ticket.ticketNumber
    }
    return null
}

String getCoupon(AirTicketsTemplateReportTicket ticket) {
    if (isSticker(ticket)) {
        StringBuilder buf = new StringBuilder()
        int couponsCount = 4

        outer:
        for (int n = 1; n <= couponsCount; n++) {
            if (ticket.segmentTariffs != null && !ticket.segmentTariffs.isEmpty()) {
                for (AirTicketsTemplateReportSegmentTariff tariff : ticket.segmentTariffs) {
                    if (tariff.segments != null && !tariff.segments.isEmpty()) {
                        for (AirTicketsTemplateReportSegment segment : tariff.segments) {
                            if (n == segment.recordNumber) {
                                buf.append("F")
                                continue outer
                            }
                        }
                    }
                }
            }
            buf.append("V")
        }

    return buf.toString()
    } else {
        StringBuilder buf = new StringBuilder()
        switch (ticket.status) {
            case ProductStatus.REFUND:
            case ProductStatus.EXCHANGE:
            case ProductStatus.SELL:
                buf.append(getCouponNames(ticket, 0))
                break
            default:
                return buf.toString()
        }
    }
}

String getCouponNames(AirTicketsTemplateReportTicket ticket, int blankIndex) {
    StringBuilder result = new StringBuilder()
    BlankType blankType =
            DictionaryCache.get().resolveReference(ticket.getBlankType())

    int couponsCnt = 0
    if (blankType != null) {
        couponsCnt = blankType.couponsCount
    }
    int segmentMaxNo = couponsCnt * (ticket.conjCount + 1)
    String[] segmentsTab = new String[segmentMaxNo]
    String voidCoupon = " "
    String notVoidCoupon = "R"
    switch (ticket.status) {
        case ProductStatus.SELL:
            voidCoupon = "V"
            notVoidCoupon = "F"
            break
        default:
            break
    }
    for (int i = 0; i < segmentMaxNo; i++) {
        segmentsTab[i] = voidCoupon
    }

    List<AirTicketsTemplateReportSegment> segments = getSegmentsList(ticket)

    for (AirTicketsTemplateReportSegment seg : segments) {
        if ((seg.recordNumber > 0)
                && (seg.recordNumber <= segmentMaxNo)) {
            segmentsTab[seg.recordNumber - 1] = notVoidCoupon
        }
    }

    if (ProductCategory.MCO.equals(ticket.productCategory)
            && ticket.eticket && (couponsCnt == 1) && (segmentMaxNo == 1)
            && !ProductStatus.REFUND.equals(ticket.status)) {
        segmentsTab[0] = "F"
    }

    for (int i = 0; i < (segmentMaxNo - 1); i++) {
        if (segmentsTab[i].equals("V") && segmentsTab[i + 1].equals("F")) {
            segmentsTab[i] = "S"
        }
    }

    for (int i = 0; i < couponsCnt; i++) {
        result.append(segmentsTab[i + (blankIndex * couponsCnt)])
    }
    return result.toString()
}

List<AirTicketsTemplateReportSegment> getSegmentsList (AirTicketsTemplateReportTicket ticket) {
    List<AirTicketsTemplateReportSegment> segments = new ArrayList<>()
    for (AirTicketsTemplateReportSegmentTariff st : ticket.segmentTariffs) {
        for (AirTicketsTemplateReportSegment seg : st.segments) {
            if (seg.departCityCode == null && seg.departureLocation != null) {
                seg.setDepartCityCode(getCityCode (seg.departureLocation.code))
            }
            if (seg.arriveCityCode == null && seg.arrivalLocation != null) {
                seg.setArriveCityCode(
                        getCityCode(seg.getArrivalLocation().getCode()))
            }
            segments.add(seg)
        }
    }
    return segments
}


String getCityCode(final String airportCode) {
    GeoLocation city = DictHelper.findCityByAirport(airportCode)
    return city != null ? city.code : null
}

String getPassengerName (AirTicketsTemplateReportTicket ticket) {
    if (TextUtil.isBlank(ticket.nameInGDS)) {
        return !TextUtil.isBlank(ticket.travellerName) ? ticket.travellerName : null
    }
    return ticket.nameInGDS
}

boolean isZeroMCO (AirTicketsTemplateReportTicket ticket) {
    return (ticket.mcoCategory == null && isSticker(ticket)) ||
            (((MCOCategory.PENALTY == ticket.mcoCategory)
            || (MCOCategory.STICKER == ticket.mcoCategory)
            || (MCOCategory.REBOOKING == ticket.mcoCategory)
            || (MCOCategory.ADDITIONAL_TARIFF == ticket.mcoCategory)
            || ((MCOCategory.UNESCORTED_MINOR == ticket.mcoCategory)
            || ((MCOCategory.INFORMATION == ticket.mcoCategory)
            || (MCOCategory.COMFORT_PLUS == ticket.mcoCategory))))
            && !ticket.eticket)
}

String getTicketBaseFare (AirTicketsTemplateReportTicket ticket) {
    return !isZeroMCO(ticket) && (ticket?.baseFare?.value != null) ? bdf(ticket.baseFare.value).toString() : "0"
}

String getTicketEquivalentFare (AirTicketsTemplateReportTicket ticket) {
    return !isZeroMCO(ticket) && (ticket.equivalentFare != null) ? bdf(ticket.equivalentFare) : "0"
}

def getTicketCurency (AirTicketsTemplateReportTicket ticket) {
    return ticket?.baseFare?.currency ? ticket.baseFare.currency : DictHelper.getLocalCurrency() ?: ""
}

String getTicketBlankOwnerCode(EntityReference<Organization> entityReference) {
    if (entityReference == null) {
        return null
    }
    EntityContainer<Organization> ctr
    try {
        ctr = EntityStorage.get().resolve(entityReference)
    } catch (Exception e) {
        return null
    }
    return (ctr != null) && (ctr.entity.airline != null) ? ctr.entity.airline.code : null
}

def getTicketRate (AirTicketsTemplateReportTicket ticket) {
    if (!isZeroMCO(ticket) && BigDecimal.ZERO.compareTo(ticket.fareRate) != 0) {
        if ((ticket.previousProductStatus != null)
                && ProductStatus.EXCHANGE == ticket.previousProductStatus) {
            return Double.valueOf(ticket.fareRate.doubleValue())
        } else {
            return Double.valueOf(BigDecimal.ZERO.compareTo(ticket.fareRate) != 0
                    ? ticket.fareRate.doubleValue()
                    : 1)
        }
    }
    return Double.valueOf(1)
}

String getCommRate (AirTicketsTemplateReportTicket ticket){
    try {
        return bdf(ticket.vendorCommissionRate ?: BigDecimal.ZERO)
    } catch (IllegalArgumentException iae) {
        return  ""
    }
}

String getCommValue (AirTicketsTemplateReportTicket ticket){
    try {
        commVal = bdf(ticket.vendorCommissionValue ?: BigDecimal.ZERO)
    } catch (IllegalArgumentException iae) {
        commVal = ""
    }
}

String getCouponName(AirTicketsTemplateReportTicket ticket) {
    String result = ""
    switch (ticket.status) {
        case ProductStatus.EXCHANGE:
        case ProductStatus.REFUND:
            result = "R"
            break
        case ProductStatus.SELL:
            result = "F"
            break
        case ProductStatus.VOID:
            result = "V"
            break
        default:
            break
    }
    return result
}

boolean hasPO(AirTicketsTemplateReportTicket ticket){
    if (isSticker(ticket) && hasNeighbourETicketMcoSticker(ticket)) {
        for (Tax t : ticket.taxes) {
            if ("PO".equals(t.code)) {
                return true
            }
        }
    }
    return false
}

boolean hasNeighbourETicketMcoSticker(AirTicketsTemplateReportTicket ticket) {
    return ticket.hasNeighbourETicketMcoSticker;
}

String getAgencyMemoTitle(AgencyMemoProductItemGroup agencyMemoProductItemGroup) {
    if (agencyMemoProductItemGroup
            .type == AgencyMemoProductEntryType.BONUS) {
        return "Бонусные комиссионные"
    } else if (agencyMemoProductItemGroup
            .type == AgencyMemoProductEntryType.COMMISSION) {
        return "Комиссионные"
    } else if ((agencyMemoProductItemGroup
            .type == AgencyMemoProductEntryType.PENALTY)
            || (agencyMemoProductItemGroup
            .type == AgencyMemoProductEntryType.CONTRACT_PENALTY)) {
        return "Штрафы"
    } else if (agencyMemoProductItemGroup
            .type == AgencyMemoProductEntryType.TARIFF) {
        return "Тариф"
    } else if ((agencyMemoProductItemGroup
            .type == AgencyMemoProductEntryType.TAX_CARRIER)
            || (agencyMemoProductItemGroup
            .type == AgencyMemoProductEntryType.TAX_FUEL)
            || (agencyMemoProductItemGroup
            .type == AgencyMemoProductEntryType.TAX_STATE)
            || (agencyMemoProductItemGroup
            .type == AgencyMemoProductEntryType.TAX_OTHERS)) {
        return "Таксы"
    }
    return null
}

String getAgencyMemoItemAmount(AgencyMemoProductItemGroup agencyMemoProductItemGroup) {
    return (agencyMemoProductItemGroup.amount != null) &&
            (agencyMemoProductItemGroup.amount.value != null) ?
            bdf(agencyMemoProductItemGroup.amount.value) : "0"
}

String baseFareMemo(AirTicketsTemplateReportTicket ticket){
    return ticket?.baseFare?.value ? bdf(ticket.baseFare.value) : "0"
}

String getCurrency() {
    return DictHelper.getCurrencyByAnyCode(DictHelper
            .getPreferenceValue(PreferenceKey.EQUIVE_CURRENCY, "RUB"))
}

String getBaseFareCurrency(AirTicketsTemplateReportTicket ticket) {
    return ticket?.baseFare?.currency ?: DictHelper.getLocalCurrency() ?: "RUB"
}

String equivalentFareMemo(AirTicketsTemplateReportTicket ticket){
    return ticket.equivalentFare != null ? bdf(ticket.equivalentFare) : "0"
}

double rateMemo(AirTicketsTemplateReportTicket ticket) {
    return Double.valueOf(BigDecimal.ZERO.compareTo(ticket.fareRate) != 0
            ? ticket.fareRate.doubleValue()
            : 1)
}

String getFromTo(AirTicketsTemplateReportTicket ticket) {
    if (ticket.agencyMemoProductType == AgencyMemoProductType.ACM) {
        return "AG"
    } else if (ticket.agencyMemoProductType == AgencyMemoProductType.ADM) {
        return "AL"
    }
    return null
}

boolean isMcoTicketReturn(AirTicketsTemplateReportTicket ticket){
    return ProductCategory.MCO == ticket.productCategory && MCOCategory.RETURN_TICKET == ticket.mcoCategory
}

boolean isMcoSeatPrereservation(AirTicketsTemplateReportTicket ticket){
    return ProductCategory.MCO == ticket.productCategory && MCOCategory.SEAT_RESERVATION == ticket.mcoCategory
}

String getEmscCode(AirTicketsTemplateReportTicket ticket, Map<MCOCategory, String> mcoCategory2emscCode) {
    if (ticket.mcoCategory == null || !mcoCategory2emscCode.containsKey(ticket.mcoCategory)) {
        return ""
    }
    return mcoCategory2emscCode.get(ticket.mcoCategory)
}

List<AirTicketsTemplateReportSegment> getActiveCoupons(AirTicketsTemplateReportTicket ticket, int conjIndex) {
    List<AirTicketsTemplateReportSegment> activeCoupons = new ArrayList<>()
    int couponsCollected = 0
    int couponCounter = 0

    BlankType blankType =
            DictionaryCache.get().resolveReference(ticket.blankType)
    for (AirTicketsTemplateReportSegment segment : getSegmentsList(ticket)) {
        if (segment.recordNumber <= (blankType.couponsCount * conjIndex)) {
            couponCounter++
            continue
        }
        if (segment.recordNumber > (blankType.couponsCount * (conjIndex + 1))) {
            break
        }
        activeCoupons.add(segment)
        couponsCollected++
        couponCounter++
    }
    return activeCoupons
}

BigDecimal cashValue = null
BigDecimal invoiceValue = null
def getCashValue = { AirTicketsTemplateReportTicket ticket ->
    for (ProductFop fop : ticket.vendorFops) {
        BigDecimal amount = fop.amount?.value ?: BigDecimal.ZERO

        if (PaymentType.CASH == fop.type) {
            if (!cashValue){
                cashValue = BigDecimal.ZERO
            }
            cashValue = cashValue.add(amount)
            continue
        }

        if (PaymentType.INVOICE == fop.type) {
            if (TariffType.BLOCKCHARTER != ticket.tariffType &&
                    TariffType.CHARTER != ticket.tariffType) {
                if (!cashValue){
                    cashValue = BigDecimal.ZERO
                }
                cashValue = cashValue.add(amount)
                fop.setType(PaymentType.CASH)
            } else {
                if (!invoiceValue){
                    invoiceValue = BigDecimal.ZERO
                }
                invoiceValue = invoiceValue.add(amount)
            }
        }
    }
}

String getMainTicketNumber(AirTicketsTemplateReportTicket ticket) {
    String mainTicketNumber = ''
    if (ticket.isHasRelatedProducts()) {
        String cn = ticket.carrierNumberFirstRelatedProduct
        String sn = ticket.systemNumberFirstRelatedProduct
        mainTicketNumber = cn ? cn + " " : ''
        mainTicketNumber = sn ? mainTicketNumber + sn : mainTicketNumber.trim()
    }

    if (TextUtil.isBlank(mainTicketNumber)) {
        String code = ticket.mainProductCode ?: ''
        String number = ticket.mainProductNumber ?: ticket.relatedTicketNumber ?: ''
        mainTicketNumber =  code ? code + " " : ''
        mainTicketNumber =  number ? mainTicketNumber + number : mainTicketNumber.trim()
    }
    return mainTicketNumber
}

int getConjRecordNumber(final int couponsCount, final int recordNumber) {
    if (recordNumber > couponsCount) {
        int recNo = recordNumber % couponsCount
        return recNo == 0 ? couponsCount : recNo
    }
    return recordNumber
}

String getRficCode(final MCOCategory mcoCategory) {
    String code = AirProductHelper.getRficCodeByMcoCategory(mcoCategory)
    return code != null ? code : "D"
}

boolean isBatchCancel(String batchType) {
    if (Operation.CANCEL.name().equals(batchType)) {
        return true
    }
    return false
}

String getUsageCode(AirTicketsTemplateReportTicket ticket,
                    Map<MCOCategory, String> mcoCategory2usageCode) {
    if (ticket.mcoCategory == null) {
        return null
    }
    return mcoCategory2usageCode.get(ticket.mcoCategory)
}

String getRelatedTicketNumber(AirTicketsTemplateReportTicket ticket) {
    return (TextUtil.nonBlankStr(ticket.mainProductCode) + " " + TextUtil.nonBlankStr(ticket.mainProductNumber)).trim()
}

boolean isBlockCharter(AirTicketsTemplateReportTicket ticket) {
    if (TariffType.BLOCKCHARTER.equals(ticket.tariffType)) {
        return true
    }
    if (TariffType.CHARTER.equals(ticket.tariffType)) {
        return true
    }
    return false
}

boolean isGroupTariff(AirTicketsTemplateReportTicket ticket) {
    if (TariffType.GROUP.equals(ticket.tariffType)) {
        return true
    }
    return false
}

def hasPoFop = {AirTicketsTemplateReportTicket ticket ->
    if (isSticker(ticket) && hasNeighbourETicketMcoSticker(ticket)) {
        for (ProductFop fop : ticket.vendorFops) {
            if ((getCashValue(ticket) != null)
                    && (fop.amount != null) && getCashValue(ticket)
                    .equals(fop.amount.value)) {
                return true
            }
        }
    }
    return false
}
String getBlockCharterAgenencyId(AirTicketsTemplateReportTicket ticket){
    return (String) ticket.reportParameters.get(
            SuReportConstants.KEY_BLOCK_CHARTER_CONTRACT_CLIENT_ID) ?: ""
}

String getBlockCharterContractNo(AirTicketsTemplateReportTicket ticket){
    SearchQuery query = new SearchQuery()
    query.getCriteria().getCriterions()
            .add(SearchCriterion.and(
            SearchCriterion.eq(ContractIndex.Property.contractType.name(),
                    ContractType.VENDOR),
            SearchCriterion.eq(ContractIndex.Property.vendor.name(),
                    ticket.getBlankOwner()),
            SearchCriterion.eq(ContractIndex.Property.supplier.name(),
                    ticket.supplier)))
    EntityStorage es = EntityStorage.get()
    EntityContainer<Contract> contract = null
    SUBlockCharterAppendix suBlockCharterAppendix = null
    String fareBasis = null
    Date flightDate = null
    OUTER_CYCLE: for (ContractIndex contractIndex : es.search(ContractIndex.class, query).getData()) {
        contract = es.resolve(contractIndex.getSource())
        for (ContractCustomerInfo contractCustomerInfo : contract
                .getEntity().getCustomers()) {
            if (contractCustomerInfo.getCustomer()
                    .equals(ticket.agency)
                    && ((contractCustomerInfo.getStartDate() == null)
                    || (contractCustomerInfo.getStartDate()
                    .compareTo(ticket.issueDate) <= 0))
                    && ((contractCustomerInfo.getEndDate() == null)
                    || (contractCustomerInfo.getEndDate()
                    .compareTo(ticket.issueDate) >= 0))) {
                for (BaseContractAppendix baseContractAppendix : contractCustomerInfo
                        .getAppendices()) {
                    if (baseContractAppendix instanceof SUBlockCharterAppendix) {
                        suBlockCharterAppendix =
                                (SUBlockCharterAppendix) baseContractAppendix

                        if ((ticket.segmentTariffs != null)
                                && (ticket.segmentTariffs.size() > 0)
                                && (ticket.segmentTariffs.get(0)
                                .segments != null)
                                && (ticket.segmentTariffs.get(0)
                                .segments.size() > 0)
                                && (ticket.segmentTariffs.get(0)
                                .segments.get(0)
                                .getFareBasis() != null)
                                && (ticket.segmentTariffs.get(0)
                                .segments.get(0)
                                .getStartDate() != null)) {
                            fareBasis = ticket.getSegmentTariffs().get(0)
                                    .getSegments().get(0).getFareBasis()
                            flightDate = ticket.getSegmentTariffs().get(0)
                                    .getSegments().get(0).getStartDate()
                        }
                        if ((suBlockCharterAppendix.getFareBasis() != null)
                                && (fareBasis != null)
                                && suBlockCharterAppendix.getFareBasis()
                                .contains(fareBasis)
                                && ((suBlockCharterAppendix
                                .getStartDate() == null)
                                || (suBlockCharterAppendix.getStartDate()
                                .compareTo(flightDate) <= 0))
                                && ((suBlockCharterAppendix
                                .getEndDate() == null)
                                || (suBlockCharterAppendix.getEndDate()
                                .compareTo(flightDate) >= 0))) {
                            result = contractCustomerInfo.getNumber()
                            break OUTER_CYCLE
                        }
                    }
                }
            }
        }
    }
    return result
}

String getRefundedCoupons(AirTicketsTemplateReportTicket ticket) {
    List<AirTicketsTemplateReportSegment> segments = new ArrayList<>()
    for (AirTicketsTemplateReportSegmentTariff st : ticket.segmentTariffs) {
        for (AirTicketsTemplateReportSegment seg : st.getSegments()) {
            if (seg.departCityCode == null && seg.departureLocation != null) {
                seg.setDepartCityCode(
                        getCityCode(seg.departureLocation.code))
            }
            if (seg.arriveCityCode == null && seg.arrivalLocation != null) {
                seg.setArriveCityCode(
                        getCityCode(seg.arrivalLocation.code))
            }
            segments.add(seg)
        }
    }
    int couponsCnt = segments.size()
    char[] coupons = new char[couponsCnt]
    for (AirTicketsTemplateReportSegment seg : segments) {
        if ((seg.recordNumber > 0)
                && (seg.recordNumber <= couponsCnt)) {
            coupons[seg.recordNumber - 1] = 'R'
        }
    }
    return new String(coupons)
}

String getCardVendorCode(CardVendor cardVendor){
    if (cardVendor) {
        switch (cardVendor) {
            case CardVendor.VISA:
                return "VI"
            case CardVendor.MASTERCARD:
                return "MC"
            case CardVendor.AMEX:
                return "AX"
            case CardVendor.JCB:
                return "JB"
            case CardVendor.DINERS_CLUB:
                return "DC"
            case CardVendor.AIR_TRAVEL_CARD:
                return "TP"
            case CardVendor.MIR:
                return "MR"
        }
    }
    return ""
}

boolean isBatchRefund(String type){
    if (Operation.REFUND.toString() == type) {
        return true
    }
    return false
}

xml.pi("xml": ["version":"1.0", "encoding":"utf-8"])
xml.REPORT("xmlns": "http://www.hypersoft.ru/XMLSchema",
           "xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance",
           "xsi:schemaLocation":"http://www.hypersoft.ru/XMLSchemaRPT_PAX_se_v_4_1_1.xsd"
           ) {
    def batchTicketList = getBatchMap(allTickets)
    def agc = agencyCode(agencyParameter)
    AIRLINE(262)
    AGENCY(agc)
    PERIOD_BEGIN(sdf(periodBeginParameter))
    PERIOD_END(sdf(periodEndParameter))
    CATEGORY('PAX')
    CURRENCY(SalesReportHelper.getEquivCurrency(null))
    SUBREPORT {
        int number = 1
        batchTicketList.forEach() { String type, List<List<AirTicketsTemplateReportTicket>> listTickets ->
            if (listTickets) {
                listTickets.forEach() { List<AirTicketsTemplateReportTicket> tickets ->
                    if (tickets) {
                        BATCH('NO': number++) {
                            NAME(type)
                            int count = 1
                            tickets.forEach() { AirTicketsTemplateReportTicket ticket ->
                                String typeName = getTicketElementName(ticket)
                                if (ticket) {
                                    TRANSACTION('NO': count++) {
                                        DAIS(sdf(ticket.issueDate))
                                        AGTN(ticket.validator)
                                        TRANS_TYPE(getTransactionType(ticket, type))
                                        VAT(isVatIncluded(ticket) ? "YES" : "NO")
                                        CURRENCY(SalesReportHelper.getEquivCurrency(null))
                                        E_TICKET(ticket.eticket ? "YES" : "NO")
                                        DOC{
                                            "$typeName" {
                                                TDNR(getTDNR(ticket))
                                                if (type.equals(Operation.MEMO.name())) {
                                                    FROM_TO(getFromTo(ticket))
                                                    TRANSPCATE("PAX")
                                                    AMOUNT{
                                                        SOURCE(baseFareMemo(ticket))
                                                        CUTP(getBaseFareCurrency(ticket))
                                                        RATE(rateMemo(ticket))
                                                        AMNT(equivalentFareMemo(ticket))
                                                        ITEMS{
                                                            int counter = 1
                                                            for (AgencyMemoProductItemGroup agencyMemoProductItemGroup : ticket.agencyMemoProductItemGroup) {
                                                                ITEM('NO':"$counter"){
                                                                    ITEM_TITLE(getAgencyMemoTitle(agencyMemoProductItemGroup))
                                                                    SOURCE(getAgencyMemoItemAmount(agencyMemoProductItemGroup))
                                                                    counter++
                                                                }
                                                            }
                                                        }
                                                    }
                                                } else {
                                                    if (((ticket.productCategory == ProductCategory.MCO)
                                                    && !isMcoTicketReturn(ticket))
                                                    && (ticket.status != ProductStatus.VOID)) {
                                                        if (ticket.eticket) {
                                                            def coupon = getCoupon(ticket)
                                                            if (coupon && !coupon.isEmpty()) {
                                                                CPUI(coupon)
                                                            } else {
                                                                CPUI{}
                                                            }

                                                            PXNM(getPassengerName(ticket))
                                                            FARE {
                                                                AMOUNT {
                                                                    SOURCE(getTicketBaseFare(ticket))
                                                                    CUTP(getBaseFareCurrency(ticket))
                                                                    RATE(getTicketRate(ticket))
                                                                    AMNT(getTicketEquivalentFare(ticket))
                                                                }
                                                                COMMISSION{
                                                                    CORT(getCommRate(ticket))
                                                                    COAM(getCommValue(ticket))
                                                                }
                                                            }
                                                            if (!((ticket.taxes == null) || ticket.taxes.isEmpty()
                                                            || (hasPO(ticket) && (ticket.taxes.size() == 1)))) {
                                                                TAXES{
                                                                    int taxNumber = 0
                                                                    for (Tax tax : ticket.taxes) {
                                                                        if ((tax.equivalentAmount.doubleValue() == 0)
                                                                                || ((tax.code == "PO") && isSticker(ticket)
                                                                                && hasNeighbourETicketMcoSticker(ticket))) {
                                                                            continue
                                                                        }
                                                                        taxNumber++
                                                                        TAX('NO':"$taxNumber") {
                                                                            AMOUNT{
                                                                                SOURCE(bdf(tax.equivalentAmount))
                                                                                CUTP(getCurrency())
                                                                                RATE("1")
                                                                                AMNT(bdf(tax.equivalentAmount))
                                                                            }
                                                                            TMFT(tax.code.toUpperCase())
                                                                        }
                                                                    }
                                                                }
                                                            }
                                                            if (TextUtil.nonBlank(ticket.tourCode) &&
                                                                    (ticket.mcoCategory == MCOCategory.RETURN_TICKET)){
                                                                TOUR(ticket.tourCode)
                                                            }
                                                            List<AirTicketsTemplateReportSegment> segments = getActiveCoupons(ticket, 0)
                                                            int segmentCount = 0

                                                            String baseFare = ticket?.baseFare?.value ? bdf(ticket.baseFare.value) : "0"

                                                            String curr = getBaseFareCurrency(ticket)

                                                            getCashValue(ticket)

                                                            if ((MCOCategory.STICKER == ticket.mcoCategory) && ticket.eticket
                                                            && (cashValue != null)) {
                                                                baseFare = bdf(cashValue)
                                                                curr = getCurrency()
                                                            }
                                                            if (!segments.isEmpty() && !isBatchCancel(type)) {
                                                                COUPONS{
                                                                    for (AirTicketsTemplateReportSegment segment : segments) {
                                                                        segmentCount++
                                                                        COUPON('NO':"$segmentCount"){
                                                                            def couponName = getCouponName(ticket)
                                                                            if (couponName && !couponName.isEmpty()){
                                                                                CPUI(couponName)
                                                                            } else {
                                                                                CPUI{}
                                                                            }
                                                                            CARR(segment.airLineCode.trim())
                                                                            if (!Arrays.asList("995", "98F", "98D", "98E").contains(
                                                                                    getEmscCode(ticket, mcoCategory2emscCode))) {
                                                                                if (segment.departureLocation != null) {
                                                                                    ORAC(DictHelper.getAirportCode(
                                                                                            segment.departureLocation,
                                                                                            CodeSystem.IATA))
                                                                                }
                                                                                if (segment.arrivalLocation != null) {
                                                                                    DSTC(DictHelper.getAirportCode(
                                                                                            segment.arrivalLocation,
                                                                                            CodeSystem.IATA))
                                                                                }
                                                                            }
                                                                            if (segments.size() == 1) {
                                                                                EMCV(baseFare)
                                                                                CUTPC(curr ?:"RUB")
                                                                            }
                                                                            if (!TextUtil.isBlank(getEmscCode(ticket, mcoCategory2emscCode))) {
                                                                                EMSC(getEmscCode(ticket, mcoCategory2emscCode))
                                                                            }
                                                                            EMRT(getMainTicketNumber(ticket))
                                                                            if (!Arrays.asList("995", "98F", "98D", "98E").contains(
                                                                                    getEmscCode(ticket, mcoCategory2emscCode))) {
                                                                                def emrcCode = segment.getEmrcCode()
                                                                                if (!TextUtil.isBlank(emrcCode)) {
                                                                                    EMRC(emrcCode)
                                                                                }
                                                                            }
                                                                            if (MCOCategory.EXCESS_LUGGAGE.equals(ticket.mcoCategory)) {
                                                                                XBOA(LuggageUnits.WEIGHT_KG == ticket.luggageUnits ? "K" : "P")
                                                                                XBRU(bdf(ticket?.baseFare?.value?: BigDecimal.ZERO))
                                                                                XBNE(ticket.luggageWeight)
                                                                            }
                                                                            EMNS("1")
                                                                            FTDA(ticket.issueDate ? sdf(ticket.issueDate) : '')
                                                                            if (!isBatchCancel(type)) {
                                                                                List <String> conjunctions = ticket.conjunctions
                                                                                if (conjunctions.size() > 0) {
                                                                                    CONJUNCTIONS{
                                                                                        for (int conjTicketNo = 1; conjTicketNo <= conjunctions.size(); conjTicketNo++) {
                                                                                            CONJUNCTION_DOC('NO':"$conjTicketNo"){
                                                                                                TDNR(ticket.ticketSeries + " " + conjunctions.get(conjTicketNo - 1))
                                                                                                def getConjunctionScns = ticket.conjunctionScns
                                                                                                BlankType blankType =
                                                                                                        DictionaryCache.get().resolveReference(ticket.blankType)
                                                                                                if (blankType != null) {
                                                                                                    couponsCount = blankType.couponsCount
                                                                                                }
                                                                                                if (getConjunctionScns.size() > 0) {
                                                                                                    SCNR(getConjunctionScns.get(conjTicketNo - 1))
                                                                                                }
                                                                                                def couponNames = getCouponNames(ticket, conjTicketNo)
                                                                                                if (couponNames && !couponNames.isEmpty()) {
                                                                                                    CPUI(couponNames)
                                                                                                } else {
                                                                                                    CPUI{}
                                                                                                }

                                                                                                COUPONS{
                                                                                                    int conjCouponNumber = 0
                                                                                                    for (AirTicketsTemplateReportSegment seg : getActiveCoupons(ticket, conjTicketNo)) {
                                                                                                        conjCouponNumber++
                                                                                                        int num = getConjRecordNumber(blankType.couponsCount, seg.recordNumber)
                                                                                                        COUPON('NO':"$num")
                                                                                                        def couponName1 = getCouponName(ticket)
                                                                                                        if (couponName1 && !couponName1.isEmpty()) {
                                                                                                            CPUI(couponName1)
                                                                                                        } else {
                                                                                                            CPUI{}
                                                                                                        }
                                                                                                        CARR(seg.airLineCode)
                                                                                                        FTNR(seg.flightNo)
                                                                                                        ORAC(DictHelper.getAirportCode(
                                                                                                                seg.departureLocation, CodeSystem.IATA))
                                                                                                        DSTC(DictHelper.getAirportCode(
                                                                                                                seg.arrivalLocation, CodeSystem.IATA))

                                                                                                        if (seg.startDate != null) {
                                                                                                            FTDA(sdf(segment.startDate))
                                                                                                            FTDT(new SimpleDateFormat("HH:mm:ss")
                                                                                                                    .format(segment.startDate))
                                                                                                        }
                                                                                                        FBTD(seg.getFareBasis())
                                                                                                    }
                                                                                                }
                                                                                            }
                                                                                        }
                                                                                    }
                                                                                }
                                                                                RFIC(getRficCode(ticket.mcoCategory))
                                                                                if (!TextUtil.isBlank(ticket.endorsement)) {
                                                                                    ENRS(ticket.endorsement)
                                                                                }
                                                                                if (!TextUtil.isBlank(ticket.fareCalculationData)) {
                                                                                    FRCA(ticket.fareCalculationData)
                                                                                }
                                                                            }
                                                                        }
                                                                    }
                                                                }
                                                            }
                                                        } else {
                                                            def couponNames = getCouponNames(ticket, 0)
                                                            if (couponNames && !couponNames.isEmpty()) {
                                                                CPUI(couponNames)
                                                            } else {
                                                                CPUI{}
                                                            }
                                                            PXNM(getPassengerName(ticket))
                                                            FARE{
                                                                if ((MCOCategory.PENALTY == ticket.mcoCategory)
                                                                ||  (MCOCategory.REBOOKING == ticket.mcoCategory)
                                                                ||  (MCOCategory.STICKER == ticket.mcoCategory)
                                                                ||  (MCOCategory.RETURN_TICKET == ticket.mcoCategory)
                                                                ||  (MCOCategory.ADDITIONAL_TARIFF == ticket.mcoCategory)
                                                                ||  (MCOCategory.DUPLICATE_TICKET == ticket.mcoCategory)
                                                                ||  (MCOCategory.INFORMATION == ticket.mcoCategory)) {
                                                                    if (!isBatchCancel(type)) {
                                                                        boolean zeroMco = isZeroMCO(ticket)
                                                                        String curr = getCurrency()
                                                                        AMOUNT{
                                                                            SOURCE(isSticker(ticket) && !zeroMco && ticket?.baseFare?.value ? bdf(ticket.baseFare.value) : "0")
                                                                            CUTP(isSticker(ticket) && curr ? curr : "")
                                                                            RATE(getTicketRate(ticket))
                                                                            AMNT(getTicketEquivalentFare(ticket))
                                                                        }
                                                                        COMMISSION {
                                                                            CORT(getCommRate(ticket))
                                                                            COAM(getCommValue(ticket))
                                                                        }
                                                                    }
                                                                } else if (ticket.mcoCategory == MCOCategory.GROUP_PENALTY) {
                                                                    if (!isBatchCancel(type)){
                                                                        AMOUNT{
                                                                            SOURCE(ticket?.baseFare?.value ? bdf(ticket.baseFare.value) : "0")
                                                                            CUTP(getCurrency() ?: "")
                                                                            RATE(getTicketRate(ticket))
                                                                            AMNT(getTicketEquivalentFare(ticket))
                                                                        }
                                                                        COMMISSION{
                                                                            CORT("0")
                                                                            COAM(bdf(BigDecimal.ZERO))
                                                                        }
                                                                    }
                                                                }  else {
                                                                    if (isBatchCancel(type)){
                                                                        boolean zeroMco = isZeroMCO(ticket)
                                                                        AMOUNT{
                                                                            SOURCE(!zeroMco && ticket?.baseFare?.value ? bdf(ticket.baseFare.value) : "0")
                                                                            CUTP(getBaseFareCurrency(ticket) ?: "")
                                                                            RATE(!zeroMco && getTicketRate(ticket))
                                                                            AMNT(!zeroMco && getTicketEquivalentFare(ticket))
                                                                        }
                                                                        COMMISSION{
                                                                            CORT(getCommRate(ticket))
                                                                            COAM(getCommValue(ticket))
                                                                        }
                                                                    }
                                                                }
                                                                if (!isBatchCancel(type) && (isMcoTicketReturn(ticket)
                                                                || isMcoSeatPrereservation(ticket)
                                                                || (MCOCategory.GROUP_PENALTY == ticket.mcoCategory)
                                                                || (MCOCategory.GROUP_PREPAYMENT == ticket.mcoCategory)
                                                                || (MCOCategory.UNESCORTED_MINOR == ticket.mcoCategory)
                                                                || (MCOCategory.NOT_SOLD_SEATS == ticket.mcoCategory)
                                                                || (MCOCategory.COMFORT_PLUS == ticket.mcoCategory)
                                                                || (MCOCategory.SPECIAL_FOOD == ticket.mcoCategory)
                                                                || (MCOCategory.PAPER_SURCHARGE == ticket.mcoCategory))) {

                                                                    if (!(ticket.taxes != null || ticket.taxes.isEmpty()
                                                                    || (hasPO(ticket) && ticket.taxes.size() == 1))) {
                                                                        TAXES{
                                                                            int taxNumber = 0
                                                                            for (Tax tax : ticket.taxes) {
                                                                                if ((tax.equivalentAmount.doubleValue() == 0)
                                                                                        || ((tax.code == "PO") && isSticker(ticket)
                                                                                        && hasNeighbourETicketMcoSticker(ticket))) {
                                                                                    continue
                                                                                }
                                                                                taxNumber++
                                                                                TAX('NO':"$taxNumber") {
                                                                                    AMOUNT{
                                                                                        SOURCE(bdf(tax.equivalentAmount))
                                                                                        CUTP(getCurrency())
                                                                                        RATE("1")
                                                                                        AMNT(bdf(tax.equivalentAmount))
                                                                                    }
                                                                                    TMFT(tax.code.toUpperCase())
                                                                                }
                                                                            }
                                                                        }
                                                                    }
                                                                }
                                                            }
                                                            if (ticket.mcoCategory != null) {
                                                                switch (ticket.mcoCategory) {
                                                                    case MCOCategory.PTA:
                                                                    case MCOCategory.GROUP_PREPAYMENT:
                                                                    case MCOCategory.NOT_SOLD_SEATS:
                                                                        if (TextUtil.nonBlank(getUsageCode(ticket, mcoCategory2usageCode))) {
                                                                            USAGECODE(getUsageCode(ticket, mcoCategory2usageCode))
                                                                            if ("FV" != getTicketBlankOwnerCode(ticket.blankOwner)) {
                                                                                String otherKind = getOtherKind(ticket, mcoCategory2otherKind)
                                                                                if (TextUtil.nonBlank(otherKind)) {
                                                                                    OTHER_KIND(otherKind)
                                                                                }
                                                                            }
                                                                        }
                                                                        break
                                                                    case MCOCategory.RETURN_FARE_DIFFERENCE:
                                                                    case MCOCategory.PENALTY:
                                                                    case MCOCategory.REBOOKING:
                                                                    case MCOCategory.STICKER:
                                                                    case MCOCategory.ADDITIONAL_TARIFF:
                                                                    case MCOCategory.DUPLICATE_TICKET:
                                                                    case MCOCategory.INFORMATION:
                                                                        MAIN_DOCS{
                                                                            if (!ticket.isHasRelatedProducts()) {
                                                                                if (TextUtil.nonBlank(ticket.mainProductCode)
                                                                                        ||  TextUtil.nonBlank(ticket.mainProductNumber)) {
                                                                                    MAIN_DOC{
                                                                                        TDNR(getRelatedTicketNumber(ticket))
                                                                                    }
                                                                                }
                                                                            } else {
                                                                                String relatedTicketNumber = (!ticket.isHasRelatedProducts()
                                                                                        || (TicketType.FAKE != ticket.ticketTypeFirstRelatedProduct) ?
                                                                                        ticket.carrierNumberFirstRelatedProduct :
                                                                                        (ticket.carrierNumber + " " + ticket.systemNumberFirstRelatedProduct))
                                                                                MAIN_DOC{
                                                                                    TDNR(relatedTicketNumber)
                                                                                }
                                                                            }
                                                                        }
                                                                        if (TextUtil.nonBlank(getUsageCode(ticket, mcoCategory2usageCode))) {
                                                                            USAGECODE(getUsageCode(ticket, mcoCategory2usageCode))
                                                                            if ("FV" != ticket.agentCode) {
                                                                                String otherKind = getOtherKind(ticket, mcoCategory2otherKind)
                                                                                if (TextUtil.nonBlank(otherKind)) {
                                                                                    OTHER_KIND(otherKind)
                                                                                }
                                                                            }
                                                                        }
                                                                        break
                                                                    case MCOCategory.GROUP_PENALTY:
                                                                    case MCOCategory.SEAT_RESERVATION:
                                                                    case MCOCategory.SPECIAL_FOOD:
                                                                    case MCOCategory.UNESCORTED_MINOR:
                                                                    case MCOCategory.COMFORT_PLUS:
                                                                    case MCOCategory.PAPER_SURCHARGE:
                                                                    case MCOCategory.EXCESS_LUGGAGE:
                                                                    case MCOCategory.ADDITIONAL_TAX:
                                                                    case MCOCategory.POSTAGE_FEE:
                                                                    case MCOCategory.ANIMALS:
                                                                    case MCOCategory.ADDITIONAL_SERVICES:
                                                                        MAIN_DOCS{
                                                                            if (!ticket.isHasRelatedProducts()) {
                                                                                if (TextUtil.nonBlank(ticket.mainProductCode)
                                                                                        ||  TextUtil.nonBlank(ticket.mainProductNumber)) {
                                                                                    MAIN_DOC{
                                                                                        TDNR(getRelatedTicketNumber(ticket))
                                                                                    }
                                                                                }
                                                                            } else {
                                                                                for (Product relProduct : relatedProducts) {
                                                                                    MAIN_DOC{
                                                                                        TDNR(TextUtil.nonBlankStr(relProduct.carrierNumber) +
                                                                                                " " + TextUtil.nonBlankStr(relProduct.systemNumber))
                                                                                    }
                                                                                }
                                                                            }
                                                                        }
                                                                        if (TextUtil.nonBlank(getUsageCode(ticket, mcoCategory2usageCode))) {
                                                                            USAGECODE(getUsageCode(ticket, mcoCategory2usageCode))
                                                                            if ("FV" != ticket.agentCode) {
                                                                                String otherKind = getOtherKind(ticket, mcoCategory2otherKind)
                                                                                if (TextUtil.nonBlank(otherKind)) {
                                                                                    OTHER_KIND(otherKind)
                                                                                }
                                                                            }
                                                                        }
                                                                        break
                                                                    default:
                                                                        break
                                                                }
                                                                if (MCOCategory.GROUP_PREPAYMENT == ticket.mcoCategory) {
                                                                    if (!TextUtil.isBlank(ticket.pnr)) {
                                                                        PNRR(ticket.pnr)
                                                                    }
                                                                }
                                                            }
                                                            if (!TextUtil.isBlank(ticket.tourCode)
                                                                    && (MCOCategory.RETURN_TICKET == ticket.mcoCategory)) {
                                                                TOUR(ticket.tourCode)
                                                            }
                                                            FOP{
                                                                int fopNumber = 0
                                                                if (ticket.vendorFops.isEmpty() || hasPoFop(ticket)) {
                                                                    fopNumber++
                                                                    CASH('NO':"$fopNumber"){
                                                                        FPTP("CA")
                                                                        AMOUNT{
                                                                            SOURCE(bdf(BigDecimal.ZERO))
                                                                            CUTP(DictHelper.getLocalCurrency())
                                                                            RATE("1")
                                                                            AMNT(bdf(BigDecimal.ZERO))
                                                                        }
                                                                    }
                                                                }
                                                                String fopTypeStr = ""
                                                                String fopPaymentTypeStr = ""
                                                                String fopDocNumber = ""
                                                                for (ProductFop fop : ticket.vendorFops) {
                                                                    if (PaymentType.TICKET.equals(fop.type)) {
                                                                        continue
                                                                    }
                                                                    if ((fop.amount == null)
                                                                            || (getCashValue(ticket) != null
                                                                            && getCashValue(ticket).compareTo(fop.amount.value) == 0
                                                                            && isSticker(ticket)
                                                                            && hasNeighbourETicketMcoSticker(ticket))) {
                                                                        continue
                                                                    }
                                                                    if (isBlockCharter(ticket)) {
                                                                        if ((ticket.vendorFops.size() > 1)
                                                                                && (PaymentType.TICKET == fop.type)) {
                                                                            continue
                                                                        }
                                                                    }
                                                                    fopNumber++
                                                                    switch (fop.type) {
                                                                        case PaymentType.CASH:
                                                                            fopTypeStr = "CASH"
                                                                            fopPaymentTypeStr = "CA"
                                                                            break
                                                                        case PaymentType.INVOICE:
                                                                            if ((!TextUtil.isBlank(ticket.endorsement)
                                                                                    && ticket.endorsement.contains("RD"))
                                                                                    || !TextUtil.isBlank(ticket.telexNumber)) {
                                                                                fopTypeStr = "INVOICE"
                                                                                fopPaymentTypeStr = "TX"
                                                                            }
                                                                            break
                                                                        case PaymentType.CREDIT_CARD:
                                                                            fopTypeStr = "CREDIT_CARD"
                                                                            fopPaymentTypeStr = "CC"
                                                                            break
                                                                        case PaymentType.MCO:
                                                                            fopTypeStr = "EXCHANGE"
                                                                            fopPaymentTypeStr = "MC"
                                                                            break
                                                                        case PaymentType.TICKET:
                                                                            fopTypeStr = "CASH"
                                                                            fopPaymentTypeStr = "CA"
                                                                            if (ticket.duplicate) {
                                                                                fopTypeStr = "EXCHANGE"
                                                                                fopPaymentTypeStr = "DL"
                                                                            }
                                                                            break
                                                                        case PaymentType.CREDIT:
                                                                            if ((fop.passengerStatus == null) || ((PassengerStatus.DEP_GD !=
                                                                                    fop.passengerStatus)
                                                                                    && (PassengerStatus.SOV_FED != fop.passengerStatus))) {
                                                                                break
                                                                            }
                                                                        case PaymentType.MTD:
                                                                            fopTypeStr = "INVOICE"
                                                                            fopPaymentTypeStr = "AI"
                                                                            break
                                                                        default:
                                                                            break
                                                                    }
                                                                    if (isBlockCharter(ticket)) {
                                                                        fopTypeStr = "INVOICE"
                                                                        if ((fop.getAmount().getValue() != null)
                                                                                && (fop.getAmount().getValue().doubleValue() != 0)) {
                                                                            fopPaymentTypeStr = "BC"
                                                                        } else {
                                                                            fopPaymentTypeStr = "CH"
                                                                        }
                                                                    }
                                                                    "$fopTypeStr"('NO':"$fopNumber"){
                                                                        FPTP(fopPaymentTypeStr)
                                                                        AMOUNT{
                                                                            SOURCE(bdf(fop.amount.value))
                                                                            CUTP(getTicketCurency(ticket))
                                                                            RATE("1")
                                                                            AMNT(bdf(fop.amount.value))
                                                                        }
                                                                        if (isBlockCharter(ticket)){
                                                                            FPAC(getBlockCharterContractNo(ticket))
                                                                            CLID(getBlockCharterAgenencyId(ticket))
                                                                        } else {
                                                                            switch (fop.type) {
                                                                                case PaymentType.CREDIT_CARD:
                                                                                    if (fop.card != null) {
                                                                                        CardVendor cardVendor = fop.card.vendor

                                                                                        if (cardVendor != null) {
                                                                                            CCCC(getCardVendorCode(fop.card.vendor))
                                                                                        }

                                                                                        if (fop.card.number != null) {
                                                                                            fopDocNumber = fop.card.number
                                                                                        }
                                                                                        FPAC(fopDocNumber)

                                                                                        if (fop.card.expiration != null) {
                                                                                            EXDA(sdf(fop.card.expiration))
                                                                                        }

                                                                                        if (fop.card.securityCode != null) {
                                                                                            APLC(fop.card.securityCode)
                                                                                        }

                                                                                        if (TextUtil.nonBlank(fop.card.nameOnCard)) {
                                                                                            CCHN(fop.card.nameOnCard)
                                                                                        }
                                                                                    }
                                                                                    break
                                                                                case PaymentType.INVOICE:
                                                                                    if (isGroupTariff(ticket)
                                                                                            && TextUtil.nonBlank(ticket.telexNumber)) {
                                                                                        FPAC(ticket.telexNumber)
                                                                                        CLID("*")
                                                                                    }
                                                                                    break
                                                                                case PaymentType.MCO:
                                                                                    String coupons = ""
                                                                                    fopDocNumber = ticket.ticketSeries + " " + fop.relatedTicketNumber
                                                                                    TDNR(fopDocNumber)
                                                                                    if (TextUtil.nonBlank(fopDocNumber)) {
                                                                                        if (fopDocNumber
                                                                                                .matches("[ 0-9]{0,4}401[0-9]{7}")) {
                                                                                            coupons = "R"
                                                                                        }
                                                                                        if (fopDocNumber
                                                                                                .matches("[ 0-9]{0,4}402[0-9]{7}")) {
                                                                                            coupons = "RR"
                                                                                        }
                                                                                    }
                                                                                    if (coupons && !coupons.isEmpty()) {
                                                                                        CPUI(coupons)
                                                                                    } else {
                                                                                        CPUI{}
                                                                                    }
                                                                                    break
                                                                                case PaymentType.TICKET:
                                                                                    if (ticket.duplicate) {
                                                                                        fopDocNumber = ticket.ticketSeries + " " + fop.relatedTicketNumber
                                                                                        TDNR(fopDocNumber)
                                                                                        def couponRef = getRefundedCoupons(ticket)
                                                                                        if (couponRef && !couponRef.isEmpty()){
                                                                                            CPUI(couponRef)
                                                                                        } else {
                                                                                            CPUI{}
                                                                                        }
                                                                                    }
                                                                                    break
                                                                                case  PaymentType.MTD:
                                                                                    String mtdNumber = fop.relatedTicketNumber
                                                                                    String mtdType = ''

                                                                                    FPAC(TextUtil.nonBlank(mtdNumber) ? mtdNumber.trim() : "*")

                                                                                    PassengerStatus passengerStatus = fop.passengerStatus

                                                                                    if (PassengerStatus.VS_MVD == passengerStatus) {
                                                                                        mtdType = "424954"
                                                                                    } else if ((PassengerStatus.DEP_GD == passengerStatus)
                                                                                            || (PassengerStatus.SOV_FED == passengerStatus)) {
                                                                                        mtdType = TextUtil.nonBlank(mtdNumber) ? mtdNumber.trim() : "*"
                                                                                    } else {
                                                                                        mtdType = "*"
                                                                                    }

                                                                                    CLID(mtdType)
                                                                                    break
                                                                                case PaymentType.CREDIT:
                                                                                    PassengerStatus status = fop.passengerStatus
                                                                                    String pType = ''

                                                                                    if (PassengerStatus.DEP_GD == status) {
                                                                                        pType = "GD"
                                                                                        break
                                                                                    } else if (PassengerStatus.SOV_FED == status) {
                                                                                        pType = "SF"
                                                                                        break
                                                                                    } else {
                                                                                        break
                                                                                    }
                                                                                    FPAC(TextUtil.nonBlank(fop.relatedTicketNumber) ? fop.relatedTicketNumber.trim() : "*")
                                                                                    CLID(pType)
                                                                            }
                                                                        }
                                                                    }
                                                                }
                                                                if (ProductStatus.SELL.equals(ticket.status)
                                                                && (ticket.hasPreviousProduct)
                                                                && ProductStatus.EXCHANGE.equals(
                                                                        ticket.previousProductStatus)) {

                                                                    List<String> ticketNumbers = new ArrayList<String>()
                                                                    int couponsMax = 4
                                                                    if (ticket.hasPreviousPreviousProduct) {
                                                                        ticketNumbers.add(ticket.blankOwnerNumberPreviousPreviousProduct + " " + ticket.systemNumberPreviousPreviousProduct)
                                                                        List <String> conjunctions = ticket.conjunctionsPreviousPreviousProduct
                                                                        for (String c : conjunctions) {
                                                                            ticketNumbers
                                                                                    .add(ticket.blankOwnerNumberPreviousPreviousProduct + " " + c)
                                                                        }

                                                                        if (ticket.blankTypePreviousPreviousProduct != null) {
                                                                            BlankType bt = DictionaryCache.get()
                                                                                    .resolveReference(ticket.blankTypePreviousPreviousProduct)
                                                                            couponsMax = bt.couponsCount
                                                                        }
                                                                    } else {
                                                                        ticketNumbers.add(ticket.relatedTicketNumber)
                                                                    }
                                                                    StringBuilder coupons = new StringBuilder("               ")

                                                                    for (Integer recordNumber : ticket.getAllRecorderNumbersSegmentsPreviousProduct()) {
                                                                        if ((recordNumber - 1) < coupons.length()) {
                                                                            coupons.setCharAt(recordNumber - 1, 'R' as char)
                                                                        }
                                                                    }

                                                                    int num = 0
                                                                    for (String ticketNumber : ticketNumbers) {
                                                                        fopNumber++
                                                                        EXCHANGE('NO':"$fopNumber"){
                                                                            FPTP("EX")
                                                                            AMOUNT{
                                                                                SOURCE("0")
                                                                                CUTP(getTicketCurency())
                                                                                RATE("1")
                                                                                AMNT("0")
                                                                            }
                                                                            TDNR(ticketNumber)
                                                                            String couponsString = ''
                                                                            if ((num * couponsMax) < coupons.length()) {
                                                                                int end = ((num + 1) * couponsMax) < coupons.length() ?
                                                                                        (num + 1) * couponsMax :
                                                                                        coupons.length() - 1
                                                                                couponsString = coupons.substring(num * couponsMax, end)
                                                                            }
                                                                            num++
                                                                            def couponsStr = couponsString.replaceAll('\\s+$', '')
                                                                            if (couponsStr && !couponsStr.isEmpty()){
                                                                                CPUI(couponsStr)
                                                                            } else {
                                                                                CPUI{}
                                                                            }
                                                                        }
                                                                    }
                                                                }
                                                            }
                                                        }
                                                    }  else if (ProductCategory.EXCESS_BAGAGE == ticket.productCategory) {
                                                        if (ticket.eticket) {
                                                            def stickerCoupon = isSticker(ticket) ? getCoupon(ticket) : ""
                                                            if (stickerCoupon && !stickerCoupon.isEmpty()){
                                                                CPUI(stickerCoupon)
                                                            } else {
                                                                CPUI{}
                                                            }
                                                            PXNM(getPassengerName(ticket))
                                                            FARE{
                                                                AMOUNT{
                                                                    String curr = getCurrency()
                                                                    SOURCE(getTicketBaseFare(ticket))
                                                                    CUTP(curr ? curr : "RUB")
                                                                    if (!isZeroMCO(ticket) && (getTicketRate(ticket) != null)) {
                                                                        if ((ticket.hasPreviousProduct)
                                                                        && ProductStatus.EXCHANGE.equals(ticket.previousProductStatus)) {
                                                                            RATE(Double.valueOf(getTicketRate(ticket)))
                                                                        } else {
                                                                            RATE(!Double.valueOf(BigDecimal.ZERO).equals(getTicketRate(ticket)) ?
                                                                                    getTicketRate(ticket).doubleValue() : Double.valueOf(1))
                                                                        }
                                                                    } else {
                                                                        RATE(Double.valueOf(1))
                                                                    }
                                                                    AMNT(getTicketEquivalentFare(ticket))
                                                                }
                                                                COMMISSION {
                                                                    CORT(getCommRate(ticket))
                                                                    COAM(getCommValue(ticket))
                                                                }
                                                            }
                                                            TAXES{
                                                                int taxNumber = 0
                                                                for (Tax tax : ticket.taxes) {
                                                                    if ((tax.equivalentAmount.doubleValue() == 0)
                                                                            || ((tax.code == "PO") && isSticker(ticket)
                                                                            && hasNeighbourETicketMcoSticker(ticket))) {
                                                                        continue
                                                                    }
                                                                    taxNumber++
                                                                    TAX('NO':"$taxNumber") {
                                                                        AMOUNT{
                                                                            SOURCE(bdf(tax.equivalentAmount))
                                                                            CUTP(getCurrency())
                                                                            RATE("1")
                                                                            AMNT(bdf(tax.equivalentAmount))
                                                                        }
                                                                        TMFT(tax.code.toUpperCase())
                                                                    }
                                                                }
                                                            }
                                                            List<AirTicketsTemplateReportSegment> segments = getActiveCoupons(ticket, 0)
                                                            Collections.sort(segments, new Comparator<AirTicketsTemplateReportSegment>() {
                                                                @Override
                                                                public int compare(final AirTicketsTemplateReportSegment o1, final AirTicketsTemplateReportSegment o2) {
                                                                    if ((o1 == null) && (o2 == null)) {
                                                                        return 0
                                                                    }
                                                                    if (o1 == null) {
                                                                        return -1
                                                                    } else if (o2 == null) {
                                                                        return 1
                                                                    }
                                                                    return (o1.recordNumber - o2.recordNumber)}
                                                            })
                                                            AirTicketsTemplateReportSegment generalSegment = new AirTicketsTemplateReportSegment()
                                                            generalSegment.setRecordNumber(1)
                                                            for (AirTicketsTemplateReportSegment seg : segments) {
                                                                if (generalSegment.departureLocation == null) {
                                                                    generalSegment
                                                                            .setDepartureLocation(seg.departureLocation)
                                                                }
                                                                generalSegment.setArrivalLocation(seg.getArrivalLocation())
                                                                generalSegment.setAirLine(seg.airLine)
                                                                generalSegment.setFareBasis(seg.getFareBasis())
                                                            }
                                                            if(!isBatchCancel(type)){
                                                                COUPONS{
                                                                    COUPON('NO':"$generalSegment.recordNumber"){
                                                                        def couponName = getCouponName(ticket)
                                                                        if (couponName && !couponName.isEmpty()){
                                                                            CPUI(couponName)
                                                                        } else {
                                                                            CPUI{}
                                                                        }
                                                                        CARR("" + generalSegment.airLineCode.trim())
                                                                        if (generalSegment.departureLocation != null) {
                                                                            ORAC(DictHelper.getAirportCode(
                                                                                    generalSegment.departureLocation, CodeSystem.IATA))
                                                                        }
                                                                        if (generalSegment.arrivalLocation != null) {
                                                                            DSTC(DictHelper.getAirportCode(
                                                                                    generalSegment.arrivalLocation, CodeSystem.IATA))
                                                                        }
                                                                        EMSC(getEmscCode(ticket, mcoCategory2emscCode))
                                                                        EMRT(getMainTicketNumber(ticket))
                                                                        EMRC(generalSegment.recordNumber)
                                                                        XBOA(LuggageUnits.WEIGHT_KG == ticket.luggageUnits ? "K" : "P")
                                                                        XBRU(ticket?.baseFare?.value ?: "")
                                                                        XBNE(ticket.luggageWeight)
                                                                    }
                                                                }
                                                            }
                                                            if (!isBatchCancel(type)) {
                                                                List <String> conjunctions = ticket.conjunctions
                                                                if (conjunctions.size() > 0) {
                                                                    CONJUNCTIONS{
                                                                        for (int conjTicketNo = 1;
                                                                             conjTicketNo <= conjunctions.size(); conjTicketNo++) {

                                                                            CONJUNCTION_DOC('NO':"$conjTicketNo"){
                                                                                TDNR(ticket.ticketSeries + " " + conjunctions.get(conjTicketNo - 1))
                                                                                if (ticket.conjunctions.size() > 0) {
                                                                                    SCNR(ticket.conjunctions.get(conjTicketNo - 1))
                                                                                }
                                                                                def couponName = getCouponNames(ticket, conjTicketNo)
                                                                                if (couponName && !couponName.isEmpty()){
                                                                                    CPUI(couponName)
                                                                                } else {
                                                                                    CPUI{}
                                                                                }
                                                                                COUPONS{
                                                                                    int conjCouponNumber = 0
                                                                                    for (AirTicketsTemplateReportSegment segment : getActiveCoupons(ticket, conjTicketNo)) {
                                                                                        conjCouponNumber++
                                                                                        BlankType blankType =
                                                                                                DictionaryCache.get().resolveReference(ticket.getBlankType())
                                                                                        int conjRecNo = getConjRecordNumber(blankType.couponsCount, segment.recordNumber)
                                                                                        COUPON('NO':"$conjRecNo"){
                                                                                            def couponName1 =getCouponName(ticket)
                                                                                            if (couponName1 && !couponName1.isEmpty()){
                                                                                                CPUI(couponName1)
                                                                                            } else {
                                                                                                CPUI{}
                                                                                            }
                                                                                            CARR(segment.airLineCode)
                                                                                            FTNR(segment.flightNo)
                                                                                            ORAC(DictHelper.getAirportCode(
                                                                                                    segment.departureLocation, CodeSystem.IATA))
                                                                                            DSTC(DictHelper.getAirportCode(
                                                                                                    segment.arrivalLocation, CodeSystem.IATA))
                                                                                            if (segment.startDate != null) {
                                                                                                FTDA(sdf(segment.startDate))
                                                                                                FTDT(new SimpleDateFormat("HH:mm:ss")
                                                                                                                .format(segment.startDate))
                                                                                                FBTD(segment.fareBasis)
                                                                                            }
                                                                                        }
                                                                                    }
                                                                                }
                                                                            }
                                                                        }
                                                                    }
                                                                }
                                                            }
                                                            RFIC("C")
                                                        } else {
                                                            def couponName = getCoupon(ticket)
                                                            if (couponName && !couponName.isEmpty()){
                                                                CPUI(couponName)
                                                            }else {
                                                                CPUI{}
                                                            }
                                                            FARE{
                                                                AMOUNT{
                                                                    String curr = getCurrency()
                                                                    SOURCE(getTicketBaseFare(ticket))
                                                                    CUTP(curr ? curr : "RUB")
                                                                    if (!isZeroMCO(ticket) && (getTicketRate(ticket) != null)) {
                                                                        if ((ticket.hasPreviousProduct)
                                                                        && ProductStatus.EXCHANGE.equals(ticket.previousProductStatus)) {
                                                                            RATE(Double.valueOf(getTicketRate(ticket)))
                                                                        } else {
                                                                            RATE(!Double.valueOf(BigDecimal.ZERO).equals(getTicketRate(ticket)) ?
                                                                                    getTicketRate(ticket).doubleValue() : Double.valueOf(1))
                                                                        }
                                                                    } else {
                                                                        RATE(Double.valueOf(1))
                                                                    }
                                                                    AMNT(getTicketEquivalentFare(ticket))
                                                                }
                                                                COMMISSION {
                                                                    CORT(getCommRate(ticket))
                                                                    COAM(getCommValue(ticket))
                                                                }
                                                            }
                                                            TAXES{
                                                                int taxNumber = 0
                                                                for (Tax tax : ticket.taxes) {
                                                                    if ((tax.equivalentAmount.doubleValue() == 0)
                                                                            || ((tax.code == "PO") && isSticker(ticket)
                                                                            && hasNeighbourETicketMcoSticker(ticket))) {
                                                                        continue
                                                                    }
                                                                    taxNumber++
                                                                    TAX('NO':"$taxNumber") {
                                                                        AMOUNT{
                                                                            SOURCE(bdf(tax.equivalentAmount))
                                                                            CUTP(getCurrency())
                                                                            RATE("1")
                                                                            AMNT(bdf(tax.equivalentAmount))
                                                                        }
                                                                        TMFT(tax.code.toUpperCase())
                                                                    }
                                                                }
                                                            }
                                                            if (TextUtil.nonBlank(getMainTicketNumber(ticket))) {
                                                                MAIN_DOCS{
                                                                    MAIN_DOC{
                                                                        TDNR(getMainTicketNumber(ticket))
                                                                    }
                                                                }
                                                            }
                                                            WEIGHT(ticket.luggageWeight)
                                                            WEIGHT_RATE("0")
                                                            int segmentNo = 0
                                                            List<AirTicketsTemplateReportSegment> segments = getActiveCoupons(ticket, 0)
                                                            if (!segments.isEmpty() && !isBatchCancel(type)) {
                                                                COUPONS{
                                                                    for (AirTicketsTemplateReportSegment segment : segments) {
                                                                        segmentNo++
                                                                        COUPON('NO':"$segmentNo"){
                                                                            CPUI(getCouponName(ticket))
                                                                            CARR(segment.airLineCode.trim())
                                                                            ORAC(DictHelper.getAirportCode(
                                                                                    segment.departureLocation,
                                                                                    CodeSystem.IATA))
                                                                            DSTC(DictHelper.getAirportCode(
                                                                                    segment.arrivalLocation, CodeSystem.IATA))
                                                                            FBTD(segment.fareBasis)
                                                                        }
                                                                    }
                                                                }
                                                            }
                                                            TRANSACTION{
                                                                FOP{
                                                                    int fopNumber = 0
                                                                    if (ticket.vendorFops.isEmpty() || hasPoFop(ticket)) {
                                                                        fopNumber++
                                                                        CASH('NO':"$fopNumber"){
                                                                            FPTP("CA")
                                                                            AMOUNT{
                                                                                SOURCE(bdf(BigDecimal.ZERO))
                                                                                CUTP(getTicketCurency(ticket))
                                                                                RATE("1")
                                                                                AMNT(bdf(BigDecimal.ZERO))
                                                                            }
                                                                        }
                                                                    }
                                                                    String fopTypeStr = ""
                                                                    String fopPaymentTypeStr = ""
                                                                    String fopDocNumber = ""
                                                                    for (ProductFop fop : ticket.vendorFops) {
                                                                        if (PaymentType.TICKET.equals(fop.type)) {
                                                                            continue
                                                                        }
                                                                        if ((fop.amount == null)
                                                                                || (getCashValue(ticket) != null
                                                                                && getCashValue(ticket).compareTo(fop.amount.value) == 0
                                                                                && isSticker(ticket)
                                                                                && hasNeighbourETicketMcoSticker(ticket))) {
                                                                            continue
                                                                        }
                                                                        if (isBlockCharter(ticket)) {
                                                                            if ((ticket.vendorFops.size() > 1)
                                                                                    && (PaymentType.TICKET == fop.type)) {
                                                                                continue
                                                                            }
                                                                        }
                                                                        fopNumber++
                                                                        switch (fop.type) {
                                                                            case PaymentType.CASH:
                                                                                fopTypeStr = "CASH"
                                                                                fopPaymentTypeStr = "CA"
                                                                                break
                                                                            case PaymentType.INVOICE:
                                                                                if ((!TextUtil.isBlank(ticket.endorsement)
                                                                                        && ticket.endorsement.contains("RD"))
                                                                                        || !TextUtil.isBlank(ticket.telexNumber)) {
                                                                                    fopTypeStr = "INVOICE"
                                                                                    fopPaymentTypeStr = "TX"
                                                                                }
                                                                                break
                                                                            case PaymentType.CREDIT_CARD:
                                                                                fopTypeStr = "CREDIT_CARD"
                                                                                fopPaymentTypeStr = "CC"
                                                                                break
                                                                            case PaymentType.MCO:
                                                                                fopTypeStr = "EXCHANGE"
                                                                                fopPaymentTypeStr = "MC"
                                                                                break
                                                                            case PaymentType.TICKET:
                                                                                fopTypeStr = "CASH"
                                                                                fopPaymentTypeStr = "CA"
                                                                                if (ticket.duplicate) {
                                                                                    fopTypeStr = "EXCHANGE"
                                                                                    fopPaymentTypeStr = "DL"
                                                                                }
                                                                                break
                                                                            case PaymentType.CREDIT:
                                                                                if ((fop.passengerStatus == null) || ((PassengerStatus.DEP_GD !=
                                                                                        fop.passengerStatus)
                                                                                        && (PassengerStatus.SOV_FED != fop.passengerStatus))) {
                                                                                    break
                                                                                }
                                                                            case PaymentType.MTD:
                                                                                fopTypeStr = "INVOICE"
                                                                                fopPaymentTypeStr = "AI"
                                                                                break
                                                                            default:
                                                                                break
                                                                        }
                                                                        if (isBlockCharter(ticket)) {
                                                                            fopTypeStr = "INVOICE"
                                                                            if ((fop.getAmount().getValue() != null)
                                                                                    && (fop.getAmount().getValue().doubleValue() != 0)) {
                                                                                fopPaymentTypeStr = "BC"
                                                                            } else {
                                                                                fopPaymentTypeStr = "CH"
                                                                            }
                                                                        }
                                                                        "$fopTypeStr"('NO':"$fopNumber"){
                                                                            FPTP(fopPaymentTypeStr)
                                                                            AMOUNT{
                                                                                SOURCE(bdf(fop.amount.value))
                                                                                CUTP(getTicketCurency(ticket))
                                                                                RATE("1")
                                                                                AMNT(bdf(fop.amount.value))
                                                                            }
                                                                            if (isBlockCharter(ticket)){
                                                                                FPAC(getBlockCharterContractNo(ticket))
                                                                                CLID(getBlockCharterAgenencyId(ticket))
                                                                            } else {
                                                                                switch (fop.type) {
                                                                                    case PaymentType.CREDIT_CARD:
                                                                                        if (fop.card != null) {
                                                                                            CardVendor cardVendor = fop.card.vendor

                                                                                            if (cardVendor != null) {
                                                                                                CCCC(getCardVendorCode(fop.card.vendor))
                                                                                            }

                                                                                            if (fop.card.number != null) {
                                                                                                fopDocNumber = fop.card.number
                                                                                            }
                                                                                            FPAC(fopDocNumber)

                                                                                            if (fop.card.expiration != null) {
                                                                                                EXDA(sdf(fop.card.expiration))
                                                                                            }

                                                                                            if (fop.card.securityCode != null) {
                                                                                                APLC(fop.card.securityCode)
                                                                                            }

                                                                                            if (TextUtil.nonBlank(fop.card.nameOnCard)) {
                                                                                                CCHN(fop.card.nameOnCard)
                                                                                            }
                                                                                        }
                                                                                        break
                                                                                    case PaymentType.INVOICE:
                                                                                        if (isGroupTariff(ticket)
                                                                                                && TextUtil.nonBlank(ticket.telexNumber)) {
                                                                                            FPAC(ticket.telexNumber)
                                                                                            CLID("*")
                                                                                        }
                                                                                        break
                                                                                    case PaymentType.MCO:
                                                                                        String coupons = ""
                                                                                        fopDocNumber = ticket.ticketSeries + " " + fop.relatedTicketNumber
                                                                                        TDNR(fopDocNumber)
                                                                                        if (TextUtil.nonBlank(fopDocNumber)) {
                                                                                            if (fopDocNumber
                                                                                                    .matches("[ 0-9]{0,4}401[0-9]{7}")) {
                                                                                                coupons = "R"
                                                                                            }
                                                                                            if (fopDocNumber
                                                                                                    .matches("[ 0-9]{0,4}402[0-9]{7}")) {
                                                                                                coupons = "RR"
                                                                                            }
                                                                                        }
                                                                                        CPUI(coupons)
                                                                                        break
                                                                                    case PaymentType.TICKET:
                                                                                        if (ticket.duplicate) {
                                                                                            fopDocNumber = ticket.ticketSeries + " " + fop.relatedTicketNumber
                                                                                            TDNR(fopDocNumber)
                                                                                            CPUI(getRefundedCoupons(ticket))
                                                                                        }
                                                                                        break
                                                                                    case  PaymentType.MTD:
                                                                                        String mtdNumber = fop.relatedTicketNumber
                                                                                        String mtdType = ''

                                                                                        FPAC(TextUtil.nonBlank(mtdNumber) ? mtdNumber.trim() : "*")

                                                                                        PassengerStatus passengerStatus = fop.passengerStatus

                                                                                        if (PassengerStatus.VS_MVD == passengerStatus) {
                                                                                            mtdType = "424954"
                                                                                        } else if ((PassengerStatus.DEP_GD == passengerStatus)
                                                                                                || (PassengerStatus.SOV_FED == passengerStatus)) {
                                                                                            mtdType = TextUtil.nonBlank(mtdNumber) ? mtdNumber.trim() : "*"
                                                                                        } else {
                                                                                            mtdType = "*"
                                                                                        }

                                                                                        CLID(mtdType)
                                                                                        break
                                                                                    case PaymentType.CREDIT:
                                                                                        PassengerStatus status = fop.passengerStatus
                                                                                        String pType = ''

                                                                                        if (PassengerStatus.DEP_GD == status) {
                                                                                            pType = "GD"
                                                                                            break
                                                                                        } else if (PassengerStatus.SOV_FED == status) {
                                                                                            pType = "SF"
                                                                                            break
                                                                                        } else {
                                                                                            break
                                                                                        }
                                                                                        FPAC(TextUtil.nonBlank(fop.relatedTicketNumber) ? fop.relatedTicketNumber.trim() : "*")
                                                                                        CLID(pType)
                                                                                }
                                                                            }
                                                                        }
                                                                    }
                                                                    if (ProductStatus.SELL.equals(ticket.status)
                                                                    && (ticket.hasPreviousProduct)
                                                                    && ProductStatus.EXCHANGE.equals(
                                                                            ticket.previousProductStatus)) {

                                                                        List<String> ticketNumbers = new ArrayList<String>()
                                                                        int couponsMax = 4
                                                                        if (ticket.isHasPreviousPreviousProduct()) {
                                                                            ticketNumbers.add(ticket.blankOwnerNumberPreviousPreviousProduct + " " +
                                                                                   ticket.systemNumberPreviousPreviousProduct)
                                                                            List <String> conjunctions = ticket.conjunctionsPreviousPreviousProduct
                                                                            for (String c : conjunctions) {
                                                                                ticketNumbers
                                                                                        .add(ticket.blankOwnerNumberPreviousPreviousProduct + " " + c)
                                                                            }

                                                                            if (ticket.blankTypePreviousPreviousProduct != null) {
                                                                                BlankType bt = DictionaryCache.get()
                                                                                        .resolveReference(ticket.blankTypePreviousPreviousProduct)
                                                                                couponsMax = bt.couponsCount
                                                                            }
                                                                        } else {
                                                                            ticketNumbers.add(ticket.relatedTicketNumber)
                                                                        }
                                                                        StringBuilder coupons = new StringBuilder("               ")

                                                                        for (Integer recordNumber : ticket.getAllRecorderNumbersSegmentsPreviousProduct()) {
                                                                            if ((recordNumber - 1) < coupons.length()) {
                                                                                coupons.setCharAt(recordNumber - 1, 'R' as char)
                                                                            }
                                                                        }

                                                                        int num = 0
                                                                        for (String ticketNumber : ticketNumbers) {
                                                                            fopNumber++
                                                                            EXCHANGE('NO':"$fopNumber"){
                                                                                FPTP("EX")
                                                                                AMOUNT{
                                                                                    SOURCE("0")
                                                                                    CUTP(getTicketCurency())
                                                                                    RATE("1")
                                                                                    AMNT("0")
                                                                                }
                                                                                TDNR(ticketNumber)
                                                                                String couponsString = ''
                                                                                if ((num * couponsMax) < coupons.length()) {
                                                                                    int end = ((num + 1) * couponsMax) < coupons.length() ?
                                                                                            (num + 1) * couponsMax :
                                                                                            coupons.length() - 1
                                                                                    couponsString = coupons.substring(num * couponsMax, end)
                                                                                }
                                                                                num++
                                                                                CPUI(couponsString.replaceAll('\\s+$', ''))
                                                                            }
                                                                        }
                                                                    }
                                                                }
                                                                if (!isBatchCancel(type)) {
                                                                    List <String> conjunctions = ticket.conjunctions
                                                                    if (conjunctions.size() > 0) {
                                                                        CONJUNCTIONS{
                                                                            for (int conjTicketNo = 1; conjTicketNo <= conjunctions.size(); conjTicketNo++) {
                                                                                CONJUNCTION_DOC('NO':"$conjTicketNo"){
                                                                                    TDNR(ticket.ticketSeries + " " + conjunctions.get(conjTicketNo - 1))
                                                                                    def getConjunctionScns = ticket.conjunctionScns
                                                                                    BlankType blankType =
                                                                                            DictionaryCache.get().resolveReference(ticket.blankType)
                                                                                    if (blankType != null) {
                                                                                        couponsCount = blankType.couponsCount
                                                                                    }
                                                                                    if (getConjunctionScns.size() > 0) {
                                                                                        SCNR(getConjunctionScns.get(conjTicketNo - 1))
                                                                                    }
                                                                                    CPUI(getCouponNames(ticket, conjTicketNo))
                                                                                    COUPONS{
                                                                                        int conjCouponNumber = 0
                                                                                        for (AirTicketsTemplateReportSegment seg : getActiveCoupons(ticket, conjTicketNo)) {
                                                                                            conjCouponNumber++
                                                                                            int num = getConjRecordNumber(blankType.couponsCount, seg.recordNumber)
                                                                                            COUPON('NO':"$num")
                                                                                            CPUI(getCouponName(ticket))
                                                                                            CARR(seg.airLineCode)
                                                                                            FTNR(seg.flightNo)
                                                                                            ORAC(DictHelper.getAirportCode(
                                                                                                    seg.departureLocation, CodeSystem.IATA))
                                                                                            DSTC(DictHelper.getAirportCode(
                                                                                                    seg.arrivalLocation, CodeSystem.IATA))

                                                                                            if (seg.startDate != null) {
                                                                                                FTDA(sdf(seg.startDate))
                                                                                                FTDT(new SimpleDateFormat("HH:mm:ss")
                                                                                                        .format(seg.startDate))
                                                                                            }
                                                                                            FBTD(seg.fareBasis)
                                                                                        }
                                                                                    }
                                                                                }
                                                                            }
                                                                        }
                                                                    }
                                                                    XBOA(LuggageUnits.WEIGHT_KG == ticket.luggageUnits ? "K" : "P")
                                                                }
                                                            }
                                                        }
                                                    } else {
                                                        if (!isBatchCancel(type)) {
                                                            CPUI(getCoupon(ticket))
                                                            PXNM(getPassengerName(ticket))
                                                            FARE{
                                                                AMOUNT{
                                                                    String curr = getCurrency()
                                                                    SOURCE(isBatchRefund(type) && getTicketBaseFare(ticket) != "0" ?
                                                                            "-" + getTicketBaseFare(ticket) : getTicketBaseFare(ticket))
                                                                    CUTP(curr ? curr : "RUB")
                                                                    if (!isZeroMCO(ticket) && (getTicketRate(ticket) != null)) {
                                                                        if ((ticket.hasPreviousProduct)
                                                                        && ProductStatus.EXCHANGE
                                                                                .equals(ticket.previousProductStatus)) {
                                                                            RATE(Double.valueOf(getTicketRate(ticket)))
                                                                        } else {
                                                                            RATE(!Double.valueOf(BigDecimal.ZERO).equals(getTicketRate(ticket)) ?
                                                                                    getTicketRate(ticket).doubleValue() : Double.valueOf(1))
                                                                        }
                                                                    } else {
                                                                        RATE(Double.valueOf(1))
                                                                    }
                                                                    AMNT(isBatchRefund(type) && getTicketEquivalentFare(ticket) != "0" ?
                                                                            "-" + getTicketEquivalentFare(ticket) : getTicketEquivalentFare(ticket))
                                                                }
                                                                COMMISSION {
                                                                    CORT(getCommRate(ticket))
                                                                    COAM(isBatchRefund(type) && getCommValue(ticket) != "0" ?
                                                                            "-" + getCommValue(ticket) : getCommValue(ticket))
                                                                }
                                                            }
                                                            if (!ticket.taxes.isEmpty()) {
                                                                TAXES{
                                                                    int taxNumber = 0
                                                                    for (Tax tax : ticket.taxes) {
                                                                        if ((tax.equivalentAmount.doubleValue() == 0)
                                                                                || ((tax.code == "PO") && isSticker(ticket)
                                                                                && hasNeighbourETicketMcoSticker(ticket))) {
                                                                            continue
                                                                        }
                                                                        taxNumber++
                                                                        TAX('NO':"$taxNumber") {
                                                                            AMOUNT{
                                                                                SOURCE(isBatchRefund(type) && tax.equivalentAmount != BigDecimal.ZERO ?
                                                                                        "-" + bdf(tax.equivalentAmount) : bdf(tax.equivalentAmount))
                                                                                CUTP(getTicketCurency(ticket))
                                                                                RATE("1")
                                                                                AMNT(isBatchRefund(type) && tax.equivalentAmount != BigDecimal.ZERO ?
                                                                                        "-" + bdf(tax.equivalentAmount) : bdf(tax.equivalentAmount))
                                                                            }
                                                                            TMFT(tax.code.toUpperCase())
                                                                        }
                                                                    }
                                                                    if (ticket.penalty && ticket.penalty != BigDecimal.ZERO) {
                                                                        taxNumber++
                                                                        TAX('NO':"$taxNumber") {
                                                                            AMOUNT{
                                                                                SOURCE(ticket.penalty)
                                                                                CUTP(getTicketCurency(ticket))
                                                                                RATE("1")
                                                                                AMNT(ticket.penalty)
                                                                            }
                                                                            TMFT("CP")
                                                                        }
                                                                    }
                                                                }
                                                            }
                                                            boolean u6Exchange = false
                                                            if (ticket.hasPreviousProduct
                                                            && ProductStatus.EXCHANGE == ticket.previousProductStatus) {
                                                                u6Exchange = true
                                                            }
                                                            if (!u6Exchange && Operation.EXC.toString() != type) {
                                                                IFEXCNG("НЕТ")
                                                            } else if (Operation.EXC.toString() == type){
                                                                IFEXCNG("ДА")
                                                            }

                                                            PNRR(!TextUtil.isBlank(ticket.reservationRecordLocatorLatin)
                                                                    ? ticket.reservationRecordLocatorLatin : ticket.pnr)
                                                        }
                                                        if (!TextUtil.isBlank(ticket.tourCode)) {
                                                            TOUR(ticket.tourCode)
                                                        }
                                                        if (!isBatchCancel(type)){
                                                            COUPONS{
                                                                List<AirTicketsTemplateReportSegment> segments = getActiveCoupons(ticket, 0)
                                                                int segmentNo = 0
                                                                if (!segments.isEmpty()) {
                                                                    for (AirTicketsTemplateReportSegment segment : segments) {
                                                                        segmentNo++
                                                                        COUPON('NO':"$segmentNo"){
                                                                            def coouponName = getCouponName(ticket)
                                                                            if (coouponName && !coouponName.isEmpty()) {
                                                                                CPUI(coouponName)
                                                                            } else {
                                                                                CPUI{}
                                                                            }
                                                                            CARR(segment.airLineCode.trim())
                                                                            FTNR(segment.flightNo)
                                                                            ORAC(DictHelper.getAirportCode(
                                                                                    segment.departureLocation,
                                                                                    CodeSystem.IATA))
                                                                            DSTC(DictHelper.getAirportCode(
                                                                                    segment.arrivalLocation, CodeSystem.IATA))
                                                                            if (segment.startDate != null) {
                                                                                FTDA(sdf(segment.startDate))
                                                                                FTDT(new SimpleDateFormat("HH:mm:ss")
                                                                                        .format(segment.startDate))
                                                                            }
                                                                            FBTD(segment.fareBasis)
                                                                            RBKD(segment.classOfService)
                                                                        }
                                                                    }
                                                                }
                                                            }
                                                        }
                                                        if (!isBatchCancel(type)) {
                                                            List <String> conjunctions = ticket.conjunctions
                                                            if (conjunctions.size() > 0) {
                                                                CONJUNCTIONS{
                                                                    for (int conjTicketNo = 1; conjTicketNo <= conjunctions.size(); conjTicketNo++) {
                                                                        CONJUNCTION_DOC('NO':"$conjTicketNo"){
                                                                            TDNR(ticket.ticketSeries + " " + conjunctions.get(conjTicketNo - 1))
                                                                            def getConjunctionScns = ticket.getConjunctionScns()
                                                                            BlankType blankType =
                                                                                    DictionaryCache.get().resolveReference(ticket.blankType)
                                                                            if (blankType != null) {
                                                                                couponsCount = blankType.couponsCount
                                                                            }
                                                                            if (getConjunctionScns.size() > 0) {
                                                                                SCNR(getConjunctionScns.get(conjTicketNo - 1))
                                                                            }
                                                                            def coouponName1 = getCouponNames(ticket, conjTicketNo)
                                                                            if (coouponName1 && !coouponName1.isEmpty()) {
                                                                                CPUI(coouponName1)
                                                                            } else {
                                                                                CPUI{}
                                                                            }
                                                                            COUPONS{
                                                                                int conjCouponNumber = 0
                                                                                for (AirTicketsTemplateReportSegment seg : getActiveCoupons(ticket, conjTicketNo)) {
                                                                                    conjCouponNumber++
                                                                                    int num = getConjRecordNumber(blankType.couponsCount, seg.recordNumber)
                                                                                    COUPON('NO':"$num"){
                                                                                        def coouponName = getCouponName(ticket)
                                                                                        if (coouponName && !coouponName.isEmpty()) {
                                                                                            CPUI(coouponName)
                                                                                        } else {
                                                                                            CPUI{}
                                                                                        }
                                                                                        CARR(seg.airLineCode)
                                                                                        FTNR(seg.flightNo)
                                                                                        ORAC(DictHelper.getAirportCode(
                                                                                                seg.departureLocation, CodeSystem.IATA))
                                                                                        DSTC(DictHelper.getAirportCode(
                                                                                                seg.arrivalLocation, CodeSystem.IATA))

                                                                                        if (seg.startDate != null) {
                                                                                            FTDA(sdf(seg.startDate))
                                                                                            FTDT(new SimpleDateFormat("HH:mm:ss")
                                                                                                    .format(seg.startDate))
                                                                                        }
                                                                                        FBTD(seg.fareBasis)
                                                                                        RBKD(seg.classOfService)
                                                                                    }
                                                                                }
                                                                            }
                                                                        }
                                                                    }
                                                                }
                                                            }
                                                            if (ticket.endorsement) {
                                                                ENRS(ticket.endorsement)
                                                            } else {
                                                                ENRS{}
                                                            }
                                                            if (ticket.fareCalculationData) {
                                                                FRCA(ticket.fareCalculationData)
                                                            } else {
                                                                FRCA{}
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                        if (!isBatchCancel(type)) {
                                            FOP {
                                                int fopNumber = 0
                                                if (ticket.vendorFops.isEmpty() || hasPoFop(ticket)) {
                                                    fopNumber++
                                                    CASH('NO': "$fopNumber") {
                                                        FPTP("CA")
                                                        AMOUNT {
                                                            SOURCE(bdf(BigDecimal.ZERO))
                                                            CUTP(getTicketCurency(ticket))
                                                            RATE("1")
                                                            AMNT(bdf(BigDecimal.ZERO))
                                                        }
                                                    }
                                                }
                                                String fopTypeStr = ""
                                                String fopPaymentTypeStr = ""
                                                String fopDocNumber = ""
                                                for (ProductFop fop : ticket.vendorFops) {
                                                    if (PaymentType.TICKET.equals(fop.type)) {
                                                        continue
                                                    }
                                                    if ((fop.amount == null)
                                                            || (getCashValue(ticket) != null
                                                            && getCashValue(ticket).compareTo(fop.amount.value) == 0
                                                            && isSticker(ticket)
                                                            && hasNeighbourETicketMcoSticker(ticket))) {
                                                        continue
                                                    }
                                                    if (isBlockCharter(ticket)) {
                                                        if ((ticket.vendorFops.size() > 1)
                                                                && (PaymentType.TICKET == fop.type)) {
                                                            continue
                                                        }
                                                    }
                                                    fopNumber++
                                                    switch (fop.type) {
                                                        case PaymentType.CASH:
                                                            fopTypeStr = "CASH"
                                                            fopPaymentTypeStr = "CA"
                                                            break
                                                        case PaymentType.INVOICE:
                                                            if ((!TextUtil.isBlank(ticket.endorsement)
                                                                    && ticket.endorsement.contains("RD"))
                                                                    || !TextUtil.isBlank(ticket.telexNumber)) {
                                                                fopTypeStr = "INVOICE"
                                                                fopPaymentTypeStr = "TX"
                                                            }
                                                            break
                                                        case PaymentType.CREDIT_CARD:
                                                            fopTypeStr = "CREDIT_CARD"
                                                            fopPaymentTypeStr = "CC"
                                                            break
                                                        case PaymentType.MCO:
                                                            fopTypeStr = "EXCHANGE"
                                                            fopPaymentTypeStr = "MC"
                                                            break
                                                        case PaymentType.TICKET:
                                                            fopTypeStr = "CASH"
                                                            fopPaymentTypeStr = "CA"
                                                            if (ticket.duplicate) {
                                                                fopTypeStr = "EXCHANGE"
                                                                fopPaymentTypeStr = "U6".equals(
                                                                        getTicketBlankOwnerCode(ticket.blankOwner)) ? "DL" : "DU"
                                                            }
                                                            break
                                                        case PaymentType.CREDIT:
                                                            if ((fop.passengerStatus == null) || ((PassengerStatus.DEP_GD !=
                                                                    fop.passengerStatus)
                                                                    && (PassengerStatus.SOV_FED != fop.passengerStatus))) {
                                                                break
                                                            }
                                                        case PaymentType.MTD:
                                                            fopTypeStr = "INVOICE"
                                                            fopPaymentTypeStr = "AI"
                                                            break
                                                        default:
                                                            break
                                                    }
                                                    if (isBlockCharter(ticket)) {
                                                        fopTypeStr = "INVOICE"
                                                        if ((fop.getAmount().getValue() != null)
                                                                && (fop.getAmount().getValue().doubleValue() != 0)) {
                                                            fopPaymentTypeStr = "BC"
                                                        } else {
                                                            fopPaymentTypeStr = "CH"
                                                        }
                                                    }
                                                    "$fopTypeStr"('NO': "$fopNumber") {
                                                        FPTP(fopPaymentTypeStr)
                                                        AMOUNT {
                                                            SOURCE(isBatchRefund(type) && bdf(fop.amount.value) != "0" ?
                                                                    "-" + bdf(fop.amount.value) : bdf(fop.amount.value))
                                                            CUTP(getTicketCurency(ticket))
                                                            RATE("1")
                                                            AMNT(isBatchRefund(type) && bdf(fop.amount.value) != "0" ?
                                                                    "-" + bdf(fop.amount.value) : bdf(fop.amount.value))
                                                        }
                                                        if (isBlockCharter(ticket)) {
                                                            FPAC(getBlockCharterContractNo(ticket))
                                                            CLID(getBlockCharterAgenencyId(ticket))
                                                        } else {
                                                            switch (fop.type) {
                                                                case PaymentType.CREDIT_CARD:
                                                                    if (fop.card != null) {
                                                                        CardVendor cardVendor = fop.card.vendor

                                                                        if (cardVendor != null) {
                                                                            CCCC(getCardVendorCode(fop.card.vendor))
                                                                        }

                                                                        if (fop.card.number != null) {
                                                                            fopDocNumber = fop.card.number
                                                                        }
                                                                        FPAC(fopDocNumber)

                                                                        if (fop.card.expiration != null) {
                                                                            EXDA(sdf(fop.card.expiration))
                                                                        }

                                                                        if (fop.card.securityCode != null) {
                                                                            APLC(fop.card.securityCode)
                                                                        }

                                                                        if (TextUtil.nonBlank(fop.card.nameOnCard)) {
                                                                            CCHN(fop.card.nameOnCard)
                                                                        }
                                                                    }
                                                                    break
                                                                case PaymentType.INVOICE:
                                                                    if (isGroupTariff(ticket)
                                                                            && TextUtil.nonBlank(ticket.telexNumber)) {
                                                                        FPAC(ticket.telexNumber)
                                                                        CLID("*")
                                                                    }
                                                                    break
                                                                case PaymentType.MCO:
                                                                    String coupons = ""
                                                                    fopDocNumber = ticket.ticketSeries + " " + fop.relatedTicketNumber
                                                                    TDNR(fopDocNumber)
                                                                    if (TextUtil.nonBlank(fopDocNumber)) {
                                                                        if (fopDocNumber
                                                                                .matches("[ 0-9]{0,4}401[0-9]{7}")) {
                                                                            coupons = "R"
                                                                        }
                                                                        if (fopDocNumber
                                                                                .matches("[ 0-9]{0,4}402[0-9]{7}")) {
                                                                            coupons = "RR"
                                                                        }
                                                                    }
                                                                    if (coupons && !coupons.isEmpty()) {
                                                                        CPUI(coupons)
                                                                    } else {
                                                                        CPUI{}
                                                                    }
                                                                    break
                                                                case PaymentType.TICKET:
                                                                    if (ticket.duplicate) {
                                                                        fopDocNumber = ticket.ticketSeries + " " + fop.relatedTicketNumber
                                                                        TDNR(fopDocNumber)
                                                                        def coouponName = getRefundedCoupons(ticket)
                                                                        if (coouponName && !coouponName.isEmpty()) {
                                                                            CPUI(coouponName)
                                                                        } else {
                                                                            CPUI{}
                                                                        }
                                                                    }
                                                                    break
                                                                case PaymentType.MTD:
                                                                    String mtdNumber = fop.relatedTicketNumber

                                                                    FPAC(TextUtil.nonBlank(mtdNumber) ? mtdNumber.trim() : "*")

                                                                    PassengerStatus passengerStatus = fop.passengerStatus

                                                                    if (PassengerStatus.VS_MVD == passengerStatus) {
                                                                        mtdType = "424954"
                                                                    } else if ((PassengerStatus.DEP_GD == passengerStatus)
                                                                            || (PassengerStatus.SOV_FED == passengerStatus)) {
                                                                        mtdType = TextUtil.nonBlank(mtdNumber) ? mtdNumber.trim() : "*"
                                                                    } else {
                                                                        mtdType = "*"
                                                                    }

                                                                    CLID(mtdType)
                                                                    break
                                                                case PaymentType.CREDIT:
                                                                    PassengerStatus status = fop.passengerStatus
                                                                    String pType = ''

                                                                    if (PassengerStatus.DEP_GD == status) {
                                                                        pType = "GD"
                                                                        break
                                                                    } else if (PassengerStatus.SOV_FED == status) {
                                                                        pType = "SF"
                                                                        break
                                                                    } else {
                                                                        break
                                                                    }
                                                                    FPAC(TextUtil.nonBlank(fop.relatedTicketNumber) ? fop.relatedTicketNumber.trim() : "*")
                                                                    CLID(pType)
                                                            }
                                                        }
                                                    }
                                                }
                                                if (ProductStatus.SELL.equals(ticket.status)
                                                        && (ticket.hasPreviousProduct)
                                                        && ProductStatus.EXCHANGE.equals(
                                                        ticket.previousProductStatus)) {

                                                    List<String> ticketNumbers = new ArrayList<String>()
                                                    int couponsMax = 4
                                                    if (ticket.hasPreviousPreviousProduct) {
                                                        ticketNumbers.add(ticket.blankOwnerNumberPreviousPreviousProduct + " " +
                                                                ticket.systemNumberPreviousPreviousProduct)
                                                        List <String> conjunctions = ticket.conjunctionsPreviousPreviousProduct
                                                        for (String c : conjunctions) {
                                                            ticketNumbers
                                                                    .add(ticket.blankOwnerNumberPreviousPreviousProduct + " " + c)
                                                        }

                                                        if (ticket.blankTypePreviousPreviousProduct != null) {
                                                            BlankType bt = DictionaryCache.get()
                                                                    .resolveReference(ticket.blankTypePreviousPreviousProduct)
                                                            couponsMax = bt.couponsCount
                                                        }
                                                    } else {
                                                        ticketNumbers.add(ticket.relatedTicketNumber)
                                                    }
                                                    StringBuilder coupons = new StringBuilder("               ")

                                                    for (Integer recordNumber : ticket.getAllRecorderNumbersSegmentsPreviousProduct()) {
                                                        if ((recordNumber - 1) < coupons.length()) {
                                                            coupons.setCharAt(recordNumber - 1, 'R' as char)
                                                        }
                                                    }

                                                    int num = 0
                                                    for (String ticketNumber : ticketNumbers) {
                                                        fopNumber++
                                                        EXCHANGE('NO': "$fopNumber") {
                                                            FPTP("EX")
                                                            AMOUNT {
                                                                SOURCE("0")
                                                                CUTP(getTicketCurency())
                                                                RATE("1")
                                                                AMNT("0")
                                                            }
                                                            TDNR(ticketNumber)
                                                            String couponsString = ''
                                                            if ((num * couponsMax) < coupons.length()) {
                                                                int end = ((num + 1) * couponsMax) < coupons.length() ?
                                                                        (num + 1) * couponsMax :
                                                                        coupons.length() - 1
                                                                couponsString = coupons.substring(num * couponsMax, end)
                                                            }
                                                            num++
                                                            def couponName = couponsString.replaceAll('\\s+$', '')
                                                            if (couponName && !couponName.isEmpty()) {
                                                                CPUI(couponName)
                                                            } else {
                                                                CPUI{}
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

enum Operation {
    CANCEL,
    REFUND,
    EXC,
    SALE,
    MEMO
}